#!/usr/bin/env python3 # -*- coding: utf-8 -*- """开机账本(boot_stats)的主机侧纯逻辑单元测试运行器(纯标准库)。 做什么: 用本机 MSVC 编译 test/host/boot_stats/boot_stats_test.c(以 #include 纳入**生产源** App/util/boot_stats.c),在 PC 上离线验证上电次数的**四态判定**(不可信 / 首次启用 / 延续 / 电池域掉过电)、记录校验与格式版本、I2C 读失败时**绝不回写**、写失败时 "少一笔账"的记号、幂等、饱和,以及复位原因优先级链的唯一出处。 工具链与判定委托 test/host/common/unit_test_harness.py。 为什么要主机侧单测(而不是只靠真机): 这一格数字的唯一用途是告诉人"这台装置上了多少次电",而它错的时候**永远不像错**: 1. I2C 读失败若当成"没有记录",计数会在某次通信抖动后悄悄从 1 重来——屏幕上 是个很像样的小数字,没有任何一行日志会说它假; 2. 读失败若还顺手回写,就把可能完好的旧记录**覆盖掉**,故障从"读不到"升级成 "永久少账"; 3. "第一次上电"与"RTC 掉过电"合并成一个状态,就等于拿前者冒充后者(文案会写错 整整一格的口径); 4. 只信 magic 不信校验:电池坏区常是整片全 0 / 全 FF(magic 那关自然过不去), 但更常见的"个别字节翻转"里 magic 可能仍然好,次数会错 256 倍。 这些都要能把 SRAM 内容、BLF、I2C 错误码逐个构造出来才测得到;真机上跑一次 只覆盖"一切正常"那一条。桩边界见下面的注释;上板另有一组判据(断电 ≥5 min 再 上电 +1、换电池后落 RESTARTED),见 task-12 卡片,主机测不出来。 桩的边界: - stub/main.h:只给 RCC_CSR 六个复位标志位掩码(位号照抄 CMSIS,见该文件注释); 真实 main.h 拉整棵 HAL 头树,PC 上编不动。 - sd2506.h / bsp_err.h / boot_stats.h / app_main.h 一律用**真实头**(它们不依赖 HAL 类型);被测源用到的三个 SD2506 函数 sd2506_read_sram / sd2506_write_sram / sd2506_read_ctr1 由本测试单元实现为 可注入失败的假件(含一片 70 B 假 SRAM,地址越界按真驱动语义返 BSP_ERR_PARAM)。 - g_rcc_csr_latched 的真实定义在 App/app/app_main.c(本套件不编译它), 由本测试单元给出。 用法(仓库根目录): python test/host/boot_stats/run_unit_test.py 退出码:0=PASS / 1=测试失败 / 2=环境错误。 设计依据:App/util/boot_stats.h、docs/任务规划/UI补齐.md §2(运行报告四个数的口径) 创建日期:2026-10-27(道G task-12) """ import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE))) sys.path.insert(0, os.path.join(ROOT, "test", "host", "common")) from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402 # 顺序敏感:stub 必须排在最前,才能用桩 main.h 覆盖真实 main.h。 BUILD = TestBuild( name="Boot Stats Tests", src=os.path.join(HERE, "boot_stats_test.c"), include_dirs=[ os.path.join(HERE, "stub"), os.path.join(ROOT, "App", "util"), os.path.join(ROOT, "App", "app"), os.path.join(ROOT, "Drivers", "BSP"), os.path.join(ROOT, "Drivers", "BSP", "SD2506"), HERE, ], summary_marker="=== Boot Stats Tests:", ) if __name__ == "__main__": sys.exit(run_host_unit_test(BUILD))