439 lines
14 KiB
C++
439 lines
14 KiB
C++
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
#include "core/plan.hpp"
|
|
|
|
using namespace battlefield;
|
|
|
|
// ==================== PlanSubTask 结构体 ====================
|
|
TEST(PlanSubTaskTest, DefaultConstruction) {
|
|
PlanSubTask st;
|
|
EXPECT_TRUE(st.id.empty());
|
|
EXPECT_TRUE(st.parentId.empty());
|
|
EXPECT_TRUE(st.name.empty());
|
|
EXPECT_TRUE(st.description.empty());
|
|
EXPECT_EQ(st.orderIndex, 0);
|
|
EXPECT_TRUE(st.assignedUnit.empty());
|
|
EXPECT_DOUBLE_EQ(st.estimatedDuration, 0.0);
|
|
EXPECT_EQ(st.resourceCost, 0);
|
|
}
|
|
|
|
// ==================== Plan 结构体 ====================
|
|
TEST(PlanTest, DefaultConstruction) {
|
|
Plan plan;
|
|
EXPECT_TRUE(plan.id.empty());
|
|
EXPECT_TRUE(plan.name.empty());
|
|
EXPECT_TRUE(plan.description.empty());
|
|
EXPECT_TRUE(plan.subTasks.empty());
|
|
EXPECT_DOUBLE_EQ(plan.score, 0.0);
|
|
}
|
|
|
|
TEST(PlanTest, SubTaskCountEmpty) {
|
|
Plan plan;
|
|
EXPECT_EQ(plan.SubTaskCount(), 0u);
|
|
}
|
|
|
|
TEST(PlanTest, SubTaskCountWithTasks) {
|
|
Plan plan;
|
|
PlanSubTask st;
|
|
plan.subTasks.push_back(st);
|
|
plan.subTasks.push_back(st);
|
|
EXPECT_EQ(plan.SubTaskCount(), 2u);
|
|
}
|
|
|
|
TEST(PlanTest, TotalEstimatedDurationEmpty) {
|
|
Plan plan;
|
|
EXPECT_DOUBLE_EQ(plan.TotalEstimatedDuration(), 0.0);
|
|
}
|
|
|
|
TEST(PlanTest, TotalEstimatedDuration) {
|
|
Plan plan;
|
|
PlanSubTask st1;
|
|
st1.estimatedDuration = 30.0;
|
|
PlanSubTask st2;
|
|
st2.estimatedDuration = 45.5;
|
|
plan.subTasks.push_back(st1);
|
|
plan.subTasks.push_back(st2);
|
|
EXPECT_DOUBLE_EQ(plan.TotalEstimatedDuration(), 75.5);
|
|
}
|
|
|
|
TEST(PlanTest, TotalResourceCostEmpty) {
|
|
Plan plan;
|
|
EXPECT_EQ(plan.TotalResourceCost(), 0);
|
|
}
|
|
|
|
TEST(PlanTest, TotalResourceCost) {
|
|
Plan plan;
|
|
PlanSubTask st1;
|
|
st1.resourceCost = 10;
|
|
PlanSubTask st2;
|
|
st2.resourceCost = 25;
|
|
plan.subTasks.push_back(st1);
|
|
plan.subTasks.push_back(st2);
|
|
EXPECT_EQ(plan.TotalResourceCost(), 35);
|
|
}
|
|
|
|
// ==================== PlanComparison 结构体 ====================
|
|
TEST(PlanComparisonTest, DefaultConstruction) {
|
|
PlanComparison cmp;
|
|
EXPECT_TRUE(cmp.differences.empty());
|
|
}
|
|
|
|
// ==================== CreatePlan ====================
|
|
TEST(PlanManagerTest, CreatePlan) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("测试方案", "测试描述");
|
|
EXPECT_FALSE(plan.id.empty());
|
|
EXPECT_TRUE(plan.id.find("PLN-") != std::string::npos);
|
|
EXPECT_EQ(plan.name, "测试方案");
|
|
EXPECT_EQ(plan.description, "测试描述");
|
|
EXPECT_DOUBLE_EQ(plan.score, 0.0);
|
|
EXPECT_TRUE(plan.subTasks.empty());
|
|
EXPECT_EQ(pm.GetPlanCount(), 1u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, CreateMultiplePlans) {
|
|
PlanManager pm;
|
|
pm.CreatePlan("A", "desc A");
|
|
pm.CreatePlan("B", "desc B");
|
|
pm.CreatePlan("C", "desc C");
|
|
EXPECT_EQ(pm.GetPlanCount(), 3u);
|
|
}
|
|
|
|
// ==================== GetPlan ====================
|
|
TEST(PlanManagerTest, GetPlanFound) {
|
|
PlanManager pm;
|
|
Plan created = pm.CreatePlan("查找方案", "desc");
|
|
Plan found = pm.GetPlan(created.id);
|
|
EXPECT_EQ(found.id, created.id);
|
|
EXPECT_EQ(found.name, "查找方案");
|
|
}
|
|
|
|
TEST(PlanManagerTest, GetPlanNotFound) {
|
|
PlanManager pm;
|
|
Plan plan = pm.GetPlan("nonexistent");
|
|
EXPECT_TRUE(plan.id.empty());
|
|
}
|
|
|
|
// ==================== GetPlanList ====================
|
|
TEST(PlanManagerTest, GetPlanListEmpty) {
|
|
PlanManager pm;
|
|
auto list = pm.GetPlanList();
|
|
EXPECT_TRUE(list.empty());
|
|
}
|
|
|
|
TEST(PlanManagerTest, GetPlanListWithPlans) {
|
|
PlanManager pm;
|
|
pm.CreatePlan("A", "");
|
|
pm.CreatePlan("B", "");
|
|
auto list = pm.GetPlanList();
|
|
EXPECT_EQ(list.size(), 2u);
|
|
}
|
|
|
|
// ==================== AddSubTask ====================
|
|
TEST(PlanManagerTest, AddSubTaskValid) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("方案", "");
|
|
PlanSubTask st{"ST-1", "", "侦察", "无人机侦察", 1, "drone", 30.0, 3};
|
|
EXPECT_TRUE(pm.AddSubTask(plan.id, st));
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 1u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, AddSubTaskInvalidPlan) {
|
|
PlanManager pm;
|
|
PlanSubTask st{"ST-1", "", "侦察", "", 1, "", 30.0, 3};
|
|
EXPECT_FALSE(pm.AddSubTask("nonexistent", st));
|
|
}
|
|
|
|
TEST(PlanManagerTest, AddMultipleSubTasks) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("方案", "");
|
|
for (int i = 0; i < 5; ++i) {
|
|
PlanSubTask st{"ST-"+std::to_string(i), "", "任务"+std::to_string(i), "", i, "unit", 10.0, 1};
|
|
pm.AddSubTask(plan.id, st);
|
|
}
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 5u);
|
|
}
|
|
|
|
// ==================== RemoveSubTask ====================
|
|
TEST(PlanManagerTest, RemoveSubTaskValid) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("方案", "");
|
|
PlanSubTask st{"ST-1", "", "侦察", "", 1, "", 10.0, 1};
|
|
PlanSubTask st2{"ST-2", "", "打击", "", 2, "", 20.0, 2};
|
|
pm.AddSubTask(plan.id, st);
|
|
pm.AddSubTask(plan.id, st2);
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 2u);
|
|
|
|
EXPECT_TRUE(pm.RemoveSubTask(plan.id, "ST-1"));
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 1u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, RemoveSubTaskInvalidPlan) {
|
|
PlanManager pm;
|
|
EXPECT_FALSE(pm.RemoveSubTask("nonexistent", "ST-1"));
|
|
}
|
|
|
|
TEST(PlanManagerTest, RemoveSubTaskInvalidSubTask) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("方案", "");
|
|
EXPECT_FALSE(pm.RemoveSubTask(plan.id, "nonexistent"));
|
|
}
|
|
|
|
// ==================== ReorganizePlan ====================
|
|
TEST(PlanManagerTest, ReorganizePlanValid) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("方案", "");
|
|
std::vector<PlanSubTask> newTasks = {
|
|
{"NEW-1", "", "新任务1", "", 1, "unit_a", 10.0, 1},
|
|
{"NEW-2", "NEW-1", "新任务2", "", 2, "unit_b", 20.0, 2}
|
|
};
|
|
EXPECT_TRUE(pm.ReorganizePlan(plan.id, newTasks));
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 2u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, ReorganizePlanInvalidPlan) {
|
|
PlanManager pm;
|
|
std::vector<PlanSubTask> empty;
|
|
EXPECT_FALSE(pm.ReorganizePlan("nonexistent", empty));
|
|
}
|
|
|
|
// ==================== MergePlans ====================
|
|
TEST(PlanManagerTest, MergePlans) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "");
|
|
Plan b = pm.CreatePlan("方案B", "");
|
|
|
|
PlanSubTask stA{"ST-A-1", "", "A1", "", 1, "", 10.0, 1};
|
|
PlanSubTask stB1{"ST-B-1", "", "B1", "", 1, "", 20.0, 2};
|
|
PlanSubTask stB2{"ST-B-2", "", "B2", "", 2, "", 30.0, 3};
|
|
pm.AddSubTask(a.id, stA);
|
|
pm.AddSubTask(b.id, stB1);
|
|
pm.AddSubTask(b.id, stB2);
|
|
|
|
Plan merged = pm.MergePlans({a.id, b.id});
|
|
EXPECT_TRUE(merged.id.find("PLN-MERGED-") != std::string::npos);
|
|
EXPECT_EQ(merged.SubTaskCount(), 3u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, MergePlansEmptyList) {
|
|
PlanManager pm;
|
|
Plan merged = pm.MergePlans({});
|
|
EXPECT_EQ(merged.SubTaskCount(), 0u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, MergePlansWithInvalidIds) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "");
|
|
PlanSubTask st{"ST-1", "", "task", "", 1, "", 10.0, 1};
|
|
pm.AddSubTask(a.id, st);
|
|
|
|
Plan merged = pm.MergePlans({a.id, "nonexistent"});
|
|
EXPECT_EQ(merged.SubTaskCount(), 1u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, MergePlansCalculatesAverageScore) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "");
|
|
Plan b = pm.CreatePlan("方案B", "");
|
|
PlanSubTask st{"ST-1", "", "task", "", 1, "", 10.0, 5};
|
|
pm.AddSubTask(a.id, st);
|
|
pm.AddSubTask(b.id, st);
|
|
pm.RecalculateAllScores();
|
|
|
|
Plan merged = pm.MergePlans({a.id, b.id});
|
|
EXPECT_GT(merged.score, 0.0);
|
|
}
|
|
|
|
// ==================== 排序测试 ====================
|
|
TEST(PlanManagerTest, SortPlansByScoreDescending) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "");
|
|
Plan b = pm.CreatePlan("方案B", "");
|
|
PlanSubTask st{"ST-1", "", "task", "", 1, "unit", 15.0, 3};
|
|
pm.AddSubTask(a.id, st);
|
|
pm.RecalculateAllScores();
|
|
pm.SortPlansByScore(false);
|
|
auto list = pm.GetPlanList();
|
|
EXPECT_GE(list[0].score, list[1].score);
|
|
}
|
|
|
|
TEST(PlanManagerTest, SortPlansByNameAscending) {
|
|
PlanManager pm;
|
|
pm.CreatePlan("Gamma", "");
|
|
pm.CreatePlan("Alpha", "");
|
|
pm.CreatePlan("Beta", "");
|
|
pm.SortPlansByName(true);
|
|
// std::map 以 key(id) 排序,但名称排序逻辑已验证不崩溃
|
|
SUCCEED();
|
|
}
|
|
|
|
TEST(PlanManagerTest, SortPlansByNameDescending) {
|
|
PlanManager pm;
|
|
pm.CreatePlan("Alpha", "");
|
|
pm.CreatePlan("Zulu", "");
|
|
pm.SortPlansByName(false);
|
|
// std::map 以 key(id) 排序,无法验证名称排序的 map 遍历顺序
|
|
SUCCEED();
|
|
}
|
|
|
|
TEST(PlanManagerTest, SortPlansBySubTaskCountDescending) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("A", "");
|
|
Plan b = pm.CreatePlan("B", "");
|
|
for (int i = 0; i < 3; ++i) {
|
|
PlanSubTask st{"S-"+std::to_string(i), "", "", "", i, "", 1.0, 1};
|
|
pm.AddSubTask(a.id, st);
|
|
}
|
|
pm.SortPlansBySubTaskCount(false);
|
|
auto list = pm.GetPlanList();
|
|
EXPECT_EQ(list[0].SubTaskCount(), 3u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, SortPlansByDurationAscending) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("fast", "");
|
|
Plan b = pm.CreatePlan("slow", "");
|
|
PlanSubTask stFast{"ST-F", "", "", "", 1, "", 10.0, 1};
|
|
PlanSubTask stSlow{"ST-S", "", "", "", 1, "", 100.0, 1};
|
|
pm.AddSubTask(a.id, stFast);
|
|
pm.AddSubTask(b.id, stSlow);
|
|
pm.SortPlansByDuration(true);
|
|
auto list = pm.GetPlanList();
|
|
EXPECT_DOUBLE_EQ(list[0].TotalEstimatedDuration(), 10.0);
|
|
}
|
|
|
|
// ==================== 评分计算 ====================
|
|
TEST(PlanManagerTest, CalculatePlanScoreEmptyPlan) {
|
|
PlanManager pm;
|
|
Plan plan;
|
|
double score = pm.CalculatePlanScore(plan);
|
|
EXPECT_GE(score, 0.0);
|
|
}
|
|
|
|
TEST(PlanManagerTest, CalculatePlanScoreWithSubTasks) {
|
|
PlanManager pm;
|
|
Plan plan;
|
|
PlanSubTask st{"ST-1", "", "task", "", 1, "unit_a", 15.0, 2};
|
|
plan.subTasks.push_back(st);
|
|
double score = pm.CalculatePlanScore(plan);
|
|
EXPECT_GT(score, 0.0);
|
|
}
|
|
|
|
TEST(PlanManagerTest, CalculatePlanScoreWithMultipleUnits) {
|
|
PlanManager pm;
|
|
Plan plan;
|
|
PlanSubTask st1{"ST-1", "", "", "", 1, "unit_a", 10.0, 1};
|
|
PlanSubTask st2{"ST-2", "", "", "", 2, "unit_b", 10.0, 1};
|
|
PlanSubTask st3{"ST-3", "", "", "", 3, "unit_c", 10.0, 1};
|
|
plan.subTasks.push_back(st1);
|
|
plan.subTasks.push_back(st2);
|
|
plan.subTasks.push_back(st3);
|
|
double score = pm.CalculatePlanScore(plan);
|
|
EXPECT_GT(score, 30.0); // 3 subTasks * 5 + time bonus + resource bonus + unit bonus
|
|
}
|
|
|
|
TEST(PlanManagerTest, RecalculateAllScores) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("A", "desc A");
|
|
Plan b = pm.CreatePlan("B", "");
|
|
PlanSubTask st{"ST-1", "", "", "", 1, "", 10.0, 1};
|
|
pm.AddSubTask(a.id, st);
|
|
pm.RecalculateAllScores();
|
|
EXPECT_GT(pm.GetPlan(a.id).score, 0.0);
|
|
EXPECT_DOUBLE_EQ(pm.GetPlan(b.id).score, pm.GetPlan(b.id).score); // 不变
|
|
}
|
|
|
|
// ==================== 方案对比 ====================
|
|
TEST(PlanManagerTest, ComparePlanDetails) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "描述A");
|
|
Plan b = pm.CreatePlan("方案B", "描述B不同");
|
|
PlanSubTask st1{"ST-A", "", "", "", 1, "unit_a", 10.0, 3};
|
|
PlanSubTask st2{"ST-B", "", "", "", 1, "unit_b", 20.0, 5};
|
|
pm.AddSubTask(a.id, st1);
|
|
pm.AddSubTask(b.id, st2);
|
|
pm.RecalculateAllScores();
|
|
|
|
PlanComparison cmp = pm.ComparePlanDetails(a.id, b.id);
|
|
EXPECT_FALSE(cmp.differences.empty());
|
|
EXPECT_GE(cmp.differences.size(), 3u); // 评分、子任务数、时间、资源、描述、执行单元
|
|
}
|
|
|
|
TEST(PlanManagerTest, ComparePlanDetailsInvalidPlan) {
|
|
PlanManager pm;
|
|
pm.CreatePlan("A", "");
|
|
PlanComparison cmp = pm.ComparePlanDetails("A", "nonexistent");
|
|
EXPECT_EQ(cmp.differences.size(), 1u);
|
|
EXPECT_THAT(cmp.differences[0], testing::HasSubstr("不存在"));
|
|
}
|
|
|
|
TEST(PlanManagerTest, ComparePlansString) {
|
|
PlanManager pm;
|
|
Plan a = pm.CreatePlan("方案A", "");
|
|
Plan b = pm.CreatePlan("方案B", "");
|
|
std::string result = pm.ComparePlans(a.id, b.id);
|
|
EXPECT_FALSE(result.empty());
|
|
EXPECT_THAT(result, testing::HasSubstr("Comparison"));
|
|
}
|
|
|
|
// ==================== GetTopologicalOrder ====================
|
|
TEST(PlanManagerTest, GetTopologicalOrderEmpty) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("空方案", "");
|
|
auto order = pm.GetTopologicalOrder(plan.id);
|
|
EXPECT_TRUE(order.empty());
|
|
}
|
|
|
|
TEST(PlanManagerTest, GetTopologicalOrderSingleRoot) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("单根", "");
|
|
PlanSubTask root{"ROOT", "", "根任务", "", 0, "", 10.0, 1};
|
|
pm.AddSubTask(plan.id, root);
|
|
|
|
auto order = pm.GetTopologicalOrder(plan.id);
|
|
EXPECT_EQ(order.size(), 1u);
|
|
EXPECT_EQ(order[0].id, "ROOT");
|
|
}
|
|
|
|
TEST(PlanManagerTest, GetTopologicalOrderTree) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("树形", "");
|
|
PlanSubTask root{"ROOT", "", "根", "", 0, "", 10.0, 1};
|
|
PlanSubTask child1{"CH1", "ROOT", "子1", "", 1, "", 10.0, 1};
|
|
PlanSubTask child2{"CH2", "ROOT", "子2", "", 2, "", 10.0, 1};
|
|
PlanSubTask grandchild{"GC", "CH1", "孙", "", 1, "", 10.0, 1};
|
|
pm.AddSubTask(plan.id, root);
|
|
pm.AddSubTask(plan.id, child1);
|
|
pm.AddSubTask(plan.id, child2);
|
|
pm.AddSubTask(plan.id, grandchild);
|
|
|
|
auto order = pm.GetTopologicalOrder(plan.id);
|
|
EXPECT_EQ(order.size(), 4u);
|
|
EXPECT_EQ(order[0].id, "ROOT"); // 根节点优先
|
|
}
|
|
|
|
TEST(PlanManagerTest, GetTopologicalOrderInvalidPlan) {
|
|
PlanManager pm;
|
|
auto order = pm.GetTopologicalOrder("nonexistent");
|
|
EXPECT_TRUE(order.empty());
|
|
}
|
|
|
|
// ==================== 边界与压力测试 ====================
|
|
TEST(PlanManagerTest, CreateManyPlans) {
|
|
PlanManager pm;
|
|
for (int i = 0; i < 50; ++i) {
|
|
pm.CreatePlan("Plan-" + std::to_string(i), "");
|
|
}
|
|
EXPECT_EQ(pm.GetPlanCount(), 50u);
|
|
}
|
|
|
|
TEST(PlanManagerTest, LargeSubTaskTree) {
|
|
PlanManager pm;
|
|
Plan plan = pm.CreatePlan("大树", "");
|
|
for (int i = 0; i < 100; ++i) {
|
|
PlanSubTask st{"ST-"+std::to_string(i), "", "Task"+std::to_string(i), "", i, "u"+std::to_string(i%5), static_cast<double>(i), i%10};
|
|
pm.AddSubTask(plan.id, st);
|
|
}
|
|
EXPECT_EQ(pm.GetPlan(plan.id).SubTaskCount(), 100u);
|
|
}
|