From 5129032876b7bc3818bcf351dea8d136c28eafaa Mon Sep 17 00:00:00 2001 From: lids <1713278948@qq.com> Date: Tue, 2 Jun 2026 11:02:48 +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 | 457 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 482 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..ba3de2b --- /dev/null +++ b/tests/test_app.cpp @@ -0,0 +1,457 @@ +#include +#include +#include "app.hpp" +#include +#include +#include + +using namespace testing; + +// ============================================================================ +// 测试夹具:CmsEngineTest +// ============================================================================ +class CmsEngineTest : public ::testing::Test { +protected: + void SetUp() override { + // 初始化引擎 + engine = new CmsEngine(); + + // 准备测试事件 + EventRecord event1; + event1.id = "EVT-001"; + event1.status = EventStatus::Pending; + event1.priority = 100; + + EventRecord event2; + event2.id = "EVT-002"; + event2.status = EventStatus::Pending; + event2.priority = 200; + + EventRecord event3; + event3.id = "EVT-003"; + event3.status = EventStatus::Generated; // 非 Pending 状态 + event3.priority = 150; + + // 注入测试事件 + engine->ingestEvent(event1); + engine->ingestEvent(event2); + engine->ingestEvent(event3); + } + + void TearDown() override { + delete engine; + } + + CmsEngine* engine; +}; + +// ============================================================================ +// 测试用例:processPendingEvents +// ============================================================================ + +/** + * @brief 测试正常处理待处理事件 + * + * 验证 processPendingEvents 能正确处理所有 Pending 状态的事件, + * 并返回正确的处理数量。 + */ +TEST_F(CmsEngineTest, testProcessPendingEventsNormal) { + // 执行 + size_t processedCount = engine->processPendingEvents(); + + // 验证:应该有 2 个 Pending 事件被处理 + 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); + + // 验证第一个 Plan 的关联事件 ID + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); + EXPECT_EQ(plans[0].type, PlanType::Centralized); + EXPECT_EQ(plans[0].status, PlanStatus::Drafting); + EXPECT_NEAR(plans[0].resourceQuota, 0.5, 0.001); + + // 验证第二个 Plan 的关联事件 ID + EXPECT_EQ(plans[1].relatedEventId, "EVT-002"); +} + +/** + * @brief 测试无待处理事件的情况 + * + * 验证当没有 Pending 状态的事件时,processPendingEvents 返回 0。 + */ +TEST_F(CmsEngineTest, testProcessPendingEventsNoPending) { + // 先处理所有 Pending 事件 + engine->processPendingEvents(); + + // 再次调用,此时没有 Pending 事件 + size_t processedCount = engine->processPendingEvents(); + + // 验证:应该处理 0 个事件 + EXPECT_EQ(processedCount, 0); + + // 验证 Plan 数量没有增加 + const std::vector& plans = engine->getAllPlans(); + EXPECT_EQ(plans.size(), 2); // 第一次处理时生成的 2 个 +} + +/** + * @brief 测试空事件库的情况 + * + * 验证当事件库为空时,processPendingEvents 返回 0。 + */ +TEST_F(CmsEngineTest, testProcessPendingEventsEmptyEvents) { + // 创建一个新的空引擎 + CmsEngine emptyEngine; + + // 执行 + size_t processedCount = emptyEngine.processPendingEvents(); + + // 验证:应该处理 0 个事件 + EXPECT_EQ(processedCount, 0); + + // 验证 Plan 列表为空 + const std::vector& plans = emptyEngine.getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +/** + * @brief 测试多次调用 processPendingEvents + * + * 验证多次调用不会重复处理已处理的事件。 + */ +TEST_F(CmsEngineTest, testProcessPendingEventsMultipleCalls) { + // 第一次调用 + size_t firstCount = engine->processPendingEvents(); + EXPECT_EQ(firstCount, 2); + + // 第二次调用 + size_t secondCount = engine->processPendingEvents(); + EXPECT_EQ(secondCount, 0); // 没有新的 Pending 事件 + + // 第三次调用 + size_t thirdCount = engine->processPendingEvents(); + EXPECT_EQ(thirdCount, 0); + + // 验证 Plan 数量始终为 2 + const std::vector& plans = engine->getAllPlans(); + EXPECT_EQ(plans.size(), 2); +} + +// ============================================================================ +// 测试用例:findEventById +// ============================================================================ + +/** + * @brief 测试查找已存在的事件 + * + * 验证 findEventById 能正确返回已存在的事件指针。 + */ +TEST_F(CmsEngineTest, testFindEventByIdExisting) { + // 执行 + const EventRecord* evt = engine->findEventById("EVT-001"); + + // 验证 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->id, "EVT-001"); + EXPECT_EQ(evt->status, EventStatus::Pending); + EXPECT_EQ(evt->priority, 100); +} + +/** + * @brief 测试查找不存在的事件 + * + * 验证 findEventById 在查找不存在的事件时返回 nullptr。 + */ +TEST_F(CmsEngineTest, testFindEventByIdNonExisting) { + // 执行 + const EventRecord* evt = engine->findEventById("NON-EXISTENT"); + + // 验证 + EXPECT_EQ(evt, nullptr); +} + +/** + * @brief 测试查找空字符串 ID + * + * 验证 findEventById 在查找空字符串时返回 nullptr。 + */ +TEST_F(CmsEngineTest, testFindEventByIdEmptyString) { + // 执行 + const EventRecord* evt = engine->findEventById(""); + + // 验证 + EXPECT_EQ(evt, nullptr); +} + +/** + * @brief 测试查找特殊字符 ID + * + * 验证 findEventById 能正确处理包含特殊字符的 ID。 + */ +TEST_F(CmsEngineTest, testFindEventByIdSpecialChars) { + // 准备包含特殊字符的事件 + EventRecord specialEvent; + specialEvent.id = "EVT-@#$%"; + specialEvent.status = EventStatus::Pending; + specialEvent.priority = 50; + engine->ingestEvent(specialEvent); + + // 执行 + const EventRecord* evt = engine->findEventById("EVT-@#$%"); + + // 验证 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->id, "EVT-@#$%"); +} + +/** + * @brief 测试查找长 ID 字符串 + * + * 验证 findEventById 能正确处理超长 ID 字符串。 + */ +TEST_F(CmsEngineTest, testFindEventByIdLongString) { + // 准备长 ID 事件 + std::string longId = "EVT-" + std::string(1000, 'A'); + EventRecord longEvent; + longEvent.id = longId; + longEvent.status = EventStatus::Pending; + longEvent.priority = 75; + engine->ingestEvent(longEvent); + + // 执行 + const EventRecord* evt = engine->findEventById(longId); + + // 验证 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->id, longId); +} + +/** + * @brief 测试查找后修改事件状态 + * + * 验证通过 findEventById 返回的指针可以正确反映事件状态的变化。 + */ +TEST_F(CmsEngineTest, testFindEventByIdAfterStatusChange) { + // 先处理事件,改变状态 + engine->processPendingEvents(); + + // 查找事件 + const EventRecord* evt = engine->findEventById("EVT-001"); + + // 验证状态已更新 + ASSERT_NE(evt, nullptr); + EXPECT_EQ(evt->status, EventStatus::Generated); +} + +// ============================================================================ +// 测试用例:createTaskPlan +// ============================================================================ + +/** + * @brief 测试正常创建任务方案 + * + * 验证 createTaskPlan 能正确创建有效的任务方案。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanNormal) { + // 准备 + TaskPlan plan; + plan.name = "TestPlan-001"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + plan.resourceQuota = 0.8; + plan.constraints = "测试约束"; + + // 执行 + bool result = engine->createTaskPlan(plan); + + // 验证 + EXPECT_TRUE(result); + + // 验证 ID 已被填充 + EXPECT_FALSE(plan.id.empty()); + EXPECT_THAT(plan.id, StartsWith("ID-")); + + // 验证创建时间已被填充 + auto now = std::chrono::system_clock::now(); + auto diff = std::chrono::duration_cast(now - plan.createTime).count(); + EXPECT_LT(diff, 5); // 创建时间应该在 5 秒内 + + // 验证方案已存入引擎 + const std::vector& plans = engine->getAllPlans(); + EXPECT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].name, "TestPlan-001"); + EXPECT_EQ(plans[0].id, plan.id); +} + +/** + * @brief 测试创建空名称的任务方案 + * + * 验证 createTaskPlan 在名称为空时返回 false。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanEmptyName) { + // 准备空名称方案 + TaskPlan plan; + plan.name = ""; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + + // 执行 + bool result = engine->createTaskPlan(plan); + + // 验证 + EXPECT_FALSE(result); + + // 验证方案未被加入引擎 + const std::vector& plans = engine->getAllPlans(); + EXPECT_TRUE(plans.empty()); +} + +/** + * @brief 测试创建多个任务方案 + * + * 验证 createTaskPlan 能连续创建多个方案,且 ID 自增。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanMultiple) { + // 创建第一个方案 + TaskPlan plan1; + plan1.name = "Plan-001"; + EXPECT_TRUE(engine->createTaskPlan(plan1)); + + // 创建第二个方案 + TaskPlan plan2; + plan2.name = "Plan-002"; + EXPECT_TRUE(engine->createTaskPlan(plan2)); + + // 创建第三个方案 + TaskPlan plan3; + plan3.name = "Plan-003"; + EXPECT_TRUE(engine->createTaskPlan(plan3)); + + // 验证方案数量 + const std::vector& plans = engine->getAllPlans(); + EXPECT_EQ(plans.size(), 3); + + // 验证 ID 自增 + 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-001"); + EXPECT_EQ(plans[1].name, "Plan-002"); + EXPECT_EQ(plans[2].name, "Plan-003"); +} + +/** + * @brief 测试创建方案后通过 ID 查找 + * + * 验证创建方案后可以通过 findPlanById 正确查找。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanAndFindById) { + // 创建方案 + TaskPlan plan; + plan.name = "FindablePlan"; + plan.type = PlanType::Distributed; + plan.status = PlanStatus::Drafting; + plan.resourceQuota = 0.6; + + EXPECT_TRUE(engine->createTaskPlan(plan)); + + // 通过 ID 查找 + const TaskPlan* foundPlan = engine->findPlanById(plan.id); + + // 验证 + ASSERT_NE(foundPlan, nullptr); + EXPECT_EQ(foundPlan->name, "FindablePlan"); + EXPECT_EQ(foundPlan->type, PlanType::Distributed); + EXPECT_EQ(foundPlan->status, PlanStatus::Drafting); + EXPECT_NEAR(foundPlan->resourceQuota, 0.6, 0.001); +} + +/** + * @brief 测试创建方案时带有特殊字符名称 + * + * 验证 createTaskPlan 能处理包含特殊字符的方案名称。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanSpecialName) { + // 准备特殊名称 + TaskPlan plan; + plan.name = "Plan-@#$%^&*()_+"; + 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, "Plan-@#$%^&*()_+"); +} + +/** + * @brief 测试创建方案后更新状态 + * + * 验证创建方案后可以更新其状态。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanAndUpdateStatus) { + // 创建方案 + TaskPlan plan; + plan.name = "UpdatablePlan"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + + EXPECT_TRUE(engine->createTaskPlan(plan)); + + // 更新状态 + bool updateResult = engine->updatePlanStatus(plan.id, PlanStatus::Drafting); + EXPECT_TRUE(updateResult); + + // 验证状态已更新 + const TaskPlan* updatedPlan = engine->findPlanById(plan.id); + ASSERT_NE(updatedPlan, nullptr); + EXPECT_EQ(updatedPlan->status, PlanStatus::Drafting); +} + +/** + * @brief 测试创建方案时传入已填充 ID 的方案 + * + * 验证 createTaskPlan 会覆盖传入方案的 ID。 + */ +TEST_F(CmsEngineTest, testCreateTaskPlanWithPreFilledId) { + // 准备已填充 ID 的方案 + TaskPlan plan; + plan.id = "PRE-FILLED-ID"; + plan.name = "TestPlan"; + plan.type = PlanType::Centralized; + plan.status = PlanStatus::Drafting; + + // 执行 + bool result = engine->createTaskPlan(plan); + + // 验证 + EXPECT_TRUE(result); + + // 验证 ID 已被覆盖 + EXPECT_NE(plan.id, "PRE-FILLED-ID"); + EXPECT_THAT(plan.id, StartsWith("ID-")); +} \ No newline at end of file