#include #include #include "core/dispatch.hpp" using namespace battlefield; // ==================== DispatchState 枚举 ==================== TEST(DispatchEnumTest, DispatchStateValues) { EXPECT_EQ(static_cast(DispatchState::PENDING), 0); EXPECT_EQ(static_cast(DispatchState::IN_PROGRESS), 1); EXPECT_EQ(static_cast(DispatchState::SUCCESS), 2); EXPECT_EQ(static_cast(DispatchState::FAILED), 3); EXPECT_EQ(static_cast(DispatchState::RETRYING), 4); } // ==================== CmdPacket 结构体 ==================== TEST(CmdPacketTest, DefaultConstruction) { CmdPacket pkt; EXPECT_TRUE(pkt.packetId.empty()); EXPECT_TRUE(pkt.planId.empty()); EXPECT_TRUE(pkt.targetUnit.empty()); EXPECT_TRUE(pkt.payload.empty()); EXPECT_EQ(pkt.state, DispatchState::PENDING); EXPECT_EQ(pkt.retryCount, 0); EXPECT_EQ(pkt.maxRetries, 3); } // ==================== DispatchLog 结构体 ==================== TEST(DispatchLogTest, DefaultConstruction) { DispatchLog log; EXPECT_TRUE(log.logId.empty()); EXPECT_TRUE(log.planId.empty()); EXPECT_TRUE(log.action.empty()); EXPECT_TRUE(log.success); EXPECT_EQ(log.state, DispatchState::SUCCESS); } // ==================== MonitorStatus 结构体 ==================== TEST(MonitorStatusTest, DefaultConstruction) { MonitorStatus ms; EXPECT_EQ(ms.totalTasks, 0); EXPECT_EQ(ms.completedTasks, 0); EXPECT_EQ(ms.inProgressTasks, 0); EXPECT_EQ(ms.failedTasks, 0); EXPECT_EQ(ms.pendingTasks, 0); EXPECT_DOUBLE_EQ(ms.progressPercent, 0.0); EXPECT_FALSE(ms.needsReplan); } // ==================== DispatchPlan ==================== TEST(DispatchMonitorTest, DispatchPlan) { DispatchMonitor dm; Plan plan; plan.id = "PLN-TEST-1"; plan.name = "测试方案"; plan.description = "方案描述"; PlanSubTask st{"ST-1", "", "任务1", "", 0, "unit_a", 30.0, 5}; plan.subTasks.push_back(st); CmdPacket pkt = dm.DispatchPlan(plan, "battalion-1"); EXPECT_FALSE(pkt.packetId.empty()); EXPECT_EQ(pkt.planId, "PLN-TEST-1"); EXPECT_EQ(pkt.targetUnit, "battalion-1"); EXPECT_EQ(pkt.state, DispatchState::IN_PROGRESS); EXPECT_FALSE(pkt.payload.empty()); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.totalTasks, 1); EXPECT_EQ(status.inProgressTasks, 1); } TEST(DispatchMonitorTest, DispatchPlanMultiple) { DispatchMonitor dm; Plan plan; plan.id = "PLN-Multi"; for (int i = 0; i < 5; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit-1"); dm.DispatchPlan(plan, "unit-2"); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.totalTasks, 10); EXPECT_EQ(status.inProgressTasks, 10); auto packets = dm.GetIssuedPackets(); EXPECT_EQ(packets.size(), 2u); } TEST(DispatchMonitorTest, DispatchPlanEmptyPlan) { DispatchMonitor dm; Plan plan; plan.id = "PLN-EMPTY"; CmdPacket pkt = dm.DispatchPlan(plan, "unit"); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.totalTasks, 0); EXPECT_EQ(status.inProgressTasks, 0); } // ==================== DispatchPlanAsJson ==================== TEST(DispatchMonitorTest, DispatchPlanAsJson) { DispatchMonitor dm; Plan plan; plan.id = "PLN-JSON"; plan.name = "JSON方案"; CmdPacket pkt = dm.DispatchPlanAsJson(plan, "unit-x"); EXPECT_FALSE(pkt.payload.empty()); } // ==================== RetryDispatch ==================== TEST(DispatchMonitorTest, RetryDispatchOnFailed) { DispatchMonitor dm; Plan plan; plan.id = "PLN-RETRY"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); CmdPacket pkt = dm.DispatchPlan(plan, "unit"); // 手动设为失败 auto& packets = dm.GetIssuedPackets(); for (auto& p : packets) { if (p.packetId == pkt.packetId) { p.state = DispatchState::FAILED; } } EXPECT_TRUE(dm.RetryDispatch(pkt.packetId)); // 验证状态变为 RETRYING for (const auto& p : dm.GetIssuedPackets()) { if (p.packetId == pkt.packetId) { EXPECT_EQ(p.state, DispatchState::RETRYING); EXPECT_EQ(p.retryCount, 1); } } } TEST(DispatchMonitorTest, RetryDispatchNotFound) { DispatchMonitor dm; EXPECT_FALSE(dm.RetryDispatch("nonexistent")); } TEST(DispatchMonitorTest, RetryDispatchExceedMaxRetries) { DispatchMonitor dm; Plan plan; plan.id = "PLN-MAXRETRY"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); CmdPacket pkt = dm.DispatchPlan(plan, "unit"); // 设为失败且重试次数达到上限 auto& packets = dm.GetIssuedPackets(); for (auto& p : packets) { if (p.packetId == pkt.packetId) { p.state = DispatchState::FAILED; p.retryCount = 3; } } EXPECT_FALSE(dm.RetryDispatch(pkt.packetId)); EXPECT_TRUE(dm.NeedsReplan()); // 重试耗尽触发重规划 } // ==================== AdvanceProgress ==================== TEST(DispatchMonitorTest, AdvanceProgressPartial) { DispatchMonitor dm; Plan plan; plan.id = "PLN-PROG"; for (int i = 0; i < 10; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.AdvanceProgress(3); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.completedTasks, 3); EXPECT_EQ(status.inProgressTasks, 7); EXPECT_DOUBLE_EQ(status.progressPercent, 30.0); } TEST(DispatchMonitorTest, AdvanceProgressAll) { DispatchMonitor dm; Plan plan; plan.id = "PLN-ALL"; for (int i = 0; i < 5; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.AdvanceProgress(5); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.completedTasks, 5); EXPECT_EQ(status.inProgressTasks, 0); EXPECT_DOUBLE_EQ(status.progressPercent, 100.0); } TEST(DispatchMonitorTest, AdvanceProgressMoreThanAvailable) { DispatchMonitor dm; Plan plan; plan.id = "PLN-OVER"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); dm.DispatchPlan(plan, "unit"); dm.AdvanceProgress(100); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.completedTasks, 1); EXPECT_EQ(status.inProgressTasks, 0); } TEST(DispatchMonitorTest, AdvanceProgressNoTasks) { DispatchMonitor dm; dm.AdvanceProgress(5); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.completedTasks, 0); } // ==================== ReportFailure ==================== TEST(DispatchMonitorTest, ReportFailure) { DispatchMonitor dm; Plan plan; plan.id = "PLN-FAIL"; for (int i = 0; i < 10; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.ReportFailure(2); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.failedTasks, 2); EXPECT_EQ(status.inProgressTasks, 8); } TEST(DispatchMonitorTest, ReportFailureMoreThanAvailable) { DispatchMonitor dm; Plan plan; plan.id = "PLN-FAIL2"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); dm.DispatchPlan(plan, "unit"); dm.ReportFailure(10); auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.failedTasks, 1); } // ==================== Replan Trigger ==================== TEST(DispatchMonitorTest, NeedsReplanWhenFailRateHigh) { DispatchMonitor dm; Plan plan; plan.id = "PLN-REPLAN"; for (int i = 0; i < 10; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); EXPECT_FALSE(dm.NeedsReplan()); dm.ReportFailure(5); // 50% failure > 30% threshold EXPECT_TRUE(dm.NeedsReplan()); } TEST(DispatchMonitorTest, NoReplanWhenFailRateLow) { DispatchMonitor dm; Plan plan; plan.id = "PLN-NOREPLAN"; for (int i = 0; i < 10; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.ReportFailure(2); // 20% failure < 30% threshold EXPECT_FALSE(dm.NeedsReplan()); } TEST(DispatchMonitorTest, ClearReplanFlag) { DispatchMonitor dm; Plan plan; plan.id = "PLN-CLEAR"; for (int i = 0; i < 5; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.ReportFailure(5); // 100% failure EXPECT_TRUE(dm.NeedsReplan()); dm.ClearReplanFlag(); EXPECT_FALSE(dm.NeedsReplan()); } // ==================== LogDispatch ==================== TEST(DispatchMonitorTest, LogDispatch) { DispatchMonitor dm; dm.LogDispatch("PLN-LOG", "TEST_ACTION", true); dm.LogDispatch("PLN-LOG", "ANOTHER", false, DispatchState::FAILED); auto logs = dm.GetLogs(); EXPECT_EQ(logs.size(), 2u); EXPECT_EQ(logs[0].planId, "PLN-LOG"); EXPECT_EQ(logs[0].action, "TEST_ACTION"); EXPECT_TRUE(logs[0].success); EXPECT_EQ(logs[1].action, "ANOTHER"); EXPECT_FALSE(logs[1].success); } TEST(DispatchMonitorTest, LogsContainDispatchEvents) { DispatchMonitor dm; Plan plan; plan.id = "PLN-LOG2"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); dm.DispatchPlan(plan, "unit"); auto logs = dm.GetLogs(); EXPECT_GE(logs.size(), 1u); // 分发操作会记录日志 } // ==================== Dashboard ==================== TEST(DispatchMonitorTest, GetDashboardData) { DispatchMonitor dm; Plan plan; plan.id = "PLN-DASH"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); dm.DispatchPlan(plan, "unit"); dm.AdvanceProgress(1); std::string dash = dm.GetDashboardData(); EXPECT_THAT(dash, testing::HasSubstr("Dashboard")); EXPECT_THAT(dash, testing::HasSubstr("Total Tasks:")); EXPECT_THAT(dash, testing::HasSubstr("Progress:")); } TEST(DispatchMonitorTest, GetDashboardDataShowsReplan) { DispatchMonitor dm; Plan plan; plan.id = "PLN-DASH2"; for (int i = 0; i < 5; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 10.0, 1}; plan.subTasks.push_back(st); } dm.DispatchPlan(plan, "unit"); dm.ReportFailure(5); std::string dash = dm.GetDashboardData(); EXPECT_THAT(dash, testing::HasSubstr("重规划")); } TEST(DispatchMonitorTest, GetDashboardJson) { DispatchMonitor dm; Plan plan; plan.id = "PLN-DASH3"; plan.subTasks.push_back({"ST-1", "", "", "", 0, "", 10.0, 1}); dm.DispatchPlan(plan, "unit"); dm.AdvanceProgress(1); auto jsonDash = dm.GetDashboardJson(); EXPECT_EQ(jsonDash["totalTasks"], 1); EXPECT_EQ(jsonDash["completedTasks"], 1); EXPECT_DOUBLE_EQ(jsonDash["progressPercent"], 100.0); EXPECT_EQ(jsonDash["issuedPackets"], 1); } TEST(DispatchMonitorTest, GetDashboardJsonEmpty) { DispatchMonitor dm; auto jsonDash = dm.GetDashboardJson(); EXPECT_EQ(jsonDash["totalTasks"], 0); EXPECT_DOUBLE_EQ(jsonDash["progressPercent"], 0.0); } // ==================== 边界与压力 ==================== TEST(DispatchMonitorTest, LargeScaleDispatch) { DispatchMonitor dm; Plan plan; plan.id = "PLN-BULK"; for (int i = 0; i < 200; ++i) { PlanSubTask st{"ST-"+std::to_string(i), "", "", "", i, "", 1.0, 1}; plan.subTasks.push_back(st); } for (int i = 0; i < 10; ++i) { dm.DispatchPlan(plan, "unit-"+std::to_string(i)); } auto status = dm.GetMonitorStatus(); EXPECT_EQ(status.totalTasks, 2000); EXPECT_EQ(status.inProgressTasks, 2000); dm.AdvanceProgress(1500); dm.ReportFailure(500); auto updated = dm.GetMonitorStatus(); EXPECT_EQ(updated.completedTasks, 1500); EXPECT_EQ(updated.failedTasks, 500); EXPECT_GT(updated.progressPercent, 70.0); }