6#include <catch2/catch_test_macros.hpp>
7#include "hellfire/serializers/Serializer.h"
8#include <glm/gtc/epsilon.hpp>
11#include "catch2/matchers/catch_matchers_floating_point.hpp"
14using Catch::Matchers::WithinAbs;
17bool vec3_equal(
const glm::vec3 &a,
const glm::vec3 &b,
float epsilon = 0.0001f) {
18 return glm::all(glm::epsilonEqual(a, b, epsilon));
21TEST_CASE(
"TransformComponent serialization",
"[serialization][transform]") {
22 SECTION(
"Round-trip preserves default values") {
23 TransformComponent original;
26 REQUIRE(Serializer<TransformComponent>::serialize(ss, &original));
28 TransformComponent loaded;
29 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &loaded));
31 CHECK(vec3_equal(loaded.get_position(), glm::vec3(0, 0, 0)));
32 CHECK(vec3_equal(loaded.get_rotation(), glm::vec3(0, 0, 0)));
33 CHECK(vec3_equal(loaded.get_scale(), glm::vec3(1, 1, 1)));
36 SECTION(
"Round-trip preserves custom values") {
37 TransformComponent original;
38 original.set_position(1.5f, -25.6f, 100.0f);
39 original.set_rotation(45.0f, 90.0f, 180.0f);
40 original.set_scale(2.0f, 0.5f, 1.0f);
43 REQUIRE(Serializer<TransformComponent>::serialize(ss, &original));
45 TransformComponent loaded;
46 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &loaded));
48 CHECK(vec3_equal(loaded.get_position(), original.get_position()));
49 CHECK(vec3_equal(loaded.get_rotation(), original.get_rotation()));
50 CHECK(vec3_equal(loaded.get_scale(), original.get_scale()));
53 SECTION(
"Handles extreme values") {
54 TransformComponent original;
55 original.set_position(FLT_MAX, FLT_MIN, -FLT_MAX);
56 original.set_rotation(360.0f, -360.0f, 720.0f);
57 original.set_scale(0.000001f, 10000.0f, 1.0f);
60 REQUIRE(Serializer<TransformComponent>::serialize(ss, &original));
62 TransformComponent loaded;
63 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &loaded));
65 CHECK(vec3_equal(loaded.get_position(), original.get_position()));
66 CHECK(vec3_equal(loaded.get_rotation(), original.get_rotation()));
67 CHECK(vec3_equal(loaded.get_scale(), original.get_scale()));
70 SECTION(
"Handles negative values") {
71 TransformComponent original;
72 original.set_position(-10.0f, -20.0f, -30.0f);
73 original.set_rotation(-45.0f, -90.0f, -180.0f);
74 original.set_scale(-1.0f, -1.0f, -1.0f);
77 REQUIRE(Serializer<TransformComponent>::serialize(ss, &original));
79 TransformComponent loaded;
80 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &loaded));
82 CHECK(vec3_equal(loaded.get_position(), original.get_position()));
83 CHECK(vec3_equal(loaded.get_rotation(), original.get_rotation()));
84 CHECK(vec3_equal(loaded.get_scale(), original.get_scale()));
87 SECTION(
"Preserves floating point precision") {
88 TransformComponent original;
89 original.set_position(0.123456789f, 3.14159265f, 0.987654321f);
92 REQUIRE(Serializer<TransformComponent>::serialize(ss, &original));
94 TransformComponent loaded;
95 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &loaded));
97 CHECK(vec3_equal(loaded.get_position(), original.get_position(), 0.00001f));
101TEST_CASE(
"TransformComponent serialization error handling",
"[serialization][transform]") {
102 SECTION(
"Serialize returns false for nullptr") {
103 std::stringstream ss;
104 REQUIRE_FALSE(Serializer<TransformComponent>::serialize(ss,
nullptr));
107 SECTION(
"Deserialize returns false for nullptr") {
108 std::stringstream ss;
109 ss << R"({"position":[0,0,0],"rotation":[0,0,0],"scale":[1,1,1]})";
110 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss,
nullptr));
113 SECTION(
"Deserialize returns false for empty input") {
114 std::stringstream ss;
115 TransformComponent comp;
116 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
119 SECTION(
"Deserialize returns false for invalid JSON") {
120 std::stringstream ss;
121 ss <<
"not valid json {{{";
123 TransformComponent comp;
124 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
127 SECTION(
"Deserialize returns false for missing position field") {
128 std::stringstream ss;
129 ss << R"({"rotation":[0,0,0],"scale":[1,1,1]})";
131 TransformComponent comp;
132 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
135 SECTION(
"Deserialize returns false for missing rotation field") {
136 std::stringstream ss;
137 ss << R"({"position":[0,0,0],"scale":[1,1,1]})";
139 TransformComponent comp;
140 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
143 SECTION(
"Deserialize returns false for missing scale field") {
144 std::stringstream ss;
145 ss << R"({"position":[0,0,0],"rotation":[0,0,0]})";
147 TransformComponent comp;
148 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
151 SECTION(
"Deserialize returns false for wrong type in position") {
152 std::stringstream ss;
153 ss << R"({"position":"not an array","rotation":[0,0,0],"scale":[1,1,1]})";
155 TransformComponent comp;
156 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
159 SECTION(
"Deserialize returns false for wrong array size") {
160 std::stringstream ss;
161 ss << R"({"position":[0,0],"rotation":[0,0,0],"scale":[1,1,1]})";
163 TransformComponent comp;
164 REQUIRE_FALSE(Serializer<TransformComponent>::deserialize(ss, &comp));
168TEST_CASE(
"TransformComponent JSON output format",
"[serialization][transform]") {
169 SECTION(
"Output is valid JSON") {
170 TransformComponent comp;
171 comp.set_rotation(1, 2, 3);
173 std::stringstream ss;
174 REQUIRE(Serializer<TransformComponent>::serialize(ss, &comp));
178 REQUIRE_NOTHROW(j = nlohmann::json::parse(ss.str()));
181 SECTION(
"Output contains expected keys") {
182 TransformComponent comp;
184 std::stringstream ss;
185 Serializer<TransformComponent>::serialize(ss, &comp);
187 nlohmann::json j = nlohmann::json::parse(ss.str());
189 CHECK(j.contains(
"position"));
190 CHECK(j.contains(
"rotation"));
191 CHECK(j.contains(
"scale"));
194 SECTION(
"Position is serialized as array") {
195 TransformComponent comp;
196 comp.set_position(1, 2, 3);
198 std::stringstream ss;
199 Serializer<TransformComponent>::serialize(ss, &comp);
201 nlohmann::json j = nlohmann::json::parse(ss.str());
203 REQUIRE(j[
"position"].is_array());
204 REQUIRE(j[
"position"].size() == 3);
206 REQUIRE(j[
"position"][0] == 1.0f);
207 REQUIRE(j[
"position"][1] == 2.0f);
208 REQUIRE(j[
"position"][2] == 3.0f);
212TEST_CASE(
"TransformComponent deserialization from manual JSON",
"[serialization][transform]") {
213 SECTION(
"Can load hand-written JSON") {
214 std::stringstream ss;
216 "position": [10.0, 20.0, 30.0],
217 "rotation": [0.0, 90.0, 180.0],
218 "scale": [1.0, 1.0, 1.0]
219 })";
221 TransformComponent comp;
222 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &comp));
224 CHECK(vec3_equal(comp.get_position(), glm::vec3(10, 20, 30)));
225 CHECK(vec3_equal(comp.get_rotation(), glm::vec3(0, 90, 180)));
226 CHECK(vec3_equal(comp.get_scale(), glm::vec3(1, 1, 1)));
229 SECTION(
"Ignore extra fields") {
230 std::stringstream ss;
232 "position": [1.0, 2.0, 3.0],
233 "rotation": [0.0, 0.0, 0.0],
234 "scale": [1.0, 1.0, 1.0],
235 "some_random_field": "should be ignored"
236 })";
238 TransformComponent comp;
239 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &comp));
240 CHECK(vec3_equal(comp.get_position(), glm::vec3(1, 2, 3)));
243 SECTION(
"Handles integer values in JSON") {
244 std::stringstream ss;
245 ss << R"({"position":[1.0,2.0,3.0],"rotation":[0.0,0.0,0.0],"scale":[1.0,1.0,1.0]})";
247 TransformComponent comp;
248 REQUIRE(Serializer<TransformComponent>::deserialize(ss, &comp));
249 CHECK(vec3_equal(comp.get_position(), glm::vec3(1, 2, 3)));