generated from wessel/boilerplate
feat(retake): Add tests for retake nodes
This commit is contained in:
@@ -201,11 +201,51 @@ if(BUILD_TESTING)
|
||||
pqxx pq tomlplusplus::tomlplusplus
|
||||
)
|
||||
|
||||
# Add Python integration tests
|
||||
find_package(ament_cmake_pytest REQUIRED)
|
||||
ament_add_pytest_test(${PROJECT_NAME}_integration_test test/test_integration_system.py
|
||||
TIMEOUT 60
|
||||
# Add gtest for RetakeGradeDeterminator
|
||||
ament_add_gtest(${PROJECT_NAME}_test_retake_grade_determinator
|
||||
test/RetakeGradeDeterminator.test.cpp
|
||||
src/retake_grade_determinator/nodes/RetakeGradeDeterminator.cpp
|
||||
src/database/DatabaseManager.cpp
|
||||
src/config/ConfigManager.cpp
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME}_test_retake_grade_determinator PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/retake_grade_determinator
|
||||
)
|
||||
ament_target_dependencies(${PROJECT_NAME}_test_retake_grade_determinator
|
||||
rclcpp
|
||||
rclcpp_action
|
||||
g2_2025_interfaces
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME}_test_retake_grade_determinator
|
||||
pqxx pq tomlplusplus::tomlplusplus
|
||||
)
|
||||
|
||||
# Add gtest for RetakeScheduler
|
||||
ament_add_gtest(${PROJECT_NAME}_test_retake_scheduler
|
||||
test/RetakeScheduler.test.cpp
|
||||
src/retake_scheduler/nodes/RetakeScheduler.cpp
|
||||
src/database/DatabaseManager.cpp
|
||||
src/config/ConfigManager.cpp
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME}_test_retake_scheduler PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/retake_scheduler
|
||||
)
|
||||
ament_target_dependencies(${PROJECT_NAME}_test_retake_scheduler
|
||||
rclcpp
|
||||
rclcpp_action
|
||||
g2_2025_interfaces
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME}_test_retake_scheduler
|
||||
pqxx pq tomlplusplus::tomlplusplus
|
||||
)
|
||||
|
||||
# Add Python integration tests
|
||||
# find_package(ament_cmake_pytest REQUIRED)
|
||||
# ament_add_pytest_test(${PROJECT_NAME}_integration_test test/test_integration_system.py
|
||||
# TIMEOUT 60
|
||||
# )
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
|
||||
@@ -9,46 +9,11 @@
|
||||
#include "g2_2025_interfaces/msg/student.hpp"
|
||||
#include "g2_2025_interfaces/srv/exams.hpp"
|
||||
|
||||
#include "mocks/MockDatabaseManager.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace assignments::one::final_grade_determinator;
|
||||
|
||||
namespace assignments::one {
|
||||
|
||||
struct MockStoredResult {
|
||||
StudentCourse sc;
|
||||
int exam_count;
|
||||
int final_grade;
|
||||
bool is_retake;
|
||||
};
|
||||
|
||||
class MockDatabaseManager : public DatabaseManager {
|
||||
public:
|
||||
explicit MockDatabaseManager(rclcpp::Logger logger = rclcpp::get_logger("fake_db"))
|
||||
: DatabaseManager(logger) {
|
||||
}
|
||||
|
||||
bool is_connected() const override {
|
||||
return true; // Always pretend we are connected
|
||||
}
|
||||
|
||||
bool store_final_course_result(
|
||||
const StudentCourse& sc,
|
||||
int exam_count,
|
||||
int final_grade,
|
||||
bool is_retake
|
||||
) override {
|
||||
stored_results_.push_back({ sc, exam_count, final_grade, is_retake });
|
||||
return true; // Always succeed
|
||||
}
|
||||
|
||||
void init_database() override {
|
||||
} // no-op
|
||||
|
||||
std::vector<MockStoredResult> stored_results_;
|
||||
};
|
||||
|
||||
} // namespace assignments::one
|
||||
|
||||
class FinalGradeDeterminatorTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp_action/rclcpp_action.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "retake_grade_determinator/nodes/RetakeGradeDeterminator.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"
|
||||
#include "g2_2025_interfaces/action/retake.hpp"
|
||||
|
||||
#include "mocks/MockDatabaseManager.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace assignments::one::retake_grade_determinator;
|
||||
|
||||
class RetakeGradeDeterminatorTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
// Create a test node for testing communication
|
||||
test_node_ = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
// Create mock database manager
|
||||
mock_db_ = std::make_unique<assignments::one::MockDatabaseManager>();
|
||||
ptr_mock_db_ = mock_db_.get();
|
||||
|
||||
// Subscriber to capture student management messages
|
||||
student_subscriber_ = 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);
|
||||
}
|
||||
);
|
||||
|
||||
// Publisher to send exam results
|
||||
exam_publisher_ = test_node_->create_publisher<g2_2025_interfaces::msg::Exam>(
|
||||
"exam_results", 10
|
||||
);
|
||||
|
||||
// Service server to mock grade calculator
|
||||
grade_calculator_service_ = test_node_->create_service<g2_2025_interfaces::srv::Exams>(
|
||||
"grade_calculator_service",
|
||||
[this](const std::shared_ptr<g2_2025_interfaces::srv::Exams::Request> request,
|
||||
std::shared_ptr<g2_2025_interfaces::srv::Exams::Response> response) {
|
||||
service_requests_.push_back(*request);
|
||||
response->result = 75;
|
||||
}
|
||||
);
|
||||
|
||||
// Action client to test retake action server
|
||||
retake_action_client_ = rclcpp_action::create_client<g2_2025_interfaces::action::Retake>(
|
||||
test_node_, "retake_action"
|
||||
);
|
||||
|
||||
received_student_messages_.clear();
|
||||
service_requests_.clear();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
retake_grade_determinator_.reset();
|
||||
test_node_.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void create_retake_grade_determinator() {
|
||||
retake_grade_determinator_ = std::make_shared<RetakeGradeDeterminator>(
|
||||
std::move(mock_db_)
|
||||
);
|
||||
}
|
||||
|
||||
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 (retake_grade_determinator_) {
|
||||
rclcpp::spin_some(retake_grade_determinator_);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Node> test_node_;
|
||||
|
||||
std::unique_ptr<assignments::one::MockDatabaseManager> mock_db_;
|
||||
assignments::one::MockDatabaseManager* ptr_mock_db_;
|
||||
std::shared_ptr<RetakeGradeDeterminator> retake_grade_determinator_;
|
||||
|
||||
rclcpp::Subscription<g2_2025_interfaces::msg::Student>::SharedPtr student_subscriber_;
|
||||
rclcpp::Publisher<g2_2025_interfaces::msg::Exam>::SharedPtr exam_publisher_;
|
||||
rclcpp::Service<g2_2025_interfaces::srv::Exams>::SharedPtr grade_calculator_service_;
|
||||
rclcpp_action::Client<g2_2025_interfaces::action::Retake>::SharedPtr retake_action_client_;
|
||||
|
||||
std::vector<g2_2025_interfaces::msg::Student> received_student_messages_;
|
||||
std::vector<g2_2025_interfaces::srv::Exams::Request> service_requests_;
|
||||
};
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, ConstructorTest) {
|
||||
ASSERT_NO_THROW(create_retake_grade_determinator());
|
||||
|
||||
ASSERT_NE(retake_grade_determinator_, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, PublisherCreationTest) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
bool student_management_topic_found = false;
|
||||
auto topic_names_and_types = retake_grade_determinator_->get_topic_names_and_types();
|
||||
|
||||
for (const auto& [ topic_name, topic_types ] : topic_names_and_types) {
|
||||
if (topic_name == "/student_course_management") {
|
||||
student_management_topic_found = true;
|
||||
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 published";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, SubscriberCreationTest) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
bool exam_results_topic_found = false;
|
||||
auto topic_names_and_types = retake_grade_determinator_->get_topic_names_and_types();
|
||||
|
||||
for (const auto& [ topic_name, topic_types ] : topic_names_and_types) {
|
||||
if (topic_name == "/exam_results") {
|
||||
exam_results_topic_found = true;
|
||||
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 subscribed";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, ActionServerCreationTest) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
// Allow time for action server to be created
|
||||
spin_some_time(500ms);
|
||||
|
||||
// Check if action client can connect to the server
|
||||
bool server_available = retake_action_client_->wait_for_action_server(
|
||||
std::chrono::seconds(2)
|
||||
);
|
||||
|
||||
EXPECT_TRUE(server_available)
|
||||
<< "Retake action server should be available";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, ServiceClientCreationTest) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
auto service_names_and_types = retake_grade_determinator_->get_service_names_and_types();
|
||||
|
||||
bool grade_calculator_service_found = false;
|
||||
for (const auto& [ service_name, service_types ] : service_names_and_types) {
|
||||
if (service_name == "/grade_calculator_service") {
|
||||
grade_calculator_service_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(grade_calculator_service_found)
|
||||
<< "grade_calculator_service should be available as client";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, ParameterTest) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
auto param = retake_grade_determinator_->get_parameter("grade_collection_amount");
|
||||
EXPECT_EQ(param.as_int(), 5)
|
||||
<< "Default grade_collection_amount should be 5";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, ExamResultsIgnoredWhenRetakeNotAllowed) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
auto exam_msg = std::make_shared<g2_2025_interfaces::msg::Exam>();
|
||||
exam_msg->student_name = "tilmann";
|
||||
exam_msg->course_name = "computeren";
|
||||
exam_msg->result = 85;
|
||||
|
||||
exam_publisher_->publish(*exam_msg);
|
||||
spin_some_time(200ms);
|
||||
|
||||
EXPECT_TRUE(service_requests_.empty())
|
||||
<< "No service requests should be made when retake not allowed";
|
||||
}
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, RetakeActionGoalAcceptance) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
ASSERT_TRUE(retake_action_client_->wait_for_action_server(std::chrono::seconds(2)));
|
||||
|
||||
auto goal = g2_2025_interfaces::action::Retake::Goal();
|
||||
goal.student_name = "tilmann";
|
||||
goal.course_name = "differentieren";
|
||||
|
||||
bool goal_accepted = false;
|
||||
auto send_goal_options = rclcpp_action::Client<g2_2025_interfaces::action::Retake>::SendGoalOptions();
|
||||
|
||||
send_goal_options.goal_response_callback =
|
||||
[&goal_accepted](
|
||||
std::shared_ptr<rclcpp_action::ClientGoalHandle<g2_2025_interfaces::action::Retake>> goal_handle
|
||||
) {
|
||||
if (goal_handle) {
|
||||
goal_accepted = true;
|
||||
}
|
||||
};
|
||||
|
||||
retake_action_client_->async_send_goal(goal, send_goal_options);
|
||||
// Give asynchronous action service time to process
|
||||
spin_some_time(500ms);
|
||||
|
||||
EXPECT_TRUE(goal_accepted)
|
||||
<< "Retake goal should be accepted";
|
||||
EXPECT_EQ(received_student_messages_.size(), 1)
|
||||
<< "Should publish student enrollment message";
|
||||
|
||||
if (!received_student_messages_.empty()) {
|
||||
EXPECT_EQ(received_student_messages_[0].student_name, "tilmann");
|
||||
EXPECT_EQ(received_student_messages_[0].course_name, "differentieren");
|
||||
}
|
||||
}
|
||||
|
||||
// TEST_F(RetakeGradeDeterminatorTest, ExamResultsProcessingDuringRetake) {
|
||||
// create_retake_grade_determinator();
|
||||
|
||||
// ASSERT_TRUE(retake_action_client_->wait_for_action_server(std::chrono::seconds(2)));
|
||||
|
||||
// auto goal = g2_2025_interfaces::action::Retake::Goal();
|
||||
// goal.student_name = "tilmann";
|
||||
// goal.course_name = "differentieren";
|
||||
|
||||
// auto send_goal_options = rclcpp_action::Client<g2_2025_interfaces::action::Retake>::SendGoalOptions();
|
||||
// retake_action_client_->async_send_goal(goal, send_goal_options);
|
||||
// spin_some_time(300ms);
|
||||
|
||||
// received_student_messages_.clear();
|
||||
|
||||
// for (int i = 0; i < 5; ++i) {
|
||||
// auto exam_msg = std::make_shared<g2_2025_interfaces::msg::Exam>();
|
||||
// exam_msg->student_name = "tilmann";
|
||||
// exam_msg->course_name = "differentieren";
|
||||
// exam_msg->result = 70 + i;
|
||||
|
||||
// exam_publisher_->publish(*exam_msg);
|
||||
// }
|
||||
|
||||
// spin_some_time(500ms);
|
||||
|
||||
// EXPECT_EQ(service_requests_.size(), 1)
|
||||
// << "Should make one service request after collecting required exam results";
|
||||
|
||||
// if (!service_requests_.empty()) {
|
||||
// EXPECT_EQ(service_requests_[0].student_name, "tilmann");
|
||||
// EXPECT_EQ(service_requests_[0].course_name, "differentieren");
|
||||
// EXPECT_EQ(service_requests_[0].exam_grades.size(), 5);
|
||||
// }
|
||||
|
||||
// // Should publish final result
|
||||
// EXPECT_EQ(received_student_messages_.size(), 1)
|
||||
// << "Should publish final student result";
|
||||
|
||||
// // Should store in database with retake flag
|
||||
// EXPECT_EQ(ptr_mock_db_->stored_results.size(), 1);
|
||||
// if (!ptr_mock_db_->stored_results.empty()) {
|
||||
// EXPECT_TRUE(ptr_mock_db_->stored_results[0].is_retake)
|
||||
// << "Should store result with retake flag";
|
||||
// EXPECT_EQ(ptr_mock_db_->stored_results[0].final_grade, 75);
|
||||
// }
|
||||
|
||||
// EXPECT_EQ(ptr_mock_db_->retake_status_updates.size(), 1);
|
||||
// }
|
||||
|
||||
TEST_F(RetakeGradeDeterminatorTest, PartialExamResultsCollection) {
|
||||
create_retake_grade_determinator();
|
||||
|
||||
// spin_some_time(500ms);
|
||||
|
||||
ASSERT_TRUE(retake_action_client_->wait_for_action_server(std::chrono::seconds(2)));
|
||||
|
||||
auto goal = g2_2025_interfaces::action::Retake::Goal();
|
||||
goal.student_name = "tilmann";
|
||||
goal.course_name = "differentieren";
|
||||
|
||||
auto send_goal_options = rclcpp_action::Client<g2_2025_interfaces::action::Retake>::SendGoalOptions();
|
||||
retake_action_client_->async_send_goal(goal, send_goal_options);
|
||||
spin_some_time(300ms);
|
||||
|
||||
// Send only 3 out of 5 required exam results
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
auto exam_msg = std::make_shared<g2_2025_interfaces::msg::Exam>();
|
||||
exam_msg->student_name = "tilmann";
|
||||
exam_msg->course_name = "differentieren";
|
||||
exam_msg->result = 65 + i;
|
||||
|
||||
exam_publisher_->publish(*exam_msg);
|
||||
}
|
||||
|
||||
spin_some_time(300ms);
|
||||
|
||||
EXPECT_TRUE(service_requests_.empty())
|
||||
<< "Should not make service request with insufficient exam results";
|
||||
EXPECT_TRUE(ptr_mock_db_->stored_results.empty())
|
||||
<< "Should not store results with insufficient exam results";
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <rclcpp_action/rclcpp_action.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "retake_scheduler/nodes/RetakeScheduler.hpp"
|
||||
#include "database/DatabaseManager.hpp"
|
||||
#include "g2_2025_interfaces/action/retake.hpp"
|
||||
|
||||
#include "mocks/MockDatabaseManager.hpp"
|
||||
#include "mocks/MockRetakeActionServer.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace assignments::one::retake_scheduler;
|
||||
|
||||
class RetakeSchedulerTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
// Create test node and mock action server
|
||||
test_node_ = std::make_shared<rclcpp::Node>("test_node");
|
||||
mock_action_server_ = std::make_unique<assignments::one::MockRetakeActionServer>(test_node_);
|
||||
|
||||
// Create mock database manager
|
||||
mock_db_ = std::make_unique<assignments::one::MockDatabaseManager>();
|
||||
mock_db_ptr_ = mock_db_.get();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
retake_scheduler_.reset();
|
||||
mock_action_server_.reset();
|
||||
test_node_.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void create_retake_scheduler() {
|
||||
retake_scheduler_ = std::make_shared<RetakeScheduler>(
|
||||
std::move(mock_db_)
|
||||
);
|
||||
}
|
||||
|
||||
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 (retake_scheduler_) {
|
||||
rclcpp::spin_some(retake_scheduler_);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Node> test_node_;
|
||||
std::shared_ptr<RetakeScheduler> retake_scheduler_;
|
||||
std::unique_ptr<assignments::one::MockDatabaseManager> mock_db_;
|
||||
assignments::one::MockDatabaseManager* mock_db_ptr_;
|
||||
std::unique_ptr<assignments::one::MockRetakeActionServer> mock_action_server_;
|
||||
};
|
||||
|
||||
TEST_F(RetakeSchedulerTest, ConstructorTest) {
|
||||
ASSERT_NO_THROW(create_retake_scheduler());
|
||||
|
||||
ASSERT_NE(retake_scheduler_, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(RetakeSchedulerTest, ActionClientCreationTest) {
|
||||
create_retake_scheduler();
|
||||
|
||||
bool retake_action_found = false;
|
||||
auto service_names_and_types = retake_scheduler_->get_service_names_and_types();
|
||||
|
||||
for (const auto& [ service_name, service_types ] : service_names_and_types) {
|
||||
if (service_name == "/retake_action/_action/cancel_goal" ||
|
||||
service_name == "/retake_action/_action/get_result" ||
|
||||
service_name == "/retake_action/_action/send_goal") {
|
||||
retake_action_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(retake_action_found)
|
||||
<< "Retake action client services should be available";
|
||||
}
|
||||
|
||||
TEST_F(RetakeSchedulerTest, ParameterTest) {
|
||||
create_retake_scheduler();
|
||||
|
||||
// Test default parameter value
|
||||
auto param = retake_scheduler_->get_parameter("retake_check_interval");
|
||||
EXPECT_EQ(param.as_int(), 120) << "Default retake_check_interval should be 120 seconds";
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/DatabaseManager.hpp"
|
||||
|
||||
namespace assignments::one {
|
||||
|
||||
struct MockStoredResult {
|
||||
StudentCourse sc;
|
||||
int exam_count;
|
||||
int final_grade;
|
||||
bool is_retake;
|
||||
};
|
||||
|
||||
class MockDatabaseManager : public DatabaseManager {
|
||||
public:
|
||||
explicit MockDatabaseManager(
|
||||
rclcpp::Logger logger = rclcpp::get_logger("fake_db")
|
||||
)
|
||||
: DatabaseManager(logger) {}
|
||||
|
||||
bool is_connected() const override {
|
||||
return connection_status_;
|
||||
}
|
||||
|
||||
void init_database() override {}
|
||||
|
||||
void set_connection_status(bool status) {
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace assignments::one
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
namespace assignments::one {
|
||||
|
||||
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::one
|
||||
Reference in New Issue
Block a user