392 lines
14 KiB
C++
392 lines
14 KiB
C++
/**
|
||
* @file basic_test.cpp
|
||
* @brief 战场任务规划管理系统基本单元测试
|
||
*
|
||
* 使用标准库 assert 进行单元测试,覆盖核心功能模块:
|
||
* - SU-01: 事件接收与处理
|
||
* - SU-02: 事件标准化处理
|
||
* - SU-03: 事件展示与操作
|
||
* - SU-04: 任务生成
|
||
* - SU-05: 任务模板管理
|
||
* - SU-06: 计划管理
|
||
* - SU-07: 方案分发
|
||
* - SU-08: 状态监控
|
||
*/
|
||
|
||
#include "app.hpp"
|
||
#include <cassert>
|
||
#include <iostream>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <thread>
|
||
#include <chrono>
|
||
|
||
using namespace bmp;
|
||
|
||
/**
|
||
* @brief 测试事件接收与处理(SU-01)
|
||
*
|
||
* @requirement(name="事件接收与处理", id="SU-01")
|
||
* 测试接收有效JSON事件、有效XML事件、空数据和无效数据。
|
||
*/
|
||
static void testEventReception() {
|
||
std::cout << "[测试] SU-01: 事件接收与处理" << std::endl;
|
||
EventService svc;
|
||
|
||
// 测试1: 接收有效JSON事件
|
||
std::string jsonEvent = "{ \"type\": \"敌情侦察\", \"priority\": \"HIGH\" }";
|
||
bool result = svc.receiveEvent(jsonEvent);
|
||
assert(result && "接收有效JSON事件应该成功");
|
||
|
||
// 测试2: 接收有效XML事件
|
||
std::string xmlEvent = "<event><type>空情预警</type></event>";
|
||
result = svc.receiveEvent(xmlEvent);
|
||
assert(result && "接收有效XML事件应该成功");
|
||
|
||
// 测试3: 接收空数据
|
||
result = svc.receiveEvent("");
|
||
assert(!result && "接收空数据应该失败");
|
||
|
||
// 测试4: 接收无效数据(既不是JSON也不是XML)
|
||
result = svc.receiveEvent("invalid data without brackets");
|
||
assert(!result && "接收无效格式数据应该失败");
|
||
|
||
// 测试5: 发送ACK
|
||
std::string ack = svc.sendAck("unknown");
|
||
assert(ack == "ACK:UNKNOWN" && "未知事件ID应返回 UNKNOWN");
|
||
|
||
std::cout << " SU-01 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试事件标准化处理(SU-02)
|
||
*
|
||
* @requirement(name="事件标准化处理", id="SU-02")
|
||
* 测试获取原始事件、处理事件和保存标准化事件。
|
||
*/
|
||
static void testEventStandardization() {
|
||
std::cout << "[测试] SU-02: 事件标准化处理" << std::endl;
|
||
EventService svc;
|
||
|
||
// 准备测试数据
|
||
std::string jsonEvent = "{ \"type\": \"敌情侦察\", \"priority\": \"HIGH\" }";
|
||
svc.receiveEvent(jsonEvent);
|
||
auto pending = svc.getPendingEvents();
|
||
assert(!pending.empty() && "应有待处理事件");
|
||
|
||
// 测试1: 获取原始事件
|
||
const Event* raw = svc.getRawEvent(pending[0].id);
|
||
assert(raw != nullptr && "应能获取原始事件");
|
||
assert(raw->status == EventStatus::PENDING && "原始事件状态应为PENDING");
|
||
|
||
// 测试2: 处理事件(标准化)
|
||
bool processed = svc.processEvent(pending[0].id);
|
||
assert(processed && "事件标准化应成功");
|
||
|
||
// 测试3: 再次处理应失败(状态已变更)
|
||
processed = svc.processEvent(pending[0].id);
|
||
assert(!processed && "重复处理应失败");
|
||
|
||
// 测试4: 处理不存在的事件
|
||
processed = svc.processEvent("nonexistent");
|
||
assert(!processed && "处理不存在的事件应失败");
|
||
|
||
// 测试5: 保存标准化事件
|
||
Event customEvent;
|
||
customEvent.id = "CUSTOM_001";
|
||
customEvent.title = "测试事件";
|
||
customEvent.status = EventStatus::ACCEPTED;
|
||
svc.saveStandardEvent(customEvent);
|
||
const Event* saved = svc.getRawEvent("CUSTOM_001");
|
||
assert(saved != nullptr && "保存后应能获取标准化事件");
|
||
assert(saved->status == EventStatus::ACCEPTED && "标准化事件状态应正确");
|
||
|
||
std::cout << " SU-02 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试事件展示与操作(SU-03)
|
||
*
|
||
* @requirement(name="事件展示与操作", id="SU-03")
|
||
* 测试事件查询、获取待处理事件等功能。
|
||
*/
|
||
static void testEventQuery() {
|
||
std::cout << "[测试] SU-03: 事件展示与操作" << std::endl;
|
||
EventService svc;
|
||
|
||
// 准备测试数据
|
||
svc.receiveEvent("{ \"type\": \"A\" }");
|
||
svc.receiveEvent("{ \"type\": \"B\" }");
|
||
|
||
// 测试1: 查询所有事件
|
||
auto all = svc.queryEvents({});
|
||
assert(all.size() == 2 && "应有2个事件");
|
||
|
||
// 测试2: 获取待处理事件
|
||
auto pending = svc.getPendingEvents();
|
||
assert(pending.size() == 2 && "应有2个待处理事件");
|
||
|
||
// 测试3: 过滤状态
|
||
auto filtered = svc.queryEvents({{"status", "PENDING"}});
|
||
assert(filtered.size() == 2 && "过滤PENDING应有2个");
|
||
|
||
std::cout << " SU-03 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试任务模板管理(SU-05)
|
||
*
|
||
* @requirement(name="任务模板管理", id="SU-05")
|
||
* 测试模板接收、列表、选择和推荐功能。
|
||
*/
|
||
static void testTemplateManagement() {
|
||
std::cout << "[测试] SU-05: 任务模板管理" << std::endl;
|
||
TemplateService svc;
|
||
|
||
// 测试1: 接收空数据
|
||
size_t count = svc.receiveTemplates("");
|
||
assert(count == 0 && "空数据应解析出0个模板");
|
||
|
||
// 测试2: 接收有效模板
|
||
std::string tmplData =
|
||
"{\"id\":\"T1\",\"name\":\"模板1\",\"scenario\":\"侦察\"}\n"
|
||
"{\"id\":\"T2\",\"name\":\"模板2\",\"scenario\":\"预警\"}\n";
|
||
count = svc.receiveTemplates(tmplData);
|
||
assert(count == 2 && "应有2个模板被解析");
|
||
|
||
// 测试3: 获取模板列表
|
||
auto list = svc.getTemplateList();
|
||
assert(list.size() == 2 && "模板列表应有2个");
|
||
|
||
// 测试4: 选择模板
|
||
bool selected = svc.selectTemplate("T1");
|
||
assert(selected && "选择存在的模板应成功");
|
||
|
||
selected = svc.selectTemplate("NONEXIST");
|
||
assert(!selected && "选择不存在的模板应失败");
|
||
|
||
// 测试5: 推荐模板
|
||
std::string recommended = svc.recommendTemplate("侦察");
|
||
assert(recommended == "T1" && "应为侦察推荐T1模板");
|
||
|
||
recommended = svc.recommendTemplate("未知");
|
||
assert(!recommended.empty() && "无匹配时应有默认推荐");
|
||
|
||
// 测试6: 获取配置
|
||
auto config = svc.getConfig();
|
||
assert(config.find("auto_recommend") != config.end() && "应有auto_recommend配置");
|
||
|
||
std::cout << " SU-05 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试任务生成与计划管理(SU-04, SU-06)
|
||
*
|
||
* @requirement(name="任务生成", id="SU-04")
|
||
* @requirement(name="计划管理", id="SU-06")
|
||
* 测试任务生成、提交、计划列表、排序、重配置和HITL通知。
|
||
*/
|
||
static void testPlanManagement() {
|
||
std::cout << "[测试] SU-04/SU-06: 任务生成与计划管理" << std::endl;
|
||
PlanService svc;
|
||
|
||
// 测试1: 生成任务 - 空参数应失败
|
||
std::string planId = svc.generateTask("", "T1", {});
|
||
assert(planId.empty() && "空事件ID应生成失败");
|
||
|
||
// 测试2: 生成任务 - 有效参数
|
||
planId = svc.generateTask("EVT_001", "T1", {{"remarks", "测试"}});
|
||
assert(!planId.empty() && "有效参数应生成计划");
|
||
assert(planId.find("PLAN_") == 0 && "计划ID应以PLAN_开头");
|
||
|
||
// 测试3: 提交任务
|
||
TaskPlan plan = svc.submitTask(planId);
|
||
assert(plan.status == PlanStatus::CONFIRMED && "提交后状态应为CONFIRMED");
|
||
assert(plan.id == planId && "返回的计划ID应匹配");
|
||
|
||
// 测试4: 提交不存在的计划
|
||
TaskPlan emptyPlan = svc.submitTask("NONEXIST");
|
||
assert(emptyPlan.id.empty() && "不存在的计划应返回空");
|
||
|
||
// 测试5: 获取计划列表
|
||
auto plans = svc.getPlanList();
|
||
assert(plans.size() == 1 && "应有1个计划");
|
||
|
||
// 测试6: 排序计划
|
||
svc.sortPlans({{"priority", 1}, {"time", 1}});
|
||
|
||
// 测试7: 计划重配置
|
||
std::vector<PlanNode> newNodes;
|
||
PlanNode node;
|
||
node.nodeId = "NODE_001";
|
||
node.planId = planId;
|
||
node.taskType = "侦察";
|
||
node.executeUnit = "无人机";
|
||
newNodes.push_back(node);
|
||
bool reconfigured = svc.reconfigurePlan(planId, newNodes);
|
||
assert(reconfigured && "重配置应成功");
|
||
|
||
// 重配置不存在的计划
|
||
reconfigured = svc.reconfigurePlan("NONEXIST", newNodes);
|
||
assert(!reconfigured && "重配置不存在计划应失败");
|
||
|
||
// 测试8: HITL通知
|
||
svc.notifyHITL(planId, "请确认方案");
|
||
svc.notifyHITL("NONEXIST", "测试"); // 不应崩溃
|
||
|
||
std::cout << " SU-04/SU-06 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试方案分发(SU-07)
|
||
*
|
||
* @requirement(name="方案分发", id="SU-07")
|
||
* 测试分发流程、ACK接收、重试和状态更新。
|
||
*/
|
||
static void testDistribution() {
|
||
std::cout << "[测试] SU-07: 方案分发" << std::endl;
|
||
DistributionService svc;
|
||
|
||
// 测试1: 分发计划
|
||
auto logs = svc.distributePlan("PLAN_001");
|
||
assert(logs.size() == 3 && "应有3条分发日志");
|
||
for (const auto& log : logs) {
|
||
assert(!log.logId.empty() && "日志ID不应为空");
|
||
assert(log.response == DistributionResponse::TIMEOUT && "初始状态应为TIMEOUT");
|
||
assert(log.retryCount == 0 && "初始重试次数应为0");
|
||
}
|
||
|
||
// 测试2: 接收ACK
|
||
bool ackOk = svc.receiveAck(logs[0].logId, DistributionResponse::ACK);
|
||
assert(ackOk && "接收ACK应成功");
|
||
|
||
ackOk = svc.receiveAck("NONEXIST", DistributionResponse::ACK);
|
||
assert(!ackOk && "接收不存在的日志ACK应失败");
|
||
|
||
// 测试3: 接收NACK并验证重试
|
||
bool nackOk = svc.receiveAck(logs[1].logId, DistributionResponse::NACK);
|
||
assert(nackOk && "接收NACK应成功");
|
||
|
||
// 测试4: 查询分发日志
|
||
auto queriedLogs = svc.getLogsByPlan("PLAN_001");
|
||
assert(queriedLogs.size() == 3 && "应有3条日志");
|
||
assert(queriedLogs[0].response == DistributionResponse::ACK && "第一条应为ACK");
|
||
assert(queriedLogs[1].response == DistributionResponse::NACK && "第二条应为NACK");
|
||
|
||
// 测试5: 状态更新(不崩溃即可)
|
||
svc.updateStatus("PLAN_001");
|
||
|
||
std::cout << " SU-07 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试状态监控(SU-08)
|
||
*
|
||
* @requirement(name="状态监控", id="SU-08")
|
||
* 测试遥测数据接收、解析、报警触发和数据陈旧检查。
|
||
*/
|
||
static void testStatusMonitoring() {
|
||
std::cout << "[测试] SU-08: 状态监控" << std::endl;
|
||
StatusMonitorService svc;
|
||
|
||
// 测试1: 接收有效遥测数据
|
||
std::vector<uint8_t> validData = {
|
||
0x00, 0x01, 0x00, 0x01,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x23, 0x28,
|
||
0x4E, 0x6F, 0x72, 0x6D, 0x61, 0x6C
|
||
};
|
||
StatusLog log = svc.receiveTelemetry(validData);
|
||
assert(!log.recordId.empty() && "记录ID不应为空");
|
||
assert(!log.isAbnormal && "正常状态码不应标记为异常");
|
||
|
||
// 测试2: 接收异常遥测数据
|
||
std::vector<uint8_t> abnormalData = {
|
||
0x00, 0x01, 0x00, 0x02,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x23, 0x38, // 9016 > 9000
|
||
0x45, 0x72, 0x72, 0x6F, 0x72
|
||
};
|
||
StatusLog abnormalLog = svc.receiveTelemetry(abnormalData);
|
||
assert(abnormalLog.isAbnormal && "状态码大于9000应标记为异常");
|
||
|
||
// 测试3: 接收短数据(无效)
|
||
std::vector<uint8_t> shortData = {0x00, 0x01};
|
||
StatusLog shortLog = svc.receiveTelemetry(shortData);
|
||
assert(shortLog.isAbnormal && "短数据应标记为异常");
|
||
assert(shortLog.statusCode == "ERR_INVALID_DATA" && "短数据应有错误码");
|
||
|
||
// 测试4: 触发报警
|
||
std::string alarm = svc.triggerAlarm(abnormalLog);
|
||
assert(!alarm.empty() && "异常状态应触发报警");
|
||
assert(alarm.find("[ALARM]") == 0 && "报警消息应以[ALARM]开头");
|
||
|
||
// 正常状态不触发报警
|
||
alarm = svc.triggerAlarm(log);
|
||
assert(alarm.empty() && "正常状态不应触发报警");
|
||
|
||
// 测试5: 获取最新状态
|
||
StatusLog latest = svc.getLatestStatus("AST_65537");
|
||
assert(latest.assetId == "AST_65537" && "应返回正确资产的状态");
|
||
|
||
// 不存在的资产
|
||
latest = svc.getLatestStatus("UNKNOWN");
|
||
assert(latest.isAbnormal && "不存在的资产应标记异常");
|
||
|
||
// 测试6: 数据陈旧检查
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||
svc.checkStaleData(10);
|
||
StatusLog staleLog = svc.getLatestStatus("AST_65537");
|
||
assert(staleLog.isAbnormal && "超过阈值后应标记为陈旧");
|
||
assert(staleLog.statusCode == "STALE" && "陈旧数据状态码应为STALE");
|
||
|
||
std::cout << " SU-08 测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
/**
|
||
* @brief 测试系统外观类
|
||
*
|
||
* @requirement(name="业务目标", id="REQ-BMP-001")
|
||
* 测试系统初始化、完整业务流程的协调。
|
||
*/
|
||
static void testSystemIntegration() {
|
||
std::cout << "[测试] BMP集成: 系统外观类" << std::endl;
|
||
BattlefieldMissionPlanner bmp;
|
||
|
||
// 测试1: 初始化
|
||
bool initialized = bmp.initialize();
|
||
assert(initialized && "系统初始化应成功");
|
||
|
||
// 测试2: 获取各服务引用
|
||
assert(&bmp.getEventService() != nullptr && "事件服务不应为空");
|
||
assert(&bmp.getTemplateService() != nullptr && "模板服务不应为空");
|
||
assert(&bmp.getPlanService() != nullptr && "计划服务不应为空");
|
||
assert(&bmp.getDistributionService() != nullptr && "分发服务不应为空");
|
||
assert(&bmp.getStatusMonitorService() != nullptr && "状态监控服务不应为空");
|
||
|
||
// 测试3: 运行主循环(不崩溃即可)
|
||
bmp.run();
|
||
|
||
std::cout << " BMP集成测试通过!" << std::endl << std::endl;
|
||
}
|
||
|
||
int main() {
|
||
std::cout << "========================================" << std::endl;
|
||
std::cout << " 战场任务规划管理系统 - 单元测试" << std::endl;
|
||
std::cout << "========================================" << std::endl << std::endl;
|
||
|
||
testEventReception();
|
||
testEventStandardization();
|
||
testEventQuery();
|
||
testTemplateManagement();
|
||
testPlanManagement();
|
||
testDistribution();
|
||
testStatusMonitoring();
|
||
testSystemIntegration();
|
||
|
||
std::cout << "========================================" << std::endl;
|
||
std::cout << " 全部测试通过!" << std::endl;
|
||
std::cout << "========================================" << std::endl;
|
||
return 0;
|
||
}
|