mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-22 18:32:16 +00:00
refactor: collapse speed-dial action source abstraction
Only one action source ever existed, so the IActionSource interface and ScriptActionSource are gone. ActionRegistry now subscribes to the plugin loader and enumerates actions directly in init() - no polymorphism for one impl. Replace the opaque FNV-hash SpeedDialActionId with a readable composed id of the form prefix:title:source_key. Split AppAction's single source field into source_key (stable identity, e.g. plugin_key) and source_name (display), so identity and display no longer share one field.
This commit is contained in:
@@ -8,7 +8,6 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_plugin_lifecycle.cpp
|
||||
test_slicing_pipeline_bindings.cpp
|
||||
test_plugin_sort.cpp
|
||||
test_speed_dial_action_id.cpp
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "slic3r/GUI/ActionRegistry.hpp"
|
||||
#include "slic3r/GUI/IActionSource.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -10,45 +9,42 @@
|
||||
using Slic3r::GUI::AppAction;
|
||||
using Slic3r::GUI::AppActionRunResult;
|
||||
using Slic3r::GUI::ActionRegistry;
|
||||
using Slic3r::GUI::IActionSource;
|
||||
|
||||
namespace {
|
||||
|
||||
class RecordingActionSource final : public IActionSource
|
||||
{
|
||||
public:
|
||||
explicit RecordingActionSource(bool& started) : m_started(started) {}
|
||||
|
||||
void start(ActionRegistry&) override { m_started = true; }
|
||||
|
||||
private:
|
||||
bool& m_started;
|
||||
};
|
||||
|
||||
// AppAction is abstract; this minimal concrete action lets the tests exercise
|
||||
// its constructor-set definition without involving a plugin runner.
|
||||
// AppAction is abstract; this minimal concrete action lets the tests exercise its
|
||||
// constructor-composed identity without involving a plugin runner.
|
||||
class TestAppAction final : public AppAction
|
||||
{
|
||||
public:
|
||||
TestAppAction() : AppAction("action-id", "Action title", "Action source") {}
|
||||
TestAppAction() : AppAction("test", "Action title", "src-key", "Action source") {}
|
||||
|
||||
AppActionRunResult run() const override { return {}; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("AppAction composes a stable id from prefix:title:source_key", "[speeddial][actions]")
|
||||
{
|
||||
CHECK(AppAction::compose_id("test", "Action title", "src-key") == "test:Action title:src-key");
|
||||
// source_key (not the display name) carries identity, so it is the third field.
|
||||
CHECK(AppAction::compose_id("script", "Do Thing", "pack.py") == "script:Do Thing:pack.py");
|
||||
}
|
||||
|
||||
TEST_CASE("AppAction definitions are immutable after construction", "[speeddial][actions]")
|
||||
{
|
||||
using StringAccessor = const std::string& (AppAction::*)() const;
|
||||
|
||||
STATIC_CHECK(std::is_same_v<decltype(&AppAction::id), StringAccessor>);
|
||||
STATIC_CHECK(std::is_same_v<decltype(&AppAction::title), StringAccessor>);
|
||||
STATIC_CHECK(std::is_same_v<decltype(&AppAction::source), StringAccessor>);
|
||||
STATIC_CHECK(std::is_same_v<decltype(&AppAction::source_key), StringAccessor>);
|
||||
STATIC_CHECK(std::is_same_v<decltype(&AppAction::source_name), StringAccessor>);
|
||||
|
||||
const TestAppAction action;
|
||||
CHECK(action.id() == "action-id");
|
||||
CHECK(action.id() == "test:Action title:src-key");
|
||||
CHECK(action.title() == "Action title");
|
||||
CHECK(action.source() == "Action source");
|
||||
CHECK(action.source_key() == "src-key");
|
||||
CHECK(action.source_name() == "Action source");
|
||||
}
|
||||
|
||||
TEST_CASE("ActionRegistry takes exclusive ownership of published actions", "[speeddial][actions]")
|
||||
@@ -57,14 +53,3 @@ TEST_CASE("ActionRegistry takes exclusive ownership of published actions", "[spe
|
||||
|
||||
STATIC_CHECK(std::is_same_v<decltype(&ActionRegistry::upsert), ExpectedUpsert>);
|
||||
}
|
||||
|
||||
TEST_CASE("ActionRegistry starts registered action sources", "[speeddial][actions]")
|
||||
{
|
||||
bool started = false;
|
||||
ActionRegistry registry;
|
||||
registry.add_source(std::make_unique<RecordingActionSource>(started));
|
||||
|
||||
registry.init();
|
||||
|
||||
CHECK(started);
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <slic3r/GUI/SpeedDialActionId.hpp>
|
||||
|
||||
using Slic3r::GUI::speed_dial_action_id;
|
||||
|
||||
TEST_CASE("speed_dial_action_id is deterministic and opaque", "[speeddial][id]") {
|
||||
const std::string a = speed_dial_action_id("pack.py", "Do Thing");
|
||||
const std::string b = speed_dial_action_id("pack.py", "Do Thing");
|
||||
CHECK(a == b); // stable within a run
|
||||
CHECK(a.size() == 16); // 64-bit -> 16 hex chars
|
||||
CHECK(a.find_first_not_of("0123456789abcdef") == std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("speed_dial_action_id separates key and capability", "[speeddial][id]") {
|
||||
// The 0x1f separator prevents (key+cap) run-together collisions:
|
||||
// ("ab","c") must not equal ("a","bc").
|
||||
CHECK(speed_dial_action_id("ab", "c") != speed_dial_action_id("a", "bc"));
|
||||
CHECK(speed_dial_action_id("k", "one") != speed_dial_action_id("k", "two"));
|
||||
CHECK(speed_dial_action_id("k1", "cap") != speed_dial_action_id("k2", "cap"));
|
||||
}
|
||||
|
||||
TEST_CASE("speed_dial_action_id golden vector locks cross-run stability", "[speeddial][id]") {
|
||||
// why: this value is PERSISTED as a config key; if the algorithm ever drifts,
|
||||
// every saved favourite/stat silently orphans. Pin a known input->output.
|
||||
// FNV-1a 64-bit of bytes: 'k','e','y',0x1f,'c','a','p'
|
||||
CHECK(speed_dial_action_id("key", "cap") == "c54ad154fb2c9187");
|
||||
}
|
||||
Reference in New Issue
Block a user