yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_ui_tests.cc
Go to the documentation of this file.
2
3#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
4
5#include <string>
6
7#include "imgui.h"
8#if __has_include("imgui_test_engine/imgui_te_context.h")
9#include "imgui_test_engine/imgui_te_context.h"
10#elif __has_include("imgui_te_context.h")
11#include "imgui_te_context.h"
12#else
13#error "ImGui Test Engine context header not found"
14#endif
15
16#if __has_include("imgui_test_engine/imgui_te_engine.h")
17#include "imgui_test_engine/imgui_te_engine.h"
18#elif __has_include("imgui_te_engine.h")
19#include "imgui_te_engine.h"
20#else
21#error "ImGui Test Engine engine header not found"
22#endif
23
24namespace yaze::test {
25
26namespace {
27
28void EnsureTile16PanelOpenAndFocused(ImGuiTestContext* ctx) {
29 ctx->SetRef("Tile16 Editor");
30 if (ctx->GetWindowByRef("") == nullptr) {
31 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_T);
32 ctx->Yield(4);
33 }
34
35 ctx->SetRef("Tile16 Editor");
36 IM_CHECK(ctx->GetWindowByRef("") != nullptr);
37 ctx->WindowFocus("");
38 ctx->Yield(2);
39}
40
41void ExpectActiveQuadrant(ImGuiTestContext* ctx, const char* label) {
42 const std::string pattern = std::string("**/Active ") + label + ":*";
43 IM_CHECK(ctx->ItemExists(pattern.c_str()));
44}
45
46void ExpectBrushPalette(ImGuiTestContext* ctx, int palette) {
47 const std::string pattern =
48 std::string("**/*Brush Palette: ") + std::to_string(palette) + "*";
49 IM_CHECK(ctx->ItemExists(pattern.c_str()));
50}
51
52} // namespace
53
54// ============================================================================
55// Test: Keyboard shortcut mode switching
56//
57// The overworld editor uses number keys (1, 2) and letter keys (B, F)
58// to switch between Mouse, Draw Tile, and Fill Tile modes. These shortcuts
59// are handled in OverworldEditor::HandleKeyboardShortcuts() and delegate
60// to TilePaintingManager for B/F.
61//
62// These tests verify that pressing each shortcut key doesn't crash the
63// application. Actual mode state verification would require accessing
64// the OverworldEditor's internal state, which we defer to unit tests.
65// ============================================================================
66
67static void RegisterKeyboardShortcutTests(ImGuiTestEngine* engine) {
68 // Test: Key '1' (Mouse mode) doesn't crash
69 {
70 ImGuiTest* test =
71 IM_REGISTER_TEST(engine, "overworld_keys", "mouse_mode_key_1");
72 test->TestFunc = [](ImGuiTestContext* ctx) {
73 ctx->KeyPress(ImGuiKey_1);
74 ctx->Yield(3);
75 };
76 }
77
78 // Test: Key '2' (Draw Tile mode) doesn't crash
79 {
80 ImGuiTest* test =
81 IM_REGISTER_TEST(engine, "overworld_keys", "draw_mode_key_2");
82 test->TestFunc = [](ImGuiTestContext* ctx) {
83 ctx->KeyPress(ImGuiKey_2);
84 ctx->Yield(3);
85 // Restore mouse mode
86 ctx->KeyPress(ImGuiKey_1);
87 ctx->Yield(1);
88 };
89 }
90
91 // Test: Key 'B' (Brush toggle) doesn't crash
92 {
93 ImGuiTest* test =
94 IM_REGISTER_TEST(engine, "overworld_keys", "brush_toggle_key_b");
95 test->TestFunc = [](ImGuiTestContext* ctx) {
96 ctx->KeyPress(ImGuiKey_B);
97 ctx->Yield(3);
98 // Toggle back
99 ctx->KeyPress(ImGuiKey_B);
100 ctx->Yield(1);
101 };
102 }
103
104 // Test: Key 'F' (Fill tool) doesn't crash
105 {
106 ImGuiTest* test =
107 IM_REGISTER_TEST(engine, "overworld_keys", "fill_tool_key_f");
108 test->TestFunc = [](ImGuiTestContext* ctx) {
109 ctx->KeyPress(ImGuiKey_F);
110 ctx->Yield(3);
111 // Toggle back
112 ctx->KeyPress(ImGuiKey_F);
113 ctx->Yield(1);
114 };
115 }
116
117 // Test: Key 'I' (Eyedropper / pick tile16) doesn't crash
118 {
119 ImGuiTest* test =
120 IM_REGISTER_TEST(engine, "overworld_keys", "eyedropper_key_i");
121 test->TestFunc = [](ImGuiTestContext* ctx) {
122 ctx->KeyPress(ImGuiKey_I);
123 ctx->Yield(3);
124 };
125 }
126
127 // Test: Ctrl+L (Lock map toggle) doesn't crash
128 {
129 ImGuiTest* test =
130 IM_REGISTER_TEST(engine, "overworld_keys", "lock_toggle_ctrl_l");
131 test->TestFunc = [](ImGuiTestContext* ctx) {
132 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_L);
133 ctx->Yield(3);
134 // Toggle back
135 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_L);
136 ctx->Yield(1);
137 };
138 }
139
140 // Test: F11 (Fullscreen toggle) doesn't crash
141 {
142 ImGuiTest* test =
143 IM_REGISTER_TEST(engine, "overworld_keys", "fullscreen_toggle_f11");
144 test->TestFunc = [](ImGuiTestContext* ctx) {
145 ctx->KeyPress(ImGuiKey_F11);
146 ctx->Yield(3);
147 // Toggle back
148 ctx->KeyPress(ImGuiKey_F11);
149 ctx->Yield(1);
150 };
151 }
152
153 // Test: Ctrl+Z (Undo) doesn't crash when no undo history
154 {
155 ImGuiTest* test =
156 IM_REGISTER_TEST(engine, "overworld_keys", "undo_no_crash");
157 test->TestFunc = [](ImGuiTestContext* ctx) {
158 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_Z);
159 ctx->Yield(3);
160 };
161 }
162
163 // Test: Ctrl+Y (Redo) doesn't crash when no redo history
164 {
165 ImGuiTest* test =
166 IM_REGISTER_TEST(engine, "overworld_keys", "redo_no_crash");
167 test->TestFunc = [](ImGuiTestContext* ctx) {
168 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_Y);
169 ctx->Yield(3);
170 };
171 }
172
173 // Test: Ctrl+T (Toggle Tile16 Editor panel) doesn't crash
174 {
175 ImGuiTest* test =
176 IM_REGISTER_TEST(engine, "overworld_keys", "tile16_editor_ctrl_t");
177 test->TestFunc = [](ImGuiTestContext* ctx) {
178 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_T);
179 ctx->Yield(3);
180 // Toggle back
181 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_T);
182 ctx->Yield(1);
183 };
184 }
185
186 // Test: Keys 1..4 update tile16 quadrant focus in panel state text.
187 {
188 ImGuiTest* test = IM_REGISTER_TEST(engine, "overworld_keys",
189 "tile16_quadrant_hotkeys_apply_focus");
190 test->TestFunc = [](ImGuiTestContext* ctx) {
191 EnsureTile16PanelOpenAndFocused(ctx);
192
193 struct QuadrantStep {
194 ImGuiKey key;
195 const char* label;
196 };
197 const QuadrantStep steps[] = {
198 {ImGuiKey_1, "TL"},
199 {ImGuiKey_2, "TR"},
200 {ImGuiKey_3, "BL"},
201 {ImGuiKey_4, "BR"},
202 };
203
204 for (const auto& step : steps) {
205 ctx->KeyPress(step.key);
206 ctx->Yield(2);
207 ExpectActiveQuadrant(ctx, step.label);
208 }
209
210 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_T);
211 ctx->Yield(2);
212 };
213 }
214
215 // Test: Ctrl+1..8 update brush palette while preserving active quadrant.
216 {
217 ImGuiTest* test = IM_REGISTER_TEST(
218 engine, "overworld_keys", "tile16_ctrl_numeric_hotkeys_palette_rows");
219 test->TestFunc = [](ImGuiTestContext* ctx) {
220 EnsureTile16PanelOpenAndFocused(ctx);
221
222 ctx->KeyPress(ImGuiKey_4);
223 ctx->Yield(2);
224 ExpectActiveQuadrant(ctx, "BR");
225
226 for (int palette = 0; palette < 8; ++palette) {
227 const ImGuiKey number_key = static_cast<ImGuiKey>(ImGuiKey_1 + palette);
228 ctx->KeyPress(ImGuiMod_Ctrl | number_key);
229 ctx->Yield(2);
230 ExpectBrushPalette(ctx, palette);
231 ExpectActiveQuadrant(ctx, "BR");
232 }
233
234 ctx->KeyPress(ImGuiMod_Ctrl | ImGuiKey_T);
235 ctx->Yield(2);
236 };
237 }
238}
239
240// ============================================================================
241// Test: Entity editing mode shortcuts (3-8 keys)
242//
243// Keys 3-8 activate different entity editing modes:
244// 3 = Entrances, 4 = Exits, 5 = Items,
245// 6 = Sprites, 7 = Transports, 8 = Music
246// ============================================================================
247
248static void RegisterEntityModeTests(ImGuiTestEngine* engine) {
249 // Test: Entity mode keys (3-8) don't crash
250 {
251 ImGuiTest* test =
252 IM_REGISTER_TEST(engine, "overworld_entity", "mode_keys_3_to_8");
253 test->TestFunc = [](ImGuiTestContext* ctx) {
254 // Cycle through all entity modes
255 ctx->KeyPress(ImGuiKey_3); // Entrances
256 ctx->Yield(2);
257 ctx->KeyPress(ImGuiKey_4); // Exits
258 ctx->Yield(2);
259 ctx->KeyPress(ImGuiKey_5); // Items
260 ctx->Yield(2);
261 ctx->KeyPress(ImGuiKey_6); // Sprites
262 ctx->Yield(2);
263 ctx->KeyPress(ImGuiKey_7); // Transports
264 ctx->Yield(2);
265 ctx->KeyPress(ImGuiKey_8); // Music
266 ctx->Yield(2);
267 // Return to mouse mode
268 ctx->KeyPress(ImGuiKey_1);
269 ctx->Yield(1);
270 };
271 }
272}
273
274// ============================================================================
275// Test: Mode switching round-trips
276//
277// Verifies that cycling through all editing modes and returning to Mouse
278// mode doesn't leave the editor in a broken state.
279// ============================================================================
280
281static void RegisterModeRoundTripTests(ImGuiTestEngine* engine) {
282 // Test: Full mode cycle (Mouse -> Draw -> Fill -> Mouse) is stable
283 {
284 ImGuiTest* test =
285 IM_REGISTER_TEST(engine, "overworld_modes", "full_mode_cycle");
286 test->TestFunc = [](ImGuiTestContext* ctx) {
287 // Start in mouse mode
288 ctx->KeyPress(ImGuiKey_1);
289 ctx->Yield(2);
290
291 // Switch to draw tile
292 ctx->KeyPress(ImGuiKey_2);
293 ctx->Yield(2);
294
295 // Switch to fill via F
296 ctx->KeyPress(ImGuiKey_F);
297 ctx->Yield(2);
298
299 // Toggle brush via B (should go to draw tile)
300 ctx->KeyPress(ImGuiKey_B);
301 ctx->Yield(2);
302
303 // Toggle brush again (should go back to mouse)
304 ctx->KeyPress(ImGuiKey_B);
305 ctx->Yield(2);
306
307 // Back to mouse mode explicitly
308 ctx->KeyPress(ImGuiKey_1);
309 ctx->Yield(3);
310 };
311 }
312
313 // Test: Rapid mode switching doesn't crash
314 {
315 ImGuiTest* test =
316 IM_REGISTER_TEST(engine, "overworld_modes", "rapid_mode_switch");
317 test->TestFunc = [](ImGuiTestContext* ctx) {
318 for (int i = 0; i < 10; ++i) {
319 ctx->KeyPress(ImGuiKey_B);
320 ctx->Yield(1);
321 }
322 // Settle back to mouse
323 ctx->KeyPress(ImGuiKey_1);
324 ctx->Yield(3);
325 };
326 }
327}
328
329// ============================================================================
330// Test: Canvas navigation smoke tests
331//
332// These exercise CanvasNavigationManager methods indirectly through the
333// keyboard shortcuts and UI interactions. The actual methods tested:
334// - HandleOverworldPan (via middle-click drag simulation)
335// - HandleOverworldZoom (via the zoom step mechanism)
336// - ResetOverworldView (not directly bound to a key, but used internally)
337//
338// Note: Mouse-based pan/zoom requires pixel-precise ImGui interaction that
339// the test engine handles through its coroutine system.
340// ============================================================================
341
342static void RegisterCanvasNavigationTests(ImGuiTestEngine* engine) {
343 // Test: Multiple frames of rendering without interaction is stable
344 {
345 ImGuiTest* test =
346 IM_REGISTER_TEST(engine, "overworld_canvas", "idle_frames_stable");
347 test->TestFunc = [](ImGuiTestContext* ctx) {
348 // Just yield several frames to verify the overworld editor
349 // renders stably without any interaction
350 ctx->Yield(10);
351 };
352 }
353}
354
355// ============================================================================
356// Test: World combo selector smoke tests
357//
358// The toolbar has a world selector combo (Light World, Dark World, Special).
359// These tests verify the combo exists but don't change world since that
360// triggers heavy map reloading that depends on ROM state.
361// ============================================================================
362
363static void RegisterWorldSelectorTests(ImGuiTestEngine* engine) {
364 // Test: Overworld editor remains stable after multiple frame yields
365 {
366 ImGuiTest* test =
367 IM_REGISTER_TEST(engine, "overworld_toolbar", "toolbar_renders_stable");
368 test->TestFunc = [](ImGuiTestContext* ctx) {
369 // Render several frames to verify toolbar doesn't crash
370 ctx->Yield(5);
371 // The toolbar "CanvasToolbar" table renders every frame when
372 // ROM is loaded and overworld is initialized
373 };
374 }
375}
376
377// ============================================================================
378// Public registration entry point
379// ============================================================================
380
381void RegisterOverworldUITests(ImGuiTestEngine* engine) {
382 if (engine == nullptr)
383 return;
384 RegisterKeyboardShortcutTests(engine);
385 RegisterEntityModeTests(engine);
386 RegisterModeRoundTripTests(engine);
387 RegisterCanvasNavigationTests(engine);
388 RegisterWorldSelectorTests(engine);
389}
390
391} // namespace yaze::test
392
393#else // !YAZE_ENABLE_IMGUI_TEST_ENGINE
394
395namespace yaze::test {
396void RegisterOverworldUITests(ImGuiTestEngine* /*engine*/) {}
397} // namespace yaze::test
398
399#endif // YAZE_ENABLE_IMGUI_TEST_ENGINE
void RegisterOverworldUITests(ImGuiTestEngine *)