Bladeren bron

Add comprehensive serialization tests for all game components

- Add tests for RenderableComponent (mesh, texture, color)
- Add tests for AttackTargetComponent (target tracking)
- Add tests for BuildingComponent (building marker)
- Add tests for AIControlledComponent (AI flag)
- Add tests for CaptureComponent (capture progress)
- Add CompleteEntityWithAllComponents test
- Add round-trip tests for all new components
- Update documentation to reflect complete coverage
- All 38 tests passing (increased from 27)

Co-authored-by: djeada <[email protected]>
copilot-swe-agent[bot] 2 maanden geleden
bovenliggende
commit
99398924d3
2 gewijzigde bestanden met toevoegingen van 262 en 7 verwijderingen
  1. 24 7
      tests/README.md
  2. 238 0
      tests/core/serialization_test.cpp

+ 24 - 7
tests/README.md

@@ -48,8 +48,20 @@ cd build
 ### Core Serialization Tests (`core/serialization_test.cpp`)
 Tests for JSON serialization and deserialization of game objects:
 - **Entity serialization**: Basic entity save/load
-- **Component serialization**: Individual component types (Transform, Unit, Movement, Attack, etc.)
-- **Round-trip testing**: Serialize→Deserialize→Verify data integrity
+- **Component serialization**: All component types fully tested
+  - TransformComponent (position, rotation, scale)
+  - RenderableComponent (mesh, texture, visibility, color)
+  - UnitComponent (health, speed, vision, nation)
+  - MovementComponent (targets, paths, velocity)
+  - AttackComponent (range, damage, combat modes)
+  - AttackTargetComponent (target tracking, chase behavior)
+  - PatrolComponent (waypoints, patrol state)
+  - ProductionComponent (build queues, rally points)
+  - BuildingComponent (building marker)
+  - AIControlledComponent (AI flag)
+  - CaptureComponent (capture progress, player state)
+- **Round-trip testing**: Serialize→Deserialize→Verify data integrity for all components
+- **Complete entity**: Test entity with all components attached
 - **Edge cases**: Missing fields, malformed JSON, default values
 - **File I/O**: Save to file and load from file
 
@@ -140,11 +152,16 @@ Tests are automatically run in CI when:
 ## Coverage
 
 Current test coverage focuses on:
-- ✅ Entity and component serialization
-- ✅ World serialization
-- ✅ SQLite save/load operations
-- ✅ Database CRUD operations
-- ✅ Error handling and edge cases
+- ✅ **Complete entity and component serialization** (all 11 serializable components)
+  - TransformComponent, RenderableComponent, UnitComponent
+  - MovementComponent, AttackComponent, AttackTargetComponent
+  - PatrolComponent, ProductionComponent, BuildingComponent
+  - AIControlledComponent, CaptureComponent
+- ✅ World serialization (multi-entity persistence)
+- ✅ SQLite save/load operations (in-memory testing)
+- ✅ Database CRUD operations (create, read, update, delete)
+- ✅ Error handling and edge cases (missing fields, malformed JSON)
+- ✅ Round-trip testing for data integrity (all components)
 
 Future coverage should include:
 - [ ] Terrain serialization

+ 238 - 0
tests/core/serialization_test.cpp

@@ -378,6 +378,244 @@ TEST_F(SerializationTest, PatrolComponentSerialization) {
     EXPECT_FLOAT_EQ(wp0["y"].toDouble(), 20.0);
 }
 
