diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..4cb68f5 --- /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_app.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_app.cpp b/tests/test_app.cpp new file mode 100644 index 0000000..c8a5931 --- /dev/null +++ b/tests/test_app.cpp @@ -0,0 +1,368 @@ +#include +#include +#include +#include +#include "app.hpp" + +// ============================================================ +// 测试夹具:CmsEngineTest +// ============================================================ +class CmsEngineTest : public ::testing::Test { +protected: + CmsEngine engine; + + // 辅助函数:创建一个有效的事件(注意:EventRecord 没有 timestamp 成员) + EventRecord createValidEvent(const std::string& id = "EVT-001", int priority = 100) { + EventRecord evt; + evt.id = id; + evt.priority = priority; + evt.status = EventStatus::Pending; + return evt; + } + + // 辅助函数:创建多个事件并接入 + void ingestMultipleEvents(size_t count) { + for (size_t i = 0; i < count; ++i) { + std::string id = "EVT-" + std::to_string(i + 1); + EventRecord evt = createValidEvent(id, 100); + engine.ingestEvent(evt); + } + } +}; + +// ============================================================ +// ingestEvent 测试用例 +// ============================================================ + +/** + * @requirement(name="战场事件接入", id="REQ-EVENT-INGEST-001") + * 系统需支持从外部事件总线接入原始战场事件,执行基本合法性校验 + * (ID 不能为空、优先级范围检查),通过后存入事件库。 + */ + +// 正常输入测试:接入有效事件 +TEST_F(CmsEngineTest, testIngestEventValid) { + EventRecord evt = createValidEvent("EVT-001", 100); + bool result = engine.ingestEvent(evt); + EXPECT_TRUE(result); + + // 验证事件已存入事件库 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, "EVT-001"); + EXPECT_EQ(found->priority, 100); + EXPECT_EQ(found->status, EventStatus::Pending); +} + +// 正常输入测试:接入多个事件 +TEST_F(CmsEngineTest, testIngestEventMultiple) { + EventRecord evt1 = createValidEvent("EVT-001", 50); + EventRecord evt2 = createValidEvent("EVT-002", 150); + EventRecord evt3 = createValidEvent("EVT-003", 200); + + EXPECT_TRUE(engine.ingestEvent(evt1)); + EXPECT_TRUE(engine.ingestEvent(evt2)); + EXPECT_TRUE(engine.ingestEvent(evt3)); + + // 验证所有事件都已存储 + EXPECT_NE(engine.findEventById("EVT-001"), nullptr); + EXPECT_NE(engine.findEventById("EVT-002"), nullptr); + EXPECT_NE(engine.findEventById("EVT-003"), nullptr); +} + +// 边界值测试:优先级为 0(最小值) +TEST_F(CmsEngineTest, testIngestEventPriorityMin) { + EventRecord evt = createValidEvent("EVT-001", 0); + bool result = engine.ingestEvent(evt); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 0); +} + +// 边界值测试:优先级为 255(最大值) +TEST_F(CmsEngineTest, testIngestEventPriorityMax) { + EventRecord evt = createValidEvent("EVT-001", 255); + bool result = engine.ingestEvent(evt); + EXPECT_TRUE(result); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 255); +} + +// 异常输入测试:空 ID +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + EventRecord evt = createValidEvent("", 100); + bool result = engine.ingestEvent(evt); + EXPECT_FALSE(result); + + // 验证事件未被存储 + const EventRecord* found = engine.findEventById(""); + EXPECT_EQ(found, nullptr); +} + +// 异常输入测试:优先级超过 255 +TEST_F(CmsEngineTest, testIngestEventPriorityOverflow) { + EventRecord evt = createValidEvent("EVT-001", 256); + bool result = engine.ingestEvent(evt); + EXPECT_FALSE(result); + + // 验证事件未被存储 + const EventRecord* found = engine.findEventById("EVT-001"); + EXPECT_EQ(found, nullptr); +} + +// 异常输入测试:优先级为负数 +TEST_F(CmsEngineTest, testIngestEventPriorityNegative) { + EventRecord evt = createValidEvent("EVT-001", -1); + bool result = engine.ingestEvent(evt); + EXPECT_FALSE(result); + + const EventRecord* found = engine.findEventById("EVT-001"); + EXPECT_EQ(found, nullptr); +} + +// 特殊场景测试:接入重复 ID 的事件(允许重复) +TEST_F(CmsEngineTest, testIngestEventDuplicateId) { + EventRecord evt1 = createValidEvent("EVT-001", 100); + EventRecord evt2 = createValidEvent("EVT-001", 200); + + EXPECT_TRUE(engine.ingestEvent(evt1)); + EXPECT_TRUE(engine.ingestEvent(evt2)); + + // 验证两个事件都已存储(允许重复 ID) + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + // findEventById 返回第一个匹配的事件 + EXPECT_EQ(found->priority, 100); +} + +// ============================================================ +// processPendingEvents 测试用例 +// ============================================================ + +/** + * @requirement(name="待处理事件处理", id="REQ-EVENT-PROC-001") + * 系统需遍历事件库中所有 Pending 状态的事件,自动将其状态标记为 Generated, + * 并据此生成一个对应的 TaskPlan 草案,实现事件到方案的自动转化。 + */ + +// 正常输入测试:处理单个待处理事件 +TEST_F(CmsEngineTest, testProcessPendingEventsSingle) { + EventRecord evt = createValidEvent("EVT-001", 100); + engine.ingestEvent(evt); + + size_t count = engine.processPendingEvents(); + EXPECT_EQ(count, 1); + + // 验证事件状态已更新 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->status, EventStatus::Generated); + + // 验证生成了对应的方案 + const std::vector& plans = engine.getAllPlans(); + ASSERT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); + EXPECT_EQ(plans[0].name, "AutoPlan-EVT-001"); + EXPECT_EQ(plans[0].type, PlanType::Centralized); + EXPECT_EQ(plans[0].status, PlanStatus::Drafting); + EXPECT_FLOAT_EQ(plans[0].resourceQuota, 0.5f); + EXPECT_EQ(plans[0].constraints, "自动生成约束"); +} + +// 正常输入测试:处理多个待处理事件 +TEST_F(CmsEngineTest, testProcessPendingEventsMultiple) { + ingestMultipleEvents(5); + + size_t count = engine.processPendingEvents(); + EXPECT_EQ(count, 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 std::vector& plans = engine.getAllPlans(); + ASSERT_EQ(plans.size(), 5); + for (size_t i = 0; i < 5; ++i) { + std::string expectedId = "EVT-" + std::to_string(i + 1); + EXPECT_EQ(plans[i].relatedEventId, expectedId); + } +} + +// 边界值测试:没有待处理事件 +TEST_F(CmsEngineTest, testProcessPendingEventsNone) { + size_t count = engine.processPendingEvents(); + EXPECT_EQ(count, 0); + + // 验证没有生成方案 + const std::vector& plans = engine.getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +// 边界值测试:事件状态不是 Pending +TEST_F(CmsEngineTest, testProcessPendingEventsNonPending) { + EventRecord evt = createValidEvent("EVT-001", 100); + evt.status = EventStatus::Generated; // 设置为非 Pending 状态 + engine.ingestEvent(evt); + + size_t count = engine.processPendingEvents(); + EXPECT_EQ(count, 0); + + // 验证事件状态未改变 + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->status, EventStatus::Generated); + + // 验证没有生成方案 + const std::vector& plans = engine.getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +// 特殊场景测试:混合状态的事件 +TEST_F(CmsEngineTest, testProcessPendingEventsMixed) { + EventRecord evt1 = createValidEvent("EVT-001", 100); // Pending + EventRecord evt2 = createValidEvent("EVT-002", 100); + evt2.status = EventStatus::Generated; // 非 Pending + EventRecord evt3 = createValidEvent("EVT-003", 100); // Pending + + engine.ingestEvent(evt1); + engine.ingestEvent(evt2); + engine.ingestEvent(evt3); + + size_t count = engine.processPendingEvents(); + EXPECT_EQ(count, 2); // 只处理了 2 个 Pending 事件 + + // 验证只有 Pending 事件被处理 + const EventRecord* found1 = engine.findEventById("EVT-001"); + ASSERT_NE(found1, nullptr); + EXPECT_EQ(found1->status, EventStatus::Generated); + + const EventRecord* found2 = engine.findEventById("EVT-002"); + ASSERT_NE(found2, nullptr); + EXPECT_EQ(found2->status, EventStatus::Generated); // 保持不变 + + const EventRecord* found3 = engine.findEventById("EVT-003"); + ASSERT_NE(found3, nullptr); + EXPECT_EQ(found3->status, EventStatus::Generated); + + // 验证生成了 2 个方案 + const std::vector& plans = engine.getAllPlans(); + ASSERT_EQ(plans.size(), 2); + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); + EXPECT_EQ(plans[1].relatedEventId, "EVT-003"); +} + +// 特殊场景测试:多次调用 processPendingEvents +TEST_F(CmsEngineTest, testProcessPendingEventsMultipleCalls) { + EventRecord evt = createValidEvent("EVT-001", 100); + engine.ingestEvent(evt); + + // 第一次调用 + size_t count1 = engine.processPendingEvents(); + EXPECT_EQ(count1, 1); + + // 第二次调用,没有 Pending 事件了 + size_t count2 = engine.processPendingEvents(); + EXPECT_EQ(count2, 0); + + // 验证只生成了一个方案 + const std::vector& plans = engine.getAllPlans(); + ASSERT_EQ(plans.size(), 1); +} + +// ============================================================ +// findEventById 测试用例 +// ============================================================ + +/** + * @requirement(name="事件查询", id="REQ-EVENT-QUERY-001") + * 系统需支持根据事件 ID 在事件库中查找对应的事件记录, + * 找到则返回事件指针,未找到返回 nullptr。 + */ + +// 正常输入测试:查找存在的事件 +TEST_F(CmsEngineTest, testFindEventByIdExisting) { + EventRecord evt = createValidEvent("EVT-001", 100); + engine.ingestEvent(evt); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, "EVT-001"); + EXPECT_EQ(found->priority, 100); + EXPECT_EQ(found->status, EventStatus::Pending); +} + +// 正常输入测试:查找多个事件中的某一个 +TEST_F(CmsEngineTest, testFindEventByIdAmongMultiple) { + ingestMultipleEvents(10); + + const EventRecord* found = engine.findEventById("EVT-005"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, "EVT-005"); +} + +// 边界值测试:查找空字符串 ID +TEST_F(CmsEngineTest, testFindEventByIdEmptyString) { + EventRecord evt = createValidEvent("", 100); + engine.ingestEvent(evt); + + const EventRecord* found = engine.findEventById(""); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, ""); +} + +// 异常输入测试:查找不存在的 ID +TEST_F(CmsEngineTest, testFindEventByIdNonExisting) { + ingestMultipleEvents(5); + + const EventRecord* found = engine.findEventById("NONEXISTENT"); + EXPECT_EQ(found, nullptr); +} + +// 异常输入测试:在空事件库中查找 +TEST_F(CmsEngineTest, testFindEventByIdEmptyStore) { + const EventRecord* found = engine.findEventById("EVT-001"); + EXPECT_EQ(found, nullptr); +} + +// 特殊场景测试:查找重复 ID 的事件(返回第一个) +TEST_F(CmsEngineTest, testFindEventByIdDuplicateIds) { + EventRecord evt1 = createValidEvent("EVT-001", 100); + EventRecord evt2 = createValidEvent("EVT-001", 200); + engine.ingestEvent(evt1); + engine.ingestEvent(evt2); + + const EventRecord* found = engine.findEventById("EVT-001"); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->priority, 100); // 返回第一个 +} + +// 特殊场景测试:查找特殊字符 ID +TEST_F(CmsEngineTest, testFindEventByIdSpecialChars) { + std::string specialId = "EVT-@#$%^&"; + EventRecord evt = createValidEvent(specialId, 100); + engine.ingestEvent(evt); + + const EventRecord* found = engine.findEventById(specialId); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, specialId); +} + +// 特殊场景测试:查找长 ID 字符串 +TEST_F(CmsEngineTest, testFindEventByIdLongId) { + std::string longId = "EVT-" + std::string(1000, 'a'); + EventRecord evt = createValidEvent(longId, 100); + engine.ingestEvent(evt); + + const EventRecord* found = engine.findEventById(longId); + ASSERT_NE(found, nullptr); + EXPECT_EQ(found->id, longId); +}