| @@ -2,6 +2,7 @@ | |||||
| - 项目作者:`suyu` | - 项目作者:`suyu` | ||||
| - 文件、终端输出和源代码统一使用 UTF-8 | - 文件、终端输出和源代码统一使用 UTF-8 | ||||
| - 答复我的文本要说人话,口语化答复我 | |||||
| ## 新会话阅读顺序 | ## 新会话阅读顺序 | ||||
| @@ -24,6 +25,7 @@ | |||||
| - 数量边界最终规则:`docs/用户使用/数量边界确认方案.md` | - 数量边界最终规则:`docs/用户使用/数量边界确认方案.md` | ||||
| - 工程 JSON 格式:`docs/工程格式说明.md` | - 工程 JSON 格式:`docs/工程格式说明.md` | ||||
| - 开发阶段顺序:`docs/开发顺序.md`(阶段参考,当前状态以 handoff 为准) | - 开发阶段顺序:`docs/开发顺序.md`(阶段参考,当前状态以 handoff 为准) | ||||
| - 测试约定:`docs/测试约定.md` | |||||
| - XDH-60T4-E 硬件接线:`docs/XDH-60T4-E硬件与接线要点.md` | - XDH-60T4-E 硬件接线:`docs/XDH-60T4-E硬件与接线要点.md` | ||||
| - XDH-60T4-E 指令与 Modbus:`docs/XDH-60T4-E指令与Modbus要点.md` | - XDH-60T4-E 指令与 Modbus:`docs/XDH-60T4-E指令与Modbus要点.md` | ||||
| - PLC 官方手册:`docs/pdf/` | - PLC 官方手册:`docs/pdf/` | ||||
| @@ -8,6 +8,7 @@ | |||||
| - 核心软件闭环已经可用:HMI 编辑、结构化梯形图、离线仿真、工程 JSON、统一运行监控大屏、自由监控和 Modbus RTU 真机读写均已实现 | - 核心软件闭环已经可用:HMI 编辑、结构化梯形图、离线仿真、工程 JSON、统一运行监控大屏、自由监控和 Modbus RTU 真机读写均已实现 | ||||
| - HMI 外观属性和梯形图十列可选插入网格均已按逻辑完成提交;开始工作前仍需先看 `git status` 和相关 diff,不要重置来源不明的改动 | - HMI 外观属性和梯形图十列可选插入网格均已按逻辑完成提交;开始工作前仍需先看 `git status` 和相关 diff,不要重置来源不明的改动 | ||||
| - `docs/二次开发/` 是已有未跟踪资料,不能因当前任务删除 | - `docs/二次开发/` 是已有未跟踪资料,不能因当前任务删除 | ||||
| - 测试体系已建立统一 `app/tests/tests.pro` 聚合入口、分层 `.pri` 清单、公共 `TestSupport` fixture 和独立 `performance_tests` 目标;长期测试规则见 `docs/测试约定.md` | |||||
| - 当前没有代码阻塞;实际设备 RUN 联动和离线演示均已完成,下一步是最终交付资料整理 | - 当前没有代码阻塞;实际设备 RUN 联动和离线演示均已完成,下一步是最终交付资料整理 | ||||
| ## 已实现能力 | ## 已实现能力 | ||||
| @@ -31,6 +32,7 @@ | |||||
| ## 最近完成 | ## 最近完成 | ||||
| - 修复梯形图编辑失败回滚后错误保留工程脏标记的问题:回滚同时恢复工程内容和操作前的 `modified_` 状态;保存后超出十列的条件插入不会再触发退出保存提示,已有未保存修改也不会被失败操作清除 | |||||
| - 根据编程器面向设备交付、调试和验证工程师的需求定位,不恢复旧的纯 HMI 操作员窗口;新增职责明确的 `RuntimeMonitorWindow`,承载离线和真机共用的完整运行监控大屏 | - 根据编程器面向设备交付、调试和验证工程师的需求定位,不恢复旧的纯 HMI 操作员窗口;新增职责明确的 `RuntimeMonitorWindow`,承载离线和真机共用的完整运行监控大屏 | ||||
| - 主窗口已移除运行监控页签和运行时布局切换;点击离线或真机运行会弹出最大化监控窗口,右上角返回、关闭弹窗和应用退出分别按统一生命周期处理,且全应用只创建一份 `RuntimeMonitorWidget` | - 主窗口已移除运行监控页签和运行时布局切换;点击离线或真机运行会弹出最大化监控窗口,右上角返回、关闭弹窗和应用退出分别按统一生命周期处理,且全应用只创建一份 `RuntimeMonitorWidget` | ||||
| - CTU/CTD 输出节点保持现有梯形图网格宽度,将 CV 和 PV 拆成带水平内边距的两行显示,避免 `D4000`、`32767` 等边界配置在右侧电源轨处被裁剪 | - CTU/CTD 输出节点保持现有梯形图网格宽度,将 CV 和 PV 拆成带水平内边距的两行显示,避免 `D4000`、`32767` 等边界配置在右侧电源轨处被裁剪 | ||||
| @@ -46,11 +48,14 @@ | |||||
| ## 最近验证 | ## 最近验证 | ||||
| - 脏标记回滚修复已通过独立 Release 构建的 `logic_editor_service_tests`、`project_management_tests` 和 `main_window_tests`;新增覆盖保存后第 11 个条件插入失败以及已有脏状态下失败操作的回归测试;Qt Release 主程序重新构建成功 | |||||
| - 独立运行监控窗口改动已通过重新生成 qmake 构建后的 `main_window_tests`、`hmi_editor_service_tests`、`offline_simulation_service_tests`、`runtime_mode_service_tests` 和 `plc_runtime_tests`;主窗口测试覆盖最大化弹窗、唯一监控实例、主窗口布局不变、离线与模拟真机 HMI 写入、页面跳转、返回编辑、关闭弹窗、通信故障隐藏、重复进入刷新和应用退出 | - 独立运行监控窗口改动已通过重新生成 qmake 构建后的 `main_window_tests`、`hmi_editor_service_tests`、`offline_simulation_service_tests`、`runtime_mode_service_tests` 和 `plc_runtime_tests`;主窗口测试覆盖最大化弹窗、唯一监控实例、主窗口布局不变、离线与模拟真机 HMI 写入、页面跳转、返回编辑、关闭弹窗、通信故障隐藏、重复进入刷新和应用退出 | ||||
| - 独立运行监控窗口改动后的 Qt Release 主程序已在 `build/runtime-monitor-window-release` 完整构建并链接成功 | - 独立运行监控窗口改动后的 Qt Release 主程序已在 `build/runtime-monitor-window-release` 完整构建并链接成功 | ||||
| - 2026-08-20 使用 `COM3 / 9600 / 8E1 / 站号 1` 完成真实 PLC 安全读写:读取 `D4000=0`,临时写入并读回 `1`,随后恢复并读回 `0`;没有下载 PLC 程序,也没有执行切换 RUN 的动作 | - 2026-08-20 使用 `COM3 / 9600 / 8E1 / 站号 1` 完成真实 PLC 安全读写:读取 `D4000=0`,临时写入并读回 `1`,随后恢复并读回 `0`;没有下载 PLC 程序,也没有执行切换 RUN 的动作 | ||||
| - 2026-08-21 用户使用 `json/motor_forward_reverse.json` 在已连接的真实 PLC 上完成 RUN 联动手动验收:通过运行监控 HMI 操作正转启动、反转启动和停止,并确认对应运行状态反馈正常;该项验证覆盖实际 PLC 内部程序、接线和现场负载,不由自动化替身测试替代 | - 2026-08-21 用户使用 `json/motor_forward_reverse.json` 在已连接的真实 PLC 上完成 RUN 联动手动验收:通过运行监控 HMI 操作正转启动、反转启动和停止,并确认对应运行状态反馈正常;该项验证覆盖实际 PLC 内部程序、接线和现场负载,不由自动化替身测试替代 | ||||
| - 2026-08-21 在 Qt/MinGW 运行库 PATH 正确的独立环境中重新执行 10 个 Release 测试目标,全部通过:`alarm_service_tests`、`domain_tests`、`hmi_editor_service_tests`、`logic_editor_service_tests`、`main_window_tests`、`offline_simulation_service_tests`、`plc_runtime_tests`、`project_management_tests`、`register_monitor_service_tests`、`runtime_mode_service_tests` | - 2026-08-21 在 Qt/MinGW 运行库 PATH 正确的独立环境中重新执行 10 个 Release 测试目标,全部通过:`alarm_service_tests`、`domain_tests`、`hmi_editor_service_tests`、`logic_editor_service_tests`、`main_window_tests`、`offline_simulation_service_tests`、`plc_runtime_tests`、`project_management_tests`、`register_monitor_service_tests`、`runtime_mode_service_tests` | ||||
| - 2026-08-21 使用 `scripts/run_qt_tests.ps1 -Configuration Release` 重新构建并运行上述 10 个功能目标及独立 `performance_tests`;功能回归全部通过,性能目标包含 100 个控制逻辑扫描和 4001 个 M 寄存器访问基准 | |||||
| - 2026-08-21 在 `build/tests_aggregate` 对 `app/tests/tests.pro` 执行完整 Release 聚合构建,11 个测试目标(10 个功能目标和 `performance_tests`)全部成功生成可执行文件 | |||||
| - CTU/CTD 显示修复已通过 `main_window_tests`(`QT_QPA_PLATFORM=offscreen`)和 Qt Release 主程序重新构建;改动仅涉及梯形图 UI 投影,因此未连接真实 PLC | - CTU/CTD 显示修复已通过 `main_window_tests`(`QT_QPA_PLATFORM=offscreen`)和 Qt Release 主程序重新构建;改动仅涉及梯形图 UI 投影,因此未连接真实 PLC | ||||
| - 报警列表和分页改动已通过 `domain_tests`、`alarm_service_tests`、`hmi_editor_service_tests`、`project_management_tests` 和 `main_window_tests`;覆盖输入长度、默认五行尺寸、默认字号、运行态高度收缩、六条报警翻页和跨页确认 | - 报警列表和分页改动已通过 `domain_tests`、`alarm_service_tests`、`hmi_editor_service_tests`、`project_management_tests` 和 `main_window_tests`;覆盖输入长度、默认五行尺寸、默认字号、运行态高度收缩、六条报警翻页和跨页确认 | ||||
| - 报警列表改动后的 Qt Release 主程序已重新构建并链接成功;改动不涉及 PLC 通信、Modbus 地址或运行模式,因此未连接真实 PLC | - 报警列表改动后的 Qt Release 主程序已重新构建并链接成功;改动不涉及 PLC 通信、Modbus 地址或运行模式,因此未连接真实 PLC | ||||
| @@ -0,0 +1,118 @@ | |||||
| # 测试约定 | |||||
| 本文件是测试代码的长期约束。新增或修改测试时,先判断被验证的契约、所属生产层和运行环境,再决定测试文件和测试目标。测试数量不是质量指标,重复断言也不是覆盖率;每个测试必须能回答一个明确的失败问题。 | |||||
| ## 测试目标 | |||||
| 项目测试分为四种用途,不能互相替代: | |||||
| | 用途 | 证明什么 | 是否进入普通自动化回归 | | |||||
| | --- | --- | --- | | |||||
| | 功能测试 | 业务规则、服务用例和用户工作流结果正确 | 是 | | |||||
| | 性能测试 | 在固定代表性规模下满足时间、吞吐或资源预算 | 单独运行 | | |||||
| | 真机/HIL 测试 | 串口、PLC 响应、接线和现场联动真实有效 | 手工执行 | | |||||
| | 稳定性/故障测试 | 断线、超时、非法输入和恢复状态可控 | 是;真实设备部分除外 | | |||||
| 功能测试按生产边界分层: | |||||
| ```text | |||||
| Domain -> Services -> Infrastructure -> UI -> E2E | |||||
| ``` | |||||
| - `Domain`:纯模型和值对象规则,不依赖 Qt Widgets、文件系统或串口 | |||||
| - `Services`:业务用例、原子编辑、撤销重做、离线扫描和运行模式,使用内存仓库或 Fake | |||||
| - `Infrastructure`:JSON 文件、PLC 缓存、Modbus 地址规划和错误分类 | |||||
| - `UI`:Qt Widgets 事件、动作、投影和窗口生命周期,使用 `QT_QPA_PLATFORM=offscreen` | |||||
| - `E2E`:少量跨层用户主流程,不重复覆盖每一条领域边界 | |||||
| 当前目标映射如下: | |||||
| | 测试目标 | 所属层 | 主要契约 | | |||||
| | --- | --- | --- | | |||||
| | `domain_tests` | Domain | 地址、模型、边界和运行前校验 | | |||||
| | `alarm_service_tests` | Services | 报警编辑和运行记录生命周期 | | |||||
| | `hmi_editor_service_tests` | Services | HMI 编辑、原子删除、历史和导航 | | |||||
| | `logic_editor_service_tests` | Services | 结构化梯形图编辑、归一化和历史 | | |||||
| | `offline_simulation_service_tests` | Services | 扫描语义、定时器、计数器和故障 | | |||||
| | `runtime_mode_service_tests` | Services | 编辑/离线/真机状态转换和准入条件 | | |||||
| | `register_monitor_service_tests` | Services | 监视地址、读写和活动仓库 | | |||||
| | `project_management_tests` | Infrastructure + Services | JSON 往返、非法文件和工程保存状态 | | |||||
| | `plc_runtime_tests` | Infrastructure + Services | PLC 缓存、轮询约束、错误恢复和 Fake gateway | | |||||
| | `main_window_tests` | UI | 编辑工作流、运行监控窗口和 UI 投影 | | |||||
| | `performance_tests` | Performance | 代表性梯形图扫描和寄存器访问基准 | | |||||
| ## 防止冗余 | |||||
| 同一业务场景可以在多层出现,但断言必须不同: | |||||
| - Domain 断言“什么配置合法” | |||||
| - Service 断言“用例如何改变模型或寄存器” | |||||
| - UI 断言“用户操作是否调用正确用例并显示结果” | |||||
| - 真机断言“设备实际响应和现场动作是否正确” | |||||
| 如果两个测试的输入、执行路径和断言都相同,只保留更低层、更快且失败定位更清晰的那个。UI 测试不得重新复制领域边界矩阵;真机测试不得把软件执行器的内部语义再测一遍。 | |||||
| 每个生产契约至少有一个主要测试负责人。跨层回归只保留一条关键链路,例如“运行监控按钮 -> 活动仓库 -> Fake PLC gateway”,不要在每个窗口测试中重复构造完整 PLC 状态机。 | |||||
| ## 测试代码规范 | |||||
| - 测试名称使用“对象_行为_结果”语义,例如 `project_rejectsDuplicatePageIds` | |||||
| - 一个测试函数只验证一个行为主题;多个边界属于同一规则时可以放在同一函数 | |||||
| - 失败消息必须说明预期和业务含义,不能只写 `failed` | |||||
| - Fixture 放在 `app/tests/support/`;只有语义确实不同的 Fake 才允许留在测试文件内 | |||||
| - `TestSupport::require` 和 `TestSupport::InMemoryProjectStorage` 是通用基础设施,不要重新定义同名版本 | |||||
| - 测试不得修改 `app/src` 生产代码来迁就断言;发现难以测试时,优先检查服务契约和依赖注入边界 | |||||
| - 测试不依赖测试执行顺序;每个函数建立自己的仓库、服务和工程状态 | |||||
| - 测试使用 UTF-8;中文错误消息和工程样例必须按 UTF-8 读取 | |||||
| 当前历史测试仍使用轻量自定义 `main()`,后续新增测试优先使用 Qt Test:`QTEST_APPLESS_MAIN` 或 `QTEST_MAIN`、`private slots`、`QVERIFY/QCOMPARE` 和 `QBENCHMARK`。迁移旧测试时按测试目标逐步进行,不为了换框架重写无关业务覆盖。 | |||||
| ## qmake 组织 | |||||
| - `app/tests/tests.pro` 是所有自动化目标的聚合入口 | |||||
| - `app/tests/pri/test_defaults.pri` 只放公共 qmake 配置 | |||||
| - `app/tests/pri/test_layers.pri` 只放按生产层分组的源码清单 | |||||
| - 测试 `.pro` 只选择自己需要的层变量,不得重新手写重复的 `../src` 清单 | |||||
| - 不把所有生产源码放入一个“大而全”测试库;测试目标应能反映真实依赖边界 | |||||
| - 构建输出放在 `build/`,不能写入 `app/` | |||||
| 统一运行: | |||||
| ```powershell | |||||
| pwsh -NoLogo -NoProfile -File .\scripts\run_qt_tests.ps1 -Configuration Release | |||||
| ``` | |||||
| 只构建聚合目标而不运行时,可在独立构建目录对 `app/tests/tests.pro` 执行 qmake 和 `mingw32-make`。UI 测试必须设置 `QT_QPA_PLATFORM=offscreen`。 | |||||
| ## 性能测试 | |||||
| 性能测试必须单独命名为 `*_performance` 或 `performance_tests`,使用固定规模输入和 `QBENCHMARK`。每个基准需要说明: | |||||
| - 输入规模,例如 100 个控制逻辑、4001 个寄存器 | |||||
| - 测量对象,例如一次扫描或一次完整读写遍历 | |||||
| - 不纳入测量的准备工作 | |||||
| - 结果用途,是观察趋势还是有明确预算 | |||||
| 性能基准不是功能断言,不能因为机器差异设置脆弱的毫秒硬阈值。功能前置校验可以使用 `QVERIFY`,但基准循环只测目标操作。 | |||||
| ## 真机/HIL 测试 | |||||
| 真实 PLC 测试不放入普通 `tests.pro` 的自动执行流程,也不使用自动化 Fake 测试冒充真机结论。真机测试必须记录: | |||||
| - `COM3 / 9600 / 8E1 / 站号 1` 或实际批准的配置 | |||||
| - 测试前读取的原值、测试动作、读回结果和恢复结果 | |||||
| - STOP/RUN 状态、接线和现场负载条件 | |||||
| - 测试是否涉及 PLC 内部程序;本项目不下载或生成 PLC 程序 | |||||
| 通信安全验证先读原值,测试后恢复并再次读回。未经确认接线和地址,不执行 RUN 联动。真机记录属于验收证据,不应把设备状态写进可重复的单元测试。 | |||||
| ## 修改测试的决策流程 | |||||
| 1. 写出要保护的业务契约和失败风险 | |||||
| 2. 选择能以最低环境成本证明契约的层 | |||||
| 3. 搜索现有测试,确认不是已有相同断言 | |||||
| 4. 复用 `support/` fixture 和 `.pri` 层变量 | |||||
| 5. 运行受影响目标,再运行完整自动化集合 | |||||
| 6. 涉及 PLC 通信、运行模式或 UI 工作流时扩大回归,并在 `docs/ai/handoff.md` 记录未做真机测试的原因 | |||||
| 提交前至少完成对应自动化测试、Release 构建和 `git diff --check`。性能结果和真机记录不替代功能测试结果,三者必须分别报告。 | |||||