serialization_test.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. #include <gtest/gtest.h>
  2. #include <QJsonDocument>
  3. #include <QJsonObject>
  4. #include <QJsonArray>
  5. #include <QTemporaryFile>
  6. #include "core/entity.h"
  7. #include "core/world.h"
  8. #include "core/component.h"
  9. #include "core/serialization.h"
  10. #include "systems/nation_id.h"
  11. #include "units/spawn_type.h"
  12. #include "units/troop_type.h"
  13. using namespace Engine::Core;
  14. class SerializationTest : public ::testing::Test {
  15. protected:
  16. void SetUp() override {
  17. world = std::make_unique<World>();
  18. }
  19. void TearDown() override {
  20. world.reset();
  21. }
  22. std::unique_ptr<World> world;
  23. };
  24. TEST_F(SerializationTest, EntitySerializationBasic) {
  25. auto* entity = world->createEntity();
  26. ASSERT_NE(entity, nullptr);
  27. auto entity_id = entity->getId();
  28. QJsonObject json = Serialization::serializeEntity(entity);
  29. EXPECT_TRUE(json.contains("id"));
  30. EXPECT_EQ(json["id"].toVariant().toULongLong(), static_cast<qulonglong>(entity_id));
  31. }
  32. TEST_F(SerializationTest, TransformComponentSerialization) {
  33. auto* entity = world->createEntity();
  34. auto* transform = entity->addComponent<TransformComponent>();
  35. transform->position.x = 10.5F;
  36. transform->position.y = 20.3F;
  37. transform->position.z = 30.1F;
  38. transform->rotation.x = 0.5F;
  39. transform->rotation.y = 1.0F;
  40. transform->rotation.z = 1.5F;
  41. transform->scale.x = 2.0F;
  42. transform->scale.y = 2.5F;
  43. transform->scale.z = 3.0F;
  44. transform->hasDesiredYaw = true;
  45. transform->desiredYaw = 45.0F;
  46. QJsonObject json = Serialization::serializeEntity(entity);
  47. ASSERT_TRUE(json.contains("transform"));
  48. QJsonObject transform_obj = json["transform"].toObject();
  49. EXPECT_FLOAT_EQ(transform_obj["posX"].toDouble(), 10.5);
  50. EXPECT_FLOAT_EQ(transform_obj["posY"].toDouble(), 20.3);
  51. EXPECT_FLOAT_EQ(transform_obj["posZ"].toDouble(), 30.1);
  52. EXPECT_FLOAT_EQ(transform_obj["rotX"].toDouble(), 0.5);
  53. EXPECT_FLOAT_EQ(transform_obj["rotY"].toDouble(), 1.0);
  54. EXPECT_FLOAT_EQ(transform_obj["rotZ"].toDouble(), 1.5);
  55. EXPECT_FLOAT_EQ(transform_obj["scale_x"].toDouble(), 2.0);
  56. EXPECT_FLOAT_EQ(transform_obj["scaleY"].toDouble(), 2.5);
  57. EXPECT_FLOAT_EQ(transform_obj["scale_z"].toDouble(), 3.0);
  58. EXPECT_TRUE(transform_obj["hasDesiredYaw"].toBool());
  59. EXPECT_FLOAT_EQ(transform_obj["desiredYaw"].toDouble(), 45.0);
  60. }
  61. TEST_F(SerializationTest, UnitComponentSerialization) {
  62. auto* entity = world->createEntity();
  63. auto* unit = entity->addComponent<UnitComponent>();
  64. unit->health = 80;
  65. unit->max_health = 100;
  66. unit->speed = 5.5F;
  67. unit->vision_range = 15.0F;
  68. unit->spawn_type = Game::Units::SpawnType::Archer;
  69. unit->owner_id = 1;
  70. unit->nation_id = Game::Systems::NationID::RomanRepublic;
  71. QJsonObject json = Serialization::serializeEntity(entity);
  72. ASSERT_TRUE(json.contains("unit"));
  73. QJsonObject unit_obj = json["unit"].toObject();
  74. EXPECT_EQ(unit_obj["health"].toInt(), 80);
  75. EXPECT_EQ(unit_obj["max_health"].toInt(), 100);
  76. EXPECT_FLOAT_EQ(unit_obj["speed"].toDouble(), 5.5);
  77. EXPECT_FLOAT_EQ(unit_obj["vision_range"].toDouble(), 15.0);
  78. EXPECT_EQ(unit_obj["unit_type"].toString(), QString("archer"));
  79. EXPECT_EQ(unit_obj["owner_id"].toInt(), 1);
  80. EXPECT_EQ(unit_obj["nation_id"].toString(), QString("roman_republic"));
  81. }
  82. TEST_F(SerializationTest, MovementComponentSerialization) {
  83. auto* entity = world->createEntity();
  84. auto* movement = entity->addComponent<MovementComponent>();
  85. movement->hasTarget = true;
  86. movement->target_x = 50.0F;
  87. movement->target_y = 60.0F;
  88. movement->goalX = 55.0F;
  89. movement->goalY = 65.0F;
  90. movement->vx = 1.5F;
  91. movement->vz = 2.0F;
  92. movement->pathPending = false;
  93. movement->pendingRequestId = 42;
  94. movement->repathCooldown = 1.0F;
  95. movement->lastGoalX = 45.0F;
  96. movement->lastGoalY = 55.0F;
  97. movement->timeSinceLastPathRequest = 0.5F;
  98. movement->path.emplace_back(10.0F, 20.0F);
  99. movement->path.emplace_back(30.0F, 40.0F);
  100. QJsonObject json = Serialization::serializeEntity(entity);
  101. ASSERT_TRUE(json.contains("movement"));
  102. QJsonObject movement_obj = json["movement"].toObject();
  103. EXPECT_TRUE(movement_obj["hasTarget"].toBool());
  104. EXPECT_FLOAT_EQ(movement_obj["target_x"].toDouble(), 50.0);
  105. EXPECT_FLOAT_EQ(movement_obj["target_y"].toDouble(), 60.0);
  106. EXPECT_FLOAT_EQ(movement_obj["goalX"].toDouble(), 55.0);
  107. EXPECT_FLOAT_EQ(movement_obj["goalY"].toDouble(), 65.0);
  108. EXPECT_FLOAT_EQ(movement_obj["vx"].toDouble(), 1.5);
  109. EXPECT_FLOAT_EQ(movement_obj["vz"].toDouble(), 2.0);
  110. EXPECT_FALSE(movement_obj["pathPending"].toBool());
  111. EXPECT_EQ(movement_obj["pendingRequestId"].toVariant().toULongLong(), 42ULL);
  112. ASSERT_TRUE(movement_obj.contains("path"));
  113. QJsonArray path_array = movement_obj["path"].toArray();
  114. ASSERT_EQ(path_array.size(), 2);
  115. QJsonObject waypoint1 = path_array[0].toObject();
  116. EXPECT_FLOAT_EQ(waypoint1["x"].toDouble(), 10.0);
  117. EXPECT_FLOAT_EQ(waypoint1["y"].toDouble(), 20.0);
  118. QJsonObject waypoint2 = path_array[1].toObject();
  119. EXPECT_FLOAT_EQ(waypoint2["x"].toDouble(), 30.0);
  120. EXPECT_FLOAT_EQ(waypoint2["y"].toDouble(), 40.0);
  121. }
  122. TEST_F(SerializationTest, AttackComponentSerialization) {
  123. auto* entity = world->createEntity();
  124. auto* attack = entity->addComponent<AttackComponent>();
  125. attack->range = 10.0F;
  126. attack->damage = 25;
  127. attack->cooldown = 2.0F;
  128. attack->timeSinceLast = 0.5F;
  129. attack->meleeRange = 2.0F;
  130. attack->meleeDamage = 15;
  131. attack->meleeCooldown = 1.5F;
  132. attack->preferredMode = AttackComponent::CombatMode::Ranged;
  133. attack->currentMode = AttackComponent::CombatMode::Ranged;
  134. attack->canMelee = true;
  135. attack->canRanged = true;
  136. attack->max_heightDifference = 5.0F;
  137. attack->inMeleeLock = false;
  138. attack->meleeLockTargetId = 0;
  139. QJsonObject json = Serialization::serializeEntity(entity);
  140. ASSERT_TRUE(json.contains("attack"));
  141. QJsonObject attack_obj = json["attack"].toObject();
  142. EXPECT_FLOAT_EQ(attack_obj["range"].toDouble(), 10.0);
  143. EXPECT_EQ(attack_obj["damage"].toInt(), 25);
  144. EXPECT_FLOAT_EQ(attack_obj["cooldown"].toDouble(), 2.0);
  145. EXPECT_FLOAT_EQ(attack_obj["timeSinceLast"].toDouble(), 0.5);
  146. EXPECT_FLOAT_EQ(attack_obj["meleeRange"].toDouble(), 2.0);
  147. EXPECT_EQ(attack_obj["meleeDamage"].toInt(), 15);
  148. EXPECT_FLOAT_EQ(attack_obj["meleeCooldown"].toDouble(), 1.5);
  149. EXPECT_EQ(attack_obj["preferredMode"].toString(), QString("ranged"));
  150. EXPECT_EQ(attack_obj["currentMode"].toString(), QString("ranged"));
  151. EXPECT_TRUE(attack_obj["canMelee"].toBool());
  152. EXPECT_TRUE(attack_obj["canRanged"].toBool());
  153. EXPECT_FLOAT_EQ(attack_obj["max_heightDifference"].toDouble(), 5.0);
  154. EXPECT_FALSE(attack_obj["inMeleeLock"].toBool());
  155. }
  156. TEST_F(SerializationTest, EntityDeserializationRoundTrip) {
  157. auto* original_entity = world->createEntity();
  158. auto* transform = original_entity->addComponent<TransformComponent>();
  159. transform->position.x = 100.0F;
  160. transform->position.y = 200.0F;
  161. transform->position.z = 300.0F;
  162. auto* unit = original_entity->addComponent<UnitComponent>();
  163. unit->health = 75;
  164. unit->max_health = 100;
  165. unit->speed = 6.0F;
  166. QJsonObject json = Serialization::serializeEntity(original_entity);
  167. auto* new_entity = world->createEntity();
  168. Serialization::deserializeEntity(new_entity, json);
  169. auto* deserialized_transform = new_entity->getComponent<TransformComponent>();
  170. ASSERT_NE(deserialized_transform, nullptr);
  171. EXPECT_FLOAT_EQ(deserialized_transform->position.x, 100.0F);
  172. EXPECT_FLOAT_EQ(deserialized_transform->position.y, 200.0F);
  173. EXPECT_FLOAT_EQ(deserialized_transform->position.z, 300.0F);
  174. auto* deserialized_unit = new_entity->getComponent<UnitComponent>();
  175. ASSERT_NE(deserialized_unit, nullptr);
  176. EXPECT_EQ(deserialized_unit->health, 75);
  177. EXPECT_EQ(deserialized_unit->max_health, 100);
  178. EXPECT_FLOAT_EQ(deserialized_unit->speed, 6.0F);
  179. }
  180. TEST_F(SerializationTest, DeserializationWithMissingFields) {
  181. QJsonObject json;
  182. json["id"] = 1;
  183. QJsonObject unit_obj;
  184. unit_obj["health"] = 50;
  185. json["unit"] = unit_obj;
  186. auto* entity = world->createEntity();
  187. Serialization::deserializeEntity(entity, json);
  188. auto* unit = entity->getComponent<UnitComponent>();
  189. ASSERT_NE(unit, nullptr);
  190. EXPECT_EQ(unit->health, 50);
  191. EXPECT_EQ(unit->max_health, Defaults::kUnitDefaultHealth);
  192. }
  193. TEST_F(SerializationTest, DeserializationWithMalformedJSON) {
  194. QJsonObject json;
  195. json["id"] = 1;
  196. QJsonObject transform_obj;
  197. transform_obj["posX"] = "not_a_number";
  198. json["transform"] = transform_obj;
  199. auto* entity = world->createEntity();
  200. EXPECT_NO_THROW({
  201. Serialization::deserializeEntity(entity, json);
  202. });
  203. auto* transform = entity->getComponent<TransformComponent>();
  204. ASSERT_NE(transform, nullptr);
  205. EXPECT_FLOAT_EQ(transform->position.x, 0.0F);
  206. }
  207. TEST_F(SerializationTest, WorldSerializationRoundTrip) {
  208. auto* entity1 = world->createEntity();
  209. auto* transform1 = entity1->addComponent<TransformComponent>();
  210. transform1->position.x = 10.0F;
  211. auto* entity2 = world->createEntity();
  212. auto* transform2 = entity2->addComponent<TransformComponent>();
  213. transform2->position.x = 20.0F;
  214. QJsonDocument doc = Serialization::serializeWorld(world.get());
  215. ASSERT_TRUE(doc.isObject());
  216. QJsonObject world_obj = doc.object();
  217. EXPECT_TRUE(world_obj.contains("entities"));
  218. EXPECT_TRUE(world_obj.contains("nextEntityId"));
  219. EXPECT_TRUE(world_obj.contains("schemaVersion"));
  220. auto new_world = std::make_unique<World>();
  221. Serialization::deserializeWorld(new_world.get(), doc);
  222. const auto& entities = new_world->getEntities();
  223. EXPECT_EQ(entities.size(), 2UL);
  224. }
  225. TEST_F(SerializationTest, SaveAndLoadFromFile) {
  226. auto* entity = world->createEntity();
  227. auto* transform = entity->addComponent<TransformComponent>();
  228. transform->position.x = 42.0F;
  229. transform->position.y = 43.0F;
  230. transform->position.z = 44.0F;
  231. QJsonDocument doc = Serialization::serializeWorld(world.get());
  232. QTemporaryFile temp_file;
  233. ASSERT_TRUE(temp_file.open());
  234. QString filename = temp_file.fileName();
  235. temp_file.close();
  236. EXPECT_TRUE(Serialization::saveToFile(filename, doc));
  237. QJsonDocument loaded_doc = Serialization::loadFromFile(filename);
  238. EXPECT_FALSE(loaded_doc.isNull());
  239. auto new_world = std::make_unique<World>();
  240. Serialization::deserializeWorld(new_world.get(), loaded_doc);
  241. const auto& entities = new_world->getEntities();
  242. EXPECT_EQ(entities.size(), 1UL);
  243. if (!entities.empty()) {
  244. auto* loaded_entity = entities.begin()->second.get();
  245. auto* loaded_transform = loaded_entity->getComponent<TransformComponent>();
  246. ASSERT_NE(loaded_transform, nullptr);
  247. EXPECT_FLOAT_EQ(loaded_transform->position.x, 42.0F);
  248. EXPECT_FLOAT_EQ(loaded_transform->position.y, 43.0F);
  249. EXPECT_FLOAT_EQ(loaded_transform->position.z, 44.0F);
  250. }
  251. }
  252. TEST_F(SerializationTest, ProductionComponentSerialization) {
  253. auto* entity = world->createEntity();
  254. auto* production = entity->addComponent<ProductionComponent>();
  255. production->inProgress = true;
  256. production->buildTime = 10.0F;
  257. production->timeRemaining = 5.0F;
  258. production->producedCount = 3;
  259. production->maxUnits = 10;
  260. production->product_type = Game::Units::TroopType::Archer;
  261. production->rallyX = 100.0F;
  262. production->rallyZ = 200.0F;
  263. production->rallySet = true;
  264. production->villagerCost = 2;
  265. production->productionQueue.push_back(Game::Units::TroopType::Spearman);
  266. production->productionQueue.push_back(Game::Units::TroopType::Archer);
  267. QJsonObject json = Serialization::serializeEntity(entity);
  268. ASSERT_TRUE(json.contains("production"));
  269. QJsonObject prod_obj = json["production"].toObject();
  270. EXPECT_TRUE(prod_obj["inProgress"].toBool());
  271. EXPECT_FLOAT_EQ(prod_obj["buildTime"].toDouble(), 10.0);
  272. EXPECT_FLOAT_EQ(prod_obj["timeRemaining"].toDouble(), 5.0);
  273. EXPECT_EQ(prod_obj["producedCount"].toInt(), 3);
  274. EXPECT_EQ(prod_obj["maxUnits"].toInt(), 10);
  275. EXPECT_EQ(prod_obj["product_type"].toString(), QString("archer"));
  276. EXPECT_FLOAT_EQ(prod_obj["rallyX"].toDouble(), 100.0);
  277. EXPECT_FLOAT_EQ(prod_obj["rallyZ"].toDouble(), 200.0);
  278. EXPECT_TRUE(prod_obj["rallySet"].toBool());
  279. EXPECT_EQ(prod_obj["villagerCost"].toInt(), 2);
  280. ASSERT_TRUE(prod_obj.contains("queue"));
  281. QJsonArray queue = prod_obj["queue"].toArray();
  282. EXPECT_EQ(queue.size(), 2);
  283. EXPECT_EQ(queue[0].toString(), QString("spearman"));
  284. EXPECT_EQ(queue[1].toString(), QString("archer"));
  285. }
  286. TEST_F(SerializationTest, PatrolComponentSerialization) {
  287. auto* entity = world->createEntity();
  288. auto* patrol = entity->addComponent<PatrolComponent>();
  289. patrol->currentWaypoint = 1;
  290. patrol->patrolling = true;
  291. patrol->waypoints.emplace_back(10.0F, 20.0F);
  292. patrol->waypoints.emplace_back(30.0F, 40.0F);
  293. patrol->waypoints.emplace_back(50.0F, 60.0F);
  294. QJsonObject json = Serialization::serializeEntity(entity);
  295. ASSERT_TRUE(json.contains("patrol"));
  296. QJsonObject patrol_obj = json["patrol"].toObject();
  297. EXPECT_EQ(patrol_obj["currentWaypoint"].toInt(), 1);
  298. EXPECT_TRUE(patrol_obj["patrolling"].toBool());
  299. ASSERT_TRUE(patrol_obj.contains("waypoints"));
  300. QJsonArray waypoints = patrol_obj["waypoints"].toArray();
  301. EXPECT_EQ(waypoints.size(), 3);
  302. QJsonObject wp0 = waypoints[0].toObject();
  303. EXPECT_FLOAT_EQ(wp0["x"].toDouble(), 10.0);
  304. EXPECT_FLOAT_EQ(wp0["y"].toDouble(), 20.0);
  305. }
  306. TEST_F(SerializationTest, EmptyWorldSerialization) {
  307. QJsonDocument doc = Serialization::serializeWorld(world.get());
  308. ASSERT_TRUE(doc.isObject());
  309. QJsonObject world_obj = doc.object();
  310. EXPECT_TRUE(world_obj.contains("entities"));
  311. QJsonArray entities = world_obj["entities"].toArray();
  312. EXPECT_EQ(entities.size(), 0);
  313. }