diff --git a/src/g2_2025_imu_reader_pkg/CMakeLists.txt b/src/g2_2025_imu_reader_pkg/CMakeLists.txt index 4b814d8..460a0c4 100644 --- a/src/g2_2025_imu_reader_pkg/CMakeLists.txt +++ b/src/g2_2025_imu_reader_pkg/CMakeLists.txt @@ -60,39 +60,40 @@ if(BUILD_TESTING) tomlplusplus::tomlplusplus ) + # Add gtest for IMUDatabaseWriter node + ament_add_gtest(${PROJECT_NAME}_test_imu_database_writer + test/IMUDatabaseWriter.test.cpp + src/imu_database_writer/nodes/IMUDatabaseWriter.cpp + src/database/DatabaseManager.cpp + src/config/ConfigManager.cpp + ) + target_include_directories(${PROJECT_NAME}_test_imu_database_writer PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/imu_database_writer + ) + ament_target_dependencies(${PROJECT_NAME}_test_imu_database_writer + rclcpp + sensor_msgs + ) + target_link_libraries(${PROJECT_NAME}_test_imu_database_writer + pqxx pq tomlplusplus::tomlplusplus + ) + # Add gtest for DatabaseManager -# ament_add_gtest(${PROJECT_NAME}_test_database_manager -# test/DatabaseManager.test.cpp -# src/database/DatabaseManager.cpp -# src/config/ConfigManager.cpp -# ) -# target_include_directories(${PROJECT_NAME}_test_database_manager PRIVATE -# ${CMAKE_CURRENT_SOURCE_DIR}/src -# ) -# ament_target_dependencies(${PROJECT_NAME}_test_database_manager -# rclcpp -# ) -# target_link_libraries(${PROJECT_NAME}_test_database_manager -# pqxx pq tomlplusplus::tomlplusplus -# ) - # - # # Add gtest for ExamResultGenerator - # ament_add_gtest(${PROJECT_NAME}_test_exam_result_generator - # test/ExamResultGenerator.test.cpp - # src/exam_result_generator/nodes/ExamResultGenerator.cpp - # src/database/DatabaseManager.cpp - # src/config/ConfigManager.cpp - # ) - # target_include_directories(${PROJECT_NAME}_test_exam_result_generator PRIVATE - # ${CMAKE_CURRENT_SOURCE_DIR}/src - # ${CMAKE_CURRENT_SOURCE_DIR}/src/exam_result_generator - # ) - # ament_target_dependencies(${PROJECT_NAME}_test_exam_result_generator - # rclcpp - # ) - # target_link_libraries(${PROJECT_NAME}_test_exam_result_generator - # pqxx pq tomlplusplus::tomlplusplus - # ) + ament_add_gtest(${PROJECT_NAME}_test_database_manager + test/DatabaseManager.test.cpp + src/database/DatabaseManager.cpp + src/config/ConfigManager.cpp + ) + target_include_directories(${PROJECT_NAME}_test_database_manager PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + ament_target_dependencies(${PROJECT_NAME}_test_database_manager + rclcpp + ) + target_link_libraries(${PROJECT_NAME}_test_database_manager + pqxx pq tomlplusplus::tomlplusplus + ) # Add Python integration tests # find_package(ament_cmake_pytest REQUIRED) diff --git a/src/g2_2025_imu_reader_pkg/test/DatabaseManager.test.cpp b/src/g2_2025_imu_reader_pkg/test/DatabaseManager.test.cpp index 731ebf3..dc62699 100644 --- a/src/g2_2025_imu_reader_pkg/test/DatabaseManager.test.cpp +++ b/src/g2_2025_imu_reader_pkg/test/DatabaseManager.test.cpp @@ -2,7 +2,6 @@ #include #include "database/DatabaseManager.hpp" -#include "database/StudentCourse.hpp" using namespace assignments::two; @@ -31,49 +30,18 @@ TEST_F(DatabaseManagerTest, ConstructorTest) { } TEST_F(DatabaseManagerTest, ConnectionStatusTest) { + // Should return a boolean (connected or not) — here no DB configured so expect false bool status = db_manager_->is_connected(); - EXPECT_TRUE(status == true || status == false); } -TEST_F(DatabaseManagerTest, QueuePendingCombinationsTest) { - auto combinations = db_manager_->queue_pending_combinations(); - - EXPECT_GE(combinations.size(), 0); +TEST_F(DatabaseManagerTest, StoreIMUDataWhenNotConnected) { + // Without a real DB connection, storing IMU data should return false + bool result = db_manager_->store_imu_data(1.0, 2.0, 3.0, 0.1, 0.2, 0.3); + EXPECT_FALSE(result); } -TEST_F(DatabaseManagerTest, StoreExamResultTest) { - bool result = db_manager_->store_exam_result("TestStudent", "TestCourse", 85); - - EXPECT_TRUE(result == true || result == false); -} - -TEST_F(DatabaseManagerTest, EnrollStudentTest) { - StudentCourse sc; - sc.student_name = "TestStudent"; - sc.course_name = "TestCourse"; - - bool result = db_manager_->enroll_student_into_course(sc); - - EXPECT_TRUE(result == true || result == false); -} - -TEST_F(DatabaseManagerTest, GetFinalGradeTest) { - StudentCourse sc; - sc.student_name = "NonExistentStudent"; - sc.course_name = "NonExistentCourse"; - - int grade = db_manager_->get_final_course_grade(sc); - - EXPECT_EQ(grade, -1); -} - -TEST_F(DatabaseManagerTest, StoreFinalResultTest) { - StudentCourse sc; - sc.student_name = "TestStudent"; - sc.course_name = "TestCourse"; - - bool result = db_manager_->store_final_course_result(sc, 3, 75, false); - - EXPECT_TRUE(result == true || result == false); +TEST_F(DatabaseManagerTest, CreateTablesNoCrash) { + // create_tables should be safe to call even when not connected (no throw) + EXPECT_NO_THROW(db_manager_->create_tables()); } diff --git a/src/g2_2025_imu_reader_pkg/test/IMUDatabaseWriter.test.cpp b/src/g2_2025_imu_reader_pkg/test/IMUDatabaseWriter.test.cpp new file mode 100644 index 0000000..dd33c25 --- /dev/null +++ b/src/g2_2025_imu_reader_pkg/test/IMUDatabaseWriter.test.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +#include "imu_database_writer/nodes/IMUDatabaseWriter.hpp" +#include "database/DatabaseManager.hpp" +#include "sensor_msgs/msg/imu.hpp" + +using namespace std::chrono_literals; +using namespace assignments::two::imu_database_writer; + +namespace assignments::two { + +class MockDatabaseManager : public DatabaseManager { +public: + explicit MockDatabaseManager(rclcpp::Logger logger = rclcpp::get_logger("mock_db")) + : DatabaseManager(logger) { + } + + // Match the current DatabaseManager::store_imu_data signature (no timestamp) + bool store_imu_data( + double linear_accel_x, double linear_accel_y, double linear_accel_z, + double angular_vel_x, double angular_vel_y, double angular_vel_z + ) override { + called_ = true; + last_la_x_ = linear_accel_x; + last_la_y_ = linear_accel_y; + last_la_z_ = linear_accel_z; + last_av_x_ = angular_vel_x; + last_av_y_ = angular_vel_y; + last_av_z_ = angular_vel_z; + return true; + } + + bool called_ = false; + double last_la_x_ = 0.0, last_la_y_ = 0.0, last_la_z_ = 0.0; + double last_av_x_ = 0.0, last_av_y_ = 0.0, last_av_z_ = 0.0; +}; + +} // namespace assignments::two + +class IMUDatabaseWriterTest : public ::testing::Test { +protected: + void SetUp() override { + rclcpp::init(0, nullptr); + test_node_ = std::make_shared("test_node"); + + // create mock db and inject + mock_db_ = std::make_unique(rclcpp::get_logger("test_mock_db")); + mock_db_ptr_ = mock_db_.get(); + + imu_node_ = std::make_shared(std::move(mock_db_)); + + // publisher in test node + imu_publisher_ = test_node_->create_publisher("imu_data", 10); + } + + void TearDown() override { + imu_node_.reset(); + test_node_.reset(); + rclcpp::shutdown(); + } + + void spin_some_time(std::chrono::milliseconds duration = 200ms) { + auto start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < duration) { + rclcpp::spin_some(test_node_); + if (imu_node_) rclcpp::spin_some(imu_node_); + std::this_thread::sleep_for(10ms); + } + } + + std::shared_ptr test_node_; + std::shared_ptr imu_node_; + std::unique_ptr mock_db_; + assignments::two::MockDatabaseManager* mock_db_ptr_ = nullptr; + rclcpp::Publisher::SharedPtr imu_publisher_; +}; + +TEST_F(IMUDatabaseWriterTest, ConstructorTest) { + ASSERT_NE(imu_node_, nullptr); +} + +TEST_F(IMUDatabaseWriterTest, ReceivesAndForwardsIMU) { + auto msg = sensor_msgs::msg::Imu(); + msg.linear_acceleration.x = 1.23; + msg.linear_acceleration.y = -0.5; + msg.linear_acceleration.z = 0.0; + msg.angular_velocity.x = 0.01; + msg.angular_velocity.y = -0.02; + msg.angular_velocity.z = 0.03; + + imu_publisher_->publish(msg); + + spin_some_time(300ms); + + EXPECT_TRUE(mock_db_ptr_->called_); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/g2_2025_imu_reader_pkg/test/mocks/MockDatabaseManager.hpp b/src/g2_2025_imu_reader_pkg/test/mocks/MockDatabaseManager.hpp index 33d6f39..a7f8885 100644 --- a/src/g2_2025_imu_reader_pkg/test/mocks/MockDatabaseManager.hpp +++ b/src/g2_2025_imu_reader_pkg/test/mocks/MockDatabaseManager.hpp @@ -5,7 +5,6 @@ namespace assignments::two { struct MockStoredResult { - StudentCourse sc; int exam_count; int final_grade; bool is_retake; @@ -28,45 +27,13 @@ public: connection_status_ = status; } - std::vector get_failed_course_results() { - return failed_students_; - } - - bool store_final_course_result( - const StudentCourse& sc, - int exam_count, - int final_grade, - bool is_retake - ) { - stored_results.push_back({ sc, exam_count, final_grade, is_retake }); - return true; - } - - bool update_retake_status(const StudentCourse& sc) { - retake_status_updates.push_back(sc); - return true; - } - void clear_failed_students() { failed_students_.clear(); } - void add_failed_student(const std::string& student_name, const std::string& course_name) { - StudentCourse sc; - sc.student_name = student_name; - sc.course_name = course_name; - failed_students_.push_back(sc); - } - - void set_failed_students(const std::vector& failed_students) { - failed_students_ = failed_students; - } - std::vector stored_results; - std::vector retake_status_updates; private: - std::vector failed_students_; bool connection_status_ = true; }; diff --git a/src/g2_2025_imu_reader_pkg/test/mocks/MockRetakeActionServer.hpp b/src/g2_2025_imu_reader_pkg/test/mocks/MockRetakeActionServer.hpp deleted file mode 100644 index 823721d..0000000 --- a/src/g2_2025_imu_reader_pkg/test/mocks/MockRetakeActionServer.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -namespace assignments::two { - -class MockRetakeActionServer { -public: - MockRetakeActionServer(std::shared_ptr node) : node_(node) { - action_server_ = rclcpp_action::create_server( - node_, - "retake_action", - std::bind(&MockRetakeActionServer::handle_goal, this, std::placeholders::_1, std::placeholders::_2), - std::bind(&MockRetakeActionServer::handle_cancel, this, std::placeholders::_1), - std::bind(&MockRetakeActionServer::handle_accepted, this, std::placeholders::_1) - ); - } - - rclcpp_action::GoalResponse handle_goal( - const rclcpp_action::GoalUUID & uuid, - std::shared_ptr goal - ) { - (void)uuid; - received_goals_.push_back(*goal); - return goal_response_; - } - - rclcpp_action::CancelResponse handle_cancel( - const std::shared_ptr> goal_handle - ) { - (void)goal_handle; - cancel_requests++; - return rclcpp_action::CancelResponse::ACCEPT; - } - - void handle_accepted(const std::shared_ptr> goal_handle) { - accepted_goals++; - - // Simulate immediate success - auto result = std::make_shared(); - result->result = 0.0; - goal_handle->succeed(result); - } - - void set_goal_response(rclcpp_action::GoalResponse response) { - goal_response_ = response; - } - - std::vector received_goals_; - int accepted_goals = 0; - int cancel_requests = 0; - -private: - std::shared_ptr node_; - rclcpp_action::Server::SharedPtr action_server_; - rclcpp_action::GoalResponse goal_response_ = rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; -}; - -} // namespace assignments::two