From 11707364ba28c88a69a618a48f2031dedbb2a101 Mon Sep 17 00:00:00 2001 From: Wojciech Piwocha Date: Thu, 18 Jun 2026 16:29:41 +0200 Subject: [PATCH 1/9] tests: added parser tests --- tests/unit_tests/parser_unit_test.cpp | 375 ++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 tests/unit_tests/parser_unit_test.cpp diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp new file mode 100644 index 0000000..ddab6e1 --- /dev/null +++ b/tests/unit_tests/parser_unit_test.cpp @@ -0,0 +1,375 @@ +#include + +#include +#include +#include +#include + +#include "neuronide.pb.h" +#include "parser/Parser.hpp" +#include "scene/Scene.hpp" +#include "scene/SceneObject.hpp" +#include "scene/components/BlinkComponent.hpp" + +// Pomocnicze funkcje do tworzenia tymczasowych plików .pb +namespace { + +std::string writeTempProto(const NeuronIDE::Scene& scene, const std::string& filename) { + const std::string path = (std::filesystem::temp_directory_path() / filename).string(); + std::ofstream out(path, std::ios::binary | std::ios::trunc); + EXPECT_TRUE(out.is_open()) << "Nie mozna otworzyc pliku tymczasowego: " << path; + scene.SerializeToOstream(&out); + return path; +} + +NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", + const std::string& objectName = "ObiektA", + bool isVisible = true, + double blinkFrequency = 1.5) { + NeuronIDE::Scene scene; + scene.set_project_name(projectName); + + auto* obj = scene.add_scene_objects(); + obj->set_name(objectName); + obj->set_is_visible(isVisible); + + auto* comp = obj->add_components(); + auto* blinker = comp->mutable_blinker(); + blinker->set_blink_frequency_hz(blinkFrequency); + + return scene; +} + +} // namespace + + +// Grupa: Parser -- otwieranie pliku +TEST(ParserFileTest, ThrowsWhenFileDoesNotExist) { + Parser parser; + EXPECT_THROW(parser.parse("/nonexistent/path/scene.pb"), std::runtime_error); +} + + +TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { + auto scene = buildSimpleScene(); + const std::string path = writeTempProto(scene, "valid_scene.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_NE(result, nullptr); +} + + +// Grupa: Parser -- nazwa projektu (Scene.project_name) +TEST(ParserSceneNameTest, SetsProjectName) { + auto scene = buildSimpleScene("MojProjekt"); + const std::string path = writeTempProto(scene, "name_test.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(result->getExperimentName(), "MojProjekt"); +} + +TEST(ParserSceneNameTest, EmptyProjectNameIsPreserved) { + auto scene = buildSimpleScene(""); + const std::string path = writeTempProto(scene, "empty_name.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(result->getExperimentName(), ""); +} + + +// Grupa: Parser -- liczba obiektow sceny +TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { + NeuronIDE::Scene protoScene; + const std::string path = writeTempProto(protoScene, "no_objects.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_TRUE(result->getObjects().empty()); +} + +TEST(ParserObjectCountTest, SingleObjectIsLoaded) { + auto scene = buildSimpleScene("P", "Obj1"); + const std::string path = writeTempProto(scene, "one_object.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(result->getObjects().size(), 1u); +} + +TEST(ParserObjectCountTest, MultipleObjectsAreAllLoaded) { + NeuronIDE::Scene scene; + scene.set_project_name("Multi"); + for (int i = 0; i < 5; ++i) { + auto* obj = scene.add_scene_objects(); + obj->set_name("Obj" + std::to_string(i)); + obj->set_is_visible(true); + auto* comp = obj->add_components(); + comp->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); + } + const std::string path = writeTempProto(scene, "multi_objects.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(result->getObjects().size(), 5u); +} + + +// Grupa: Parser -- atrybuty SceneObject +TEST(ParserSceneObjectTest, ObjectNameIsCorrect) { + auto scene = buildSimpleScene("P", "MojaRakieta"); + const std::string path = writeTempProto(scene, "obj_name.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(result->getObjects()[0]->name, "MojaRakieta"); +} + +TEST(ParserSceneObjectTest, ObjectIsVisibleWhenTrue) { + auto scene = buildSimpleScene("P", "Obj", true); + const std::string path = writeTempProto(scene, "obj_visible_true.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_TRUE(result->getObjects()[0]->isVisible); +} + +TEST(ParserSceneObjectTest, ObjectIsHiddenWhenFalse) { + auto scene = buildSimpleScene("P", "Obj", false); + const std::string path = writeTempProto(scene, "obj_visible_false.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_FALSE(result->getObjects()[0]->isVisible); +} + +TEST(ParserSceneObjectTest, ObjectsPreserveInsertionOrder) { + NeuronIDE::Scene scene; + scene.set_project_name("Order"); + const std::vector names = {"Alpha", "Beta", "Gamma"}; + for (const auto& n : names) { + auto* obj = scene.add_scene_objects(); + obj->set_name(n); + } + const std::string path = writeTempProto(scene, "order_test.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), names.size()); + for (size_t i = 0; i < names.size(); ++i) { + EXPECT_EQ(result->getObjects()[i]->name, names[i]); + } +} + + +// Grupa: Parser -- Transform +TEST(ParserTransformTest, TransformFieldsAreParsedCorrectly) { + NeuronIDE::Scene scene; + scene.set_project_name("TransformTest"); + auto* obj = scene.add_scene_objects(); + obj->set_name("Sprite"); + obj->set_is_visible(true); + + auto* tra = obj->mutable_transform(); + tra->set_x(10.5); + tra->set_y(20.25); + tra->set_width(64.0); + tra->set_height(128.0); + tra->set_rotation(45.0); + + const std::string path = writeTempProto(scene, "transform_test.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), 1u); + + const auto& t = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(t.posX, 10.5); + EXPECT_DOUBLE_EQ(t.posY, 20.25); + EXPECT_DOUBLE_EQ(t.width, 64.0); + EXPECT_DOUBLE_EQ(t.height, 128.0); + EXPECT_DOUBLE_EQ(t.rotation, 45.0); +} + +TEST(ParserTransformTest, DefaultTransformIsZeroWhenNotProvided) { + NeuronIDE::Scene scene; + scene.set_project_name("NoTransform"); + auto* obj = scene.add_scene_objects(); + obj->set_name("Bezpozycyjny"); + obj->set_is_visible(true); + + const std::string path = writeTempProto(scene, "no_transform.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), 1u); + + const auto& t = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(t.posX, 0.0); + EXPECT_DOUBLE_EQ(t.posY, 0.0); + EXPECT_DOUBLE_EQ(t.width, 0.0); + EXPECT_DOUBLE_EQ(t.height, 0.0); + EXPECT_DOUBLE_EQ(t.rotation, 0.0); +} + +TEST(ParserTransformTest, NegativeTransformValuesAreAccepted) { + NeuronIDE::Scene scene; + scene.set_project_name("NegTrans"); + auto* obj = scene.add_scene_objects(); + obj->set_name("Ujemny"); + obj->set_is_visible(true); + auto* tra = obj->mutable_transform(); + tra->set_x(-50.0); + tra->set_y(-100.0); + tra->set_rotation(-90.0); + + const std::string path = writeTempProto(scene, "neg_transform.pb"); + + Parser parser; + auto result = parser.parse(path); + const auto& t = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(t.posX, -50.0); + EXPECT_DOUBLE_EQ(t.posY, -100.0); + EXPECT_DOUBLE_EQ(t.rotation, -90.0); +} + + +// Grupa: Parser -- komponenty (BlinkComponent) +TEST(ParserComponentTest, ObjectWithNoComponentsHasEmptyComponentList) { + NeuronIDE::Scene scene; + scene.set_project_name("NoComp"); + auto* obj = scene.add_scene_objects(); + obj->set_name("PustyObiekt"); + + const std::string path = writeTempProto(scene, "no_components.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_TRUE(result->getObjects()[0]->components.empty()); +} + +TEST(ParserComponentTest, BlinkComponentIsCreated) { + auto scene = buildSimpleScene("P", "Mrugacz", true, 2.0); + const std::string path = writeTempProto(scene, "blink_comp.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), 1u); + EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); + EXPECT_NE(result->getObjects()[0]->components[0], nullptr); +} + +TEST(ParserComponentTest, BlinkComponentIsCorrectType) { + auto scene = buildSimpleScene("P", "Blinker", true, 3.0); + const std::string path = writeTempProto(scene, "blink_type.pb"); + + Parser parser; + auto result = parser.parse(path); + auto* raw = result->getObjects()[0]->components[0].get(); + EXPECT_NE(dynamic_cast(raw), nullptr); +} + +TEST(ParserComponentTest, UnknownComponentTypeIsIgnored) { + NeuronIDE::Scene scene; + scene.set_project_name("UnknownComp"); + auto* obj = scene.add_scene_objects(); + obj->set_name("Obiekt"); + obj->set_is_visible(true); + obj->add_components(); // pusty Component, brak oneof + + const std::string path = writeTempProto(scene, "unknown_comp.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_TRUE(result->getObjects()[0]->components.empty()); +} + +TEST(ParserComponentTest, DuplicateComponentTypeThrows) { + NeuronIDE::Scene scene; + scene.set_project_name("DupComp"); + auto* obj = scene.add_scene_objects(); + obj->set_name("Zduplikowany"); + obj->set_is_visible(true); + + obj->add_components()->mutable_blinker()->set_blink_frequency_hz(1.0); + obj->add_components()->mutable_blinker()->set_blink_frequency_hz(2.0); + + const std::string path = writeTempProto(scene, "dup_comp.pb"); + + Parser parser; + EXPECT_THROW(parser.parse(path), std::runtime_error); +} + +TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { + NeuronIDE::Scene scene; + scene.set_project_name("IndepComp"); + for (int i = 0; i < 3; ++i) { + auto* obj = scene.add_scene_objects(); + obj->set_name("Obj" + std::to_string(i)); + obj->set_is_visible(true); + obj->add_components()->mutable_blinker()->set_blink_frequency_hz(static_cast(i + 1)); + } + const std::string path = writeTempProto(scene, "indep_comp.pb"); + + Parser parser; + auto result = parser.parse(path); + for (const auto& obj : result->getObjects()) { + EXPECT_EQ(obj->components.size(), 1u); + } +} + + +// Grupa: Parser -- scenariusze brzegowe +TEST(ParserEdgeCaseTest, ObjectWithEmptyNameIsParsed) { + NeuronIDE::Scene scene; + scene.set_project_name("EmptyObjName"); + auto* obj = scene.add_scene_objects(); + obj->set_name(""); + obj->set_is_visible(true); + + const std::string path = writeTempProto(scene, "empty_obj_name.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), 1u); + EXPECT_EQ(result->getObjects()[0]->name, ""); +} + +TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { + NeuronIDE::Scene scene; + scene.set_project_name("LargeScene"); + const int N = 500; + for (int i = 0; i < N; ++i) { + auto* obj = scene.add_scene_objects(); + obj->set_name("O" + std::to_string(i)); + obj->set_is_visible(i % 2 == 0); + obj->add_components()->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); + } + const std::string path = writeTempProto(scene, "large_scene.pb"); + + Parser parser; + auto result = parser.parse(path); + EXPECT_EQ(static_cast(result->getObjects().size()), N); +} + +TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { + auto scene = buildSimpleScene("P", "ZeroHz", true, 0.0); + const std::string path = writeTempProto(scene, "zero_freq.pb"); + + Parser parser; + auto result = parser.parse(path); + ASSERT_EQ(result->getObjects().size(), 1u); + EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); +} + +TEST(ParserEdgeCaseTest, ParseReturnsDifferentObjectEachCall) { + auto scene = buildSimpleScene(); + const std::string path = writeTempProto(scene, "two_calls.pb"); + + Parser parser; + auto r1 = parser.parse(path); + auto r2 = parser.parse(path); + EXPECT_NE(r1.get(), r2.get()); +} \ No newline at end of file From d50dadb3825ec1fd3dd0ee5a614db7e78abace31 Mon Sep 17 00:00:00 2001 From: Wojciech Piwocha Date: Mon, 22 Jun 2026 08:53:57 +0200 Subject: [PATCH 2/9] fix: used clang-format on file --- tests/unit_tests/parser_unit_test.cpp | 53 ++++++++++++--------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index ddab6e1..e97ce60 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -22,14 +22,14 @@ std::string writeTempProto(const NeuronIDE::Scene& scene, const std::string& fil return path; } -NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", - const std::string& objectName = "ObiektA", - bool isVisible = true, - double blinkFrequency = 1.5) { - NeuronIDE::Scene scene; - scene.set_project_name(projectName); +NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", + const std::string& objectName = "ObiektA", bool isVisible = true, + double blinkFrequency = 1.5) { + NeuronIDE::Scene scene; + scene.set_project_name(projectName + ); - auto* obj = scene.add_scene_objects(); + auto* obj = scene.add_scene_objects(); obj->set_name(objectName); obj->set_is_visible(isVisible); @@ -42,16 +42,14 @@ NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProje } // namespace - // Grupa: Parser -- otwieranie pliku TEST(ParserFileTest, ThrowsWhenFileDoesNotExist) { Parser parser; EXPECT_THROW(parser.parse("/nonexistent/path/scene.pb"), std::runtime_error); } - TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { - auto scene = buildSimpleScene(); + auto scene = buildSimpleScene(); const std::string path = writeTempProto(scene, "valid_scene.pb"); Parser parser; @@ -59,10 +57,9 @@ TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { ASSERT_NE(result, nullptr); } - // Grupa: Parser -- nazwa projektu (Scene.project_name) TEST(ParserSceneNameTest, SetsProjectName) { - auto scene = buildSimpleScene("MojProjekt"); + auto scene = buildSimpleScene("MojProjekt"); const std::string path = writeTempProto(scene, "name_test.pb"); Parser parser; @@ -71,7 +68,7 @@ TEST(ParserSceneNameTest, SetsProjectName) { } TEST(ParserSceneNameTest, EmptyProjectNameIsPreserved) { - auto scene = buildSimpleScene(""); + auto scene = buildSimpleScene(""); const std::string path = writeTempProto(scene, "empty_name.pb"); Parser parser; @@ -79,7 +76,6 @@ TEST(ParserSceneNameTest, EmptyProjectNameIsPreserved) { EXPECT_EQ(result->getExperimentName(), ""); } - // Grupa: Parser -- liczba obiektow sceny TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { NeuronIDE::Scene protoScene; @@ -91,7 +87,7 @@ TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { } TEST(ParserObjectCountTest, SingleObjectIsLoaded) { - auto scene = buildSimpleScene("P", "Obj1"); + auto scene = buildSimpleScene("P", "Obj1"); const std::string path = writeTempProto(scene, "one_object.pb"); Parser parser; @@ -116,10 +112,9 @@ TEST(ParserObjectCountTest, MultipleObjectsAreAllLoaded) { EXPECT_EQ(result->getObjects().size(), 5u); } - // Grupa: Parser -- atrybuty SceneObject TEST(ParserSceneObjectTest, ObjectNameIsCorrect) { - auto scene = buildSimpleScene("P", "MojaRakieta"); + auto scene = buildSimpleScene("P", "MojaRakieta"); const std::string path = writeTempProto(scene, "obj_name.pb"); Parser parser; @@ -128,7 +123,7 @@ TEST(ParserSceneObjectTest, ObjectNameIsCorrect) { } TEST(ParserSceneObjectTest, ObjectIsVisibleWhenTrue) { - auto scene = buildSimpleScene("P", "Obj", true); + auto scene = buildSimpleScene("P", "Obj", true); const std::string path = writeTempProto(scene, "obj_visible_true.pb"); Parser parser; @@ -137,7 +132,7 @@ TEST(ParserSceneObjectTest, ObjectIsVisibleWhenTrue) { } TEST(ParserSceneObjectTest, ObjectIsHiddenWhenFalse) { - auto scene = buildSimpleScene("P", "Obj", false); + auto scene = buildSimpleScene("P", "Obj", false); const std::string path = writeTempProto(scene, "obj_visible_false.pb"); Parser parser; @@ -163,7 +158,6 @@ TEST(ParserSceneObjectTest, ObjectsPreserveInsertionOrder) { } } - // Grupa: Parser -- Transform TEST(ParserTransformTest, TransformFieldsAreParsedCorrectly) { NeuronIDE::Scene scene; @@ -227,15 +221,14 @@ TEST(ParserTransformTest, NegativeTransformValuesAreAccepted) { const std::string path = writeTempProto(scene, "neg_transform.pb"); - Parser parser; - auto result = parser.parse(path); - const auto& t = result->getObjects()[0]->transform; + Parser parser; + auto result = parser.parse(path); + const auto& t = result->getObjects()[0]->transform; EXPECT_DOUBLE_EQ(t.posX, -50.0); EXPECT_DOUBLE_EQ(t.posY, -100.0); EXPECT_DOUBLE_EQ(t.rotation, -90.0); } - // Grupa: Parser -- komponenty (BlinkComponent) TEST(ParserComponentTest, ObjectWithNoComponentsHasEmptyComponentList) { NeuronIDE::Scene scene; @@ -251,7 +244,7 @@ TEST(ParserComponentTest, ObjectWithNoComponentsHasEmptyComponentList) { } TEST(ParserComponentTest, BlinkComponentIsCreated) { - auto scene = buildSimpleScene("P", "Mrugacz", true, 2.0); + auto scene = buildSimpleScene("P", "Mrugacz", true, 2.0); const std::string path = writeTempProto(scene, "blink_comp.pb"); Parser parser; @@ -262,7 +255,7 @@ TEST(ParserComponentTest, BlinkComponentIsCreated) { } TEST(ParserComponentTest, BlinkComponentIsCorrectType) { - auto scene = buildSimpleScene("P", "Blinker", true, 3.0); + auto scene = buildSimpleScene("P", "Blinker", true, 3.0); const std::string path = writeTempProto(scene, "blink_type.pb"); Parser parser; @@ -309,7 +302,8 @@ TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { auto* obj = scene.add_scene_objects(); obj->set_name("Obj" + std::to_string(i)); obj->set_is_visible(true); - obj->add_components()->mutable_blinker()->set_blink_frequency_hz(static_cast(i + 1)); + obj->add_components()->mutable_blinker()->set_blink_frequency_hz( + static_cast(i + 1)); } const std::string path = writeTempProto(scene, "indep_comp.pb"); @@ -320,7 +314,6 @@ TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { } } - // Grupa: Parser -- scenariusze brzegowe TEST(ParserEdgeCaseTest, ObjectWithEmptyNameIsParsed) { NeuronIDE::Scene scene; @@ -355,7 +348,7 @@ TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { } TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { - auto scene = buildSimpleScene("P", "ZeroHz", true, 0.0); + auto scene = buildSimpleScene("P", "ZeroHz", true, 0.0); const std::string path = writeTempProto(scene, "zero_freq.pb"); Parser parser; @@ -365,7 +358,7 @@ TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { } TEST(ParserEdgeCaseTest, ParseReturnsDifferentObjectEachCall) { - auto scene = buildSimpleScene(); + auto scene = buildSimpleScene(); const std::string path = writeTempProto(scene, "two_calls.pb"); Parser parser; From 270dd018c1a56cfbfea49f1ccfca15901e9397a4 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 22 Jun 2026 12:21:10 +0200 Subject: [PATCH 3/9] refactor: use stringstream in tests instead of ifstream and move some tests to component_tests --- include/parser/Parser.hpp | 1 + src/parser/Parser.cpp | 17 +- tests/component_tests/CMakeLists.txt | 1 + .../component_tests/parser_component_test.cpp | 34 ++++ tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/parser_unit_test.cpp | 186 ++++-------------- tests/utils/ParserTestUtils.hpp | 40 ++++ 7 files changed, 133 insertions(+), 147 deletions(-) create mode 100644 tests/component_tests/parser_component_test.cpp create mode 100644 tests/utils/ParserTestUtils.hpp diff --git a/include/parser/Parser.hpp b/include/parser/Parser.hpp index 09c2e1f..d4e8660 100644 --- a/include/parser/Parser.hpp +++ b/include/parser/Parser.hpp @@ -19,6 +19,7 @@ class Parser { Parser() = default; static std::shared_ptr parse(const std::string& filePath); + static std::shared_ptr parseStream(std::istream& stream); private: static std::shared_ptr buildSceneObject(const NeuronIDE::SceneObject& protoObj); diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index f11a383..32009c7 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -11,15 +11,24 @@ #include "scene/components/ComponentRegistry.hpp" std::shared_ptr Parser::parse(const std::string& filePath) { - NeuronIDE::Scene protoScene; - std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) { throw std::runtime_error("Parser: cannot open file: " + filePath); } - if (!protoScene.ParseFromIstream(&file)) { - throw std::runtime_error("Parser: failed to parse protobuf from: " + filePath); + try { + return parseStream(file); + } catch (const std::runtime_error& e) { + throw std::runtime_error(std::string("Parser: failed to parse file ") + filePath + " - " + + e.what()); + } +} + +std::shared_ptr Parser::parseStream(std::istream& stream) { + NeuronIDE::Scene protoScene; + + if (!protoScene.ParseFromIstream(&stream)) { + throw std::runtime_error("Parser: failed to parse protobuf from stream"); } auto scene = std::make_shared<::Scene>(); diff --git a/tests/component_tests/CMakeLists.txt b/tests/component_tests/CMakeLists.txt index 998cfe3..4ec7a3e 100644 --- a/tests/component_tests/CMakeLists.txt +++ b/tests/component_tests/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB COMP_TEST_SOURCES "*.cpp") if(COMP_TEST_SOURCES) add_executable(component_tests ${COMP_TEST_SOURCES}) target_link_libraries(component_tests PRIVATE gtest_main runtime_core) + target_include_directories(component_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) gtest_discover_tests(component_tests PROPERTIES LABELS "component") endif() \ No newline at end of file diff --git a/tests/component_tests/parser_component_test.cpp b/tests/component_tests/parser_component_test.cpp new file mode 100644 index 0000000..5bc0020 --- /dev/null +++ b/tests/component_tests/parser_component_test.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include +#include +#include +#include + +#include "parser/Parser.hpp" +#include "scene/Scene.hpp" +#include "scene/SceneObject.hpp" +#include "scene/components/BlinkComponent.hpp" +#include "utils/ParserTestUtils.hpp" + +TEST(ParserFileTest, ThrowsWhenFileDoesNotExist) { + Parser parser; + EXPECT_THROW(parser.parse("/nonexistent/path/scene.pb"), std::runtime_error); +} + +TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { + auto scene = utils::buildSimpleScene(); + const std::string path = (std::filesystem::temp_directory_path() / "valid_scene.pb").string(); + { + std::ofstream out(path, std::ios::binary | std::ios::trunc); + ASSERT_TRUE(out.is_open()); + scene.SerializeToOstream(&out); + } + + Parser parser; + auto result = parser.parse(path); + ASSERT_NE(result, nullptr); + + std::filesystem::remove(path); +} \ No newline at end of file diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cddb417..040706c 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB UNIT_TEST_SOURCES "*.cpp") if(UNIT_TEST_SOURCES) add_executable(unit_tests ${UNIT_TEST_SOURCES}) target_link_libraries(unit_tests PRIVATE gtest_main runtime_core) + target_include_directories(unit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) gtest_discover_tests(unit_tests PROPERTIES LABELS "unit") endif() \ No newline at end of file diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index e97ce60..b425a4e 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,88 +11,31 @@ #include "scene/Scene.hpp" #include "scene/SceneObject.hpp" #include "scene/components/BlinkComponent.hpp" - -// Pomocnicze funkcje do tworzenia tymczasowych plików .pb -namespace { - -std::string writeTempProto(const NeuronIDE::Scene& scene, const std::string& filename) { - const std::string path = (std::filesystem::temp_directory_path() / filename).string(); - std::ofstream out(path, std::ios::binary | std::ios::trunc); - EXPECT_TRUE(out.is_open()) << "Nie mozna otworzyc pliku tymczasowego: " << path; - scene.SerializeToOstream(&out); - return path; -} - -NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", - const std::string& objectName = "ObiektA", bool isVisible = true, - double blinkFrequency = 1.5) { - NeuronIDE::Scene scene; - scene.set_project_name(projectName - ); - - auto* obj = scene.add_scene_objects(); - obj->set_name(objectName); - obj->set_is_visible(isVisible); - - auto* comp = obj->add_components(); - auto* blinker = comp->mutable_blinker(); - blinker->set_blink_frequency_hz(blinkFrequency); - - return scene; -} - -} // namespace - -// Grupa: Parser -- otwieranie pliku -TEST(ParserFileTest, ThrowsWhenFileDoesNotExist) { - Parser parser; - EXPECT_THROW(parser.parse("/nonexistent/path/scene.pb"), std::runtime_error); -} - -TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { - auto scene = buildSimpleScene(); - const std::string path = writeTempProto(scene, "valid_scene.pb"); - - Parser parser; - auto result = parser.parse(path); - ASSERT_NE(result, nullptr); -} +#include "utils/ParserTestUtils.hpp" // Grupa: Parser -- nazwa projektu (Scene.project_name) TEST(ParserSceneNameTest, SetsProjectName) { - auto scene = buildSimpleScene("MojProjekt"); - const std::string path = writeTempProto(scene, "name_test.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("MojProjekt"); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getExperimentName(), "MojProjekt"); } TEST(ParserSceneNameTest, EmptyProjectNameIsPreserved) { - auto scene = buildSimpleScene(""); - const std::string path = writeTempProto(scene, "empty_name.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene(""); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getExperimentName(), ""); } // Grupa: Parser -- liczba obiektow sceny TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { - NeuronIDE::Scene protoScene; - const std::string path = writeTempProto(protoScene, "no_objects.pb"); - - Parser parser; - auto result = parser.parse(path); + NeuronIDE::Scene protoScene; + auto result = utils::parseProtoScene(protoScene); EXPECT_TRUE(result->getObjects().empty()); } TEST(ParserObjectCountTest, SingleObjectIsLoaded) { - auto scene = buildSimpleScene("P", "Obj1"); - const std::string path = writeTempProto(scene, "one_object.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "Obj1"); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getObjects().size(), 1u); } @@ -105,38 +49,26 @@ TEST(ParserObjectCountTest, MultipleObjectsAreAllLoaded) { auto* comp = obj->add_components(); comp->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); } - const std::string path = writeTempProto(scene, "multi_objects.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getObjects().size(), 5u); } // Grupa: Parser -- atrybuty SceneObject TEST(ParserSceneObjectTest, ObjectNameIsCorrect) { - auto scene = buildSimpleScene("P", "MojaRakieta"); - const std::string path = writeTempProto(scene, "obj_name.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "MojaRakieta"); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getObjects()[0]->name, "MojaRakieta"); } TEST(ParserSceneObjectTest, ObjectIsVisibleWhenTrue) { - auto scene = buildSimpleScene("P", "Obj", true); - const std::string path = writeTempProto(scene, "obj_visible_true.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "Obj", true); + auto result = utils::parseProtoScene(scene); EXPECT_TRUE(result->getObjects()[0]->isVisible); } TEST(ParserSceneObjectTest, ObjectIsHiddenWhenFalse) { - auto scene = buildSimpleScene("P", "Obj", false); - const std::string path = writeTempProto(scene, "obj_visible_false.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "Obj", false); + auto result = utils::parseProtoScene(scene); EXPECT_FALSE(result->getObjects()[0]->isVisible); } @@ -148,10 +80,7 @@ TEST(ParserSceneObjectTest, ObjectsPreserveInsertionOrder) { auto* obj = scene.add_scene_objects(); obj->set_name(n); } - const std::string path = writeTempProto(scene, "order_test.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), names.size()); for (size_t i = 0; i < names.size(); ++i) { EXPECT_EQ(result->getObjects()[i]->name, names[i]); @@ -173,10 +102,7 @@ TEST(ParserTransformTest, TransformFieldsAreParsedCorrectly) { tra->set_height(128.0); tra->set_rotation(45.0); - const std::string path = writeTempProto(scene, "transform_test.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1u); const auto& t = result->getObjects()[0]->transform; @@ -194,10 +120,7 @@ TEST(ParserTransformTest, DefaultTransformIsZeroWhenNotProvided) { obj->set_name("Bezpozycyjny"); obj->set_is_visible(true); - const std::string path = writeTempProto(scene, "no_transform.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1u); const auto& t = result->getObjects()[0]->transform; @@ -219,10 +142,7 @@ TEST(ParserTransformTest, NegativeTransformValuesAreAccepted) { tra->set_y(-100.0); tra->set_rotation(-90.0); - const std::string path = writeTempProto(scene, "neg_transform.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); const auto& t = result->getObjects()[0]->transform; EXPECT_DOUBLE_EQ(t.posX, -50.0); EXPECT_DOUBLE_EQ(t.posY, -100.0); @@ -236,31 +156,22 @@ TEST(ParserComponentTest, ObjectWithNoComponentsHasEmptyComponentList) { auto* obj = scene.add_scene_objects(); obj->set_name("PustyObiekt"); - const std::string path = writeTempProto(scene, "no_components.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); EXPECT_TRUE(result->getObjects()[0]->components.empty()); } TEST(ParserComponentTest, BlinkComponentIsCreated) { - auto scene = buildSimpleScene("P", "Mrugacz", true, 2.0); - const std::string path = writeTempProto(scene, "blink_comp.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "Mrugacz", true, 2.0); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1u); EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); EXPECT_NE(result->getObjects()[0]->components[0], nullptr); } TEST(ParserComponentTest, BlinkComponentIsCorrectType) { - auto scene = buildSimpleScene("P", "Blinker", true, 3.0); - const std::string path = writeTempProto(scene, "blink_type.pb"); - - Parser parser; - auto result = parser.parse(path); - auto* raw = result->getObjects()[0]->components[0].get(); + auto scene = utils::buildSimpleScene("P", "Blinker", true, 3.0); + auto result = utils::parseProtoScene(scene); + auto* raw = result->getObjects()[0]->components[0].get(); EXPECT_NE(dynamic_cast(raw), nullptr); } @@ -272,10 +183,7 @@ TEST(ParserComponentTest, UnknownComponentTypeIsIgnored) { obj->set_is_visible(true); obj->add_components(); // pusty Component, brak oneof - const std::string path = writeTempProto(scene, "unknown_comp.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); EXPECT_TRUE(result->getObjects()[0]->components.empty()); } @@ -289,10 +197,11 @@ TEST(ParserComponentTest, DuplicateComponentTypeThrows) { obj->add_components()->mutable_blinker()->set_blink_frequency_hz(1.0); obj->add_components()->mutable_blinker()->set_blink_frequency_hz(2.0); - const std::string path = writeTempProto(scene, "dup_comp.pb"); + std::stringstream ss; + scene.SerializeToOstream(&ss); Parser parser; - EXPECT_THROW(parser.parse(path), std::runtime_error); + EXPECT_THROW(parser.parseStream(ss), std::runtime_error); } TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { @@ -305,10 +214,7 @@ TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { obj->add_components()->mutable_blinker()->set_blink_frequency_hz( static_cast(i + 1)); } - const std::string path = writeTempProto(scene, "indep_comp.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); for (const auto& obj : result->getObjects()) { EXPECT_EQ(obj->components.size(), 1u); } @@ -322,10 +228,7 @@ TEST(ParserEdgeCaseTest, ObjectWithEmptyNameIsParsed) { obj->set_name(""); obj->set_is_visible(true); - const std::string path = writeTempProto(scene, "empty_obj_name.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1u); EXPECT_EQ(result->getObjects()[0]->name, ""); } @@ -340,29 +243,26 @@ TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { obj->set_is_visible(i % 2 == 0); obj->add_components()->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); } - const std::string path = writeTempProto(scene, "large_scene.pb"); - - Parser parser; - auto result = parser.parse(path); + auto result = utils::parseProtoScene(scene); EXPECT_EQ(static_cast(result->getObjects().size()), N); } TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { - auto scene = buildSimpleScene("P", "ZeroHz", true, 0.0); - const std::string path = writeTempProto(scene, "zero_freq.pb"); - - Parser parser; - auto result = parser.parse(path); + auto scene = utils::buildSimpleScene("P", "ZeroHz", true, 0.0); + auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1u); EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); } TEST(ParserEdgeCaseTest, ParseReturnsDifferentObjectEachCall) { - auto scene = buildSimpleScene(); - const std::string path = writeTempProto(scene, "two_calls.pb"); + auto scene = utils::buildSimpleScene(); + std::stringstream ss1; + scene.SerializeToOstream(&ss1); + std::stringstream ss2; + scene.SerializeToOstream(&ss2); Parser parser; - auto r1 = parser.parse(path); - auto r2 = parser.parse(path); + auto r1 = parser.parseStream(ss1); + auto r2 = parser.parseStream(ss2); EXPECT_NE(r1.get(), r2.get()); } \ No newline at end of file diff --git a/tests/utils/ParserTestUtils.hpp b/tests/utils/ParserTestUtils.hpp new file mode 100644 index 0000000..0f1c391 --- /dev/null +++ b/tests/utils/ParserTestUtils.hpp @@ -0,0 +1,40 @@ +#ifndef PARSER_TEST_UTILS_HPP +#define PARSER_TEST_UTILS_HPP + +#include +#include +#include + +#include "neuronide.pb.h" +#include "parser/Parser.hpp" +#include "scene/Scene.hpp" + +namespace utils { + +inline NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", + const std::string& objectName = "ObiektA", + bool isVisible = true, double blinkFrequency = 1.5) { + NeuronIDE::Scene scene; + scene.set_project_name(projectName); + + auto* obj = scene.add_scene_objects(); + obj->set_name(objectName); + obj->set_is_visible(isVisible); + + auto* comp = obj->add_components(); + auto* blinker = comp->mutable_blinker(); + blinker->set_blink_frequency_hz(blinkFrequency); + + return scene; +} + +inline std::shared_ptr parseProtoScene(const NeuronIDE::Scene& scene) { + std::stringstream ss; + scene.SerializeToOstream(&ss); + Parser parser; + return parser.parseStream(ss); +} + +} // namespace utils + +#endif // PARSER_TEST_UTILS_HPP From 7b43d9608ebfca0c9e00df42f25c5c61e528d846 Mon Sep 17 00:00:00 2001 From: Wojciech Piwocha Date: Sat, 27 Jun 2026 20:05:43 +0200 Subject: [PATCH 4/9] ref: parser throws invalid argument errorfor empty variables --- src/parser/Parser.cpp | 8 ++++++++ tests/unit_tests/parser_unit_test.cpp | 19 ++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index 32009c7..f651cc9 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -31,6 +31,10 @@ std::shared_ptr Parser::parseStream(std::istream& stream) { throw std::runtime_error("Parser: failed to parse protobuf from stream"); } + if (protoScene.project_name().empty()) { + throw std::invalid_argument("Parser: project name cannot be empty"); + } + auto scene = std::make_shared<::Scene>(); scene->setExperimentName(protoScene.project_name()); @@ -43,6 +47,10 @@ std::shared_ptr Parser::parseStream(std::istream& stream) { } std::shared_ptr Parser::buildSceneObject(const NeuronIDE::SceneObject& protoObj) { + if (protoObj.name().empty()) { + throw std::invalid_argument("SceneObject name cannot be empty"); + } + auto obj = std::make_shared(protoObj.name(), protoObj.is_visible()); if (protoObj.has_transform()) { diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index b425a4e..1361f16 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -20,10 +20,9 @@ TEST(ParserSceneNameTest, SetsProjectName) { EXPECT_EQ(result->getExperimentName(), "MojProjekt"); } -TEST(ParserSceneNameTest, EmptyProjectNameIsPreserved) { - auto scene = utils::buildSimpleScene(""); - auto result = utils::parseProtoScene(scene); - EXPECT_EQ(result->getExperimentName(), ""); +TEST(ParserSceneNameTest, EmptyProjectNameThrowsError) { + auto scene = utils::buildSimpleScene(""); + EXPECT_THROW({ utils::parseProtoScene(scene); }, std::invalid_argument); } // Grupa: Parser -- liczba obiektow sceny @@ -42,13 +41,13 @@ TEST(ParserObjectCountTest, SingleObjectIsLoaded) { TEST(ParserObjectCountTest, MultipleObjectsAreAllLoaded) { NeuronIDE::Scene scene; scene.set_project_name("Multi"); + for (int i = 0; i < 5; ++i) { auto* obj = scene.add_scene_objects(); obj->set_name("Obj" + std::to_string(i)); obj->set_is_visible(true); - auto* comp = obj->add_components(); - comp->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); } + auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getObjects().size(), 5u); } @@ -221,16 +220,14 @@ TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { } // Grupa: Parser -- scenariusze brzegowe -TEST(ParserEdgeCaseTest, ObjectWithEmptyNameIsParsed) { +TEST(ParserEdgeCaseTest, ObjectWithEmptyNameThrowsError) { NeuronIDE::Scene scene; scene.set_project_name("EmptyObjName"); auto* obj = scene.add_scene_objects(); obj->set_name(""); obj->set_is_visible(true); - auto result = utils::parseProtoScene(scene); - ASSERT_EQ(result->getObjects().size(), 1u); - EXPECT_EQ(result->getObjects()[0]->name, ""); + EXPECT_THROW({utils::parseProtoScene(scene)}, std::invalid_argument); } TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { @@ -254,7 +251,7 @@ TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); } -TEST(ParserEdgeCaseTest, ParseReturnsDifferentObjectEachCall) { +TEST(ParserEdgeCaseTest, ParserReturnsDifferentObjectEachCall) { auto scene = utils::buildSimpleScene(); std::stringstream ss1; scene.SerializeToOstream(&ss1); From 1133e24916e77a6937c1c12c17c0a33aca236779 Mon Sep 17 00:00:00 2001 From: Wojciech Piwocha Date: Sat, 27 Jun 2026 20:19:18 +0200 Subject: [PATCH 5/9] fix: fixed EXPECT_THROW in 2 tests --- tests/unit_tests/parser_unit_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index 1361f16..6ba84e5 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -22,7 +22,7 @@ TEST(ParserSceneNameTest, SetsProjectName) { TEST(ParserSceneNameTest, EmptyProjectNameThrowsError) { auto scene = utils::buildSimpleScene(""); - EXPECT_THROW({ utils::parseProtoScene(scene); }, std::invalid_argument); + EXPECT_THROW(utils::parseProtoScene(scene), std::invalid_argument); } // Grupa: Parser -- liczba obiektow sceny @@ -227,7 +227,7 @@ TEST(ParserEdgeCaseTest, ObjectWithEmptyNameThrowsError) { obj->set_name(""); obj->set_is_visible(true); - EXPECT_THROW({utils::parseProtoScene(scene)}, std::invalid_argument); + EXPECT_THROW(utils::parseProtoScene(scene), std::invalid_argument); } TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { From d8465a052425d7a42570b202ea8c9460c14dda66 Mon Sep 17 00:00:00 2001 From: Wojciech Piwocha Date: Sat, 27 Jun 2026 20:36:16 +0200 Subject: [PATCH 6/9] fix: created scenes and objects have names --- tests/unit_tests/parser_unit_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index 6ba84e5..1bb57e6 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -28,7 +28,8 @@ TEST(ParserSceneNameTest, EmptyProjectNameThrowsError) { // Grupa: Parser -- liczba obiektow sceny TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { NeuronIDE::Scene protoScene; - auto result = utils::parseProtoScene(protoScene); + protoScene.set_project_name("EmptyScene"); + auto result = utils::parseProtoScene(protoScene); EXPECT_TRUE(result->getObjects().empty()); } From 5c17c44dc922545701921b071ab2be457f274d20 Mon Sep 17 00:00:00 2001 From: Michal Date: Sun, 28 Jun 2026 14:49:45 +0200 Subject: [PATCH 7/9] fix: resolve clang-tidy warnings --- tests/.clang-tidy | 9 +++++++++ tests/component_tests/parser_component_test.cpp | 6 ++---- tests/unit_tests/parser_unit_test.cpp | 8 +++----- tests/utils/ParserTestUtils.hpp | 7 +++---- 4 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 tests/.clang-tidy diff --git a/tests/.clang-tidy b/tests/.clang-tidy new file mode 100644 index 0000000..c5998d0 --- /dev/null +++ b/tests/.clang-tidy @@ -0,0 +1,9 @@ +# Test code intentionally uses literal sample data, short loop/index +# variables, and convenience helpers. Relax the checks that flag those +# patterns while keeping the rest of the project configuration. +InheritParentConfig: true +Checks: > + -readability-magic-numbers, + -readability-identifier-length, + -readability-uppercase-literal-suffix, + -bugprone-easily-swappable-parameters diff --git a/tests/component_tests/parser_component_test.cpp b/tests/component_tests/parser_component_test.cpp index 5bc0020..87b073f 100644 --- a/tests/component_tests/parser_component_test.cpp +++ b/tests/component_tests/parser_component_test.cpp @@ -13,8 +13,7 @@ #include "utils/ParserTestUtils.hpp" TEST(ParserFileTest, ThrowsWhenFileDoesNotExist) { - Parser parser; - EXPECT_THROW(parser.parse("/nonexistent/path/scene.pb"), std::runtime_error); + EXPECT_THROW(Parser::parse("/nonexistent/path/scene.pb"), std::runtime_error); } TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { @@ -26,8 +25,7 @@ TEST(ParserFileTest, ReturnsNonNullSceneForValidFile) { scene.SerializeToOstream(&out); } - Parser parser; - auto result = parser.parse(path); + auto result = Parser::parse(path); ASSERT_NE(result, nullptr); std::filesystem::remove(path); diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index 1bb57e6..c97eec1 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -200,8 +200,7 @@ TEST(ParserComponentTest, DuplicateComponentTypeThrows) { std::stringstream ss; scene.SerializeToOstream(&ss); - Parser parser; - EXPECT_THROW(parser.parseStream(ss), std::runtime_error); + EXPECT_THROW(Parser::parseStream(ss), std::runtime_error); } TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { @@ -259,8 +258,7 @@ TEST(ParserEdgeCaseTest, ParserReturnsDifferentObjectEachCall) { std::stringstream ss2; scene.SerializeToOstream(&ss2); - Parser parser; - auto r1 = parser.parseStream(ss1); - auto r2 = parser.parseStream(ss2); + auto r1 = Parser::parseStream(ss1); + auto r2 = Parser::parseStream(ss2); EXPECT_NE(r1.get(), r2.get()); } \ No newline at end of file diff --git a/tests/utils/ParserTestUtils.hpp b/tests/utils/ParserTestUtils.hpp index 0f1c391..7fe8c5f 100644 --- a/tests/utils/ParserTestUtils.hpp +++ b/tests/utils/ParserTestUtils.hpp @@ -29,10 +29,9 @@ inline NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestP } inline std::shared_ptr parseProtoScene(const NeuronIDE::Scene& scene) { - std::stringstream ss; - scene.SerializeToOstream(&ss); - Parser parser; - return parser.parseStream(ss); + std::stringstream stream; + scene.SerializeToOstream(&stream); + return Parser::parseStream(stream); } } // namespace utils From bab75ad33432d950b133ed51f45e0596d7596967 Mon Sep 17 00:00:00 2001 From: Michal Date: Sun, 28 Jun 2026 15:36:51 +0200 Subject: [PATCH 8/9] fix: resolve clang-tidy warnings in parser test --- tests/.clang-tidy | 9 -- tests/unit_tests/parser_unit_test.cpp | 145 +++++++++++++++----------- tests/utils/ParserTestUtils.hpp | 21 ++-- 3 files changed, 99 insertions(+), 76 deletions(-) delete mode 100644 tests/.clang-tidy diff --git a/tests/.clang-tidy b/tests/.clang-tidy deleted file mode 100644 index c5998d0..0000000 --- a/tests/.clang-tidy +++ /dev/null @@ -1,9 +0,0 @@ -# Test code intentionally uses literal sample data, short loop/index -# variables, and convenience helpers. Relax the checks that flag those -# patterns while keeping the rest of the project configuration. -InheritParentConfig: true -Checks: > - -readability-magic-numbers, - -readability-identifier-length, - -readability-uppercase-literal-suffix, - -bugprone-easily-swappable-parameters diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index c97eec1..f816515 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -13,15 +13,32 @@ #include "scene/components/BlinkComponent.hpp" #include "utils/ParserTestUtils.hpp" +namespace { +constexpr int kMultipleObjects = 5; +constexpr int kLargeObjectCount = 500; + +constexpr double kPosX = 10.5; +constexpr double kPosY = 20.25; +constexpr double kWidth = 64.0; +constexpr double kHeight = 128.0; +constexpr double kRotation = 45.0; +constexpr double kNegPosX = -50.0; +constexpr double kNegPosY = -100.0; +constexpr double kNegRotation = -90.0; + +constexpr double kBlinkFreq2Hz = 2.0; +constexpr double kBlinkFreq3Hz = 3.0; +} // namespace + // Grupa: Parser -- nazwa projektu (Scene.project_name) TEST(ParserSceneNameTest, SetsProjectName) { - auto scene = utils::buildSimpleScene("MojProjekt"); + auto scene = utils::buildSimpleScene({.projectName = "MojProjekt"}); auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getExperimentName(), "MojProjekt"); } TEST(ParserSceneNameTest, EmptyProjectNameThrowsError) { - auto scene = utils::buildSimpleScene(""); + auto scene = utils::buildSimpleScene({.projectName = ""}); EXPECT_THROW(utils::parseProtoScene(scene), std::invalid_argument); } @@ -34,40 +51,42 @@ TEST(ParserObjectCountTest, EmptySceneHasNoObjects) { } TEST(ParserObjectCountTest, SingleObjectIsLoaded) { - auto scene = utils::buildSimpleScene("P", "Obj1"); + auto scene = utils::buildSimpleScene({.projectName = "P", .objectName = "Obj1"}); auto result = utils::parseProtoScene(scene); - EXPECT_EQ(result->getObjects().size(), 1u); + EXPECT_EQ(result->getObjects().size(), 1U); } TEST(ParserObjectCountTest, MultipleObjectsAreAllLoaded) { NeuronIDE::Scene scene; scene.set_project_name("Multi"); - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < kMultipleObjects; ++i) { auto* obj = scene.add_scene_objects(); obj->set_name("Obj" + std::to_string(i)); obj->set_is_visible(true); } auto result = utils::parseProtoScene(scene); - EXPECT_EQ(result->getObjects().size(), 5u); + EXPECT_EQ(static_cast(result->getObjects().size()), kMultipleObjects); } // Grupa: Parser -- atrybuty SceneObject TEST(ParserSceneObjectTest, ObjectNameIsCorrect) { - auto scene = utils::buildSimpleScene("P", "MojaRakieta"); + auto scene = utils::buildSimpleScene({.projectName = "P", .objectName = "MojaRakieta"}); auto result = utils::parseProtoScene(scene); EXPECT_EQ(result->getObjects()[0]->name, "MojaRakieta"); } TEST(ParserSceneObjectTest, ObjectIsVisibleWhenTrue) { - auto scene = utils::buildSimpleScene("P", "Obj", true); + auto scene = + utils::buildSimpleScene({.projectName = "P", .objectName = "Obj", .isVisible = true}); auto result = utils::parseProtoScene(scene); EXPECT_TRUE(result->getObjects()[0]->isVisible); } TEST(ParserSceneObjectTest, ObjectIsHiddenWhenFalse) { - auto scene = utils::buildSimpleScene("P", "Obj", false); + auto scene = + utils::buildSimpleScene({.projectName = "P", .objectName = "Obj", .isVisible = false}); auto result = utils::parseProtoScene(scene); EXPECT_FALSE(result->getObjects()[0]->isVisible); } @@ -76,9 +95,9 @@ TEST(ParserSceneObjectTest, ObjectsPreserveInsertionOrder) { NeuronIDE::Scene scene; scene.set_project_name("Order"); const std::vector names = {"Alpha", "Beta", "Gamma"}; - for (const auto& n : names) { + for (const auto& name : names) { auto* obj = scene.add_scene_objects(); - obj->set_name(n); + obj->set_name(name); } auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), names.size()); @@ -96,21 +115,21 @@ TEST(ParserTransformTest, TransformFieldsAreParsedCorrectly) { obj->set_is_visible(true); auto* tra = obj->mutable_transform(); - tra->set_x(10.5); - tra->set_y(20.25); - tra->set_width(64.0); - tra->set_height(128.0); - tra->set_rotation(45.0); + tra->set_x(kPosX); + tra->set_y(kPosY); + tra->set_width(kWidth); + tra->set_height(kHeight); + tra->set_rotation(kRotation); auto result = utils::parseProtoScene(scene); - ASSERT_EQ(result->getObjects().size(), 1u); - - const auto& t = result->getObjects()[0]->transform; - EXPECT_DOUBLE_EQ(t.posX, 10.5); - EXPECT_DOUBLE_EQ(t.posY, 20.25); - EXPECT_DOUBLE_EQ(t.width, 64.0); - EXPECT_DOUBLE_EQ(t.height, 128.0); - EXPECT_DOUBLE_EQ(t.rotation, 45.0); + ASSERT_EQ(result->getObjects().size(), 1U); + + const auto& transform = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(transform.posX, kPosX); + EXPECT_DOUBLE_EQ(transform.posY, kPosY); + EXPECT_DOUBLE_EQ(transform.width, kWidth); + EXPECT_DOUBLE_EQ(transform.height, kHeight); + EXPECT_DOUBLE_EQ(transform.rotation, kRotation); } TEST(ParserTransformTest, DefaultTransformIsZeroWhenNotProvided) { @@ -121,14 +140,14 @@ TEST(ParserTransformTest, DefaultTransformIsZeroWhenNotProvided) { obj->set_is_visible(true); auto result = utils::parseProtoScene(scene); - ASSERT_EQ(result->getObjects().size(), 1u); - - const auto& t = result->getObjects()[0]->transform; - EXPECT_DOUBLE_EQ(t.posX, 0.0); - EXPECT_DOUBLE_EQ(t.posY, 0.0); - EXPECT_DOUBLE_EQ(t.width, 0.0); - EXPECT_DOUBLE_EQ(t.height, 0.0); - EXPECT_DOUBLE_EQ(t.rotation, 0.0); + ASSERT_EQ(result->getObjects().size(), 1U); + + const auto& transform = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(transform.posX, 0.0); + EXPECT_DOUBLE_EQ(transform.posY, 0.0); + EXPECT_DOUBLE_EQ(transform.width, 0.0); + EXPECT_DOUBLE_EQ(transform.height, 0.0); + EXPECT_DOUBLE_EQ(transform.rotation, 0.0); } TEST(ParserTransformTest, NegativeTransformValuesAreAccepted) { @@ -138,15 +157,15 @@ TEST(ParserTransformTest, NegativeTransformValuesAreAccepted) { obj->set_name("Ujemny"); obj->set_is_visible(true); auto* tra = obj->mutable_transform(); - tra->set_x(-50.0); - tra->set_y(-100.0); - tra->set_rotation(-90.0); - - auto result = utils::parseProtoScene(scene); - const auto& t = result->getObjects()[0]->transform; - EXPECT_DOUBLE_EQ(t.posX, -50.0); - EXPECT_DOUBLE_EQ(t.posY, -100.0); - EXPECT_DOUBLE_EQ(t.rotation, -90.0); + tra->set_x(kNegPosX); + tra->set_y(kNegPosY); + tra->set_rotation(kNegRotation); + + auto result = utils::parseProtoScene(scene); + const auto& transform = result->getObjects()[0]->transform; + EXPECT_DOUBLE_EQ(transform.posX, kNegPosX); + EXPECT_DOUBLE_EQ(transform.posY, kNegPosY); + EXPECT_DOUBLE_EQ(transform.rotation, kNegRotation); } // Grupa: Parser -- komponenty (BlinkComponent) @@ -161,15 +180,21 @@ TEST(ParserComponentTest, ObjectWithNoComponentsHasEmptyComponentList) { } TEST(ParserComponentTest, BlinkComponentIsCreated) { - auto scene = utils::buildSimpleScene("P", "Mrugacz", true, 2.0); + auto scene = utils::buildSimpleScene({.projectName = "P", + .objectName = "Mrugacz", + .isVisible = true, + .blinkFrequency = kBlinkFreq2Hz}); auto result = utils::parseProtoScene(scene); - ASSERT_EQ(result->getObjects().size(), 1u); - EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); + ASSERT_EQ(result->getObjects().size(), 1U); + EXPECT_EQ(result->getObjects()[0]->components.size(), 1U); EXPECT_NE(result->getObjects()[0]->components[0], nullptr); } TEST(ParserComponentTest, BlinkComponentIsCorrectType) { - auto scene = utils::buildSimpleScene("P", "Blinker", true, 3.0); + auto scene = utils::buildSimpleScene({.projectName = "P", + .objectName = "Blinker", + .isVisible = true, + .blinkFrequency = kBlinkFreq3Hz}); auto result = utils::parseProtoScene(scene); auto* raw = result->getObjects()[0]->components[0].get(); EXPECT_NE(dynamic_cast(raw), nullptr); @@ -195,12 +220,12 @@ TEST(ParserComponentTest, DuplicateComponentTypeThrows) { obj->set_is_visible(true); obj->add_components()->mutable_blinker()->set_blink_frequency_hz(1.0); - obj->add_components()->mutable_blinker()->set_blink_frequency_hz(2.0); + obj->add_components()->mutable_blinker()->set_blink_frequency_hz(kBlinkFreq2Hz); - std::stringstream ss; - scene.SerializeToOstream(&ss); + std::stringstream stream; + scene.SerializeToOstream(&stream); - EXPECT_THROW(Parser::parseStream(ss), std::runtime_error); + EXPECT_THROW(Parser::parseStream(stream), std::runtime_error); } TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { @@ -215,7 +240,7 @@ TEST(ParserComponentTest, MultipleObjectsEachHaveTheirOwnComponents) { } auto result = utils::parseProtoScene(scene); for (const auto& obj : result->getObjects()) { - EXPECT_EQ(obj->components.size(), 1u); + EXPECT_EQ(obj->components.size(), 1U); } } @@ -233,22 +258,22 @@ TEST(ParserEdgeCaseTest, ObjectWithEmptyNameThrowsError) { TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { NeuronIDE::Scene scene; scene.set_project_name("LargeScene"); - const int N = 500; - for (int i = 0; i < N; ++i) { + for (int i = 0; i < kLargeObjectCount; ++i) { auto* obj = scene.add_scene_objects(); obj->set_name("O" + std::to_string(i)); obj->set_is_visible(i % 2 == 0); obj->add_components()->mutable_blinker()->set_blink_frequency_hz(static_cast(i)); } auto result = utils::parseProtoScene(scene); - EXPECT_EQ(static_cast(result->getObjects().size()), N); + EXPECT_EQ(static_cast(result->getObjects().size()), kLargeObjectCount); } TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { - auto scene = utils::buildSimpleScene("P", "ZeroHz", true, 0.0); + auto scene = utils::buildSimpleScene( + {.projectName = "P", .objectName = "ZeroHz", .isVisible = true, .blinkFrequency = 0.0}); auto result = utils::parseProtoScene(scene); - ASSERT_EQ(result->getObjects().size(), 1u); - EXPECT_EQ(result->getObjects()[0]->components.size(), 1u); + ASSERT_EQ(result->getObjects().size(), 1U); + EXPECT_EQ(result->getObjects()[0]->components.size(), 1U); } TEST(ParserEdgeCaseTest, ParserReturnsDifferentObjectEachCall) { @@ -258,7 +283,7 @@ TEST(ParserEdgeCaseTest, ParserReturnsDifferentObjectEachCall) { std::stringstream ss2; scene.SerializeToOstream(&ss2); - auto r1 = Parser::parseStream(ss1); - auto r2 = Parser::parseStream(ss2); - EXPECT_NE(r1.get(), r2.get()); -} \ No newline at end of file + auto resultA = Parser::parseStream(ss1); + auto resultB = Parser::parseStream(ss2); + EXPECT_NE(resultA.get(), resultB.get()); +} diff --git a/tests/utils/ParserTestUtils.hpp b/tests/utils/ParserTestUtils.hpp index 7fe8c5f..ba30d2b 100644 --- a/tests/utils/ParserTestUtils.hpp +++ b/tests/utils/ParserTestUtils.hpp @@ -11,19 +11,26 @@ namespace utils { -inline NeuronIDE::Scene buildSimpleScene(const std::string& projectName = "TestProject", - const std::string& objectName = "ObiektA", - bool isVisible = true, double blinkFrequency = 1.5) { +inline constexpr double kDefaultBlinkFrequencyHz = 1.5; + +struct SimpleSceneParams { + std::string projectName = "TestProject"; + std::string objectName = "ObiektA"; + bool isVisible = true; + double blinkFrequency = kDefaultBlinkFrequencyHz; +}; + +inline NeuronIDE::Scene buildSimpleScene(const SimpleSceneParams& params = {}) { NeuronIDE::Scene scene; - scene.set_project_name(projectName); + scene.set_project_name(params.projectName); auto* obj = scene.add_scene_objects(); - obj->set_name(objectName); - obj->set_is_visible(isVisible); + obj->set_name(params.objectName); + obj->set_is_visible(params.isVisible); auto* comp = obj->add_components(); auto* blinker = comp->mutable_blinker(); - blinker->set_blink_frequency_hz(blinkFrequency); + blinker->set_blink_frequency_hz(params.blinkFrequency); return scene; } From d4982c0d66a446652577d1d3dc9a6173094b37eb Mon Sep 17 00:00:00 2001 From: Michal Date: Sun, 28 Jun 2026 15:39:50 +0200 Subject: [PATCH 9/9] fix: format code --- tests/unit_tests/parser_unit_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/parser_unit_test.cpp b/tests/unit_tests/parser_unit_test.cpp index f816515..222e3f3 100644 --- a/tests/unit_tests/parser_unit_test.cpp +++ b/tests/unit_tests/parser_unit_test.cpp @@ -14,8 +14,8 @@ #include "utils/ParserTestUtils.hpp" namespace { -constexpr int kMultipleObjects = 5; -constexpr int kLargeObjectCount = 500; +constexpr int kMultipleObjects = 5; +constexpr int kLargeObjectCount = 500; constexpr double kPosX = 10.5; constexpr double kPosY = 20.25; @@ -269,7 +269,7 @@ TEST(ParserEdgeCaseTest, LargeNumberOfObjectsIsHandled) { } TEST(ParserEdgeCaseTest, BlinkFrequencyZeroIsValid) { - auto scene = utils::buildSimpleScene( + auto scene = utils::buildSimpleScene( {.projectName = "P", .objectName = "ZeroHz", .isVisible = true, .blinkFrequency = 0.0}); auto result = utils::parseProtoScene(scene); ASSERT_EQ(result->getObjects().size(), 1U);