From 3851b2eebfe967d327b5a33186c248d0244aa2d9 Mon Sep 17 00:00:00 2001 From: lids <1713278948@qq.com> Date: Tue, 2 Jun 2026 15:44:22 +0800 Subject: [PATCH] =?UTF-8?q?AI=20=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/CMakeLists.txt | 25 ++++ tests/test_app.cpp | 324 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test_app.cpp 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..01f549e --- /dev/null +++ b/tests/test_app.cpp @@ -0,0 +1,324 @@ +#include +#include "app.hpp" +#include +#include + +// ============================================================ +// 测试夹具:CmsEngineTest +// ============================================================ +class CmsEngineTest : public ::testing::Test { +protected: + CmsEngine engine; + + void SetUp() override { + // 初始化一些事件用于测试 + EventRecord evt1; + evt1.id = "EVT-001"; + evt1.priority = 100; + evt1.status = EventStatus::Pending; + engine.ingestEvent(evt1); + + EventRecord evt2; + evt2.id = "EVT-002"; + evt2.priority = 200; + evt2.status = EventStatus::Pending; + engine.ingestEvent(evt2); + + EventRecord evt3; + evt3.id = "EVT-003"; + evt3.priority = 50; + evt3.status = EventStatus::Generated; // 非 Pending 状态 + engine.ingestEvent(evt3); + } + + void TearDown() override { + // 清理工作(如果需要) + } +}; + +// ============================================================ +// 测试用例:processPendingEvents +// ============================================================ + +/** + * @requirement(name="待处理事件处理", id="REQ-EVENT-PROC-001") + * 系统需遍历事件库中所有 Pending 状态的事件,自动将其状态标记为 Generated, + * 并据此生成一个对应的 TaskPlan 草案,实现事件到方案的自动转化。 + */ + +// 正常输入测试:处理所有 Pending 事件 +TEST_F(CmsEngineTest, testProcessPendingEventsNormal) { + // 执行:处理待处理事件 + size_t processedCount = engine.processPendingEvents(); + + // 验证:应该处理了 2 个 Pending 事件(EVT-001 和 EVT-002) + EXPECT_EQ(processedCount, 2); + + // 验证:事件状态已更新 + const EventRecord* evt1 = engine.findEventById("EVT-001"); + ASSERT_NE(evt1, nullptr); + EXPECT_EQ(evt1->status, EventStatus::Generated); + + const EventRecord* evt2 = engine.findEventById("EVT-002"); + ASSERT_NE(evt2, nullptr); + EXPECT_EQ(evt2->status, EventStatus::Generated); + + // 验证:非 Pending 事件状态不变 + const EventRecord* evt3 = engine.findEventById("EVT-003"); + ASSERT_NE(evt3, nullptr); + EXPECT_EQ(evt3->status, EventStatus::Generated); // 原本就是 Generated + + // 验证:生成了对应的 TaskPlan + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 2); + + // 验证第一个方案的属性 + 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_FLOAT_EQ(plans[0].resourceQuota, 0.5); + EXPECT_EQ(plans[0].constraints, "自动生成约束"); + + // 验证第二个方案的属性 + EXPECT_EQ(plans[1].name, "AutoPlan-EVT-002"); + EXPECT_EQ(plans[1].relatedEventId, "EVT-002"); +} + +// 边界值测试:没有 Pending 事件 +TEST_F(CmsEngineTest, testProcessPendingEventsNoPending) { + // 先处理所有 Pending 事件 + engine.processPendingEvents(); + + // 再次调用,应该没有 Pending 事件了 + size_t processedCount = engine.processPendingEvents(); + + // 验证:处理数量为 0 + EXPECT_EQ(processedCount, 0); + + // 验证:方案数量没有增加 + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 2); // 仍然是第一次处理生成的 2 个方案 +} + +// 边界值测试:事件列表为空 +TEST_F(CmsEngineTest, testProcessPendingEventsEmptyEvents) { + // 创建一个新的空引擎 + CmsEngine emptyEngine; + + // 执行:处理待处理事件 + size_t processedCount = emptyEngine.processPendingEvents(); + + // 验证:处理数量为 0 + EXPECT_EQ(processedCount, 0); + + // 验证:方案列表为空 + const std::vector& plans = emptyEngine.getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +// 特殊场景测试:多次调用 processPendingEvents +TEST_F(CmsEngineTest, testProcessPendingEventsMultipleCalls) { + // 第一次调用 + size_t firstCount = engine.processPendingEvents(); + EXPECT_EQ(firstCount, 2); + + // 第二次调用(已经没有 Pending 事件了) + size_t secondCount = engine.processPendingEvents(); + EXPECT_EQ(secondCount, 0); + + // 验证:方案数量为 2(只有第一次生成的) + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 2); +} + +// ============================================================ +// 测试用例:findEventById +// ============================================================ + +/** + * @requirement(name="事件查询", id="REQ-EVENT-QUERY-001") + * 系统需支持根据事件 ID 在事件库中查找对应的事件记录, + * 找到则返回事件指针,未找到返回 nullptr。 + */ + +// 正常输入测试:查找存在的事件 +TEST_F(CmsEngineTest, testFindEventByIdNormal) { + const EventRecord* evt = engine.findEventById("EVT-001"); + + // 验证:找到事件 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->id, "EVT-001"); + EXPECT_EQ(evt->priority, 100); + EXPECT_EQ(evt->status, EventStatus::Pending); +} + +// 边界值测试:查找不存在的事件 +TEST_F(CmsEngineTest, testFindEventByIdNotFound) { + const EventRecord* evt = engine.findEventById("NONEXISTENT"); + + // 验证:返回 nullptr + EXPECT_EQ(evt, nullptr); +} + +// 边界值测试:查找空字符串 ID +TEST_F(CmsEngineTest, testFindEventByIdEmptyId) { + const EventRecord* evt = engine.findEventById(""); + + // 验证:返回 nullptr(因为事件 ID 不能为空,所以不会有空 ID 的事件) + EXPECT_EQ(evt, nullptr); +} + +// 边界值测试:查找特殊字符 ID +TEST_F(CmsEngineTest, testFindEventByIdSpecialChars) { + const EventRecord* evt = engine.findEventById("EVT-@#$"); + + // 验证:返回 nullptr(不存在该事件) + EXPECT_EQ(evt, nullptr); +} + +// 特殊场景测试:查找已处理的事件(状态已改变) +TEST_F(CmsEngineTest, testFindEventByIdAfterProcessing) { + // 先处理事件 + engine.processPendingEvents(); + + // 查找已处理的事件 + const EventRecord* evt = engine.findEventById("EVT-001"); + + // 验证:找到事件,且状态已更新 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->id, "EVT-001"); + EXPECT_EQ(evt->status, EventStatus::Generated); +} + +// ============================================================ +// 测试用例:createTaskPlan +// ============================================================ + +/** + * @requirement(name="任务方案创建", id="REQ-PLAN-CREATE-001") + * 系统需校验方案名称非空后,为其分配系统 ID 并存入方案库, + * 确保方案数据的完整性和可追溯性。 + */ + +// 正常输入测试:创建有效的任务方案 +TEST_F(CmsEngineTest, testCreateTaskPlanNormal) { + TaskPlan plan; + plan.name = "Test Plan"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + plan.resourceQuota = 0.8; + plan.constraints = "测试约束"; + + // 执行:创建任务方案 + bool result = engine.createTaskPlan(plan); + + // 验证:创建成功 + EXPECT_TRUE(result); + + // 验证:方案 ID 已被填充(格式为 ID-xxxxx) + EXPECT_FALSE(plan.id.empty()); + EXPECT_EQ(plan.id.substr(0, 3), "ID-"); + EXPECT_EQ(plan.id.length(), 8); // "ID-" + 5 位数字 + + // 验证:创建时间已被填充 + // 由于 createTime 是 system_clock::time_point,我们无法直接比较, + // 但可以确认它已经被设置(不为默认值) + auto now = std::chrono::system_clock::now(); + auto diff = std::chrono::duration_cast(now - plan.createTime).count(); + EXPECT_LE(diff, 1); // 创建时间应该在 1 秒内 + + // 验证:方案已被添加到方案库 + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].id, plan.id); + EXPECT_EQ(plans[0].name, "Test Plan"); + EXPECT_EQ(plans[0].type, PlanType::Centralized); + EXPECT_EQ(plans[0].status, PlanStatus::Drafting); + EXPECT_FLOAT_EQ(plans[0].resourceQuota, 0.8); + EXPECT_EQ(plans[0].constraints, "测试约束"); +} + +// 边界值测试:创建名称为空的方案 +TEST_F(CmsEngineTest, testCreateTaskPlanEmptyName) { + TaskPlan plan; + plan.name = ""; // 空名称 + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + + // 执行:创建任务方案 + bool result = engine.createTaskPlan(plan); + + // 验证:创建失败 + EXPECT_FALSE(result); + + // 验证:方案 ID 未被填充 + EXPECT_TRUE(plan.id.empty()); + + // 验证:方案未被添加到方案库 + const std::vector& plans = engine.getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +// 边界值测试:创建名称为空格的方案 +TEST_F(CmsEngineTest, testCreateTaskPlanSpaceName) { + TaskPlan plan; + plan.name = " "; // 空格名称 + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + + // 执行:创建任务方案 + bool result = engine.createTaskPlan(plan); + + // 验证:创建成功(因为空格不是空字符串) + EXPECT_TRUE(result); + + // 验证:方案已被添加到方案库 + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].name, " "); +} + +// 特殊场景测试:创建多个方案,验证 ID 自增 +TEST_F(CmsEngineTest, testCreateTaskPlanMultiplePlans) { + TaskPlan plan1; + plan1.name = "Plan 1"; + engine.createTaskPlan(plan1); + + TaskPlan plan2; + plan2.name = "Plan 2"; + engine.createTaskPlan(plan2); + + TaskPlan plan3; + plan3.name = "Plan 3"; + engine.createTaskPlan(plan3); + + // 验证:生成了 3 个方案 + const std::vector& plans = engine.getAllPlans(); + EXPECT_EQ(plans.size(), 3); + + // 验证:ID 自增(ID-00001, ID-00002, ID-00003) + EXPECT_EQ(plans[0].id, "ID-00001"); + EXPECT_EQ(plans[1].id, "ID-00002"); + EXPECT_EQ(plans[2].id, "ID-00003"); + + // 验证:名称正确 + EXPECT_EQ(plans[0].name, "Plan 1"); + EXPECT_EQ(plans[1].name, "Plan 2"); + EXPECT_EQ(plans[2].name, "Plan 3"); +} + +// 特殊场景测试:创建方案后,通过 findPlanById 验证 +TEST_F(CmsEngineTest, testCreateTaskPlanAndFindById) { + TaskPlan plan; + plan.name = "Findable Plan"; + engine.createTaskPlan(plan); + + // 通过 ID 查找方案 + const TaskPlan* foundPlan = engine.findPlanById(plan.id); + + // 验证:找到方案 + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->id, plan.id); + EXPECT_EQ(foundPlan->name, "Findable Plan"); +}