From 51c5707f49d0bbd74f61e4d573ee30897d714c26 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Mon, 21 Sep 2026 03:22:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=A5=97=E4=BB=B6=E5=A2=9E?= =?UTF-8?q?=E8=AE=BE=E7=A0=B4=E5=9D=8F=E6=80=A7=E7=94=A8=E4=BE=8B=E6=80=BB?= =?UTF-8?q?=E5=BC=80=E5=85=B3=EF=BC=88=E9=BB=98=E8=AE=A4=E5=8F=AA=E8=AF=BB?= =?UTF-8?q?=EF=BC=89=EF=BC=8CGD5F/=E5=AD=98=E5=82=A8/RTC=20=E7=A0=B4?= =?UTF-8?q?=E5=9D=8F=E6=80=A7=E7=94=A8=E4=BE=8B=E6=94=B9=E4=B8=BA=E6=8C=89?= =?UTF-8?q?=E9=9C=80=E5=BC=80=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- docs/内存管理/内存内容分配策略.md | 6 +- docs/测试说明/GD5F2GQ5UE_Test_Guide.md | 46 ++++++-- docs/测试说明/SD2506_Test_Guide.md | 7 ++ docs/测试说明/自动化测试流程.md | 5 +- test/gd5f_test_task.c | 152 +++++++++++++++---------- test/sd2506_test_task.c | 100 ++++++++++------ test/storage_test_task.c | 78 ++++++++----- test/test_config.h | 40 +++++-- 9 files changed, 287 insertions(+), 149 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 735cd06c..283be213 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -84,7 +84,7 @@ STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2);**仅 Keil MDK-ARM v - 所有 `net_*` socket 操作经 `netMsgQueue` 消息队列在 netTask 串行执行(线程安全);非 netTask 任务直接调 `net_*` API,勿直接碰 CH395F 驱动 - 网口配置:`/config.ini` [network] 段(工厂默认 192.168.1.100/24、gw .1;MAC 全零 = 芯片内置;UI 保存立即生效;旧档 `/cfg/netcfg.bin` 首启自动迁移)→ `docs/网络配置说明.md` - **新 .c 文件必须手动注册到两处**才能双端编译:`MDK-ARM/STM32F407-Demo.uvprojx` + `simulator/CMakeLists.txt`(用 `new-source-file` skill) -- 测试构建:`test/test_config.h` 的 `TEST_SUITE_CH395F/STORAGE/GD5F/RTC` **互斥**(一次只能启用一个,`#error`);全部注释 = 产品构建;CH395F 套件 `test/ch395f_test.h`:硬件层 Phase 1~5 与 NET 层 Phase 6~11 互斥;CH395F 硬件层模式下 netTask(基础线程:无条件创建、全构建唯一实例)入口自挂起(不运行),testTask 独占 CH395F +- 测试构建:`test/test_config.h` 的 `TEST_SUITE_CH395F/STORAGE/GD5F/RTC` **互斥**(一次只能启用一个,`#error`);全部注释 = 产品构建;**破坏性用例默认关闭**:同文件 `ENABLE_DESTRUCTIVE_TESTS`(默认注释)控制写/擦 NAND、格式化、坏块注入与 FatFS 写类用例——关闭时套件为**只读模式**(开机横幅 `mode: READ-ONLY`,可随时在在用设备上跑、不动卷内容与容量),只在**产品首次上电/产线首启**或明确需要时打开(`mode: DESTRUCTIVE`);2026-09-21 实测一轮破坏性 GD5F 运行即消耗一个块(容量 95248 → 95200,TC-STO-04 恢复不回来);CH395F 套件 `test/ch395f_test.h`:硬件层 Phase 1~5 与 NET 层 Phase 6~11 互斥;CH395F 硬件层模式下 netTask(基础线程:无条件创建、全构建唯一实例)入口自挂起(不运行),testTask 独占 CH395F - **改驱动前先读对应 `docs/问题记录/*_Trap_Records.md`**(CH395F / GD5F2GQ5UE / SD2506);各模块测试指南见 `docs/测试说明/` ## 串口助手 serial_tool diff --git a/docs/内存管理/内存内容分配策略.md b/docs/内存管理/内存内容分配策略.md index 847255b4..0402fe8e 100644 --- a/docs/内存管理/内存内容分配策略.md +++ b/docs/内存管理/内存内容分配策略.md @@ -125,10 +125,10 @@ LVGL 集合明细(均在 SRAM):LVGL 池 40,960B(`lv_mem_core_builtin.o` 1. `@build`(0 错误 0 警告) 2. 产品构建 `@test`(`test/run_boot_test.py`):标记校验 PASS,且**零 `[ERR]`**(默认 Forbidden 已含 `[ERR]`) -3. 存储套件(`test_config.h` → `TEST_SUITE_STORAGE`):期望 **20/20 PASSED** -4. GD5F 套件(`TEST_SUITE_GD5F`):期望 **68/68 PASSED**,并确认日志出现 2 条守卫负路径 `[ERR]`(TC-GD5F-704/705)——套件运行须放宽 Forbidden(`--forbidden "FAIL;HardFault"`) +3. 存储套件(`test_config.h` → `TEST_SUITE_STORAGE`):只读模式实测 **3/3 PASSED**(破坏性关,卷内容不变);开启 `ENABLE_DESTRUCTIVE_TESTS` 后为全量集成用例(2026-09-20 实测 **20/20 PASSED**,含 TC-STO-01/02/03/05/06) +4. GD5F 套件(`TEST_SUITE_GD5F`):只读模式实测 **16/16 PASSED**(含只读的守卫负路径 TC-GD5F-704);开启 `ENABLE_DESTRUCTIVE_TESTS` 后 **68/68 PASSED**,并确认日志出现 2 条守卫负路径 `[ERR]`(TC-GD5F-704/705)——破坏性模式运行须放宽 Forbidden(`--forbidden "FAIL;HardFault"`) 5. 恢复跑(`ENABLE_STORAGE_RECOVERY_TESTS`):期望 **3/3 PASSED** -6. 套件**不得**在未做恢复的情况下连续重复运行:TC-STO-03 与 GD5F 套件的大写入都会累积运行期坏块(2026-09-21 实测一次 GD5F 整轮运行容量 95248 → 95200,且阶段 8 会 `nand_ftl_format` 清空卷)。注意 §7.5 的恢复跑只重建 BBT + 重新格式化,**不恢复容量**(实测恢复后仍 95200,断电重扫亦然——被排除的块会被 dhara 每次扫描重新判定为坏);容量基线以 95200 pages 为准。 +6. **破坏性用例默认关闭**(见 `test_config.h` 的 `ENABLE_DESTRUCTIVE_TESTS`):写/擦 NAND、格式化、坏块注入与 FatFS 写类用例只在首次上电/产线首启或明确需要时开启——2026-09-21 实测一轮破坏性 GD5F 运行即消耗一个块(容量 95248 → 95200,TC-STO-04 恢复不回来);日常回归请保持只读模式 通过全部关卡后:更新 §6 台账、`AGENTS.md` 栈表、`.sct` 头注释,并把本次裁决补入 §5。 diff --git a/docs/测试说明/GD5F2GQ5UE_Test_Guide.md b/docs/测试说明/GD5F2GQ5UE_Test_Guide.md index 719045a3..9396df3d 100644 --- a/docs/测试说明/GD5F2GQ5UE_Test_Guide.md +++ b/docs/测试说明/GD5F2GQ5UE_Test_Guide.md @@ -53,27 +53,40 @@ ECC 状态(`ECCS1:ECCS0`):`00`=无错;`01/10/11`=纠正 1/2/3 bit;`10` 所有测试套件(CH395F / 存储 / GD5F)统一收归 `ch395fTestTask` 线程(`test/ch395f_test_task.c` 的 `StartCh395fTestTask`),通过 `test/test_config.h` 中的 `TEST_SUITE_*` 宏在**编译期**选择要跑的套件。 -选中 `TEST_SUITE_GD5F` 时,`test_config.h` 会自动 `#define ENABLE_GD5F_TESTS` 及全部 -`ENABLE_GD5F_PHASE*_TESTS`,随后由 `gd5f_test_run()`(`test/gd5f_test_task.c`)按阶段串行执行。 +选中 `TEST_SUITE_GD5F` 时,`test_config.h` 会自动 `#define ENABLE_GD5F_TESTS` 及**只读阶段**宏 +(PHASE1/2/10),随后由 `gd5f_test_run()`(`test/gd5f_test_task.c`)按阶段串行执行。 -> `defaultTask` **不再承担任何测试**(仅空闲);所有测试都只在 `ch395fTestTask` 中运行。 +> **破坏性用例开关(2026-09-21 新增,默认关闭)**:`test_config.h` 的 `ENABLE_DESTRUCTIVE_TESTS` +> 控制所有**会改变设备持久状态**的用例——写/擦 NAND、`nand_ftl_format`、FatFS 建文件写文件、 +> 向 BBT 持久化坏块标记(永久减少可用容量)。关闭时为**只读模式**,可随时在在用设备上跑; +> 打开后才编译并运行 PHASE3~9,以及 PHASE2 的 TC-206/207、PHASE10 的 TC-1003/1004。 +> 依据:2026-09-21 实测一轮破坏性 GD5F 运行即消耗一个块(容量 95248 → 95200,TC-STO-04 恢复不回来), +> 故破坏性用例只在**产品首次上电/产线首启**或明确需要时开启。 `test/test_config.h` 配置示例(三选一,互斥,多选触发 `#error`): ```c // #define TEST_SUITE_CH395F // #define TEST_SUITE_STORAGE -#define TEST_SUITE_GD5F /* 选中后自动开启 gd5f 全部阶段宏 */ +#define TEST_SUITE_GD5F /* 选中后自带 gd5f 只读阶段宏 */ +// #define ENABLE_DESTRUCTIVE_TESTS /* 需写/擦/格式化/落盘坏块时才打开(默认关闭) */ ``` 执行顺序(由 `gd5f_test_run()` 按阶段串行,与函数依赖一致): -`init/read_id/reset` → `BBT` → `页读写` → `块擦除` → `ECC` → `SPI 原语` → `DMA 边界` → `FTL` → `FatFS` → `边界条件`。 + +- **只读(默认)**:`init/read_id/reset` → `BBT(读取与 RAM 内标记)` → `DMA 守卫读方向(TC-704)` → `边界条件(越界 / size=0)` +- **破坏性(开关打开时插入其间)**:`页读写` → `块擦除` → `ECC` → `SPI 原语` → `DMA 边界(701~703、705)` → `FTL(含 format)` → `FatFS` + +开机横幅会明确打印本次模式:`mode: READ-ONLY (...)` 或 `mode: DESTRUCTIVE (...)`。 + +> `defaultTask` **不再承担任何测试**(仅空闲);所有测试都只在 `ch395fTestTask` 中运行。 ### 启用测试 -1. 在 `test/test_config.h` 取消注释所需的 `TEST_SUITE_*` 宏(如 `TEST_SUITE_GD5F`); +1. 在 `test/test_config.h` 取消注释所需的 `TEST_SUITE_*` 宏(如 `TEST_SUITE_GD5F`), + 需要写/擦类用例时再打开 `ENABLE_DESTRUCTIVE_TESTS`; 2. 编译(`@build`)确认 0 错误 0 警告; -3. 烧录后串口观察 `[NAND-TEST]` 日志与各 TC 的 `PASS/FAIL`,结尾打印 +3. 烧录后串口观察 `mode: ...` 横幅、`[NAND-TEST]` 日志与各 TC 的 `PASS/FAIL`,结尾打印 `=== GD5F2GQ5UE Tests: X/Y PASSED ===`; 4. 不选任何 `TEST_SUITE_*` 时为正常产品构建(`ch395fTestTask` 空闲,不跑测试)。 @@ -468,13 +481,13 @@ ECC 状态(`ECCS1:ECCS0`):`00`=无错;`01/10/11`=纠正 1/2/3 bit;`10` | **通过标准** | 数据一致 | | **覆盖原则** | DMA 大数据路径 | -### TC-GD5F-704: DMA 可达性守卫负路径(读方向) +### TC-GD5F-704: DMA 可达性守卫负路径(读方向,只读,两种模式都跑) | 字段 | 值 | |------|-----| | **ID** | TC-GD5F-704 | | **优先级** | P0 | -| **类型** | 边界/健壮性测试 | +| **类型** | 边界/健壮性测试(**只读**,不受 `ENABLE_DESTRUCTIVE_TESTS` 门控) | | **标题** | 目的缓冲位于 CCM(不可达区)时守卫应拒绝 DMA 并回落轮询,数据仍正确 | | **前置条件** | FreeRTOS 堆驻留 CCM(scatter `RW_CCM`);探针缓冲由 `pvPortMalloc(2048)` 取得(堆不在 CCM 时用例跳过) | | **测试步骤** | 1. 先用 SRAM 静态缓冲参考读同页
2. 探针缓冲预填参考值的反码
3. `gd5f2gq5ue_read()` 整页读入 CCM 探针缓冲
4. 与参考缓冲逐字节比对 | @@ -496,7 +509,8 @@ ECC 状态(`ECCS1:ECCS0`):`00`=无错;`01/10/11`=纠正 1/2/3 bit;`10` | **判定依据** | 同 TC-GD5F-704(写方向 DMA 读 CCM 同样不落地) | | **覆盖原则** | DMA 可达性守卫(写方向);堆缓冲禁入 DMA 的运行时强制(内存分配策略 R3) | -> ⚠ **卷破坏警告**:阶段 8 会调用 `nand_ftl_format()` 清空 dhara map,阶段 9 会写入文件—— +> ⚠ **卷破坏警告(仅 `ENABLE_DESTRUCTIVE_TESTS` 打开时)**:阶段 8 会调用 `nand_ftl_format()` +> 清空 dhara map,阶段 9 会写入文件—— > 整轮 GD5F 套件(含 Phase 8/9)运行后卷数据即失效,且大写入可能使 dhara 把若干块记为 > 运行期坏块(2026-09-21 实测:一次整轮运行容量 95248 → 95200,日志见 > `[ERR][NAND_FTL] program verify: data mismatch, blk 305`,该块由 dhara 自行隔离,测试仍 68/68 通过)。 @@ -732,7 +746,17 @@ ECC 状态(`ECCS1:ECCS0`):`00`=无错;`01/10/11`=纠正 1/2/3 bit;`10` | TC-STO-03 | 坏块注入下存储写入 | P1 | PASS | | TC-STO-04 | BBT 重建 + 重新格式化(恢复) | P1 | 受 `ENABLE_STORAGE_RECOVERY_TESTS` 控制(默认关) | -> 当前实测:`TEST_SUITE_GD5F` 构建一次运行全部阶段,结尾输出 `=== GD5F2GQ5UE Tests: 68/68 PASSED (failed=0) ===`(68 为各 TC 内部断言总数,含 TC-GD5F-205~207 BBT 持久化用例、TC-GD5F-704/705 DMA 守卫负路径;TC 覆盖见上表)。`TEST_SUITE_STORAGE` 套件结尾输出 `=== Storage Tests: 11/11 PASSED (failed=0) ===`(TC-STO-01/02/03 各含若干内部断言,且 TC-STO-01 依赖的 `s_cached_lpn` 哨兵 bug 已修复)。两套件均 0 错误 0 警告通过 MDK 编译。 +> 当前实测(2026-09-21,`ENABLE_DESTRUCTIVE_TESTS` 关闭 / 打开两种配置): +> 只读模式 `=== GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===`(PHASE1/2/7 的 TC-704/10,无任何写操作, +> 卷内容与容量均不变);破坏性模式 `=== GD5F2GQ5UE Tests: 68/68 PASSED (failed=0) ===`(全部阶段)。 +> `TEST_SUITE_STORAGE` 只读模式实测 `=== Storage Tests: 3/3 PASSED (failed=0) ===`(mount + TC-STO-05, +> 卷内容与容量不变);破坏性模式为全量集成用例(2026-09-20 实测 **20/20 PASSED**,含 TC-STO-01/02/03/05/06)。 +> 各套件在两种模式下均 0 错误 0 警告通过 MDK 编译。 +> +> **破坏性用例清单**(仅在 `ENABLE_DESTRUCTIVE_TESTS` 打开时编译运行): +> GD5F:TC-GD5F-206/207、301~305、401~404、501~502、601~605、701~703、705、801~802、901、1003~1004 +> (TC-GD5F-704 为**只读**,两次运行都跑);存储:TC-STO-01、02、03、06 与内建文件系统写读/ +> 性能压测(TC-STO-05 为只读,始终运行);RTC:TC-RTC-002(写时间,跑完复原)、014(写 SRAM)、015(写报警)。 --- diff --git a/docs/测试说明/SD2506_Test_Guide.md b/docs/测试说明/SD2506_Test_Guide.md index 589890bb..9b8f5e1d 100644 --- a/docs/测试说明/SD2506_Test_Guide.md +++ b/docs/测试说明/SD2506_Test_Guide.md @@ -22,8 +22,15 @@ //#define TEST_SUITE_STORAGE //#define TEST_SUITE_GD5F #define TEST_SUITE_RTC /* 启用 RTC 测试套件 */ +//#define ENABLE_DESTRUCTIVE_TESTS /* 需跑改设备状态的用例时才打开(默认关闭) */ ``` +> **破坏性用例开关(2026-09-21 新增,默认关闭)**:本套件有 3 处会**改变设备状态**—— +> TC-RTC-002(写 RTC 时间,套件结束会复原)、TC-RTC-014(写芯片 SRAM)、TC-RTC-015(写报警寄存器)。 +> 关闭 `ENABLE_DESTRUCTIVE_TESTS` 时这三条跳过(开机打印 `mode: READ-ONLY` 与 `skipped` 提示), +> 只跑只读用例:init/读时间、BCD 互转、温度、电池电压、ID、走时校验; +> 打开后(`mode: DESTRUCTIVE`)全跑并在结尾复原原始时间——**中途断电可能把板子时间留在测试值**。 + `test/sd2506_test.h` 中控制各阶段(默认全开): ```c diff --git a/docs/测试说明/自动化测试流程.md b/docs/测试说明/自动化测试流程.md index c902609d..a5d0c1a6 100644 --- a/docs/测试说明/自动化测试流程.md +++ b/docs/测试说明/自动化测试流程.md @@ -78,10 +78,13 @@ `-Required "App init OK;Network Init;net init: OK;PHY: 100M FULL"`。 - **测试套件构建**:`test/test_config.h` 开启某个 `TEST_SUITE_*` 后,把套件打印的 `=== ... Test:` 标题作为 Required 标记,配合 `test/` 下各用例输出判断。 + **破坏性用例默认不跑**:`test_config.h` 的 `ENABLE_DESTRUCTIVE_TESTS`(默认关闭)控制写/擦 NAND、 + 格式化、坏块注入与 FatFS 写类用例;关闭时套件为只读模式(开机打印 `mode: READ-ONLY`), + 可随时在在用设备上执行且不改动卷内容。首次上电/产线验证时才打开(打印 `mode: DESTRUCTIVE`)。 **注意**:套件内**预期**的错误日志会打印 `[ERR]`(GD5F 的 `TC-GD5F-704/705` DMA 守卫 负路径与 FTL 运行期坏块、FatFs 非法名校验等),与默认 Forbidden 冲突,须显式放宽: `--forbidden "FAIL;HardFault"`(`[FAIL]` 仍会拦截真实断言失败); - 例:GD5F 套件 `--required "=== GD5F2GQ5UE Tests: 68/68 PASSED"`。 + 例:GD5F 只读 `--required "=== GD5F2GQ5UE Tests: 16/16 PASSED"`,破坏性 `68/68`。 - **长时间采集**:daemon 全量持续写入 `state/serial.log`(20MB 上限自动截头保尾 10MB),直接读文件即可;或用 `log --tail 0 --grep <标记>` 过滤。 ## 变更记录 diff --git a/test/gd5f_test_task.c b/test/gd5f_test_task.c index 2bc3b699..83f9d99a 100644 --- a/test/gd5f_test_task.c +++ b/test/gd5f_test_task.c @@ -170,10 +170,6 @@ static void gd5f_phase2(void) { uint32_t usable = gd5f_get_usable_blocks(); uint32_t X = gd5f_find_run(1); /* 管理区内的一个好块 */ - uint8_t flash_bbt[GD5F_TOTAL_BLOCKS / 8]; - uint32_t ver = 0; - uint32_t cnt_a, cnt_b; - int32_t marked; /* TC-GD5F-205: 保留块池已预留,dhara 可用块数 < 总块数; * 且测试用块 X 落在管理区内(不会擦到 BBT 存储区) */ @@ -182,36 +178,48 @@ static void gd5f_phase2(void) GD5F_TEST_CHECK(X < usable, "TC-GD5F-205 test block X=%lu < usable=%lu", X, usable); - /* TC-GD5F-206: 标记坏块后 RAM 生效,且已落盘(闪存副本含该位、版本>=1) */ - gd5f2gq5ue_mark_block_bad(X); - marked = gd5f2gq5ue_is_block_bad(X); - GD5F_TEST_CHECK(marked == 1, "TC-GD5F-206 RAM mark X=%lu -> %d", X, marked); - if (GD5F_OK == gd5f_bbt_dump_flash(flash_bbt, &ver)) { - int32_t flash_bit = (flash_bbt[X >> 3] >> (X & 7)) & 1; - GD5F_TEST_CHECK(flash_bit == 1 && ver >= 1, - "TC-GD5F-206 flash persist X bit=%d ver=%lu", flash_bit, ver); - } else { - GD5F_TEST_CHECK(0, "TC-GD5F-206 dump_flash failed"); - } +#ifdef ENABLE_DESTRUCTIVE_TESTS + { + uint8_t flash_bbt[GD5F_TOTAL_BLOCKS / 8]; + uint32_t ver = 0; + uint32_t cnt_a, cnt_b; + int32_t marked; - /* TC-GD5F-207: 清空 RAM 后从闪存重载,坏块记录被恢复(验证 load 链路) */ - cnt_a = gd5f_count_bad(); - gd5f2gq5ue_bbt_clear(); - gd5f_bbt_reload(); - marked = gd5f2gq5ue_is_block_bad(X); - cnt_b = gd5f_count_bad(); - GD5F_TEST_CHECK(marked == 1 && cnt_b >= cnt_a, - "TC-GD5F-207 reload X=%lu restored (cnt %lu->%lu)", X, cnt_a, cnt_b); - - /* 清理:还原出厂快照(RAM)并擦除保留池(闪存),设备回到干净态, - * 保证测试可重复且不在 NAND 上留下伪坏块 */ - gd5f2gq5ue_bbt_clear(); - for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) { - if ((orig_bad[blk >> 5] & (1U << (blk & 31))) != 0) { - gd5f2gq5ue_mark_block_bad(blk); + /* TC-GD5F-206: 标记坏块后 RAM 生效,且已落盘(闪存副本含该位、版本>=1) */ + gd5f2gq5ue_mark_block_bad(X); + marked = gd5f2gq5ue_is_block_bad(X); + GD5F_TEST_CHECK(marked == 1, "TC-GD5F-206 RAM mark X=%lu -> %d", X, marked); + if (GD5F_OK == gd5f_bbt_dump_flash(flash_bbt, &ver)) { + int32_t flash_bit = (flash_bbt[X >> 3] >> (X & 7)) & 1; + GD5F_TEST_CHECK(flash_bit == 1 && ver >= 1, + "TC-GD5F-206 flash persist X bit=%d ver=%lu", flash_bit, ver); + } else { + GD5F_TEST_CHECK(0, "TC-GD5F-206 dump_flash failed"); } + + /* TC-GD5F-207: 清空 RAM 后从闪存重载,坏块记录被恢复(验证 load 链路) */ + cnt_a = gd5f_count_bad(); + gd5f2gq5ue_bbt_clear(); + gd5f_bbt_reload(); + marked = gd5f2gq5ue_is_block_bad(X); + cnt_b = gd5f_count_bad(); + GD5F_TEST_CHECK(marked == 1 && cnt_b >= cnt_a, + "TC-GD5F-207 reload X=%lu restored (cnt %lu->%lu)", X, cnt_a, cnt_b); + + /* 清理:还原出厂快照(RAM)并擦除保留池(闪存),设备回到干净态, + * 保证测试可重复且不在 NAND 上留下伪坏块 */ + gd5f2gq5ue_bbt_clear(); + for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) { + if ((orig_bad[blk >> 5] & (1U << (blk & 31))) != 0) { + gd5f2gq5ue_mark_block_bad(blk); + } + } + gd5f_bbt_wipe_pool(); } - gd5f_bbt_wipe_pool(); +#else + DBG_INFO("TC-GD5F-206/207 skipped: destructive tests disabled " + "(BBT flash persist + pool wipe)"); +#endif } } #endif /* ENABLE_GD5F_PHASE2_TESTS */ @@ -536,6 +544,7 @@ static void gd5f_phase7(void) blk = gd5f_find_run(1); base = (int32_t)blk * GD5F_BLOCK_SIZE; +#ifdef ENABLE_DESTRUCTIVE_TESTS /* TC-GD5F-701: 阈值下界(= 阈值,轮询路径) */ gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); for (i = 0; i < (int32_t)GD5F_DMA_THRESHOLD; i++) { @@ -582,6 +591,9 @@ static void gd5f_phase7(void) if (s_rbuf[i] != (uint8_t)(i & 0xFF)) { ok = 0; break; } } GD5F_TEST_CHECK(ok, "TC-GD5F-703 DMA whole page %s", ok ? "OK" : "MISMATCH"); +#else + DBG_INFO("TC-GD5F-701/702/703 skipped: destructive tests disabled (write/erase)"); +#endif /* ================= DMA 可达性守卫(负路径) ================= * 探针缓冲取自 FreeRTOS 堆:堆驻留 CCM(0x10000000),DMA1/DMA2 不可达。 @@ -598,7 +610,7 @@ static void gd5f_phase7(void) DBG_INFO("TC-GD5F-704/705 SKIP: heap buf not in CCM (p=0x%08lX)", (unsigned long)p_addr); } else { - /* TC-GD5F-704: 读方向 —— 目的缓冲在 CCM,应回落轮询 */ + /* TC-GD5F-704: 读方向 —— 目的缓冲在 CCM,应回落轮询(只读,日常回归亦运行) */ memset(s_rbuf, 0, GD5F_PAGE_SIZE); (void)gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); /* 参考读(SRAM 静态) */ for (i = 0; i < GD5F_PAGE_SIZE; i++) { @@ -610,7 +622,8 @@ static void gd5f_phase7(void) GD5F_TEST_CHECK(ok, "TC-GD5F-704 CCM(heap) rx poll fallback %s", ok ? "OK" : "MISMATCH"); - /* TC-GD5F-705: 写方向 —— 源缓冲在 CCM,应回落轮询 */ +#ifdef ENABLE_DESTRUCTIVE_TESTS + /* TC-GD5F-705: 写方向 —— 源缓冲在 CCM,应回落轮询(需擦写) */ gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); for (i = 0; i < GD5F_PAGE_SIZE; i++) { p_heap[i] = (uint8_t)(i ^ 0x5A); @@ -625,6 +638,9 @@ static void gd5f_phase7(void) } GD5F_TEST_CHECK(ok, "TC-GD5F-705 CCM(heap) tx poll fallback %s", ok ? "OK" : "MISMATCH"); +#else + DBG_INFO("TC-GD5F-705 skipped: destructive tests disabled (write/erase)"); +#endif } vPortFree(p_heap); } @@ -709,7 +725,7 @@ static void gd5f_phase10(void) { uint32_t blk; int32_t base; - int32_t ret, i, ne, ok; + int32_t ret; DBG_INFO("=== GD5F Phase 10: boundary conditions ==="); blk = gd5f_find_run(1); @@ -729,35 +745,43 @@ static void gd5f_phase10(void) ret = gd5f2gq5ue_write(base, s_wbuf, 0); GD5F_TEST_CHECK(ret == GD5F_OK, "TC-GD5F-1002 write size=0 ret=%d", ret); - /* TC-GD5F-1003: 写前未擦除 → 数据不可预期(NAND 不可原地覆盖) - * 先擦除写入全 0x00,再不清擦写入全 0xFF(需要 0→1,物理不可行), - * 读回应仍 != 0xFF。 */ - gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); - memset(s_wbuf, 0x00, GD5F_PAGE_SIZE); - gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); - memset(s_wbuf, 0xFF, GD5F_PAGE_SIZE); - gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); /* 未擦除,0→1 不可能 */ - memset(s_rbuf, 0, GD5F_PAGE_SIZE); - gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); - ne = 0; - for (i = 0; i < GD5F_PAGE_SIZE; i++) { - if (s_rbuf[i] != 0xFF) ne++; - } - GD5F_TEST_CHECK(ne > 0, - "TC-GD5F-1003 write-without-erase not applied (bytes!=0xFF: %d)", ne); +#ifdef ENABLE_DESTRUCTIVE_TESTS + { + int32_t i, ne, ok; - /* TC-GD5F-1004: 写保护解除验证(init 后 Protect=0x00,可正常编程) */ - gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); - memset(s_wbuf, 0x5A, GD5F_PAGE_SIZE); - ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); - memset(s_rbuf, 0, GD5F_PAGE_SIZE); - gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); - ok = 1; - for (i = 0; i < GD5F_PAGE_SIZE; i++) { - if (s_rbuf[i] != 0x5A) { ok = 0; break; } + /* TC-GD5F-1003: 写前未擦除 → 数据不可预期(NAND 不可原地覆盖) + * 先擦除写入全 0x00,再不清擦写入全 0xFF(需要 0→1,物理不可行), + * 读回应仍 != 0xFF。 */ + gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); + memset(s_wbuf, 0x00, GD5F_PAGE_SIZE); + gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); + memset(s_wbuf, 0xFF, GD5F_PAGE_SIZE); + gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); /* 未擦除,0→1 不可能 */ + memset(s_rbuf, 0, GD5F_PAGE_SIZE); + gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); + ne = 0; + for (i = 0; i < GD5F_PAGE_SIZE; i++) { + if (s_rbuf[i] != 0xFF) ne++; + } + GD5F_TEST_CHECK(ne > 0, + "TC-GD5F-1003 write-without-erase not applied (bytes!=0xFF: %d)", ne); + + /* TC-GD5F-1004: 写保护解除验证(init 后 Protect=0x00,可正常编程) */ + gd5f2gq5ue_erase(base, GD5F_BLOCK_SIZE); + memset(s_wbuf, 0x5A, GD5F_PAGE_SIZE); + ret = gd5f2gq5ue_write(base, s_wbuf, GD5F_PAGE_SIZE); + memset(s_rbuf, 0, GD5F_PAGE_SIZE); + gd5f2gq5ue_read(base, s_rbuf, GD5F_PAGE_SIZE); + ok = 1; + for (i = 0; i < GD5F_PAGE_SIZE; i++) { + if (s_rbuf[i] != 0x5A) { ok = 0; break; } + } + GD5F_TEST_CHECK(ret == GD5F_OK && ok, + "TC-GD5F-1004 write unprotected (ret=%d) %s", ret, ok ? "OK" : "FAIL"); } - GD5F_TEST_CHECK(ret == GD5F_OK && ok, - "TC-GD5F-1004 write unprotected (ret=%d) %s", ret, ok ? "OK" : "FAIL"); +#else + DBG_INFO("TC-GD5F-1003/1004 skipped: destructive tests disabled (write/erase)"); +#endif } #endif /* ENABLE_GD5F_PHASE10_TESTS */ @@ -768,6 +792,12 @@ void gd5f_test_run(void) { memset(&g_gd5f_test_stats, 0, sizeof(g_gd5f_test_stats)); DBG_INFO("########## GD5F2GQ5UE Test Start ##########"); +#ifdef ENABLE_DESTRUCTIVE_TESTS + DBG_INFO("mode: DESTRUCTIVE (write/erase/format/BBT-persist enabled)"); +#else + DBG_INFO("mode: READ-ONLY (destructive cases skipped; set " + "ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)"); +#endif #ifdef ENABLE_GD5F_PHASE1_TESTS gd5f_phase1(); diff --git a/test/sd2506_test_task.c b/test/sd2506_test_task.c index 583d5d1c..c95eb4b3 100644 --- a/test/sd2506_test_task.c +++ b/test/sd2506_test_task.c @@ -49,28 +49,36 @@ static void sd2506_phase_basic(void) (unsigned int)r.second, (unsigned int)r.week); } - /* TC-RTC-002: 写固定时间,立即回读比对。 + /* TC-RTC-002: 写固定时间,立即回读比对(破坏性:改设备 RTC 时间,套件结束会复原)。 * 若 FAIL -> 先查 sd2506.h 寄存器宏后缀是否为 H(曾因 0x0FH 被 KEIL 编译为 0 * 导致 SD2506_REG_CTR1 指向秒寄存器、WRTC 写错地址)。详见 docs/SD2506_Trap_Records.md。 */ - sd2506_time_t t; - t.year = 2026; t.month = 8; t.day = 28; t.hour = 12; t.minute = 0; t.second = 0; t.week = 4; - ret = sd2506_set_time(&t); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_time() ret=%d (expect 0)", ret); +#ifdef ENABLE_DESTRUCTIVE_TESTS + { + sd2506_time_t t; + int match; - ret = sd2506_get_time(&r); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret); - if (ret == SD2506_OK) { - DBG_INFO(" set : %04u-%02u-%02u %02u:%02u:%02u", - (unsigned int)t.year, (unsigned int)t.month, (unsigned int)t.day, - (unsigned int)t.hour, (unsigned int)t.minute, (unsigned int)t.second); - DBG_INFO(" readback: %04u-%02u-%02u %02u:%02u:%02u", - (unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day, - (unsigned int)r.hour, (unsigned int)r.minute, (unsigned int)r.second); + t.year = 2026; t.month = 8; t.day = 28; t.hour = 12; t.minute = 0; t.second = 0; t.week = 4; + ret = sd2506_set_time(&t); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_time() ret=%d (expect 0)", ret); + + ret = sd2506_get_time(&r); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret); + if (ret == SD2506_OK) { + DBG_INFO(" set : %04u-%02u-%02u %02u:%02u:%02u", + (unsigned int)t.year, (unsigned int)t.month, (unsigned int)t.day, + (unsigned int)t.hour, (unsigned int)t.minute, (unsigned int)t.second); + DBG_INFO(" readback: %04u-%02u-%02u %02u:%02u:%02u", + (unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day, + (unsigned int)r.hour, (unsigned int)r.minute, (unsigned int)r.second); + } + match = (r.year == t.year && r.month == t.month && r.day == t.day && + r.hour == t.hour && r.minute == t.minute && + (r.second == t.second || r.second == ((t.second + 1U) % 60U))); + SD2506_TEST_CHECK(match, "set==readback (in-session write takes effect)"); } - int match = (r.year == t.year && r.month == t.month && r.day == t.day && - r.hour == t.hour && r.minute == t.minute && - (r.second == t.second || r.second == ((t.second + 1U) % 60U))); - SD2506_TEST_CHECK(match, "set==readback (in-session write takes effect)"); +#else + DBG_INFO("TC-RTC-002 skipped: destructive tests disabled (RTC time write)"); +#endif /* TC-RTC-003: 走时。写后等 3s,应前进约 3 秒 */ if (ret == SD2506_OK) { @@ -147,25 +155,37 @@ static void sd2506_phase_api(void) id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]); } - /* TC-RTC-014: 用户 SRAM 读写回环(addr=0, len=8) */ - uint8_t wbuf[8] = {0xA1U, 0xB2U, 0xC3U, 0xD4U, 0xE5U, 0xF6U, 0x07U, 0x18U}; - uint8_t rbuf[8] = {0}; - ret = sd2506_write_sram(0U, wbuf, 8U); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_write_sram() ret=%d (expect 0)", ret); - if (ret == SD2506_OK) { - ret = sd2506_read_sram(0U, rbuf, 8U); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_read_sram() ret=%d (expect 0)", ret); - int sram_ok = (memcmp(wbuf, rbuf, 8U) == 0); - SD2506_TEST_CHECK(sram_ok, "SRAM write==readback"); + /* TC-RTC-014: 用户 SRAM 读写回环(addr=0, len=8;破坏性:写芯片 SRAM) */ +#ifdef ENABLE_DESTRUCTIVE_TESTS + { + uint8_t wbuf[8] = {0xA1U, 0xB2U, 0xC3U, 0xD4U, 0xE5U, 0xF6U, 0x07U, 0x18U}; + uint8_t rbuf[8] = {0}; + ret = sd2506_write_sram(0U, wbuf, 8U); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_write_sram() ret=%d (expect 0)", ret); + if (ret == SD2506_OK) { + ret = sd2506_read_sram(0U, rbuf, 8U); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_read_sram() ret=%d (expect 0)", ret); + int sram_ok = (memcmp(wbuf, rbuf, 8U) == 0); + SD2506_TEST_CHECK(sram_ok, "SRAM write==readback"); + } } +#else + DBG_INFO("TC-RTC-014 skipped: destructive tests disabled (RTC SRAM write)"); +#endif - /* TC-RTC-015: 报警设置 + 清除(验证 API 通路,不影响时间寄存器) */ - sd2506_time_t at; - at.year = 2026; at.month = 8; at.day = 28; at.hour = 12; at.minute = 0; at.second = 30; at.week = 4; - ret = sd2506_set_alarm(&at, (uint8_t)(SD2506_AL_EN_EAS | SD2506_AL_EN_EAMN)); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_alarm() ret=%d (expect 0)", ret); - ret = sd2506_clear_alarm(); - SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_clear_alarm() ret=%d (expect 0)", ret); + /* TC-RTC-015: 报警设置 + 清除(破坏性:写报警寄存器;验证 API 通路,不影响时间寄存器) */ +#ifdef ENABLE_DESTRUCTIVE_TESTS + { + sd2506_time_t at; + at.year = 2026; at.month = 8; at.day = 28; at.hour = 12; at.minute = 0; at.second = 30; at.week = 4; + ret = sd2506_set_alarm(&at, (uint8_t)(SD2506_AL_EN_EAS | SD2506_AL_EN_EAMN)); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_alarm() ret=%d (expect 0)", ret); + ret = sd2506_clear_alarm(); + SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_clear_alarm() ret=%d (expect 0)", ret); + } +#else + DBG_INFO("TC-RTC-015 skipped: destructive tests disabled (RTC alarm write)"); +#endif SD2506_TEST_REPORT("Phase 2 API"); } @@ -175,6 +195,12 @@ static void sd2506_phase_api(void) void sd2506_test_run(void) { DBG_INFO("SD2506 test suite started"); +#ifdef ENABLE_DESTRUCTIVE_TESTS + DBG_INFO("mode: DESTRUCTIVE (RTC time / SRAM / alarm writes enabled)"); +#else + DBG_INFO("mode: READ-ONLY (destructive cases skipped; set " + "ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)"); +#endif int ret = sd2506_init(); SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret); @@ -188,12 +214,14 @@ void sd2506_test_run(void) (unsigned int)((c1 >> 7) & 1U)); } +#ifdef ENABLE_DESTRUCTIVE_TESTS /* 记录原始时间,测试结束恢复,避免把板子时间留在测试值 */ sd2506_time_t orig; int have_orig = (sd2506_get_time(&orig) == SD2506_OK); if (!have_orig) { DBG_INFO(" WARN: cannot read original time; restore step will be skipped"); } +#endif memset(&g_sd2506_test_stats, 0, sizeof(g_sd2506_test_stats)); @@ -205,11 +233,13 @@ void sd2506_test_run(void) sd2506_phase_api(); #endif +#ifdef ENABLE_DESTRUCTIVE_TESTS /* 恢复原始时间(用公共 API,写保护逻辑已由 sd2506.c 正确处理) */ if (have_orig) { int r = sd2506_set_time(&orig); DBG_INFO(" restored original time (ret=%d)", r); } +#endif DBG_INFO("SD2506 test suite done"); for (;;) { diff --git a/test/storage_test_task.c b/test/storage_test_task.c index 652730fa..131ec9e0 100644 --- a/test/storage_test_task.c +++ b/test/storage_test_task.c @@ -12,6 +12,7 @@ #define DBG_TAG "[STORAGE-TEST]" #include "dbg_log.h" +#include "test_config.h" /* 破坏性用例开关 ENABLE_DESTRUCTIVE_TESTS 的唯一来源 */ #include "storage_test.h" #include "nand_ftl.h" #include "diskio.h" @@ -26,6 +27,7 @@ storage_test_stats_t g_storage_stats; static FATFS fs; static FIL fil; +#ifdef ENABLE_DESTRUCTIVE_TESTS /* 性能缓冲 2048B = 恰好一个 NAND 页(RAM 预算:4096 时套件链接溢出 0x604, * L6406E 实测;2048 分块仍覆盖整页写路径,128KB 总量不变) */ static uint8_t s_perf_buf[2048]; @@ -252,6 +254,8 @@ static void storage_test_badblock(void) * 后跑一次套件,即可清除运行时注入坏块 + 清 dhara map 回出厂态) */ } +#endif /* ENABLE_DESTRUCTIVE_TESTS: s_perf_buf + TC-STO-01/02/03(写、擦、坏块注入) */ + /* TC-STO-05: 文件名/路径合法性校验(守护 ".fschk 无限重格式化" 真根因) * 本移植 FF_USE_LFN=0,只支持严格 8.3 短名(主名≤8、扩展≤3、不以点开头)。 * 非法名字 f_open 必返 FR_INVALID_NAME(纯 RAM 名字判定、不碰盘);合法但 @@ -324,6 +328,7 @@ static void storage_test_name_validation(void) STORAGE_TEST_CHECK(bok, "TC-STO-05 invalid names rejected (FR_INVALID_NAME)"); } +#ifdef ENABLE_DESTRUCTIVE_TESTS /* TC-STO-06: 打开模式与写回字节数语义(守护 FA_OPEN_APPEND / 短写陷阱) * 本移植 FA_OPEN_APPEND 不含 FA_WRITE → 不带写位打开 f_write 必返 FR_DENIED; * 追加写必须 FA_WRITE|FA_OPEN_APPEND。另 f_write 簇耗尽返 FR_OK+短写(无错误 @@ -381,6 +386,7 @@ static void storage_test_open_modes(void) (void)f_unlink("mode.tmp"); } +#endif /* ENABLE_DESTRUCTIVE_TESTS: TC-STO-06 */ #endif /* !ENABLE_STORAGE_RECOVERY_TESTS: 共享缓冲 + 正常测试函数 */ #ifdef ENABLE_STORAGE_RECOVERY_TESTS @@ -391,45 +397,65 @@ static void storage_test_recover(void); /* 正常存储测试套件主体(恢复模式见文件末尾 storage_test_run 分发) */ static void storage_test_run_normal(void) { - static uint8_t work[FF_MAX_SS]; - char read_buf[64]; - const char *test_str = "Hello FatFS + dhara FTL!"; + FRESULT res; memset(&g_storage_stats, 0, sizeof(g_storage_stats)); - /* ==================== FTL + FatFS 文件系统测试 ==================== */ +#ifdef ENABLE_DESTRUCTIVE_TESTS + DBG_INFO("mode: DESTRUCTIVE (write/erase/format/badblock-inject enabled)"); +#else + DBG_INFO("mode: READ-ONLY (destructive cases skipped; set " + "ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)"); +#endif - FRESULT res = f_mount(&fs, "", 1); + /* ==================== 挂载 ==================== */ + + res = f_mount(&fs, "", 1); +#ifdef ENABLE_DESTRUCTIVE_TESTS if (res == FR_NO_FILESYSTEM) { + static uint8_t work[FF_MAX_SS]; MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 }; res = f_mkfs("", &opts, work, sizeof(work)); STORAGE_TEST_CHECK(res == FR_OK, "f_mkfs"); res = f_mount(&fs, "", 1); } +#else + if (res == FR_NO_FILESYSTEM) { + DBG_INFO("read-only mode: no filesystem on volume, skip write cases " + "(mkfs self-heal disabled)"); + } +#endif STORAGE_TEST_CHECK(res == FR_OK, "f_mount"); - if (res == FR_OK) { - UINT bw, br; +#ifdef ENABLE_DESTRUCTIVE_TESTS + /* ==================== FTL + FatFS 文件系统测试 ==================== */ + { + char read_buf[64]; + const char *test_str = "Hello FatFS + dhara FTL!"; - res = f_open(&fil, "test.txt", FA_CREATE_ALWAYS | FA_WRITE); - STORAGE_TEST_CHECK(res == FR_OK, "f_open(write)"); if (res == FR_OK) { - f_write(&fil, test_str, (UINT)strlen(test_str), &bw); - STORAGE_TEST_CHECK(bw == (UINT)strlen(test_str), "f_write (%u bytes)", bw); - f_close(&fil); + UINT bw, br; - res = f_open(&fil, "test.txt", FA_READ); - STORAGE_TEST_CHECK(res == FR_OK, "f_open(read)"); + res = f_open(&fil, "test.txt", FA_CREATE_ALWAYS | FA_WRITE); + STORAGE_TEST_CHECK(res == FR_OK, "f_open(write)"); if (res == FR_OK) { - f_read(&fil, read_buf, (UINT)(sizeof(read_buf) - 1), &br); + f_write(&fil, test_str, (UINT)strlen(test_str), &bw); + STORAGE_TEST_CHECK(bw == (UINT)strlen(test_str), "f_write (%u bytes)", bw); f_close(&fil); - read_buf[br] = '\0'; - STORAGE_TEST_CHECK(br == (UINT)strlen(test_str) && - memcmp(test_str, read_buf, br) == 0, - "data verify \"%s\"", read_buf); + + res = f_open(&fil, "test.txt", FA_READ); + STORAGE_TEST_CHECK(res == FR_OK, "f_open(read)"); + if (res == FR_OK) { + f_read(&fil, read_buf, (UINT)(sizeof(read_buf) - 1), &br); + f_close(&fil); + read_buf[br] = '\0'; + STORAGE_TEST_CHECK(br == (UINT)strlen(test_str) && + memcmp(test_str, read_buf, br) == 0, + "data verify \"%s\"", read_buf); + } } + f_unlink("test.txt"); } - f_unlink("test.txt"); } /* ==================== FTL + FatFS 性能测试 ==================== */ @@ -478,19 +504,19 @@ static void storage_test_run_normal(void) total_kb, tr, (total_kb * 1000) / tr); } } +#endif /* ENABLE_DESTRUCTIVE_TESTS: 文件系统写读 + 性能压测 */ - /* ==================== 存储集成测试(TC-STO-05~06 契约 + 01~03 集成) ========== */ + /* ==================== 存储集成测试(TC-STO-05 契约 + 01~03 集成) ========== */ - /* 纯语义契约测试(非破坏性,先跑):名字合法性 + 打开模式 */ + /* 纯语义契约测试(非破坏性):文件名合法性 */ storage_test_name_validation(); - storage_test_open_modes(); +#ifdef ENABLE_DESTRUCTIVE_TESTS + /* 破坏性用例:打开模式契约 + 掉电恢复 / 大文件 / 坏块注入集成 */ + storage_test_open_modes(); storage_test_powerloss(); storage_test_largefile(); storage_test_badblock(); - -#ifdef ENABLE_STORAGE_RECOVERY_TESTS - storage_test_recover(); #endif /* ==================== 汇总报告 ==================== */ diff --git a/test/test_config.h b/test/test_config.h index cec7a9a1..3202eb13 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -28,6 +28,16 @@ extern "C" { //#define TEST_SUITE_GD5F //#define TEST_SUITE_RTC +/* === 破坏性测试总开关(默认关闭)=== + * 破坏性用例会改变设备的**持久状态**,跑一次即有代价(2026-09-21 实测:GD5F 整轮 + * 破坏性用例跑一次即消耗一个 NAND 块,容量 95248 → 95200,且 TC-STO-04 恢复不回来): + * - 向 BBT 持久化坏块标记 / 注入坏块(永久减少可用容量) + * - 写、擦 NAND 块(覆盖卷数据);格式化卷(nand_ftl_format / f_mkfs) + * - FatFS 建文件、写文件、性能压测 + * 默认关闭:套件只跑**只读与参数校验**类用例,可随时在在用设备上执行。 + * 仅在**产品首次上电/产线首启**或明确需要时打开;打开跑完建议做一次恢复。 */ +//#define ENABLE_DESTRUCTIVE_TESTS + #if defined(TEST_SUITE_CH395F) + defined(TEST_SUITE_STORAGE) + defined(TEST_SUITE_GD5F) + defined(TEST_SUITE_RTC) > 1 #error "测试套件互斥:一次构建只能启用一个 TEST_SUITE_*" #endif @@ -47,18 +57,26 @@ extern "C" { #endif #ifdef TEST_SUITE_GD5F -/* 选中 GD5F 套件时,自动开启 gd5f 测试总开关与全部阶段 */ +/* 选中 GD5F 套件时,自动开启 gd5f 测试总开关; + * 只读阶段无条件启用,破坏性阶段(写/擦/格式化/BBT 落盘)受 ENABLE_DESTRUCTIVE_TESTS 门控 */ #define ENABLE_GD5F_TESTS -#define ENABLE_GD5F_PHASE1_TESTS -#define ENABLE_GD5F_PHASE2_TESTS -#define ENABLE_GD5F_PHASE3_TESTS -#define ENABLE_GD5F_PHASE4_TESTS -#define ENABLE_GD5F_PHASE5_TESTS -#define ENABLE_GD5F_PHASE6_TESTS -#define ENABLE_GD5F_PHASE7_TESTS -#define ENABLE_GD5F_PHASE8_TESTS -#define ENABLE_GD5F_PHASE9_TESTS -#define ENABLE_GD5F_PHASE10_TESTS + +/* --- 只读阶段:随时可跑,不改设备状态 --- */ +#define ENABLE_GD5F_PHASE1_TESTS /* init / read_id / reset */ +#define ENABLE_GD5F_PHASE2_TESTS /* BBT 读取与 RAM 内标记(206/207 落盘另受门控) */ +#define ENABLE_GD5F_PHASE7_TESTS /* DMA 边界与守卫(701~703/705 写擦受门控;704 只读,日常回归亦跑) */ +#define ENABLE_GD5F_PHASE10_TESTS /* 越界与 size=0 参数校验(1003/1004 写入另受门控) */ + +#ifdef ENABLE_DESTRUCTIVE_TESTS +/* --- 破坏性阶段:写/擦页与块、格式化、FatFS 写入 --- */ +#define ENABLE_GD5F_PHASE3_TESTS /* 页/跨页/多页读写 */ +#define ENABLE_GD5F_PHASE4_TESTS /* 块擦除 */ +#define ENABLE_GD5F_PHASE5_TESTS /* ECC 写读 */ +#define ENABLE_GD5F_PHASE6_TESTS /* SPI 原语(块擦除 + 编程) */ +#define ENABLE_GD5F_PHASE8_TESTS /* FTL init + nand_ftl_format(格式化清卷) */ +#define ENABLE_GD5F_PHASE9_TESTS /* FatFS 挂载 + 写文件 */ +#endif + #include "gd5f_test.h" #define TEST_SUITE_NAME "GD5F" #endif