360 lines
13 KiB
C++
360 lines
13 KiB
C++
/**
|
||
* @file basic_test.cpp
|
||
* @brief FZKJ 核心软件基础单元测试(使用标准库 assert)
|
||
*
|
||
* 测试覆盖:
|
||
* - SRS-FZKJ_F-001 事件处理与任务生成
|
||
* - SRS-FZKJ_F-002 方案管理与对比
|
||
* - SRS-FZKJ_F-003 方案分发与状态监控
|
||
* - SRS-FZKJ_F-004 地图量算
|
||
* - SRS-FZKJ_F-005 数据中转与存储
|
||
* - SRS-FZKJ_F-006 系统配置与模式切换
|
||
* - 数据结构正确性验证
|
||
*/
|
||
|
||
#include "app.hpp"
|
||
#include <cassert>
|
||
#include <cstring>
|
||
#include <iostream>
|
||
|
||
// 辅助:构造合法事件
|
||
static fzkj::EventPayload MakeValidEvent(uint8_t type, uint32_t source_id) {
|
||
fzkj::EventPayload evt;
|
||
std::memset(&evt, 0, sizeof(evt));
|
||
evt.event_type = type;
|
||
evt.timestamp_ms = 1680000000000ULL;
|
||
evt.source_id = source_id;
|
||
// 计算校验码(异或)
|
||
const uint8_t* raw = reinterpret_cast<const uint8_t*>(&evt);
|
||
uint8_t cs = 0;
|
||
for (std::size_t i = 0; i < sizeof(fzkj::EventPayload) - 1; ++i) {
|
||
cs ^= raw[i];
|
||
}
|
||
evt.checksum = cs;
|
||
return evt;
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:数据结构
|
||
// ============================================================
|
||
static void TestDataStructures() {
|
||
std::cout << "[Test] 数据结构验证...\n";
|
||
|
||
// EventPayload 定长 256 字节
|
||
fzkj::EventPayload evt;
|
||
std::memset(&evt, 0, sizeof(evt));
|
||
assert(sizeof(evt) == fzkj::kEventPayloadSize);
|
||
std::cout << " ✓ EventPayload 大小为 " << sizeof(evt) << " 字节 (预期 256)\n";
|
||
|
||
// Task 默认构造
|
||
fzkj::Task task;
|
||
assert(task.task_id == 0);
|
||
task.task_id = 42;
|
||
task.description = "测试任务";
|
||
std::string str = task.ToString();
|
||
assert(str.find("42") != std::string::npos);
|
||
assert(str.find("测试任务") != std::string::npos);
|
||
std::cout << " ✓ Task::ToString() 正确: " << str << "\n";
|
||
|
||
// BattleSolution
|
||
fzkj::BattleSolution sol{1, 1, "方案A", "测试", 95.0, false};
|
||
assert(sol.solution_id == 1);
|
||
assert(sol.score == 95.0);
|
||
std::cout << " ✓ BattleSolution 构造正确\n";
|
||
|
||
// ExecutionUnit
|
||
fzkj::ExecutionUnit unit{100, "测试单元", fzkj::ExecutionStatus::kPending};
|
||
assert(unit.unit_id == 100);
|
||
assert(unit.status == fzkj::ExecutionStatus::kPending);
|
||
std::cout << " ✓ ExecutionUnit 构造正确\n";
|
||
|
||
// GeoPoint
|
||
fzkj::GeoPoint pt{39.9, 116.4, 0.0};
|
||
assert(pt.latitude == 39.9);
|
||
assert(pt.longitude == 116.4);
|
||
std::cout << " ✓ GeoPoint 构造正确\n";
|
||
|
||
// SystemConfig
|
||
fzkj::SystemConfig cfg;
|
||
std::string cfg_str = cfg.ToString();
|
||
assert(cfg_str.find("默认想定") != std::string::npos);
|
||
std::cout << " ✓ SystemConfig 正确: " << cfg_str << "\n";
|
||
|
||
std::cout << " ✓ 所有数据结构验证通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-001 事件处理
|
||
// ============================================================
|
||
static void TestEventProcessing() {
|
||
std::cout << "[Test] SRS-FZKJ_F-001 事件处理...\n";
|
||
|
||
fzkj::App app;
|
||
|
||
// 1. 接收到合法事件后应生成任务
|
||
auto evt1 = MakeValidEvent(1, 1001);
|
||
auto ret = app.ReceiveEvent(evt1);
|
||
assert(ret == fzkj::EventProcessResult::kTaskGenerated);
|
||
std::cout << " ✓ 合法事件处理成功,生成任务\n";
|
||
|
||
// 2. 冗余事件应被过滤
|
||
auto evt2 = MakeValidEvent(1, 1001);
|
||
evt2.timestamp_ms = evt1.timestamp_ms; // 同一事件
|
||
evt2.source_id = evt1.source_id;
|
||
evt2.event_type = evt1.event_type;
|
||
// 重算校验码以匹配
|
||
const uint8_t* raw2 = reinterpret_cast<const uint8_t*>(&evt2);
|
||
uint8_t cs2 = 0;
|
||
for (std::size_t i = 0; i < sizeof(fzkj::EventPayload) - 1; ++i) {
|
||
cs2 ^= raw2[i];
|
||
}
|
||
evt2.checksum = cs2;
|
||
ret = app.ReceiveEvent(evt2);
|
||
assert(ret == fzkj::EventProcessResult::kValidationFailed);
|
||
std::cout << " ✓ 冗余事件被正确过滤\n";
|
||
|
||
// 3. 无效校验码事件应被拒绝
|
||
fzkj::EventPayload bad_evt;
|
||
std::memset(&bad_evt, 0, sizeof(bad_evt));
|
||
bad_evt.event_type = 2;
|
||
bad_evt.checksum = 0xFF; // 错误的校验码
|
||
ret = app.ReceiveEvent(bad_evt);
|
||
assert(ret == fzkj::EventProcessResult::kValidationFailed);
|
||
std::cout << " ✓ 无效校验码事件被拒绝\n";
|
||
|
||
std::cout << " ✓ 事件处理测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-002 方案管理
|
||
// ============================================================
|
||
static void TestSolutionManagement() {
|
||
std::cout << "[Test] SRS-FZKJ_F-002 方案管理...\n";
|
||
|
||
fzkj::App app;
|
||
|
||
// 添加方案
|
||
fzkj::BattleSolution s1{1, 1, "方案甲", "集中突防", 90.0, false};
|
||
fzkj::BattleSolution s2{2, 1, "方案乙", "分布式突防", 85.0, true};
|
||
assert(app.AddSolution(s1));
|
||
assert(app.AddSolution(s2));
|
||
std::cout << " ✓ 添加 2 个方案成功\n";
|
||
|
||
auto solutions = app.GetSolutions();
|
||
assert(solutions.size() == 2);
|
||
std::cout << " ✓ 获取方案列表,size=" << solutions.size() << "\n";
|
||
|
||
// 按评分排序
|
||
app.SortByPreference("score");
|
||
solutions = app.GetSolutions();
|
||
assert(solutions[0].score >= solutions[1].score);
|
||
std::cout << " ✓ 按评分排序正确\n";
|
||
|
||
// 对比方案
|
||
auto cmp = app.CompareSolutions(1, 2);
|
||
assert(cmp.solution_a_id == 1);
|
||
assert(cmp.solution_b_id == 2);
|
||
std::cout << " ✓ 方案对比结果: " << cmp.summary << "\n";
|
||
|
||
std::cout << " ✓ 方案管理测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-003 方案分发与状态监控
|
||
// ============================================================
|
||
static void TestPlanDispatch() {
|
||
std::cout << "[Test] SRS-FZKJ_F-003 方案驱动与状态监控...\n";
|
||
|
||
fzkj::App app;
|
||
fzkj::BattleSolution s{10, 1, "突防方案", "测试", 90.0, false};
|
||
app.AddSolution(s);
|
||
|
||
// 分发方案
|
||
bool ok = app.DispatchPlan(10);
|
||
assert(ok);
|
||
std::cout << " ✓ 方案分发成功\n";
|
||
|
||
// 监控状态
|
||
auto status = app.MonitorExecutionStatus(1);
|
||
assert(status == fzkj::ExecutionStatus::kInProgress);
|
||
std::cout << " ✓ 执行单元状态为执行中\n";
|
||
|
||
// 触发异常
|
||
ok = app.TriggerAlertOnFailure(1);
|
||
assert(ok);
|
||
status = app.MonitorExecutionStatus(1);
|
||
assert(status == fzkj::ExecutionStatus::kFailed);
|
||
std::cout << " ✓ 异常告警触发成功,状态变更为失败\n";
|
||
|
||
// 不存在的方案
|
||
ok = app.DispatchPlan(999);
|
||
assert(!ok);
|
||
std::cout << " ✓ 不存在的方案分发返回 false\n";
|
||
|
||
std::cout << " ✓ 方案分发与监控测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-004 地图与量算
|
||
// ============================================================
|
||
static void TestMapAndMeasure() {
|
||
std::cout << "[Test] SRS-FZKJ_F-004 共用态势与地图...\n";
|
||
|
||
fzkj::App app;
|
||
|
||
// 加载地图
|
||
bool ok = app.LoadGisMap("test_map.tif");
|
||
assert(ok);
|
||
std::cout << " ✓ GIS 地图加载成功\n";
|
||
|
||
// 叠加图层
|
||
fzkj::SituationLayer layer{"雷达图层", true};
|
||
ok = app.OverlaySituationLayer(layer);
|
||
assert(ok);
|
||
std::cout << " ✓ 态势图层叠加成功\n";
|
||
|
||
// 量算距离(北京→上海约 1068 km)
|
||
fzkj::GeoPoint beijing{39.9042, 116.4074, 0.0};
|
||
fzkj::GeoPoint shanghai{31.2304, 121.4737, 0.0};
|
||
auto measure = app.MeasureDistance(beijing, shanghai);
|
||
// 预期在 1000~1200 km 之间
|
||
assert(measure.distance_m > 900000.0);
|
||
assert(measure.distance_m < 1300000.0);
|
||
std::cout << " ✓ 北京→上海距离量算: "
|
||
<< (measure.distance_m / 1000.0) << " km (预期约 1068 km)\n";
|
||
|
||
// 切换网格
|
||
app.ToggleGridDisplay();
|
||
std::cout << " ✓ 网格显示切换成功\n";
|
||
|
||
std::cout << " ✓ 地图与态势测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-005 数据中转与存储
|
||
// ============================================================
|
||
static void TestDataTransfer() {
|
||
std::cout << "[Test] SRS-FZKJ_F-005 数据中转与存储...\n";
|
||
|
||
fzkj::App app;
|
||
|
||
uint8_t data[] = {10, 20, 30, 40};
|
||
bool ok = app.ForwardDataToModule("测试模块", data, sizeof(data));
|
||
assert(ok);
|
||
std::cout << " ✓ 数据转发成功\n";
|
||
|
||
ok = app.PersistCacheToDb();
|
||
assert(ok);
|
||
std::cout << " ✓ 缓存持久化成功\n";
|
||
|
||
ok = app.ClearScenarioCache();
|
||
assert(ok);
|
||
std::cout << " ✓ 想定缓存清空成功\n";
|
||
|
||
// 空数据应返回 false
|
||
ok = app.ForwardDataToModule("测试模块", nullptr, 10);
|
||
assert(!ok);
|
||
std::cout << " ✓ 空数据转发返回 false\n";
|
||
|
||
std::cout << " ✓ 数据中转测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:SRS-FZKJ_F-006 系统设置与模式切换
|
||
// ============================================================
|
||
static void TestSystemConfig() {
|
||
std::cout << "[Test] SRS-FZKJ_F-006 系统设置与模式切换...\n";
|
||
|
||
fzkj::App app;
|
||
|
||
// 初始为正常模式
|
||
assert(app.GetCurrentMode() == fzkj::WorkMode::kNormal);
|
||
std::cout << " ✓ 初始模式为正常模式\n";
|
||
|
||
// 切换到降级模式
|
||
bool ok = app.SwitchExecutionMode(fzkj::WorkMode::kDegraded);
|
||
assert(ok);
|
||
assert(app.GetCurrentMode() == fzkj::WorkMode::kDegraded);
|
||
std::cout << " ✓ 切换到降级模式成功\n";
|
||
|
||
// 降级模式不能直接进入配置维护
|
||
ok = app.SwitchExecutionMode(fzkj::WorkMode::kConfigMaintain);
|
||
assert(!ok);
|
||
std::cout << " ✓ 降级→维护 转换被拒绝(符合状态图)\n";
|
||
|
||
// 降级→正常
|
||
ok = app.SwitchExecutionMode(fzkj::WorkMode::kNormal);
|
||
assert(ok);
|
||
std::cout << " ✓ 降级→正常 切换成功\n";
|
||
|
||
// 正常→配置维护
|
||
ok = app.SwitchExecutionMode(fzkj::WorkMode::kConfigMaintain);
|
||
assert(ok);
|
||
std::cout << " ✓ 正常→配置维护 切换成功\n";
|
||
|
||
// 配置维护只能回正常
|
||
ok = app.SwitchExecutionMode(fzkj::WorkMode::kDegraded);
|
||
assert(!ok);
|
||
std::cout << " ✓ 维护→降级 被拒绝\n";
|
||
|
||
ok = app.SwitchExecutionMode(fzkj::WorkMode::kNormal);
|
||
assert(ok);
|
||
std::cout << " ✓ 维护→正常 切换成功\n";
|
||
|
||
// 更新算法配置
|
||
app.SetAlgorithmConfig("强化学习算法", 0.8);
|
||
auto& cfg = app.GetConfig();
|
||
assert(cfg.algorithm_name == "强化学习算法");
|
||
assert(cfg.algorithm_param == 0.8);
|
||
std::cout << " ✓ 算法配置更新成功\n";
|
||
|
||
// 加载想定
|
||
ok = app.LoadScenarioParams("红蓝对抗想定-2025");
|
||
assert(ok);
|
||
assert(cfg.scenario_name == "红蓝对抗想定-2025");
|
||
std::cout << " ✓ 想定加载成功\n";
|
||
|
||
// 网络配置
|
||
ok = app.ApplyNetworkConfiguration("10.0.0.1", 8888);
|
||
assert(ok);
|
||
assert(cfg.ip_address == "10.0.0.1");
|
||
assert(cfg.port == 8888);
|
||
std::cout << " ✓ 网络配置更新成功\n";
|
||
|
||
std::cout << " ✓ 系统配置测试通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 测试用例:系统自检
|
||
// ============================================================
|
||
static void TestSelfCheck() {
|
||
std::cout << "[Test] 系统自检...\n";
|
||
fzkj::App app;
|
||
assert(app.SelfTest());
|
||
std::cout << " ✓ 系统自检通过\n\n";
|
||
}
|
||
|
||
// ============================================================
|
||
// 主测试入口
|
||
// ============================================================
|
||
int main() {
|
||
std::cout << "\n╔══════════════════════════════════════════╗\n";
|
||
std::cout << "║ FZKJ 核心软件 - 基础单元测试套件 ║\n";
|
||
std::cout << "║ 版本 1.6.0 标准库 assert ║\n";
|
||
std::cout << "╚══════════════════════════════════════════╝\n\n";
|
||
|
||
TestDataStructures();
|
||
TestEventProcessing();
|
||
TestSolutionManagement();
|
||
TestPlanDispatch();
|
||
TestMapAndMeasure();
|
||
TestDataTransfer();
|
||
TestSystemConfig();
|
||
TestSelfCheck();
|
||
|
||
std::cout << "╔══════════════════════════════════════════╗\n";
|
||
std::cout << "║ 全部测试通过! ✓ ║\n";
|
||
std::cout << "╚══════════════════════════════════════════╝\n";
|
||
return 0;
|
||
}
|