/** * @file object_selection_integration_test.cc * @brief Integration tests for ObjectSelection + DungeonObjectInteraction * * These tests verify the unified selection system works correctly when * integrated with the dungeon editor interaction layer. */ #include #include "app/editor/dungeon/dungeon_object_interaction.h" #include "app/editor/dungeon/object_selection.h" #include "app/gui/canvas/canvas.h" #include "zelda3/dungeon/room.h" #include "zelda3/dungeon/room_object.h" namespace yaze { namespace editor { namespace { class ObjectSelectionIntegrationTest : public ::testing::Test { protected: void SetUp() override { // Initialize rooms with some test objects auto& room = rooms_[0]; room.AddTileObject(zelda3::RoomObject{0x01, 10, 10, 0x12, 0}); room.AddTileObject(zelda3::RoomObject{0x02, 20, 10, 0x14, 0}); room.AddTileObject(zelda3::RoomObject{0x03, 10, 20, 0x16, 1}); room.AddTileObject(zelda3::RoomObject{0x04, 30, 30, 0x18, 2}); // Set up interaction with the room interaction_.SetCurrentRoom(&rooms_, 0); } std::array rooms_; gui::Canvas canvas_{"TestCanvas", ImVec2(512, 512)}; DungeonObjectInteraction interaction_{&canvas_}; }; // ============================================================================= // Basic Selection Tests // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, InitialStateHasNoSelection) { EXPECT_TRUE(interaction_.GetSelectedObjectIndices().empty()); EXPECT_EQ(interaction_.GetSelectionCount(), 0); EXPECT_FALSE(interaction_.IsObjectSelectActive()); } TEST_F(ObjectSelectionIntegrationTest, SetSelectedObjectsUpdatesSelection) { std::vector indices = {0, 2}; interaction_.SetSelectedObjects(indices); auto selected = interaction_.GetSelectedObjectIndices(); EXPECT_EQ(selected.size(), 2); EXPECT_TRUE(interaction_.IsObjectSelected(0)); EXPECT_FALSE(interaction_.IsObjectSelected(1)); EXPECT_TRUE(interaction_.IsObjectSelected(2)); EXPECT_FALSE(interaction_.IsObjectSelected(3)); } TEST_F(ObjectSelectionIntegrationTest, ClearSelectionRemovesAllSelections) { interaction_.SetSelectedObjects({0, 1, 2}); EXPECT_EQ(interaction_.GetSelectionCount(), 3); interaction_.ClearSelection(); EXPECT_EQ(interaction_.GetSelectionCount(), 0); EXPECT_TRUE(interaction_.GetSelectedObjectIndices().empty()); } TEST_F(ObjectSelectionIntegrationTest, IsObjectSelectedReturnsCorrectValue) { interaction_.SetSelectedObjects({1, 3}); EXPECT_FALSE(interaction_.IsObjectSelected(0)); EXPECT_TRUE(interaction_.IsObjectSelected(1)); EXPECT_FALSE(interaction_.IsObjectSelected(2)); EXPECT_TRUE(interaction_.IsObjectSelected(3)); } // ============================================================================= // Selection Callback Tests // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, SelectionCallbackFires) { int callback_count = 0; interaction_.SetSelectionChangeCallback([&callback_count]() { callback_count++; }); // Setting selection should trigger callback interaction_.SetSelectedObjects({0}); EXPECT_GE(callback_count, 1); int count_after_first = callback_count; // Clearing selection should also trigger callback interaction_.ClearSelection(); EXPECT_GT(callback_count, count_after_first); } TEST_F(ObjectSelectionIntegrationTest, MultipleSelectionChangesFireMultipleCallbacks) { std::vector> callback_selections; interaction_.SetSelectionChangeCallback([this, &callback_selections]() { callback_selections.push_back(interaction_.GetSelectedObjectIndices()); }); interaction_.SetSelectedObjects({0}); interaction_.SetSelectedObjects({0, 1}); interaction_.SetSelectedObjects({2}); interaction_.ClearSelection(); // Should have received multiple callbacks EXPECT_GE(callback_selections.size(), 2); } // ============================================================================= // Selection Count Tests // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, GetSelectionCountReturnsCorrectCount) { EXPECT_EQ(interaction_.GetSelectionCount(), 0); interaction_.SetSelectedObjects({0}); EXPECT_EQ(interaction_.GetSelectionCount(), 1); interaction_.SetSelectedObjects({0, 1, 2}); EXPECT_EQ(interaction_.GetSelectionCount(), 3); interaction_.SetSelectedObjects({0, 1, 2, 3}); EXPECT_EQ(interaction_.GetSelectionCount(), 4); } // ============================================================================= // Selection Mode Tests (via SetSelectedObjects behavior) // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, SetSelectedObjectsReplacesPreviousSelection) { interaction_.SetSelectedObjects({0, 1}); EXPECT_EQ(interaction_.GetSelectionCount(), 2); EXPECT_TRUE(interaction_.IsObjectSelected(0)); EXPECT_TRUE(interaction_.IsObjectSelected(1)); // Setting new selection should replace, not add interaction_.SetSelectedObjects({2, 3}); EXPECT_EQ(interaction_.GetSelectionCount(), 2); EXPECT_FALSE(interaction_.IsObjectSelected(0)); EXPECT_FALSE(interaction_.IsObjectSelected(1)); EXPECT_TRUE(interaction_.IsObjectSelected(2)); EXPECT_TRUE(interaction_.IsObjectSelected(3)); } TEST_F(ObjectSelectionIntegrationTest, DuplicateIndicesAreHandled) { // Setting the same index twice should only count once (using set internally) interaction_.SetSelectedObjects({0, 0, 0, 1, 1}); // Should have 2 unique selections, not 5 EXPECT_EQ(interaction_.GetSelectionCount(), 2); } // ============================================================================= // Integration with Room Data // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, SelectionPersistsAcrossRoomAccess) { interaction_.SetSelectedObjects({0, 2}); // Access room data (simulating what ObjectEditorPanel would do) auto& room = rooms_[0]; const auto& objects = room.GetTileObjects(); EXPECT_EQ(objects.size(), 4); // Selection should still be valid EXPECT_EQ(interaction_.GetSelectionCount(), 2); EXPECT_TRUE(interaction_.IsObjectSelected(0)); EXPECT_TRUE(interaction_.IsObjectSelected(2)); } TEST_F(ObjectSelectionIntegrationTest, OutOfBoundsIndicesAreAccepted) { // The selection system accepts indices without validating against room size // This is intentional - the room might not be loaded yet interaction_.SetSelectedObjects({100, 200}); EXPECT_EQ(interaction_.GetSelectionCount(), 2); EXPECT_TRUE(interaction_.IsObjectSelected(100)); } // ============================================================================= // IsObjectSelectActive Tests // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, IsObjectSelectActiveWhenHasSelection) { EXPECT_FALSE(interaction_.IsObjectSelectActive()); interaction_.SetSelectedObjects({0}); EXPECT_TRUE(interaction_.IsObjectSelectActive()); interaction_.ClearSelection(); EXPECT_FALSE(interaction_.IsObjectSelectActive()); } // ============================================================================= // Empty Selection Tests // ============================================================================= TEST_F(ObjectSelectionIntegrationTest, EmptyVectorClearsSelection) { interaction_.SetSelectedObjects({0, 1, 2}); EXPECT_EQ(interaction_.GetSelectionCount(), 3); interaction_.SetSelectedObjects({}); EXPECT_EQ(interaction_.GetSelectionCount(), 0); } TEST_F(ObjectSelectionIntegrationTest, ClearSelectionIsIdempotent) { interaction_.ClearSelection(); EXPECT_EQ(interaction_.GetSelectionCount(), 0); interaction_.ClearSelection(); EXPECT_EQ(interaction_.GetSelectionCount(), 0); interaction_.SetSelectedObjects({0}); interaction_.ClearSelection(); interaction_.ClearSelection(); EXPECT_EQ(interaction_.GetSelectionCount(), 0); } } // namespace } // namespace editor } // namespace yaze