generated from wessel/boilerplate
[PR] Implement tests for Config, Database and ExamResultGenerator #3
@@ -108,6 +108,25 @@ if(BUILD_TESTING)
|
||||
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
|
||||
g2_2025_interfaces
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME}_test_exam_result_generator
|
||||
pqxx pq tomlplusplus::tomlplusplus
|
||||
)
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "exam_result_generator/nodes/ExamResultGenerator.hpp"
|
||||
#include "g2_2025_interfaces/msg/exam.hpp"
|
||||
#include "g2_2025_interfaces/msg/student.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace assignments::one::exam_result_generator;
|
||||
|
||||
class ExamResultGeneratorTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
// Create a test subscriber to capture published messages
|
||||
test_node_ = std::make_shared<rclcpp::Node>("test_subscriber_node");
|
||||
|
||||
// Subscriber to capture exam results
|
||||
exam_subscriber_ = test_node_->create_subscription<g2_2025_interfaces::msg::Exam>(
|
||||
"exam_results", 10,
|
||||
[this](const g2_2025_interfaces::msg::Exam::SharedPtr msg) {
|
||||
received_exam_messages_.push_back(*msg);
|
||||
}
|
||||
);
|
||||
|
||||
// Publisher to send student management messages
|
||||
student_publisher_ = test_node_->create_subscription<g2_2025_interfaces::msg::Student>(
|
||||
"student_course_management", 10,
|
||||
[this](const g2_2025_interfaces::msg::Student::SharedPtr msg) {
|
||||
received_student_messages_.push_back(*msg);
|
||||
}
|
||||
);
|
||||
|
||||
received_exam_messages_.clear();
|
||||
received_student_messages_.clear();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
exam_result_generator_.reset();
|
||||
test_node_.reset();
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void create_exam_result_generator() {
|
||||
exam_result_generator_ = std::make_shared<ExamResultGenerator>();
|
||||
}
|
||||
|
||||
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 (exam_result_generator_) {
|
||||
rclcpp::spin_some(exam_result_generator_);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Node> test_node_;
|
||||
std::shared_ptr<ExamResultGenerator> exam_result_generator_;
|
||||
rclcpp::Subscription<g2_2025_interfaces::msg::Exam>::SharedPtr exam_subscriber_;
|
||||
rclcpp::Subscription<g2_2025_interfaces::msg::Student>::SharedPtr student_publisher_;
|
||||
|
||||
std::vector<g2_2025_interfaces::msg::Exam> received_exam_messages_;
|
||||
std::vector<g2_2025_interfaces::msg::Student> received_student_messages_;
|
||||
};
|
||||
|
||||
TEST_F(ExamResultGeneratorTest, ConstructorTest) {
|
||||
ASSERT_NO_THROW({
|
||||
create_exam_result_generator();
|
||||
});
|
||||
|
||||
ASSERT_NE(exam_result_generator_, nullptr);
|
||||
EXPECT_TRUE(exam_result_generator_->get_node_base_interface() != nullptr);
|
||||
EXPECT_TRUE(exam_result_generator_->get_node_clock_interface() != nullptr);
|
||||
EXPECT_TRUE(exam_result_generator_->get_node_logging_interface() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ExamResultGeneratorTest, PublisherCreationTest) {
|
||||
create_exam_result_generator();
|
||||
|
||||
// Get topic names and types to verify publisher exists
|
||||
auto topic_names_and_types = exam_result_generator_->get_topic_names_and_types();
|
||||
|
||||
bool exam_results_topic_found = false;
|
||||
for (const auto& [topic_name, topic_types] : topic_names_and_types) {
|
||||
if (topic_name == "/exam_results") {
|
||||
exam_results_topic_found = true;
|
||||
// Check if the topic type includes our Exam message type
|
||||
bool correct_type = false;
|
||||
for (const auto& type : topic_types) {
|
||||
if (type == "g2_2025_interfaces/msg/Exam") {
|
||||
correct_type = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(correct_type) << "exam_results topic should have Exam message type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(exam_results_topic_found) << "exam_results topic should be published";
|
||||
}
|
||||
|
||||
TEST_F(ExamResultGeneratorTest, SubscriberCreationTest) {
|
||||
create_exam_result_generator();
|
||||
|
||||
// Get subscription names and types to verify subscriber exists
|
||||
auto topic_names_and_types = exam_result_generator_->get_topic_names_and_types();
|
||||
|
||||
bool student_management_topic_found = false;
|
||||
for (const auto& [topic_name, topic_types] : topic_names_and_types) {
|
||||
if (topic_name == "/student_course_management") {
|
||||
student_management_topic_found = true;
|
||||
// Check if the topic type includes our Student message type
|
||||
bool correct_type = false;
|
||||
for (const auto& type : topic_types) {
|
||||
if (type == "g2_2025_interfaces/msg/Student") {
|
||||
correct_type = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(correct_type) << "student_course_management topic should have Student message type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(student_management_topic_found) << "student_course_management topic should be subscribed";
|
||||
}
|
||||
|
||||
TEST_F(ExamResultGeneratorTest, StudentManagementMessageHandlingTest) {
|
||||
create_exam_result_generator();
|
||||
|
||||
// Create a publisher to send student management messages
|
||||
auto student_mgmt_publisher = test_node_->create_publisher<g2_2025_interfaces::msg::Student>(
|
||||
"student_course_management", 10
|
||||
);
|
||||
|
||||
// Allow some time for publisher-subscriber connection
|
||||
spin_some_time(500ms);
|
||||
|
||||
// Create and send a student management message
|
||||
auto student_msg = std::make_shared<g2_2025_interfaces::msg::Student>();
|
||||
student_msg->student_name = "Test Student";
|
||||
student_msg->course_name = "Test Course";
|
||||
student_msg->timestamp = test_node_->now();
|
||||
|
||||
student_mgmt_publisher->publish(*student_msg);
|
||||
|
||||
spin_some_time(500ms);
|
||||
|
||||
// Test passes if no crash occurred during message handling
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST_F(ExamResultGeneratorTest, MultipleStudentMessagesTest) {
|
||||
create_exam_result_generator();
|
||||
|
||||
auto student_mgmt_publisher = test_node_->create_publisher<g2_2025_interfaces::msg::Student>(
|
||||
"student_course_management", 10
|
||||
);
|
||||
|
||||
spin_some_time(500ms);
|
||||
|
||||
// Send multiple student management messages
|
||||
std::vector<std::string> students = {"Alice", "Bob", "Charlie"};
|
||||
std::vector<std::string> courses = {"Math", "Physics", "Chemistry"};
|
||||
|
||||
for (const auto& student : students) {
|
||||
for (const auto& course : courses) {
|
||||
auto msg = std::make_shared<g2_2025_interfaces::msg::Student>();
|
||||
msg->student_name = student;
|
||||
msg->course_name = course;
|
||||
msg->timestamp = test_node_->now();
|
||||
|
||||
student_mgmt_publisher->publish(*msg);
|
||||
spin_some_time(50ms);
|
||||
}
|
||||
}
|
||||
|
||||
spin_some_time(1s);
|
||||
|
||||
// Test passes if no crash occurred
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
// Test for message content validation (when messages are published)
|
||||
TEST_F(ExamResultGeneratorTest, ExamMessageValidationTest) {
|
||||
create_exam_result_generator();
|
||||
|
||||
// Create exam result subscriber to capture messages
|
||||
auto exam_subscriber = test_node_->create_subscription<g2_2025_interfaces::msg::Exam>(
|
||||
"exam_results", 10,
|
||||
[this](const g2_2025_interfaces::msg::Exam::SharedPtr msg) {
|
||||
received_exam_messages_.push_back(*msg);
|
||||
}
|
||||
);
|
||||
|
||||
spin_some_time(500ms);
|
||||
|
||||
// Spin for several timer cycles to potentially catch exam messages
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - start_time < 6s) {
|
||||
spin_some_time(100ms);
|
||||
|
||||
for (const auto& exam_msg : received_exam_messages_) {
|
||||
// Validate grade range (should be 10-100)
|
||||
EXPECT_GE(exam_msg.result, 10) << "Grade should be >= 10";
|
||||
EXPECT_LE(exam_msg.result, 100) << "Grade should be <= 100";
|
||||
|
||||
EXPECT_FALSE(exam_msg.course_name.empty()) << "Course name should not be empty";
|
||||
}
|
||||
}
|
||||
|
||||
// Test passes regardless of whether messages were received (depends on database connectivity)
|
||||
SUCCEED();
|
||||
}
|
||||
Reference in New Issue
Block a user