task_plan_2/tests/test_dispatch_monitor.cpp

400 lines
12 KiB
C++

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "core/dispatch.hpp"
using namespace battlefield;
// ==================== DispatchState 枚举 ====================
TEST(DispatchEnumTest, DispatchStateValues) {
EXPECT_EQ(static_cast<int>(DispatchState::PENDING), 0);
EXPECT_EQ(static_cast<int>(DispatchState::IN_PROGRESS), 1);
EXPECT_EQ(static_cast<int>(DispatchState::SUCCESS), 2);
EXPECT_EQ(static_cast<int>(DispatchState::FAILED), 3);
EXPECT_EQ(static_cast<int>(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);
}