test: Update Database tests, create IMUDatabaseWriter tests

This commit is contained in:
2025-11-03 11:48:32 +01:00
parent cdd3a8e463
commit 613a79c4f3
5 changed files with 145 additions and 162 deletions

View File

@@ -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)

View File

@@ -2,7 +2,6 @@
#include <rclcpp/rclcpp.hpp>
#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());
}

View File

@@ -0,0 +1,104 @@
#include <chrono>
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include <gtest/gtest.h>
#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<rclcpp::Node>("test_node");
// create mock db and inject
mock_db_ = std::make_unique<assignments::two::MockDatabaseManager>(rclcpp::get_logger("test_mock_db"));
mock_db_ptr_ = mock_db_.get();
imu_node_ = std::make_shared<IMUDatabaseWriter>(std::move(mock_db_));
// publisher in test node
imu_publisher_ = test_node_->create_publisher<sensor_msgs::msg::Imu>("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<rclcpp::Node> test_node_;
std::shared_ptr<IMUDatabaseWriter> imu_node_;
std::unique_ptr<assignments::two::MockDatabaseManager> mock_db_;
assignments::two::MockDatabaseManager* mock_db_ptr_ = nullptr;
rclcpp::Publisher<sensor_msgs::msg::Imu>::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();
}

View File

@@ -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<StudentCourse> 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<StudentCourse>& failed_students) {
failed_students_ = failed_students;
}
std::vector<MockStoredResult> stored_results;
std::vector<StudentCourse> retake_status_updates;
private:
std::vector<StudentCourse> failed_students_;
bool connection_status_ = true;
};

View File

@@ -1,57 +0,0 @@
#pragma once
namespace assignments::two {
class MockRetakeActionServer {
public:
MockRetakeActionServer(std::shared_ptr<rclcpp::Node> node) : node_(node) {
action_server_ = rclcpp_action::create_server<g2_2025_interfaces::action::Retake>(
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<const g2_2025_interfaces::action::Retake::Goal> goal
) {
(void)uuid;
received_goals_.push_back(*goal);
return goal_response_;
}
rclcpp_action::CancelResponse handle_cancel(
const std::shared_ptr<rclcpp_action::ServerGoalHandle<g2_2025_interfaces::action::Retake>> goal_handle
) {
(void)goal_handle;
cancel_requests++;
return rclcpp_action::CancelResponse::ACCEPT;
}
void handle_accepted(const std::shared_ptr<rclcpp_action::ServerGoalHandle<g2_2025_interfaces::action::Retake>> goal_handle) {
accepted_goals++;
// Simulate immediate success
auto result = std::make_shared<g2_2025_interfaces::action::Retake::Result>();
result->result = 0.0;
goal_handle->succeed(result);
}
void set_goal_response(rclcpp_action::GoalResponse response) {
goal_response_ = response;
}
std::vector<g2_2025_interfaces::action::Retake::Goal> received_goals_;
int accepted_goals = 0;
int cancel_requests = 0;
private:
std::shared_ptr<rclcpp::Node> node_;
rclcpp_action::Server<g2_2025_interfaces::action::Retake>::SharedPtr action_server_;
rclcpp_action::GoalResponse goal_response_ = rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
};
} // namespace assignments::two