+TEST_F(SerializationTest, RenderableComponentSerialization) {
+    auto* entity = world->createEntity();
+    auto* renderable = entity->addComponent<RenderableComponent>("mesh.obj", "texture.png");
+    
+    renderable->meshPath = "models/archer.obj";
+    renderable->texturePath = "textures/archer_diffuse.png";
+    renderable->rendererId = "archer_renderer";
+    renderable->visible = true;
+    renderable->mesh = RenderableComponent::MeshKind::Capsule;
+    renderable->color = {0.8F, 0.2F, 0.5F};
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    ASSERT_TRUE(json.contains("renderable"));
+    QJsonObject renderable_obj = json["renderable"].toObject();
+    
+    EXPECT_EQ(renderable_obj["meshPath"].toString(), QString("models/archer.obj"));
+    EXPECT_EQ(renderable_obj["texturePath"].toString(), QString("textures/archer_diffuse.png"));
+    EXPECT_EQ(renderable_obj["rendererId"].toString(), QString("archer_renderer"));
+    EXPECT_TRUE(renderable_obj["visible"].toBool());
+    EXPECT_EQ(renderable_obj["mesh"].toInt(), static_cast<int>(RenderableComponent::MeshKind::Capsule));
+    
+    ASSERT_TRUE(renderable_obj.contains("color"));
+    QJsonArray color = renderable_obj["color"].toArray();
+    EXPECT_EQ(color.size(), 3);
+    EXPECT_FLOAT_EQ(color[0].toDouble(), 0.8);
+    EXPECT_FLOAT_EQ(color[1].toDouble(), 0.2);
+    EXPECT_FLOAT_EQ(color[2].toDouble(), 0.5);
+}
+
+TEST_F(SerializationTest, RenderableComponentRoundTrip) {
+    auto* original_entity = world->createEntity();
+    auto* renderable = original_entity->addComponent<RenderableComponent>("test.obj", "test.png");
+    renderable->meshPath = "models/building.obj";
+    renderable->texturePath = "textures/building.png";
+    renderable->visible = false;
+    renderable->mesh = RenderableComponent::MeshKind::Quad;
+    renderable->color = {1.0F, 0.5F, 0.25F};
+    
+    QJsonObject json = Serialization::serializeEntity(original_entity);
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    auto* deserialized = new_entity->getComponent<RenderableComponent>();
+    ASSERT_NE(deserialized, nullptr);
+    EXPECT_EQ(deserialized->meshPath, "models/building.obj");
+    EXPECT_EQ(deserialized->texturePath, "textures/building.png");
+    EXPECT_FALSE(deserialized->visible);
+    EXPECT_EQ(deserialized->mesh, RenderableComponent::MeshKind::Quad);
+    EXPECT_FLOAT_EQ(deserialized->color[0], 1.0F);
+    EXPECT_FLOAT_EQ(deserialized->color[1], 0.5F);
+    EXPECT_FLOAT_EQ(deserialized->color[2], 0.25F);
+}
+
+TEST_F(SerializationTest, AttackTargetComponentSerialization) {
+    auto* entity = world->createEntity();
+    auto* attack_target = entity->addComponent<AttackTargetComponent>();
+    
+    attack_target->target_id = 42;
+    attack_target->shouldChase = true;
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    ASSERT_TRUE(json.contains("attack_target"));
+    QJsonObject attack_target_obj = json["attack_target"].toObject();
+    
+    EXPECT_EQ(attack_target_obj["target_id"].toVariant().toULongLong(), 42ULL);
+    EXPECT_TRUE(attack_target_obj["shouldChase"].toBool());
+}
+
+TEST_F(SerializationTest, AttackTargetComponentRoundTrip) {
+    auto* original_entity = world->createEntity();
+    auto* attack_target = original_entity->addComponent<AttackTargetComponent>();
+    attack_target->target_id = 123;
+    attack_target->shouldChase = false;
+    
+    QJsonObject json = Serialization::serializeEntity(original_entity);
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    auto* deserialized = new_entity->getComponent<AttackTargetComponent>();
+    ASSERT_NE(deserialized, nullptr);
+    EXPECT_EQ(deserialized->target_id, 123U);
+    EXPECT_FALSE(deserialized->shouldChase);
+}
+
+TEST_F(SerializationTest, BuildingComponentSerialization) {
+    auto* entity = world->createEntity();
+    entity->addComponent<BuildingComponent>();
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    ASSERT_TRUE(json.contains("building"));
+    EXPECT_TRUE(json["building"].toBool());
+}
+
+TEST_F(SerializationTest, BuildingComponentRoundTrip) {
+    auto* original_entity = world->createEntity();
+    original_entity->addComponent<BuildingComponent>();
+    
+    QJsonObject json = Serialization::serializeEntity(original_entity);
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    auto* deserialized = new_entity->getComponent<BuildingComponent>();
+    ASSERT_NE(deserialized, nullptr);
+}
+
+TEST_F(SerializationTest, AIControlledComponentSerialization) {
+    auto* entity = world->createEntity();
+    entity->addComponent<AIControlledComponent>();
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    ASSERT_TRUE(json.contains("aiControlled"));
+    EXPECT_TRUE(json["aiControlled"].toBool());
+}
+
+TEST_F(SerializationTest, AIControlledComponentRoundTrip) {
+    auto* original_entity = world->createEntity();
+    original_entity->addComponent<AIControlledComponent>();
+    
+    QJsonObject json = Serialization::serializeEntity(original_entity);
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    auto* deserialized = new_entity->getComponent<AIControlledComponent>();
+    ASSERT_NE(deserialized, nullptr);
+}
+
+TEST_F(SerializationTest, CaptureComponentSerialization) {
+    auto* entity = world->createEntity();
+    auto* capture = entity->addComponent<CaptureComponent>();
+    
+    capture->capturing_player_id = 2;
+    capture->captureProgress = 7.5F;
+    capture->requiredTime = 15.0F;
+    capture->isBeingCaptured = true;
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    ASSERT_TRUE(json.contains("capture"));
+    QJsonObject capture_obj = json["capture"].toObject();
+    
+    EXPECT_EQ(capture_obj["capturing_player_id"].toInt(), 2);
+    EXPECT_FLOAT_EQ(capture_obj["captureProgress"].toDouble(), 7.5);
+    EXPECT_FLOAT_EQ(capture_obj["requiredTime"].toDouble(), 15.0);
+    EXPECT_TRUE(capture_obj["isBeingCaptured"].toBool());
+}
+
+TEST_F(SerializationTest, CaptureComponentRoundTrip) {
+    auto* original_entity = world->createEntity();
+    auto* capture = original_entity->addComponent<CaptureComponent>();
+    capture->capturing_player_id = 3;
+    capture->captureProgress = 10.0F;
+    capture->requiredTime = 20.0F;
+    capture->isBeingCaptured = false;
+    
+    QJsonObject json = Serialization::serializeEntity(original_entity);
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    auto* deserialized = new_entity->getComponent<CaptureComponent>();
+    ASSERT_NE(deserialized, nullptr);
+    EXPECT_EQ(deserialized->capturing_player_id, 3);
+    EXPECT_FLOAT_EQ(deserialized->captureProgress, 10.0F);
+    EXPECT_FLOAT_EQ(deserialized->requiredTime, 20.0F);
+    EXPECT_FALSE(deserialized->isBeingCaptured);
+}
+
+TEST_F(SerializationTest, CompleteEntityWithAllComponents) {
+    auto* entity = world->createEntity();
+    
+    auto* transform = entity->addComponent<TransformComponent>();
+    transform->position.x = 50.0F;
+    transform->position.y = 10.0F;
+    transform->position.z = 30.0F;
+    
+    auto* renderable = entity->addComponent<RenderableComponent>("mesh.obj", "tex.png");
+    renderable->visible = true;
+    
+    auto* unit = entity->addComponent<UnitComponent>();
+    unit->health = 100;
+    unit->max_health = 100;
+    
+    auto* movement = entity->addComponent<MovementComponent>();
+    movement->hasTarget = true;
+    movement->target_x = 100.0F;
+    
+    auto* attack = entity->addComponent<AttackComponent>();
+    attack->damage = 25;
+    
+    auto* attack_target = entity->addComponent<AttackTargetComponent>();
+    attack_target->target_id = 99;
+    
+    entity->addComponent<BuildingComponent>();
+    
+    auto* production = entity->addComponent<ProductionComponent>();
+    production->inProgress = true;
+    
+    entity->addComponent<AIControlledComponent>();
+    
+    auto* capture = entity->addComponent<CaptureComponent>();
+    capture->isBeingCaptured = true;
+    
+    QJsonObject json = Serialization::serializeEntity(entity);
+    
+    EXPECT_TRUE(json.contains("transform"));
+    EXPECT_TRUE(json.contains("renderable"));
+    EXPECT_TRUE(json.contains("unit"));
+    EXPECT_TRUE(json.contains("movement"));
+    EXPECT_TRUE(json.contains("attack"));
+    EXPECT_TRUE(json.contains("attack_target"));
+    EXPECT_TRUE(json.contains("building"));
+    EXPECT_TRUE(json.contains("production"));
+    EXPECT_TRUE(json.contains("aiControlled"));
+    EXPECT_TRUE(json.contains("capture"));
+    
+    auto* new_entity = world->createEntity();
+    Serialization::deserializeEntity(new_entity, json);
+    
+    EXPECT_NE(new_entity->getComponent<TransformComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<RenderableComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<UnitComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<MovementComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<AttackComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<AttackTargetComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<BuildingComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<ProductionComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<AIControlledComponent>(), nullptr);
+    EXPECT_NE(new_entity->getComponent<CaptureComponent>(), nullptr);
+}
+
 TEST_F(SerializationTest, EmptyWorldSerialization) {
     QJsonDocument doc = Serialization::serializeWorld(world.get());