eis/eqpalg/test/README.md

296 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.cc14 用例)
测试 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 registerRawExpression1 用例)
| 用例 | 验证内容 |
|------|---------|
| `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.cc11 用例)
测试 FbStateMachine 的完整 7 状态生命周期。
### 2.1 初始状态1 用例)
| 用例 | 验证内容 |
|------|---------|
| `initial_state_is_idle` | 新建 FSM 的 `currentState()``FbState::Idle``isActive()` 为 false |
### 2.2 Idle → Started2 用例)
| 用例 | 验证内容 |
|------|---------|
| `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 → Done1 用例)
| 用例 | 验证内容 |
|------|---------|
| `started_proceeds_to_done_via_feedback` | Started → InProgress1-2 周期)→ `checkFeedback(true)` → Done → 自动回到 Idle |
### 2.4 NotHold1 用例)
| 用例 | 验证内容 |
|------|---------|
| `keep_mode_not_hold_when_trigger_lost` | `keep_mode=true` 时触发条件丢失 → `FbState::NotHold``funVarsNeedReset=true` |
### 2.5 Timeout1 用例)
| 用例 | 验证内容 |
|------|---------|
| `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 forceReset1 用例)
| 用例 | 验证内容 |
|------|---------|
| `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.cc52 用例)
由于算法子类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 = 1bit 0 of 8 = 0 |
| `fault_code_bit_parsing_all_bits` | 0x0F → 4 个置位 bit |
| `fault_code_bit_15_set` | bit 150x8000单独置位 |
| `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 用例)
#### extractTagNumbers7 用例)
验证从 `"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]` |
#### calculateMedian7 用例)
验证中位数计算。
| 用例 | 验证内容 |
|------|---------|
| `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 连接,未在单元测试中覆盖。