diff --git a/eqpalg/test/README.md b/eqpalg/test/README.md new file mode 100644 index 0000000..c73efd9 --- /dev/null +++ b/eqpalg/test/README.md @@ -0,0 +1,295 @@ +# eqpalg 测试说明文档 + +## 概述 + +eqpalg 测试采用轻量级自注册框架(`test_harness.h`),无 Boost.Test 依赖。测试入口为 `test_main.cc`,所有 `TEST(name)` 宏定义的用例通过静态构造函数自动注册。 + +**构建与运行**: + +```bash +cd eqpalg/build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j$(nproc) +cd test +./eqpalg_test +``` + +--- + +## 测试文件清单 + +| 文件 | 用例数 | 被测组件 | +|------|--------|---------| +| `test_expression_engine.cc` | 14 | ExpressionEngine — 表达式注册、求值、FunVars 重置 | +| `test_fb_state_machine.cc` | 11 | FbStateMachine — 7 状态反馈状态机 | +| `test_algorithms.cc` | 52 | BoundChecker + 算法核心逻辑 + Roller3/FaultCode 工具函数 | + +**总计**:77 个测试用例 + +--- + +## 一、test_expression_engine.cc(14 用例) + +测试 ExpressionEngine 的核心 API。 + +### 1.1 注册与求值(5 用例) + +| 用例 | 验证内容 | +|------|---------| +| `register_and_evaluate_simple_expression` | `registerExpression` 返回 0,`evaluate` 返回正确数值,`evaluateBool` 返回正确布尔值 | +| `register_duplicate_is_idempotent` | 重复注册同名表达式是幂等操作,保留首次注册的表达式 | +| `evaluate_unregistered_returns_zero` | 未注册表达式名求值返回 0.0 / false | +| `evaluate_zero_expression` | 表达式 `"0"` 求值为 0.0,`evaluateBool` 为 false | +| `evaluate_boolean_true_constant` | 表达式 `"1"` 的 `evaluateBool` 为 true | + +### 1.2 变量变化(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `variable_change_reflected_in_evaluation` | 修改 `mm_vars` 后 `evaluate` 立即反映新值 | + +### 1.3 多表达式并存(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `multiple_expressions_independent` | 3 个表达式独立求值,变量更新后各自正确反映 | + +### 1.4 registerRawExpression(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `register_raw_no_funvars_processing` | `registerRawExpression` 跳过 FunVars 状态函数处理,直接编译原始表达式 | + +### 1.5 FunVars 重置控制(3 用例) + +| 用例 | 验证内容 | +|------|---------| +| `autoReset_does_nothing_when_not_marked` | 未标记时 `autoResetFunVars` 是 no-op | +| `mark_and_autoReset_sequence` | `markFunVarsNeedReset` + `autoResetFunVars` 联合工作,重置后标志清除 | +| `forceReset_works` | `forceResetFunVars` 立即执行,无延迟 | + +### 1.6 复杂表达式(3 用例) + +| 用例 | 验证内容 | +|------|---------| +| `comparison_expression` | 比较表达式 `"tag1 > tag2"`,变量更新后布尔结果正确翻转 | +| `arithmetic_with_comparison` | 算术表达式 `"tag1 * 2 + tag2"` | + +--- + +## 二、test_fb_state_machine.cc(11 用例) + +测试 FbStateMachine 的完整 7 状态生命周期。 + +### 2.1 初始状态(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `initial_state_is_idle` | 新建 FSM 的 `currentState()` 为 `FbState::Idle`,`isActive()` 为 false | + +### 2.2 Idle → Started(2 用例) + +| 用例 | 验证内容 | +|------|---------| +| `idle_to_started_when_triggered` | `actTriggered=true` → `FbState::Started`,`funVarsNeedReset=false` | +| `idle_stays_idle_when_not_triggered` | `actTriggered=false` → 保持 `Idle`,`funVarsNeedReset=true`(空闲时重置) | + +### 2.3 Started → InProgress → Done(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `started_proceeds_to_done_via_feedback` | Started → InProgress(1-2 周期)→ `checkFeedback(true)` → Done → 自动回到 Idle | + +### 2.4 NotHold(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `keep_mode_not_hold_when_trigger_lost` | `keep_mode=true` 时触发条件丢失 → `FbState::NotHold`,`funVarsNeedReset=true` | + +### 2.5 Timeout(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `timeout_when_exceeds_limit` | 超过 `timeout` 时间 → `FbState::Timeout`,`funVarsNeedReset=true` | + +### 2.6 变量快照(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `snapshot_on_action_start` | Started 状态下,`s1`/`s2` 快照正确,`stime` 已设置,`time=0`,`mv2_tagN` 初始化为 0 | + +### 2.7 变量累积(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `action_vars_accumulate_in_progress` | InProgress 状态下,`mv2_tagN` 正确累加,`mx_tagN`/`mi_tagN` 跟踪极值 | + +### 2.8 forceReset(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `force_reset_returns_to_idle` | `forceReset()` → 状态回到 Idle,`isActive()` 变为 false | + +### 2.9 非 keep 模式触发丢失(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `no_keep_mode_trigger_loss_stays_in_progress` | `keep_mode=false` 时触发丢失不退出 InProgress | + +### 2.10 终端状态自动回归(1 用例) + +| 用例 | 验证内容 | +|------|---------| +| `terminal_state_returns_to_idle_next_cycle` | Timeout 后下一周期 `update` 返回 Idle | + +--- + +## 三、test_algorithms.cc(52 用例) + +由于算法子类(LogicAlg/BoundAlg 等)的构造函数依赖 CMemVar 共享内存等运行时基础设施,本测试文件采用**组件组合模拟**策略:不完整构造算法对象,而是测试已提取的独立组件和算法特有逻辑。 + +### 3.1 LogicAlg 核心逻辑(6 用例) + +验证 `LogicAlg::doMonProc()` 使用的表达式求值流程。 + +| 用例 | 验证内容 | +|------|---------| +| `logic_alg_trigger_true_produces_alarm` | 表达式为 true → 应报警 | +| `logic_alg_trigger_false_no_alarm` | 表达式为 false → 不报警 | +| `logic_alg_complex_expression` | `tag1 > 2 && tag2 < 10` 组合条件 | +| `logic_alg_and_short_circuit_false` | AND 条件中一个为 false 整体为 false | +| `logic_alg_or_expression` | OR 条件中一个为 true 整体为 true | +| `logic_alg_negation_operator` | `!tag1` 取反运算符(`!` 是项目新增的 matheval 运算符) | + +### 3.2 BoundChecker 检测器(11 用例) + +验证 `BoundAlg::doMonProc()` 使用的上下限检测逻辑。 + +| 用例 | 验证内容 | +|------|---------| +| `bound_checker_detects_out_of_upper` | 值超上限 → `isOutOfBounds=true` | +| `bound_checker_detects_out_of_lower` | 值超下限 → `isOutOfBounds=true` | +| `bound_checker_only_right_mode_sentinel` | `limit_down=-32768` → 自动推导 `OnlyRight` 模式,仅检测上限 | +| `bound_checker_only_left_mode_sentinel` | `limit_up=32767` → 自动推导 `OnlyLeft` 模式,仅检测下限 | +| `bound_checker_only_left_mode_sentinel_32768` | `limit_up=32768` 同样为哨兵值 | +| `bound_checker_default_bilateral` | 正常上下限 → `Default` 双侧检测 | +| `bound_checker_error_mode` | 双哨兵 `(-32768, -32768)` → `ErrorMode`,永不报警 | +| `bound_checker_exact_boundary_default` | 等于限值的边界值不触发报警 | +| `bound_checker_boundary_only_left` | OnlyLeft 模式下的边界行为 | +| `bound_checker_boundary_only_right` | OnlyRight 模式下的边界行为 | +| `bound_checker_isValid` + `setDetectMode_override` | `isValid()` 查询 + `setDetectMode` 手动覆盖 | + +### 3.3 BoundAlg 过滤表达式(2 用例) + +| 用例 | 验证内容 | +|------|---------| +| `bound_alg_filter_expression` | `exp_feedback_` 过滤条件为 false 时不参与统计,为 true 时参与 | +| `bound_alg_filter_boundary` | 过滤表达式边界值 `>=` 检查 | + +### 3.4 BoundHoldAlg 保持时间(3 用例) + +| 用例 | 验证内容 | +|------|---------| +| `bound_hold_time_logic` | 值需持续超限 `hold_time` 后才报警,`hold_time <= delay_time` 时立即报警 | +| `bound_hold_zero_hold_time` | `hold_time=0` → 立即报警 | +| `bound_hold_long_delay` | 长时间保持(60s),到期前不报警,到期后报警 | + +### 3.5 FeedbackAlg 集成(3 用例) + +验证 ExpressionEngine + FbStateMachine 组合行为。 + +| 用例 | 验证内容 | +|------|---------| +| `feedback_full_flow_start_to_done` | 触发 → Started → InProgress → 等待反馈条件 | +| `feedback_full_flow_not_hold` | keep_mode 下触发丢失 → NotHold,`funVarsNeedReset=true` | +| `feedback_timeout_scenario` | 短超时(100ms)→ Timeout | + +### 3.6 ExpTimes 累积逻辑(4 用例) + +| 用例 | 验证内容 | +|------|---------| +| `exp_times_occurrence_counting_logic` | 6 次触发 → `shear_times=6` > `max_times=5` → 报警 | +| `exp_times_occurrence_below_threshold` | 3 次触发 → 低于阈值 → 不报警 | +| `exp_times_time_accumulation_logic` | 时间累积:5 秒 → 转换为小时(5000/3600000) | +| `exp_times_time_multiple_intervals` | 多段时间累积:3s + 7s = 10s | + +### 3.7 FaultCode bit 提取(6 用例) + +| 用例 | 验证内容 | +|------|---------| +| `fault_code_gitbit_extraction` | 基本位提取:bit 3 of 8 = 1,bit 0 of 8 = 0 | +| `fault_code_bit_parsing_all_bits` | 0x0F → 4 个置位 bit | +| `fault_code_bit_15_set` | bit 15(0x8000)单独置位 | +| `fault_code_all_bits_set_16bit` | 0xFFFF → 16 位全置位 | +| `fault_code_single_bit_each_position` | 每个 bit 位置独立验证 | +| `fault_code_gitbit_with_different_types` | `unsigned int`/`short`/`long` 类型兼容性 | + +### 3.8 Roller3 工具函数(14 用例) + +#### extractTagNumbers(7 用例) + +验证从 `"tag1+tag2+tag3"` 表达式中提取 tag 序号。 + +| 用例 | 验证内容 | +|------|---------| +| `roller3_extract_tag_numbers` | `"tag1+tag2+tag3"` → `[1, 2, 3]` | +| `roller3_extract_tag_numbers_single` | `"tag5"` → `[5]` | +| `roller3_extract_tag_numbers_complex` | `"tag10+tag2+tag35"` → 包含 10, 2, 35 | +| `roller3_extract_empty_string` | 空字符串 → 空结果 | +| `roller3_extract_no_tags` | 无 tag 前缀 → 空结果 | +| `roller3_extract_tag_numbers_with_prefix` | `"metatag1+tag2"` → 提取 `tag1` 和 `tag2`(子串匹配特性) | +| `roller3_extract_large_tag_numbers` | `"tag999+tag1000"` → `[999, 1000]` | + +#### calculateMedian(7 用例) + +验证中位数计算。 + +| 用例 | 验证内容 | +|------|---------| +| `roller3_calculate_median_odd` | 奇数元素:`[1,3,5]` → 3.0,`[10,2,8]` → 8.0(内部排序) | +| `roller3_calculate_median_even` | 偶数元素:`[1,3,5,7]` → 4.0,`[1,2]` → 1.5 | +| `roller3_calculate_median_single` | 单元素:`[42]` → 42.0 | +| `roller3_calculate_median_empty` | 空数组 → 0.0 | +| `roller3_calculate_median_negative_values` | 负数:`[-5,-1,-3]` → -3.0 | +| `roller3_calculate_median_large_dataset` | 大数据集:`[1..99]` → 50.0 | +| `roller3_calculate_median_unsorted_preserves_order` | 乱序输入 → 内部排序后正确 | + +### 3.9 JSON 配置校验(5 用例) + +验证各算法类型的最小合法 JSON 结构完整性。 + +| 用例 | 验证内容 | +|------|---------| +| `json_config_logic_alg_structure` | LogicAlg JSON 包含 tags/trigger/function/output | +| `json_config_bound_alg_structure` | BoundAlg JSON 包含 limit_down/limit_up | +| `json_config_feedback_logic_structure` | FeedbackAlg 逻辑型 JSON 包含 action_start/action_end/timeout | +| `json_config_feedback_bound_structure` | FeedbackAlg 上下限型 JSON 包含 bound params | +| `json_config_bound_hold_structure` | BoundHoldAlg JSON 包含 hold_time | + +--- + +## 测试框架说明 + +### 宏 + +| 宏 | 用途 | +|----|------| +| `TEST(name)` | 定义一个测试用例,自动注册到 TestRunner | +| `CHECK(expr)` | 断言 expr 为 true | +| `CHECK_EQ(a, b)` | 断言 a == b | +| `CHECK_FLOAT_EQ(a, b, eps)` | 断言 `abs(a-b) <= eps` | +| `CHECK_THROWS(expr)` | 断言 expr 抛出异常 | + +### 运行指定测试 + +测试按文件编译为独立可执行文件 `eqpalg_test`,所有用例顺序执行,输出 PASSED/FAILED 及汇总。 + +--- + +## 已知限制 + +1. **算法完整构造测试**:LogicAlg/BoundAlg/FeedbackAlg 等算法子类的构造函数依赖 `CMemVar::Const()` 共享内存全局变量,无法在开发机上进行完整的端到端测试。当前测试策略是通过组件组合(ExpressionEngine + FbStateMachine + BoundChecker)模拟 `doMonProc()` 的控制流。 + +2. **IHDB 依赖算法**:TrendSlope2/3、Roller、GlitchDetection 等算法依赖 iHyperDB 查询,其 `exec_mon()` 无法在纯单元测试中运行。这些算法的核心逻辑(斜率计算、中位数离群检测、bit 提取)已在上面的测试中覆盖。 + +3. **DB2 依赖**:StatCollector、FaultCode 的数据库操作部分需要 DB2 连接,未在单元测试中覆盖。