Files
panopainter/tests/app_core/canvas_hotkey_tests.cpp

298 lines
11 KiB
C++

#include "app_core/canvas_hotkey.h"
#include "test_harness.h"
#include <cmath>
namespace {
class FakeCanvasHotkeyServices final : public pp::app::CanvasHotkeyServices {
public:
pp::foundation::Status execute_tool(const pp::app::CanvasToolPlan& plan) override
{
tool_calls += 1;
last_tool_mode = plan.mode;
return pp::foundation::Status::success();
}
pp::foundation::Status execute_history(const pp::app::HistoryUiPlan& plan) override
{
history_calls += 1;
last_history_operation = plan.operation;
return pp::foundation::Status::success();
}
void save_document(pp::app::DocumentSaveIntent intent) override
{
save_calls += 1;
last_save_intent = intent;
}
void toggle_ui() override { toggle_ui_calls += 1; }
void adjust_brush_size(float delta) override
{
brush_adjust_calls += 1;
last_brush_delta = delta;
}
void show_cursor() override { show_cursor_calls += 1; }
int tool_calls = 0;
int history_calls = 0;
int save_calls = 0;
int toggle_ui_calls = 0;
int brush_adjust_calls = 0;
int show_cursor_calls = 0;
pp::app::CanvasToolMode last_tool_mode = pp::app::CanvasToolMode::draw;
pp::app::HistoryUiOperation last_history_operation = pp::app::HistoryUiOperation::undo;
pp::app::DocumentSaveIntent last_save_intent = pp::app::DocumentSaveIntent::save;
float last_brush_delta = 0.0F;
};
pp::app::CanvasHotkeyState default_state() noexcept
{
pp::app::CanvasHotkeyState state;
state.undo_count = 2;
state.redo_count = 1;
return state;
}
void e_key_toggles_erase_and_draw(pp::tests::Harness& harness)
{
const auto down = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
pp::app::CanvasHotkeyKey::e,
default_state());
const auto up = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::e,
default_state());
PP_EXPECT(harness, down);
PP_EXPECT(harness, up);
if (down) {
PP_EXPECT(harness, down.value().action == pp::app::CanvasHotkeyAction::select_tool);
PP_EXPECT(harness, down.value().tool.mode == pp::app::CanvasToolMode::erase);
PP_EXPECT(harness, !down.value().no_op);
}
if (up) {
PP_EXPECT(harness, up.value().action == pp::app::CanvasHotkeyAction::select_tool);
PP_EXPECT(harness, up.value().tool.mode == pp::app::CanvasToolMode::draw);
PP_EXPECT(harness, !up.value().no_op);
}
}
void ctrl_z_and_shift_z_plan_history(pp::tests::Harness& harness)
{
auto state = default_state();
state.ctrl_down = true;
const auto undo = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::z,
state);
state.shift_down = true;
const auto redo = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::z,
state);
PP_EXPECT(harness, undo);
PP_EXPECT(harness, redo);
if (undo) {
PP_EXPECT(harness, undo.value().action == pp::app::CanvasHotkeyAction::history);
PP_EXPECT(harness, undo.value().history.operation == pp::app::HistoryUiOperation::undo);
PP_EXPECT(harness, undo.value().history.invokes_undo);
}
if (redo) {
PP_EXPECT(harness, redo.value().action == pp::app::CanvasHotkeyAction::history);
PP_EXPECT(harness, redo.value().history.operation == pp::app::HistoryUiOperation::redo);
PP_EXPECT(harness, redo.value().history.invokes_redo);
}
}
void save_hotkeys_plan_document_intents(pp::tests::Harness& harness)
{
auto state = default_state();
state.ctrl_down = true;
const auto save = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::s,
state);
state.shift_down = true;
const auto save_version = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::s,
state);
PP_EXPECT(harness, save);
PP_EXPECT(harness, save_version);
if (save) {
PP_EXPECT(harness, save.value().action == pp::app::CanvasHotkeyAction::save_document);
PP_EXPECT(harness, save.value().save_intent == pp::app::DocumentSaveIntent::save);
}
if (save_version) {
PP_EXPECT(harness, save_version.value().action == pp::app::CanvasHotkeyAction::save_document);
PP_EXPECT(
harness,
save_version.value().save_intent == pp::app::DocumentSaveIntent::save_dirty_version);
}
}
void app_and_brush_hotkeys_are_planned(pp::tests::Harness& harness)
{
auto state = default_state();
state.mouse_focused = true;
const auto alt = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
pp::app::CanvasHotkeyKey::alt,
state);
const auto tab = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::tab,
state);
const auto smaller = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::bracket_left,
state);
const auto larger = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::bracket_right,
state);
PP_EXPECT(harness, alt);
PP_EXPECT(harness, tab);
PP_EXPECT(harness, smaller);
PP_EXPECT(harness, larger);
if (alt) {
PP_EXPECT(harness, alt.value().action == pp::app::CanvasHotkeyAction::show_cursor);
}
if (tab) {
PP_EXPECT(harness, tab.value().action == pp::app::CanvasHotkeyAction::toggle_ui);
}
if (smaller) {
PP_EXPECT(harness, smaller.value().brush_size_delta < 0.0F);
}
if (larger) {
PP_EXPECT(harness, larger.value().brush_size_delta > 0.0F);
}
}
void touch_and_android_back_plan_undo(pp::tests::Harness& harness)
{
auto state = default_state();
const auto android = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
pp::app::CanvasHotkeyKey::android_back,
state);
state.touch_finger_count = 2;
const auto tap = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::touch_tap,
pp::app::CanvasHotkeyKey::other,
state);
PP_EXPECT(harness, android);
PP_EXPECT(harness, tap);
if (android) {
PP_EXPECT(harness, android.value().action == pp::app::CanvasHotkeyAction::history);
PP_EXPECT(harness, android.value().history.operation == pp::app::HistoryUiOperation::undo);
}
if (tap) {
PP_EXPECT(harness, tap.value().action == pp::app::CanvasHotkeyAction::history);
PP_EXPECT(harness, tap.value().history.operation == pp::app::HistoryUiOperation::undo);
}
}
void planner_preserves_no_ops_and_rejects_bad_counts(pp::tests::Harness& harness)
{
auto state = default_state();
const auto no_ctrl_z = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::z,
state);
state.undo_count = -1;
const auto bad_undo = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
pp::app::CanvasHotkeyKey::android_back,
state);
PP_EXPECT(harness, no_ctrl_z);
if (no_ctrl_z) {
PP_EXPECT(harness, no_ctrl_z.value().action == pp::app::CanvasHotkeyAction::none);
PP_EXPECT(harness, no_ctrl_z.value().no_op);
}
PP_EXPECT(harness, !bad_undo);
if (!bad_undo) {
PP_EXPECT(harness, bad_undo.status().code == pp::foundation::StatusCode::out_of_range);
}
}
void executor_dispatches_actions(pp::tests::Harness& harness)
{
FakeCanvasHotkeyServices services;
auto state = default_state();
state.ctrl_down = true;
const auto save = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::s,
state);
const auto undo = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::z,
state);
const auto erase = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_down,
pp::app::CanvasHotkeyKey::e,
state);
const auto brush = pp::app::plan_canvas_hotkey(
pp::app::CanvasHotkeyEvent::key_up,
pp::app::CanvasHotkeyKey::bracket_right,
state);
PP_EXPECT(harness, save && undo && erase && brush);
if (save && undo && erase && brush) {
PP_EXPECT(harness, pp::app::execute_canvas_hotkey_plan(save.value(), services).ok());
PP_EXPECT(harness, pp::app::execute_canvas_hotkey_plan(undo.value(), services).ok());
PP_EXPECT(harness, pp::app::execute_canvas_hotkey_plan(erase.value(), services).ok());
PP_EXPECT(harness, pp::app::execute_canvas_hotkey_plan(brush.value(), services).ok());
}
PP_EXPECT(harness, services.save_calls == 1);
PP_EXPECT(harness, services.history_calls == 1);
PP_EXPECT(harness, services.tool_calls == 1);
PP_EXPECT(harness, services.brush_adjust_calls == 1);
PP_EXPECT(harness, services.last_save_intent == pp::app::DocumentSaveIntent::save);
PP_EXPECT(harness, services.last_history_operation == pp::app::HistoryUiOperation::undo);
PP_EXPECT(harness, services.last_tool_mode == pp::app::CanvasToolMode::erase);
PP_EXPECT(harness, std::fabs(services.last_brush_delta - 0.05F) < 0.001F);
}
void executor_rejects_malformed_brush_adjust(pp::tests::Harness& harness)
{
FakeCanvasHotkeyServices services;
pp::app::CanvasHotkeyPlan malformed;
malformed.action = pp::app::CanvasHotkeyAction::adjust_brush_size;
malformed.no_op = false;
const auto status = pp::app::execute_canvas_hotkey_plan(malformed, services);
PP_EXPECT(harness, !status.ok());
PP_EXPECT(harness, status.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(harness, services.brush_adjust_calls == 0);
}
} // namespace
int main()
{
pp::tests::Harness harness;
harness.run("e key toggles erase and draw", e_key_toggles_erase_and_draw);
harness.run("ctrl z and shift z plan history", ctrl_z_and_shift_z_plan_history);
harness.run("save hotkeys plan document intents", save_hotkeys_plan_document_intents);
harness.run("app and brush hotkeys are planned", app_and_brush_hotkeys_are_planned);
harness.run("touch and android back plan undo", touch_and_android_back_plan_undo);
harness.run("planner preserves no ops and rejects bad counts", planner_preserves_no_ops_and_rejects_bad_counts);
harness.run("executor dispatches actions", executor_dispatches_actions);
harness.run("executor rejects malformed brush adjust", executor_rejects_malformed_brush_adjust);
return harness.finish();
}