400 lines
12 KiB
C++
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);
|
|
}
|