yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas_automation_api_test.cc
Go to the documentation of this file.
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5
6#include "app/gui/canvas.h"
7#include "testing.h"
8
9namespace yaze {
10namespace test {
11
12using ::testing::Eq;
13using ::testing::Ge;
14using ::testing::Le;
15
16class CanvasAutomationAPITest : public ::testing::Test {
17 protected:
18 void SetUp() override {
19 // Create a test canvas with known dimensions
20 canvas_ = std::make_unique<gui::Canvas>("TestCanvas", ImVec2(512, 512),
22 api_ = canvas_->GetAutomationAPI();
23 ASSERT_NE(api_, nullptr);
24 }
25
26 std::unique_ptr<gui::Canvas> canvas_;
28};
29
30// ============================================================================
31// Coordinate Conversion Tests
32// ============================================================================
33
34TEST_F(CanvasAutomationAPITest, TileToCanvas_BasicConversion) {
35 // At 1.0x zoom, tile (0,0) should be at canvas (0,0)
36 canvas_->set_global_scale(1.0f);
37
38 ImVec2 pos = api_->TileToCanvas(0, 0);
39 EXPECT_FLOAT_EQ(pos.x, 0.0f);
40 EXPECT_FLOAT_EQ(pos.y, 0.0f);
41
42 // Tile (1,0) should be at (16,0) for 16x16 grid
43 pos = api_->TileToCanvas(1, 0);
44 EXPECT_FLOAT_EQ(pos.x, 16.0f);
45 EXPECT_FLOAT_EQ(pos.y, 0.0f);
46
47 // Tile (0,1) should be at (0,16)
48 pos = api_->TileToCanvas(0, 1);
49 EXPECT_FLOAT_EQ(pos.x, 0.0f);
50 EXPECT_FLOAT_EQ(pos.y, 16.0f);
51
52 // Tile (10,10) should be at (160,160)
53 pos = api_->TileToCanvas(10, 10);
54 EXPECT_FLOAT_EQ(pos.x, 160.0f);
55 EXPECT_FLOAT_EQ(pos.y, 160.0f);
56}
57
58TEST_F(CanvasAutomationAPITest, TileToCanvas_WithZoom) {
59 // At 2.0x zoom, tile coordinates should scale
60 canvas_->set_global_scale(2.0f);
61
62 ImVec2 pos = api_->TileToCanvas(1, 1);
63 EXPECT_FLOAT_EQ(pos.x, 32.0f); // 1 * 16 * 2.0
64 EXPECT_FLOAT_EQ(pos.y, 32.0f);
65
66 // At 0.5x zoom, tile coordinates should scale down
67 canvas_->set_global_scale(0.5f);
68 pos = api_->TileToCanvas(10, 10);
69 EXPECT_FLOAT_EQ(pos.x, 80.0f); // 10 * 16 * 0.5
70 EXPECT_FLOAT_EQ(pos.y, 80.0f);
71}
72
73TEST_F(CanvasAutomationAPITest, CanvasToTile_BasicConversion) {
74 canvas_->set_global_scale(1.0f);
75
76 // Canvas (0,0) should be tile (0,0)
77 ImVec2 tile = api_->CanvasToTile(ImVec2(0, 0));
78 EXPECT_FLOAT_EQ(tile.x, 0.0f);
79 EXPECT_FLOAT_EQ(tile.y, 0.0f);
80
81 // Canvas (16,16) should be tile (1,1)
82 tile = api_->CanvasToTile(ImVec2(16, 16));
83 EXPECT_FLOAT_EQ(tile.x, 1.0f);
84 EXPECT_FLOAT_EQ(tile.y, 1.0f);
85
86 // Canvas (160,160) should be tile (10,10)
87 tile = api_->CanvasToTile(ImVec2(160, 160));
88 EXPECT_FLOAT_EQ(tile.x, 10.0f);
89 EXPECT_FLOAT_EQ(tile.y, 10.0f);
90}
91
92TEST_F(CanvasAutomationAPITest, CanvasToTile_WithZoom) {
93 // At 2.0x zoom
94 canvas_->set_global_scale(2.0f);
95
96 ImVec2 tile = api_->CanvasToTile(ImVec2(32, 32));
97 EXPECT_FLOAT_EQ(tile.x, 1.0f); // 32 / (16 * 2.0)
98 EXPECT_FLOAT_EQ(tile.y, 1.0f);
99
100 // At 0.5x zoom
101 canvas_->set_global_scale(0.5f);
102 tile = api_->CanvasToTile(ImVec2(8, 8));
103 EXPECT_FLOAT_EQ(tile.x, 1.0f); // 8 / (16 * 0.5)
104 EXPECT_FLOAT_EQ(tile.y, 1.0f);
105}
106
107TEST_F(CanvasAutomationAPITest, CoordinateRoundTrip) {
108 canvas_->set_global_scale(1.0f);
109
110 // Test round-trip conversion
111 for (int i = 0; i < 32; ++i) {
112 ImVec2 canvas_pos = api_->TileToCanvas(i, i);
113 ImVec2 tile_pos = api_->CanvasToTile(canvas_pos);
114
115 EXPECT_FLOAT_EQ(tile_pos.x, static_cast<float>(i));
116 EXPECT_FLOAT_EQ(tile_pos.y, static_cast<float>(i));
117 }
118}
119
120// ============================================================================
121// Bounds Checking Tests
122// ============================================================================
123
124TEST_F(CanvasAutomationAPITest, IsInBounds_ValidCoordinates) {
125 EXPECT_TRUE(api_->IsInBounds(0, 0));
126 EXPECT_TRUE(api_->IsInBounds(10, 10));
127 EXPECT_TRUE(api_->IsInBounds(31, 31)); // 512/16 = 32 tiles, so 31 is max
128}
129
130TEST_F(CanvasAutomationAPITest, IsInBounds_InvalidCoordinates) {
131 EXPECT_FALSE(api_->IsInBounds(-1, 0));
132 EXPECT_FALSE(api_->IsInBounds(0, -1));
133 EXPECT_FALSE(api_->IsInBounds(-1, -1));
134 EXPECT_FALSE(api_->IsInBounds(32, 0)); // Out of bounds
135 EXPECT_FALSE(api_->IsInBounds(0, 32));
136 EXPECT_FALSE(api_->IsInBounds(100, 100));
137}
138
139// ============================================================================
140// Tile Operations Tests
141// ============================================================================
142
143TEST_F(CanvasAutomationAPITest, SetTileAt_WithCallback) {
144 // Set up a tile paint callback
145 std::vector<std::tuple<int, int, int>> painted_tiles;
146 api_->SetTilePaintCallback([&](int x, int y, int tile_id) {
147 painted_tiles.push_back({x, y, tile_id});
148 return true;
149 });
150
151 // Paint some tiles
152 EXPECT_TRUE(api_->SetTileAt(5, 5, 42));
153 EXPECT_TRUE(api_->SetTileAt(10, 10, 100));
154
155 ASSERT_EQ(painted_tiles.size(), 2);
156 EXPECT_EQ(painted_tiles[0], std::make_tuple(5, 5, 42));
157 EXPECT_EQ(painted_tiles[1], std::make_tuple(10, 10, 100));
158}
159
160TEST_F(CanvasAutomationAPITest, SetTileAt_OutOfBounds) {
161 bool callback_called = false;
162 api_->SetTilePaintCallback([&](int x, int y, int tile_id) {
163 callback_called = true;
164 return true;
165 });
166
167 // Out of bounds tiles should return false without calling callback
168 EXPECT_FALSE(api_->SetTileAt(-1, 0, 42));
169 EXPECT_FALSE(api_->SetTileAt(0, -1, 42));
170 EXPECT_FALSE(api_->SetTileAt(100, 100, 42));
171
172 EXPECT_FALSE(callback_called);
173}
174
175TEST_F(CanvasAutomationAPITest, GetTileAt_WithCallback) {
176 // Set up a tile query callback
177 api_->SetTileQueryCallback([](int x, int y) {
178 return x * 100 + y; // Simple deterministic value
179 });
180
181 EXPECT_EQ(api_->GetTileAt(0, 0), 0);
182 EXPECT_EQ(api_->GetTileAt(1, 0), 100);
183 EXPECT_EQ(api_->GetTileAt(0, 1), 1);
184 EXPECT_EQ(api_->GetTileAt(5, 7), 507);
185}
186
187TEST_F(CanvasAutomationAPITest, GetTileAt_OutOfBounds) {
188 api_->SetTileQueryCallback([](int x, int y) { return 42; });
189
190 EXPECT_EQ(api_->GetTileAt(-1, 0), -1);
191 EXPECT_EQ(api_->GetTileAt(0, -1), -1);
192 EXPECT_EQ(api_->GetTileAt(100, 100), -1);
193}
194
195TEST_F(CanvasAutomationAPITest, SetTiles_BatchOperation) {
196 std::vector<std::tuple<int, int, int>> painted_tiles;
197 api_->SetTilePaintCallback([&](int x, int y, int tile_id) {
198 painted_tiles.push_back({x, y, tile_id});
199 return true;
200 });
201
202 std::vector<std::tuple<int, int, int>> tiles_to_paint = {
203 {0, 0, 10},
204 {1, 0, 11},
205 {2, 0, 12},
206 {0, 1, 20},
207 {1, 1, 21}
208 };
209
210 EXPECT_TRUE(api_->SetTiles(tiles_to_paint));
211 EXPECT_EQ(painted_tiles.size(), 5);
212}
213
214// ============================================================================
215// Selection Tests
216// ============================================================================
217
219 api_->SelectTile(5, 5);
220
221 auto selection = api_->GetSelection();
222 EXPECT_TRUE(selection.has_selection);
223 EXPECT_EQ(selection.selected_tiles.size(), 1);
224}
225
227 api_->SelectTileRect(5, 5, 9, 9);
228
229 auto selection = api_->GetSelection();
230 EXPECT_TRUE(selection.has_selection);
231
232 // 5x5 rectangle = 25 tiles
233 EXPECT_EQ(selection.selected_tiles.size(), 25);
234
235 // Check first and last tiles
236 EXPECT_FLOAT_EQ(selection.selected_tiles[0].x, 5.0f);
237 EXPECT_FLOAT_EQ(selection.selected_tiles[0].y, 5.0f);
238 EXPECT_FLOAT_EQ(selection.selected_tiles[24].x, 9.0f);
239 EXPECT_FLOAT_EQ(selection.selected_tiles[24].y, 9.0f);
240}
241
242TEST_F(CanvasAutomationAPITest, SelectTileRect_SwappedCoordinates) {
243 // Should handle coordinates in any order
244 api_->SelectTileRect(9, 9, 5, 5); // Reversed
245
246 auto selection = api_->GetSelection();
247 EXPECT_TRUE(selection.has_selection);
248 EXPECT_EQ(selection.selected_tiles.size(), 25);
249}
250
252 api_->SelectTileRect(5, 5, 10, 10);
253
254 auto selection = api_->GetSelection();
255 EXPECT_TRUE(selection.has_selection);
256
257 api_->ClearSelection();
258
259 selection = api_->GetSelection();
260 EXPECT_FALSE(selection.has_selection);
261 EXPECT_EQ(selection.selected_tiles.size(), 0);
262}
263
264TEST_F(CanvasAutomationAPITest, SelectTile_OutOfBounds) {
265 api_->SelectTile(-1, 0);
266 auto selection = api_->GetSelection();
267 EXPECT_FALSE(selection.has_selection);
268
269 api_->SelectTile(100, 100);
270 selection = api_->GetSelection();
271 EXPECT_FALSE(selection.has_selection);
272}
273
274// ============================================================================
275// View Operations Tests
276// ============================================================================
277
278TEST_F(CanvasAutomationAPITest, SetZoom_ValidRange) {
279 api_->SetZoom(1.0f);
280 EXPECT_FLOAT_EQ(api_->GetZoom(), 1.0f);
281
282 api_->SetZoom(2.0f);
283 EXPECT_FLOAT_EQ(api_->GetZoom(), 2.0f);
284
285 api_->SetZoom(0.5f);
286 EXPECT_FLOAT_EQ(api_->GetZoom(), 0.5f);
287}
288
289TEST_F(CanvasAutomationAPITest, SetZoom_Clamping) {
290 // Should clamp to 0.25 - 4.0 range
291 api_->SetZoom(10.0f);
292 EXPECT_LE(api_->GetZoom(), 4.0f);
293
294 api_->SetZoom(0.1f);
295 EXPECT_GE(api_->GetZoom(), 0.25f);
296
297 api_->SetZoom(-1.0f);
298 EXPECT_GE(api_->GetZoom(), 0.25f);
299}
300
301TEST_F(CanvasAutomationAPITest, ScrollToTile_ValidTile) {
302 // Should not crash when scrolling to valid tiles
303 api_->ScrollToTile(0, 0, true);
304 api_->ScrollToTile(10, 10, false);
305 api_->ScrollToTile(15, 15, true);
306
307 // Just verify no crash - actual scroll behavior depends on ImGui state
308}
309
310TEST_F(CanvasAutomationAPITest, ScrollToTile_OutOfBounds) {
311 // Should handle out of bounds gracefully
312 api_->ScrollToTile(-1, 0, true);
313 api_->ScrollToTile(100, 100, true);
314
315 // Should not crash
316}
317
318TEST_F(CanvasAutomationAPITest, CenterOn_ValidTile) {
319 // Should not crash when centering on valid tiles
320 api_->CenterOn(10, 10);
321 api_->CenterOn(0, 0);
322 api_->CenterOn(20, 20);
323
324 // Verify scroll position changed (should be non-zero after centering on non-origin)
325 ImVec2 scroll = canvas_->scrolling();
326 // Scroll values will depend on canvas size, just verify they're set
327}
328
329TEST_F(CanvasAutomationAPITest, CenterOn_OutOfBounds) {
330 api_->CenterOn(-1, 0);
331 api_->CenterOn(100, 100);
332
333 // Should not crash
334}
335
336// ============================================================================
337// Query Operations Tests
338// ============================================================================
339
341 canvas_->set_global_scale(1.0f);
342
343 auto dims = api_->GetDimensions();
344 EXPECT_EQ(dims.tile_size, 16); // 16x16 grid
345 EXPECT_EQ(dims.width_tiles, 32); // 512 / 16
346 EXPECT_EQ(dims.height_tiles, 32);
347}
348
349TEST_F(CanvasAutomationAPITest, GetDimensions_WithZoom) {
350 canvas_->set_global_scale(2.0f);
351
352 auto dims = api_->GetDimensions();
353 EXPECT_EQ(dims.tile_size, 16);
354 EXPECT_EQ(dims.width_tiles, 16); // 512 / (16 * 2.0)
355 EXPECT_EQ(dims.height_tiles, 16);
356}
357
358TEST_F(CanvasAutomationAPITest, GetVisibleRegion) {
359 canvas_->set_global_scale(1.0f);
360 canvas_->set_scrolling(ImVec2(0, 0));
361
362 auto region = api_->GetVisibleRegion();
363
364 // At origin with no scroll, should start at (0,0)
365 EXPECT_GE(region.min_x, 0);
366 EXPECT_GE(region.min_y, 0);
367
368 // Should have valid bounds
369 EXPECT_GE(region.max_x, region.min_x);
370 EXPECT_GE(region.max_y, region.min_y);
371}
372
373TEST_F(CanvasAutomationAPITest, IsTileVisible_AtOrigin) {
374 canvas_->set_global_scale(1.0f);
375 canvas_->set_scrolling(ImVec2(0, 0));
376
377 // Tiles at origin should be visible
378 EXPECT_TRUE(api_->IsTileVisible(0, 0));
379 EXPECT_TRUE(api_->IsTileVisible(1, 1));
380
381 // Tiles far away might not be visible (depends on canvas size)
382 // We just verify the method doesn't crash
383 api_->IsTileVisible(50, 50);
384}
385
386TEST_F(CanvasAutomationAPITest, IsTileVisible_OutOfBounds) {
387 // Out of bounds tiles should return false
388 EXPECT_FALSE(api_->IsTileVisible(-1, 0));
389 EXPECT_FALSE(api_->IsTileVisible(0, -1));
390 EXPECT_FALSE(api_->IsTileVisible(100, 100));
391}
392
393// ============================================================================
394// Integration Tests
395// ============================================================================
396
397TEST_F(CanvasAutomationAPITest, CompleteWorkflow) {
398 // Simulate a complete automation workflow
399
400 // 1. Set zoom level
401 api_->SetZoom(1.0f);
402 EXPECT_FLOAT_EQ(api_->GetZoom(), 1.0f);
403
404 // 2. Select a tile region
405 api_->SelectTileRect(0, 0, 4, 4);
406 auto selection = api_->GetSelection();
407 EXPECT_EQ(selection.selected_tiles.size(), 25);
408
409 // 3. Query tile data with callback
410 api_->SetTileQueryCallback([](int x, int y) {
411 return x + y * 100;
412 });
413
414 EXPECT_EQ(api_->GetTileAt(2, 3), 302);
415
416 // 4. Paint tiles with callback
417 std::vector<std::tuple<int, int, int>> painted;
418 api_->SetTilePaintCallback([&](int x, int y, int tile_id) {
419 painted.push_back({x, y, tile_id});
420 return true;
421 });
422
423 std::vector<std::tuple<int, int, int>> tiles = {
424 {0, 0, 10}, {1, 0, 11}, {2, 0, 12}
425 };
426 EXPECT_TRUE(api_->SetTiles(tiles));
427 EXPECT_EQ(painted.size(), 3);
428
429 // 5. Clear selection
430 api_->ClearSelection();
431 selection = api_->GetSelection();
432 EXPECT_FALSE(selection.has_selection);
433}
434
435TEST_F(CanvasAutomationAPITest, DifferentGridSizes) {
436 // Test with 8x8 grid
437 auto canvas_8x8 = std::make_unique<gui::Canvas>(
438 "Test8x8", ImVec2(512, 512), gui::CanvasGridSize::k8x8);
439 auto api_8x8 = canvas_8x8->GetAutomationAPI();
440
441 auto dims = api_8x8->GetDimensions();
442 EXPECT_EQ(dims.tile_size, 8);
443 EXPECT_EQ(dims.width_tiles, 64); // 512 / 8
444
445 // Test with 32x32 grid
446 auto canvas_32x32 = std::make_unique<gui::Canvas>(
447 "Test32x32", ImVec2(512, 512), gui::CanvasGridSize::k32x32);
448 auto api_32x32 = canvas_32x32->GetAutomationAPI();
449
450 dims = api_32x32->GetDimensions();
451 EXPECT_EQ(dims.tile_size, 32);
452 EXPECT_EQ(dims.width_tiles, 16); // 512 / 32
453}
454
455TEST_F(CanvasAutomationAPITest, MultipleZoomLevels) {
456 float zoom_levels[] = {0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 3.0f, 4.0f};
457
458 for (float zoom : zoom_levels) {
459 api_->SetZoom(zoom);
460 float actual_zoom = api_->GetZoom();
461
462 // Should be clamped to valid range
463 EXPECT_GE(actual_zoom, 0.25f);
464 EXPECT_LE(actual_zoom, 4.0f);
465
466 // Coordinate conversion should still work
467 ImVec2 canvas_pos = api_->TileToCanvas(10, 10);
468 ImVec2 tile_pos = api_->CanvasToTile(canvas_pos);
469
470 EXPECT_FLOAT_EQ(tile_pos.x, 10.0f);
471 EXPECT_FLOAT_EQ(tile_pos.y, 10.0f);
472 }
473}
474
475} // namespace test
476} // namespace yaze
477
Programmatic interface for controlling canvas operations.
ImVec2 TileToCanvas(int x, int y) const
Convert logical tile coordinates to canvas pixel coordinates.
TEST_F(DungeonObjectRenderingE2ETests, RunAllTests)
Main namespace for the application.