Add history command service boundary

This commit is contained in:
2026-06-03 13:22:16 +02:00
parent 6d0cc4eb15
commit 6427f218e7
7 changed files with 228 additions and 22 deletions

View File

@@ -1,8 +1,36 @@
#include "app_core/history_ui.h"
#include "test_harness.h"
#include <string>
namespace {
class FakeHistoryUiServices final : public pp::app::HistoryUiServices {
public:
void invoke_undo() override
{
undo_calls += 1;
call_order += "undo;";
}
void invoke_redo() override
{
redo_calls += 1;
call_order += "redo;";
}
void clear_history() override
{
clear_calls += 1;
call_order += "clear;";
}
int undo_calls = 0;
int redo_calls = 0;
int clear_calls = 0;
std::string call_order;
};
void undo_and_redo_plan_availability(pp::tests::Harness& harness)
{
const auto undo = pp::app::plan_history_undo(2);
@@ -78,6 +106,76 @@ void rejects_negative_metrics(pp::tests::Harness& harness)
PP_EXPECT(harness, !pp::app::plan_history_clear(0, 0, -1));
}
void executor_dispatches_undo_redo_and_clear(pp::tests::Harness& harness)
{
FakeHistoryUiServices services;
const auto undo = pp::app::plan_history_undo(2);
PP_EXPECT(harness, undo);
if (undo) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(undo.value(), services).ok());
}
const auto redo = pp::app::plan_history_redo(1);
PP_EXPECT(harness, redo);
if (redo) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(redo.value(), services).ok());
}
const auto clear = pp::app::plan_history_clear(2, 1, 4096);
PP_EXPECT(harness, clear);
if (clear) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(clear.value(), services).ok());
}
PP_EXPECT(harness, services.undo_calls == 1);
PP_EXPECT(harness, services.redo_calls == 1);
PP_EXPECT(harness, services.clear_calls == 1);
PP_EXPECT(harness, services.call_order == "undo;redo;clear;");
}
void executor_preserves_empty_history_noops(pp::tests::Harness& harness)
{
FakeHistoryUiServices services;
const auto undo = pp::app::plan_history_undo(0);
PP_EXPECT(harness, undo);
if (undo) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(undo.value(), services).ok());
}
const auto redo = pp::app::plan_history_redo(0);
PP_EXPECT(harness, redo);
if (redo) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(redo.value(), services).ok());
}
const auto clear = pp::app::plan_history_clear(0, 0, 0);
PP_EXPECT(harness, clear);
if (clear) {
PP_EXPECT(harness, pp::app::execute_history_ui_plan(clear.value(), services).ok());
}
PP_EXPECT(harness, services.undo_calls == 0);
PP_EXPECT(harness, services.redo_calls == 0);
PP_EXPECT(harness, services.clear_calls == 0);
PP_EXPECT(harness, services.call_order.empty());
}
void executor_rejects_invalid_metrics(pp::tests::Harness& harness)
{
FakeHistoryUiServices services;
pp::app::HistoryUiPlan plan;
plan.operation = pp::app::HistoryUiOperation::undo;
plan.undo_count = -1;
plan.invokes_undo = true;
const auto status = pp::app::execute_history_ui_plan(plan, services);
PP_EXPECT(harness, !status.ok());
PP_EXPECT(harness, status.code == pp::foundation::StatusCode::out_of_range);
PP_EXPECT(harness, services.undo_calls == 0);
}
} // namespace
int main()
@@ -86,5 +184,8 @@ int main()
harness.run("undo and redo plan availability", undo_and_redo_plan_availability);
harness.run("clear plan tracks stacks and memory", clear_plan_tracks_stacks_and_memory);
harness.run("rejects negative metrics", rejects_negative_metrics);
harness.run("executor dispatches undo redo and clear", executor_dispatches_undo_redo_and_clear);
harness.run("executor preserves empty history noops", executor_preserves_empty_history_noops);
harness.run("executor rejects invalid metrics", executor_rejects_invalid_metrics);
return harness.finish();
}

View File

@@ -7,9 +7,24 @@ class FakeMainToolbarServices final : public pp::app::MainToolbarServices {
public:
void show_open_dialog() override { open_dialogs += 1; }
void show_save_dialog() override { save_dialogs += 1; }
void invoke_undo() override { undo_calls += 1; }
void invoke_redo() override { redo_calls += 1; }
void clear_history() override { clear_history_calls += 1; }
void invoke_undo(const pp::app::HistoryUiPlan& plan) override
{
undo_calls += 1;
last_history = plan;
}
void invoke_redo(const pp::app::HistoryUiPlan& plan) override
{
redo_calls += 1;
last_history = plan;
}
void clear_history(const pp::app::HistoryUiPlan& plan) override
{
clear_history_calls += 1;
last_history = plan;
}
void clear_canvas(const pp::app::DocumentCanvasClearPlan& plan) override
{
clear_canvas_calls += 1;
@@ -38,6 +53,7 @@ public:
int clear_canvas_calls = 0;
int message_boxes = 0;
int settings_dialogs = 0;
pp::app::HistoryUiPlan last_history;
pp::app::DocumentCanvasClearPlan last_clear;
};
@@ -74,6 +90,8 @@ void history_commands_reuse_history_breakpoints(pp::tests::Harness& harness)
PP_EXPECT(harness, undo.value().action == pp::app::MainToolbarAction::invoke_undo);
PP_EXPECT(harness, undo.value().updates_memory_label);
PP_EXPECT(harness, undo.value().updates_title);
PP_EXPECT(harness, undo.value().history.operation == pp::app::HistoryUiOperation::undo);
PP_EXPECT(harness, undo.value().history.invokes_undo);
PP_EXPECT(harness, !undo.value().no_op);
}
@@ -95,6 +113,8 @@ void history_commands_reuse_history_breakpoints(pp::tests::Harness& harness)
PP_EXPECT(harness, clear.value().action == pp::app::MainToolbarAction::clear_history);
PP_EXPECT(harness, clear.value().updates_memory_label);
PP_EXPECT(harness, !clear.value().updates_title);
PP_EXPECT(harness, clear.value().history.operation == pp::app::HistoryUiOperation::clear);
PP_EXPECT(harness, clear.value().history.clears_history);
}
}
@@ -161,6 +181,8 @@ void executor_dispatches_to_service_boundary(pp::tests::Harness& harness)
const auto status = pp::app::execute_main_toolbar_plan(undo.value(), services);
PP_EXPECT(harness, status.ok());
PP_EXPECT(harness, services.undo_calls == 1);
PP_EXPECT(harness, services.last_history.operation == pp::app::HistoryUiOperation::undo);
PP_EXPECT(harness, services.last_history.undo_count == 1);
}
auto clear_canvas = pp::app::plan_main_toolbar_command(