From a9803b825d72cd8ffefec79111d7a186e731b47e Mon Sep 17 00:00:00 2001 From: lids <1713278948@qq.com> Date: Thu, 14 May 2026 16:21:23 +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_cms_engine.cpp | 331 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test_cms_engine.cpp 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..5950558 --- /dev/null +++ b/tests/test_cms_engine.cpp @@ -0,0 +1,331 @@ +#include +#include +#include "app.hpp" +#include +#include +#include + +using namespace testing; + +// ============================================================================ +// Test Fixture for CmsEngine +// ============================================================================ +class CmsEngineTest : public ::testing::Test { +protected: + void SetUp() override { + engine = new CmsEngine(); + } + + void TearDown() override { + delete engine; + } + + CmsEngine* engine; +}; + +// ============================================================================ +// Tests for CmsEngine constructor +// ============================================================================ + +/** + * @brief Test: Constructor initializes system context correctly + * + * Verifies that after construction: + * - currentMode is RunMode::Idle + * - consistencyMark is 0 + * - switchTime is set (not default) + * - contextSnapshot contains expected JSON + */ +TEST_F(CmsEngineTest, testConstructorInitializesSystemContext) { + const auto& ctx = engine->getSystemContext(); + EXPECT_EQ(ctx.currentMode, RunMode::Idle); + EXPECT_EQ(ctx.consistencyMark, 0); + EXPECT_NE(ctx.switchTime, std::chrono::system_clock::time_point()); + EXPECT_THAT(ctx.contextSnapshot, HasSubstr("Idle")); + EXPECT_THAT(ctx.contextSnapshot, HasSubstr("1.0.0")); +} + +/** + * @brief Test: Constructor initializes empty containers + * + * Verifies that all internal containers are empty after construction. + */ +TEST_F(CmsEngineTest, testConstructorInitializesEmptyContainers) { + EXPECT_EQ(engine->getAllPlans().size(), 0); + EXPECT_EQ(engine->getUnreadNotifications().size(), 0); + EXPECT_EQ(engine->getSummary().find("Events : 0"), std::string::npos); +} + +// ============================================================================ +// Tests for ingestEvent +// ============================================================================ + +/** + * @brief Test: Ingest event with valid data succeeds + * + * Creates a valid EventRecord with non-empty ID and priority <= 255. + * Expects ingestEvent to return true. + */ +TEST_F(CmsEngineTest, testIngestEventValidData) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 100; + event.status = EventStatus::Pending; + EXPECT_TRUE(engine->ingestEvent(event)); +} + +/** + * @brief Test: Ingest event with empty ID fails + * + * Verifies that an event with an empty ID is rejected. + */ +TEST_F(CmsEngineTest, testIngestEventEmptyId) { + EventRecord event; + event.id = ""; + event.priority = 100; + EXPECT_FALSE(engine->ingestEvent(event)); +} + +/** + * @brief Test: Ingest event with priority > 255 fails + * + * Verifies that an event with priority exceeding 255 is rejected. + */ +TEST_F(CmsEngineTest, testIngestEventPriorityExceedsMax) { + EventRecord event; + event.id = "EVT-002"; + event.priority = 256; + EXPECT_FALSE(engine->ingestEvent(event)); +} + +/** + * @brief Test: Ingest event with priority = 0 succeeds (boundary) + * + * Verifies that an event with minimum priority (0) is accepted. + */ +TEST_F(CmsEngineTest, testIngestEventPriorityZero) { + EventRecord event; + event.id = "EVT-003"; + event.priority = 0; + EXPECT_TRUE(engine->ingestEvent(event)); +} + +/** + * @brief Test: Ingest event with priority = 255 succeeds (boundary) + * + * Verifies that an event with maximum valid priority (255) is accepted. + */ +TEST_F(CmsEngineTest, testIngestEventPriorityMax) { + EventRecord event; + event.id = "EVT-004"; + event.priority = 255; + EXPECT_TRUE(engine->ingestEvent(event)); +} + +/** + * @brief Test: Ingest multiple events and verify storage + * + * Ingests multiple events and checks that they are stored correctly. + */ +TEST_F(CmsEngineTest, testIngestMultipleEvents) { + EventRecord event1; + event1.id = "EVT-001"; + event1.priority = 10; + EXPECT_TRUE(engine->ingestEvent(event1)); + + EventRecord event2; + event2.id = "EVT-002"; + event2.priority = 20; + EXPECT_TRUE(engine->ingestEvent(event2)); + + // Verify events are stored by checking processPendingEvents count + EXPECT_EQ(engine->processPendingEvents(), 2); +} + +// ============================================================================ +// Tests for processPendingEvents +// ============================================================================ + +/** + * @brief Test: Process pending events with no events + * + * When there are no events in the system, processPendingEvents should return 0. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsNoEvents) { + EXPECT_EQ(engine->processPendingEvents(), 0); +} + +/** + * @brief Test: Process pending events with one pending event + * + * Ingests one pending event and verifies that processPendingEvents processes it. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsOnePending) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 50; + event.status = EventStatus::Pending; + engine->ingestEvent(event); + + EXPECT_EQ(engine->processPendingEvents(), 1); +} + +/** + * @brief Test: Process pending events with non-pending events only + * + * Events with status other than Pending should not be processed. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsNonPendingOnly) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 50; + event.status = EventStatus::Generated; + engine->ingestEvent(event); + + EXPECT_EQ(engine->processPendingEvents(), 0); +} + +/** + * @brief Test: Process pending events changes status to Generated + * + * After processing, the event status should be changed to Generated. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsChangesStatus) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 50; + event.status = EventStatus::Pending; + engine->ingestEvent(event); + + engine->processPendingEvents(); + + const EventRecord* processedEvent = engine->findEventById("EVT-001"); + ASSERT_NE(processedEvent, nullptr); + EXPECT_EQ(processedEvent->status, EventStatus::Generated); +} + +/** + * @brief Test: Process pending events creates TaskPlan + * + * Verifies that a TaskPlan is created for each processed event. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsCreatesPlan) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 50; + event.status = EventStatus::Pending; + engine->ingestEvent(event); + + engine->processPendingEvents(); + + const auto& plans = engine->getAllPlans(); + ASSERT_EQ(plans.size(), 1); + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); + EXPECT_EQ(plans[0].status, PlanStatus::Drafting); + EXPECT_EQ(plans[0].type, PlanType::Centralized); + EXPECT_EQ(plans[0].resourceQuota, 0.5); + EXPECT_EQ(plans[0].constraints, "自动生成约束"); +} + +/** + * @brief Test: Process pending events with multiple pending events + * + * Verifies that all pending events are processed correctly. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsMultiple) { + EventRecord event1; + event1.id = "EVT-001"; + event1.priority = 10; + event1.status = EventStatus::Pending; + engine->ingestEvent(event1); + + EventRecord event2; + event2.id = "EVT-002"; + event2.priority = 20; + event2.status = EventStatus::Pending; + engine->ingestEvent(event2); + + EventRecord event3; + event3.id = "EVT-003"; + event3.priority = 30; + event3.status = EventStatus::Generated; // Not pending + engine->ingestEvent(event3); + + EXPECT_EQ(engine->processPendingEvents(), 2); + + const auto& plans = engine->getAllPlans(); + EXPECT_EQ(plans.size(), 2); +} + +/** + * @brief Test: Process pending events generates unique IDs + * + * Verifies that each generated TaskPlan has a unique ID. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsUniqueIds) { + EventRecord event1; + event1.id = "EVT-001"; + event1.priority = 10; + event1.status = EventStatus::Pending; + engine->ingestEvent(event1); + + EventRecord event2; + event2.id = "EVT-002"; + event2.priority = 20; + event2.status = EventStatus::Pending; + engine->ingestEvent(event2); + + engine->processPendingEvents(); + + const auto& plans = engine->getAllPlans(); + EXPECT_NE(plans[0].id, plans[1].id); +} + +/** + * @brief Test: Process pending events twice does not reprocess + * + * After processing, events should not be processed again. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsIdempotent) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 50; + event.status = EventStatus::Pending; + engine->ingestEvent(event); + + EXPECT_EQ(engine->processPendingEvents(), 1); + EXPECT_EQ(engine->processPendingEvents(), 0); +} + +/** + * @brief Test: Process pending events with empty event ID (should not be ingested) + * + * Events with empty IDs are rejected by ingestEvent, so they won't be processed. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsEmptyIdNotIngested) { + EventRecord event; + event.id = ""; + event.priority = 50; + event.status = EventStatus::Pending; + engine->ingestEvent(event); // Should return false + + EXPECT_EQ(engine->processPendingEvents(), 0); +} + +/** + * @brief Test: Process pending events with high priority event + * + * Verifies that events with high priority (up to 255) are processed correctly. + */ +TEST_F(CmsEngineTest, testProcessPendingEventsHighPriority) { + EventRecord event; + event.id = "EVT-001"; + event.priority = 255; + event.status = EventStatus::Pending; + engine->ingestEvent(event); + + EXPECT_EQ(engine->processPendingEvents(), 1); + + const auto& plans = engine->getAllPlans(); + EXPECT_EQ(plans[0].relatedEventId, "EVT-001"); +}