diff --git a/src/g2_2025_grade_calculator_pkg/CMakeLists.txt b/src/g2_2025_grade_calculator_pkg/CMakeLists.txt index 814ea15..cb4a3e2 100644 --- a/src/g2_2025_grade_calculator_pkg/CMakeLists.txt +++ b/src/g2_2025_grade_calculator_pkg/CMakeLists.txt @@ -141,6 +141,25 @@ if(BUILD_TESTING) rclcpp g2_2025_interfaces ) + + # Add gtest for FinalGradeDeterminator + ament_add_gtest(${PROJECT_NAME}_test_final_grade_determinator + test/FinalGradeDeterminator.test.cpp + src/final_grade_determinator/nodes/FinalGradeDeterminator.cpp + src/database/DatabaseManager.cpp + src/config/ConfigManager.cpp + ) + target_include_directories(${PROJECT_NAME}_test_final_grade_determinator PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/final_grade_determinator + ) + ament_target_dependencies(${PROJECT_NAME}_test_final_grade_determinator + rclcpp + g2_2025_interfaces + ) + target_link_libraries(${PROJECT_NAME}_test_final_grade_determinator + pqxx pq tomlplusplus::tomlplusplus + ) endif() ament_package() diff --git a/src/g2_2025_grade_calculator_pkg/test/FinalGradeDeterminator.test.cpp b/src/g2_2025_grade_calculator_pkg/test/FinalGradeDeterminator.test.cpp new file mode 100644 index 0000000..bce83d9 --- /dev/null +++ b/src/g2_2025_grade_calculator_pkg/test/FinalGradeDeterminator.test.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#include "final_grade_determinator/nodes/FinalGradeDeterminator.hpp" +#include "database/DatabaseManager.hpp" +#include "g2_2025_interfaces/msg/exam.hpp" +#include "g2_2025_interfaces/msg/student.hpp" +#include "g2_2025_interfaces/srv/exams.hpp" + +using namespace std::chrono_literals; +using namespace assignments::one::final_grade_determinator; + +class FinalGradeDeterminatorTest : public ::testing::Test { +protected: + void SetUp() override { + rclcpp::init(0, nullptr); + + test_node_ = std::make_shared("test_node"); + + // Subscriber to capture student messages + student_subscriber_ = test_node_->create_subscription( + "student_course_management", 10, + [this](const g2_2025_interfaces::msg::Student::SharedPtr msg) { + received_student_messages_.push_back(*msg); + } + ); + + // Publisher to send exam messages + exam_publisher_ = test_node_->create_publisher( + "exam_results", 10 + ); + + // Mock service server for grade calculator + grade_calculator_service_ = test_node_->create_service( + "grade_calculator_service", + [this](const g2_2025_interfaces::srv::Exams::Request::SharedPtr request, + g2_2025_interfaces::srv::Exams::Response::SharedPtr response) { + service_requests_.push_back(*request); + // Mock calculation - average of grades + int sum = 0; + for (const auto& grade : request->exam_grades) { + sum += grade; + } + response->result = sum / request->exam_grades.size(); + } + ); + + received_student_messages_.clear(); + service_requests_.clear(); + } + + void TearDown() override { + final_grade_determinator_.reset(); + test_node_.reset(); + rclcpp::shutdown(); + } + + void create_final_grade_determinator() { + final_grade_determinator_ = std::make_shared(); + } + + void spin_some_time(std::chrono::milliseconds duration = 100ms) { + auto start_time = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start_time < duration) { + rclcpp::spin_some(test_node_); + if (final_grade_determinator_) { + rclcpp::spin_some(final_grade_determinator_); + } + std::this_thread::sleep_for(10ms); + } + } + + std::shared_ptr test_node_; + std::shared_ptr final_grade_determinator_; + rclcpp::Subscription::SharedPtr student_subscriber_; + rclcpp::Publisher::SharedPtr exam_publisher_; + rclcpp::Service::SharedPtr grade_calculator_service_; + + std::vector received_student_messages_; + std::vector service_requests_; +}; + +TEST_F(FinalGradeDeterminatorTest, ConstructorTest) { + ASSERT_NO_THROW({ + create_final_grade_determinator(); + }); + + ASSERT_NE(final_grade_determinator_, nullptr); +} + +TEST_F(FinalGradeDeterminatorTest, ExamCollectionTest) { + create_final_grade_determinator(); + spin_some_time(500ms); + + const std::string student_name = "Test Student"; + const std::string course_name = "Test Course"; + std::vector grades = {80, 85, 90, 75, 95}; + + // Send 5 exam results (default collection amount) + for (const auto& grade : grades) { + auto msg = std::make_shared(); + msg->student_name = student_name; + msg->course_name = course_name; + msg->result = grade; + msg->timestamp = test_node_->now(); + + exam_publisher_->publish(*msg); + spin_some_time(50ms); + } + + spin_some_time(1s); + + // Verify service request was made + ASSERT_FALSE(service_requests_.empty()); + EXPECT_EQ(service_requests_.back().student_name, student_name); + EXPECT_EQ(service_requests_.back().course_name, course_name); + EXPECT_EQ(service_requests_.back().exam_grades, grades); + + // Verify student message was published + ASSERT_FALSE(received_student_messages_.empty()); + EXPECT_EQ(received_student_messages_.back().student_name, student_name); + EXPECT_EQ(received_student_messages_.back().course_name, course_name); +} + +TEST_F(FinalGradeDeterminatorTest, PartialExamCollectionTest) { + create_final_grade_determinator(); + spin_some_time(500ms); + + // Send only 3 exam results (less than required 5) + const std::string student_name = "Test Student"; + const std::string course_name = "Test Course"; + std::vector grades = {80, 85, 90}; + + for (const auto& grade : grades) { + auto msg = std::make_shared(); + msg->student_name = student_name; + msg->course_name = course_name; + msg->result = grade; + msg->timestamp = test_node_->now(); + + exam_publisher_->publish(*msg); + spin_some_time(50ms); + } + + spin_some_time(1s); + + // Verify no service request was made yet + EXPECT_TRUE(service_requests_.empty()); + // Verify no student message was published + EXPECT_TRUE(received_student_messages_.empty()); +} + +TEST_F(FinalGradeDeterminatorTest, MultipleStudentsTest) { + create_final_grade_determinator(); + spin_some_time(500ms); + + std::vector students = {"Alice", "Bob"}; + const std::string course_name = "Test Course"; + std::vector grades = {80, 85, 90, 75, 95}; + + // Send complete set of grades for each student + for (const auto& student : students) { + for (const auto& grade : grades) { + auto msg = std::make_shared(); + msg->student_name = student; + msg->course_name = course_name; + msg->result = grade; + msg->timestamp = test_node_->now(); + + exam_publisher_->publish(*msg); + spin_some_time(50ms); + } + } + + spin_some_time(1s); + + // Verify service requests were made for both students + ASSERT_EQ(service_requests_.size(), 2); + // Verify student messages were published for both students + ASSERT_EQ(received_student_messages_.size(), 2); +}