diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a6dba10 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.10.0) +project(test_plan_execute_t1) +include(FetchContent) +if (MSVC) + add_compile_options(/utf-8) +endif() +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) + +include(CTest) +enable_testing() + +add_executable(test_plan_execute_t1 test_cms_engine.cpp ../src/app.cpp) + +target_link_libraries(test_plan_execute_t1 gtest gmock gtest_main) +include(GoogleTest) +gtest_discover_tests(test_plan_execute_t1) \ No newline at end of file diff --git a/tests/test_cms_engine.cpp b/tests/test_cms_engine.cpp new file mode 100644 index 0000000..e961f8b --- /dev/null +++ b/tests/test_cms_engine.cpp @@ -0,0 +1,365 @@ +#include +#include +#include "app.hpp" +#include +#include +#include + +using namespace testing; + +// ============================================================================ +// CmsEngine 测试套件 +// ============================================================================ + +class CmsEngineTest : public Test { +protected: + CmsEngine engine; + + void SetUp() override { + // 每个测试用例开始前重置引擎状态 + } + + void TearDown() override { + // 每个测试用例结束后清理 + } + + // 辅助函数:创建一个有效的事件记录(根据实际 EventRecord 结构定义) + EventRecord createValidEvent(const std::string& id = "EVT-001", + uint32_t priority = 100, + EventStatus status = EventStatus::Pending) { + EventRecord event; + event.id = id; + event.priority = priority; + event.status = status; + // 注意:根据编译错误,EventRecord 没有 timestamp, source, type 成员 + // 因此这里只设置实际存在的成员 + return event; + } + + // 辅助函数:创建多个事件并接入 + void ingestMultipleEvents(size_t count) { + for (size_t i = 0; i < count; ++i) { + EventRecord event = createValidEvent("EVT-" + std::to_string(i + 1), 100); + engine.ingestEvent(event); + } + } +}; + +// ============================================================================ +// CmsEngine 构造函数测试 +// ============================================================================ + +TEST_F(CmsEngineTest, testCmsEngineDefaultConstructor) { + // 正常输入测试:验证默认构造函数正确初始化 + const auto& context = engine.getSystemContext(); + EXPECT_EQ(context.currentMode, RunMode::Idle); + EXPECT_EQ(context.consistencyMark, 0); + EXPECT_EQ(context.contextSnapshot, R"({"mode":"Idle","version":"1.0.0"})"); + + // 验证初始状态为空 + EXPECT_EQ(engine.getAllPlans().size(), 0); + EXPECT_EQ(engine.getSummary().find("Events : 0") != std::string::npos, true); +} + +TEST_F(CmsEngineTest, testCmsEngineMultipleInstances) { + // 正常输入测试:多个实例独立运行 + CmsEngine engine1; + CmsEngine engine2; + + // 向 engine1 添加事件 + EventRecord event1 = createValidEvent("EVT-001", 100); + engine1.ingestEvent(event1); + + // 验证 engine2 不受影响 + EXPECT_EQ(engine2.getSummary().find("Events : 0") != std::string::npos, true); + EXPECT_EQ(engine1.getSummary().find("Events : 1") != std::string::npos, true); +} + +TEST_F(CmsEngineTest, testCmsEngineConstructorIdGeneration) { + // 特殊场景测试:验证构造函数后 ID 生成器从 0 开始 + // 通过 processPendingEvents 间接验证 ID 生成 + EventRecord event = createValidEvent("EVT-001", 100, EventStatus::Pending); + engine.ingestEvent(event); + + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, 1); + + const auto& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].id, "ID-00001"); +} + +// ============================================================================ +// ingestEvent 函数测试 +// ============================================================================ + +TEST_F(CmsEngineTest, testIngestEventValidEvent) { + // 正常输入测试:接入有效事件 + EventRecord event = createValidEvent("EVT-001", 100); + bool result = engine.ingestEvent(event); + EXPECT_TRUE(result); + + // 验证事件已存储 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 100); + EXPECT_EQ(found->status, EventStatus::Pending); +} + +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + // 边界值测试:空 ID 事件 + EventRecord event = createValidEvent("", 100); + bool result = engine.ingestEvent(event); + EXPECT_FALSE(result); + + // 验证事件未被存储 + EXPECT_EQ(engine.findEventById(""), nullptr); +} + +TEST_F(CmsEngineTest, testIngestEventMaxPriority) { + // 边界值测试:最大优先级(255) + EventRecord event = createValidEvent("EVT-001", 255); + bool result = engine.ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 255); +} + +TEST_F(CmsEngineTest, testIngestEventExceedMaxPriority) { + // 异常输入测试:超过最大优先级 + EventRecord event = createValidEvent("EVT-001", 256); + bool result = engine.ingestEvent(event); + EXPECT_FALSE(result); + + // 验证事件未被存储 + EXPECT_EQ(engine.findEventById("EVT-001"), nullptr); +} + +TEST_F(CmsEngineTest, testIngestEventZeroPriority) { + // 边界值测试:零优先级 + EventRecord event = createValidEvent("EVT-001", 0); + bool result = engine.ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 0); +} + +TEST_F(CmsEngineTest, testIngestEventMultipleEvents) { + // 正常输入测试:接入多个事件 + ingestMultipleEvents(5); + + for (size_t i = 0; i < 5; ++i) { + std::string id = "EVT-" + std::to_string(i + 1); + const EventRecord* found = engine.findEventById(id); + ASSERT_NE(found, nullptr) << "Event " << id << " not found"; + EXPECT_EQ(found->priority, 100); + } +} + +TEST_F(CmsEngineTest, testIngestEventDuplicateId) { + // 特殊场景测试:重复 ID 的事件 + EventRecord event1 = createValidEvent("EVT-001", 100); + EventRecord event2 = createValidEvent("EVT-001", 200); + + EXPECT_TRUE(engine.ingestEvent(event1)); + EXPECT_TRUE(engine.ingestEvent(event2)); // 允许重复 ID + + // 验证两个事件都被存储 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + // findEventById 返回第一个匹配的事件 + EXPECT_EQ(found->priority, 100); +} + +TEST_F(CmsEngineTest, testIngestEventSpecialCharacters) { + // 边界值测试:特殊字符 ID + EventRecord event = createValidEvent("EVT-@#$%^&*()", 100); + bool result = engine.ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById("EVT-@#$%^&*()"); + ASSERT_NE(found, nullptr); +} + +TEST_F(CmsEngineTest, testIngestEventVeryLongId) { + // 边界值测试:超长 ID + std::string longId(1000, 'A'); + EventRecord event = createValidEvent(longId, 100); + bool result = engine.ingestEvent(event); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById(longId); + ASSERT_NE(found, nullptr); +} + +// ============================================================================ +// processPendingEvents 函数测试 +// ============================================================================ + +TEST_F(CmsEngineTest, testProcessPendingEventsNoEvents) { + // 边界值测试:无事件 + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, 0); + + // 验证没有生成方案 + EXPECT_EQ(engine.getAllPlans().size(), 0); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsSingleEvent) { + // 正常输入测试:处理单个待处理事件 + EventRecord event = createValidEvent("EVT-001", 100, EventStatus::Pending); + engine.ingestEvent(event); + + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, 1); + + // 验证事件状态已更新 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->status, EventStatus::Generated); + + // 验证生成了对应的方案 + const auto& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].name, "AutoPlan-EVT-001"); + EXPECT_EQ(plans[0].type, PlanType::Centralized); + EXPECT_EQ(plans[0].status, PlanStatus::Drafting); + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); + EXPECT_EQ(plans[0].resourceQuota, 0.5); + EXPECT_EQ(plans[0].constraints, "自动生成约束"); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsMultipleEvents) { + // 正常输入测试:处理多个待处理事件 + ingestMultipleEvents(5); + + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, 5); + + // 验证所有事件状态已更新 + for (size_t i = 0; i < 5; ++i) { + std::string id = "EVT-" + std::to_string(i + 1); + const EventRecord* found = engine.findEventById(id); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->status, EventStatus::Generated); + } + + // 验证生成了对应的方案 + const auto& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 5); + for (size_t i = 0; i < 5; ++i) { + EXPECT_EQ(plans[i].name, "AutoPlan-EVT-" + std::to_string(i + 1)); + EXPECT_EQ(plans[i].relatedEventId, "EVT-" + std::to_string(i + 1)); + } +} + +TEST_F(CmsEngineTest, testProcessPendingEventsMixedStatus) { + // 特殊场景测试:混合状态的事件 + EventRecord pendingEvent1 = createValidEvent("EVT-001", 100, EventStatus::Pending); + EventRecord generatedEvent = createValidEvent("EVT-002", 100, EventStatus::Generated); + EventRecord pendingEvent2 = createValidEvent("EVT-003", 100, EventStatus::Pending); + + engine.ingestEvent(pendingEvent1); + engine.ingestEvent(generatedEvent); + engine.ingestEvent(pendingEvent2); + + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, 2); // 只处理 Pending 状态的事件 + + // 验证 Pending 事件已处理 + const EventRecord* found1 = engine.findEventById("EVT-001"); + ASSERT_NE(found1, nullptr); + EXPECT_EQ(found1->status, EventStatus::Generated); + + const EventRecord* found3 = engine.findEventById("EVT-003"); + ASSERT_NE(found3, nullptr); + EXPECT_EQ(found3->status, EventStatus::Generated); + + // 验证 Generated 事件未改变 + const EventRecord* found2 = engine.findEventById("EVT-002"); + ASSERT_NE(found2, nullptr); + EXPECT_EQ(found2->status, EventStatus::Generated); + + // 验证生成了 2 个方案 + EXPECT_EQ(engine.getAllPlans().size(), 2); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsIdGeneration) { + // 特殊场景测试:验证 ID 生成顺序 + EventRecord event1 = createValidEvent("EVT-001", 100, EventStatus::Pending); + EventRecord event2 = createValidEvent("EVT-002", 100, EventStatus::Pending); + + engine.ingestEvent(event1); + engine.ingestEvent(event2); + + engine.processPendingEvents(); + + const auto& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 2); + EXPECT_EQ(plans[0].id, "ID-00001"); + EXPECT_EQ(plans[1].id, "ID-00002"); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsMultipleCalls) { + // 特殊场景测试:多次调用 processPendingEvents + EventRecord event1 = createValidEvent("EVT-001", 100, EventStatus::Pending); + engine.ingestEvent(event1); + + // 第一次调用 + size_t processed1 = engine.processPendingEvents(); + EXPECT_EQ(processed1, 1); + + // 第二次调用,没有新的 Pending 事件 + size_t processed2 = engine.processPendingEvents(); + EXPECT_EQ(processed2, 0); + + // 验证方案数量不变 + EXPECT_EQ(engine.getAllPlans().size(), 1); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsLargeNumberOfEvents) { + // 边界值测试:大量事件 + const size_t NUM_EVENTS = 1000; + for (size_t i = 0; i < NUM_EVENTS; ++i) { + EventRecord event = createValidEvent("EVT-" + std::to_string(i + 1), 100, EventStatus::Pending); + engine.ingestEvent(event); + } + + size_t processed = engine.processPendingEvents(); + EXPECT_EQ(processed, NUM_EVENTS); + + // 验证所有事件已处理 + for (size_t i = 0; i < NUM_EVENTS; ++i) { + std::string id = "EVT-" + std::to_string(i + 1); + const EventRecord* found = engine.findEventById(id); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->status, EventStatus::Generated); + } + + // 验证生成了对应数量的方案 + EXPECT_EQ(engine.getAllPlans().size(), NUM_EVENTS); +} + +TEST_F(CmsEngineTest, testProcessPendingEventsPlanProperties) { + // 正常输入测试:验证生成的方案属性 + EventRecord event = createValidEvent("EVT-001", 100, EventStatus::Pending); + engine.ingestEvent(event); + + engine.processPendingEvents(); + + const auto& plans = engine.getAllPlans(); + ASSERT_EQ(plans.size(), 1); + + // 验证方案时间戳不为空 + auto now = std::chrono::system_clock::now(); + auto timeDiff = std::chrono::duration_cast( + now - plans[0].createTime).count(); + EXPECT_LT(timeDiff, 5); // 时间差小于 5 秒 + + // 验证方案 ID 格式 + EXPECT_THAT(plans[0].id, testing::MatchesRegex("ID-\\d{5}")); +}