Files
vd_960/vd960DBN/tests/test_offlog.c
T
wangfq 90755bf465 feat(vd960DBN): offlog TIME_ANCHOR 严格用平台下发 unix_ts
offlog_time_anchor 原丢弃传参内部重取 dev_time_now() (毫秒级误差),
改为 offlog_evt_ts() 直接写入平台下发值, 锚点事件与平台 ts 严格一致。
新增单测 test_time_anchor_exact, 7 例 ALL PASS
2026-08-04 18:10:18 +08:00

252 lines
7.4 KiB
C

/**
* test_offlog.c — offlog 环形事件日志 gcc 隔离单测
*
* 编译: gcc -I../BLE/OnlyUpdateApp_Peripheral/APP/include \
* -o test_offlog test_offlog.c
* 运行: ./test_offlog
*
* Mock: W25Q32 NOR 行为 (写=AND, 擦=0xFF), mstick/dev_time_now 固定值
* 覆盖: 全新初始化 / 环形回绕 / 掉电恢复 / 扇区切换 / 清空审计
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/* ============ mock W25Q32 (4MB, NOR 语义) ============ */
static uint8_t flash[4 * 1024 * 1024];
void SPI_Flash_Erase_Sector(uint32_t sec)
{
memset(flash + sec * 4096, 0xFF, 4096);
}
void SPI_Flash_Read(uint8_t *buf, uint32_t addr, uint16_t size)
{
memcpy(buf, flash + addr, size);
}
void SPI_Flash_Write_NoCheck(uint8_t *buf, uint32_t addr, uint16_t size)
{
uint16_t i;
for (i = 0; i < size; i++) flash[addr + i] &= buf[i]; /* NOR: 只能 1→0 */
}
void SPI_Flash_Write(uint8_t *buf, uint32_t addr, uint16_t size)
{
/* 模拟 storage.c 行为: 目标区域非全 FF 则擦整扇区再写 */
uint16_t i;
int need_erase = 0;
for (i = 0; i < size; i++) {
if (flash[addr + i] != 0xFF) { need_erase = 1; break; }
}
if (need_erase) SPI_Flash_Erase_Sector(addr / 4096);
SPI_Flash_Write_NoCheck(buf, addr, size);
}
/* ============ mock 时间 (已同步: >16亿) ============ */
static uint32_t mock_ms = 0;
uint32_t mstick(void) { return mock_ms; }
uint32_t dev_time_now(void) { return 1784768575UL; }
/* ch32v20x 库类型 (单测环境无 SDK) */
typedef unsigned char u8;
/* ============ 被测模块 (直接包含实现) ============ */
#include "../BLE/OnlyUpdateApp_Peripheral/APP/offlog.c"
/* ============ 断言 ============ */
static int failures = 0;
#define CHECK(cond) do { \
if (!(cond)) { printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); failures++; } \
} while (0)
static void reset_flash(void)
{
memset(flash, 0xFF, sizeof(flash));
mock_ms = 0;
}
/* 测试1: 全新初始化 → BOOT 事件可读回 */
static void test_fresh_init(void)
{
OfflogEvt e;
uint8_t payload[4] = {0x18, 0, 0, 0}; /* 假复位原因 (大端序写入 payload) */
reset_flash();
offlog_init();
CHECK(offlog_boot_seq() == 1);
CHECK(offlog_count() == 0); /* init 不写事件 */
offlog_boot(0x18000000); /* bit27+bit24 之类 */
CHECK(offlog_count() == 1);
CHECK(offlog_read_idx(0, &e) == 0);
CHECK(e.magic == OFFLOG_EVT_MAGIC);
CHECK(e.type == OFFLOG_EVT_BOOT);
CHECK(e.seq == 1);
CHECK(e.boot_seq == 1);
CHECK(e.flags & OFFLOG_UNIX_VALID_FLAG);
CHECK(e.len == 4);
CHECK(memcmp(e.payload, payload, 4) == 0);
CHECK(offlog_read_idx(1, &e) == -1); /* 越界 */
printf("PASS test_fresh_init\n");
}
/* 测试2: 环形回绕 — 写满 8064+5 条, count 封顶, 序号单调 */
static void test_ring_wrap(void)
{
OfflogEvt e;
uint32_t i, n = OFFLOG_MAX_RECORDS + 5;
reset_flash();
offlog_init();
for (i = 0; i < n; i++) {
offlog_evt(OFFLOG_EVT_IOT_RECONN, NULL, 0);
}
CHECK(offlog_count() == OFFLOG_MAX_RECORDS - OFFLOG_EVT_PER_SECTOR + 5); /* 8064-128+5 */
/* 最新一条是最后写入的, seq 应等于 n (从 1 开始) */
CHECK(offlog_read_idx(offlog_count() - 1, &e) == 0);
CHECK(e.seq == n);
/* 逻辑首: 回绕时整扇区(128条)被擦 + 写回5条 → 最旧 = 129 */
CHECK(offlog_read_idx(0, &e) == 0);
CHECK(e.seq == n - OFFLOG_MAX_RECORDS + OFFLOG_EVT_PER_SECTOR - 4);
printf("PASS test_ring_wrap (seq %lu..%lu, count %u)\n",
(unsigned long)(n - OFFLOG_MAX_RECORDS + OFFLOG_EVT_PER_SECTOR - 4),
(unsigned long)n, offlog_count());
}
/* 测试3: 掉电恢复 — 写 10 条后重新 init, boot_seq 递增, seq 连续 */
static void test_power_loss_recovery(void)
{
OfflogEvt e;
uint32_t i;
reset_flash();
offlog_init();
for (i = 0; i < 10; i++) {
offlog_evt(OFFLOG_EVT_COIL, NULL, 0);
}
CHECK(offlog_count() == 10);
/* 模拟掉电重启 (flash 保留, RAM 全失) */
offlog_init();
CHECK(offlog_boot_seq() == 2);
CHECK(offlog_count() == 10); /* 上电恢复出之前 10 条 */
offlog_evt(OFFLOG_EVT_IOT_CONNECT, NULL, 0);
CHECK(offlog_count() == 11);
CHECK(offlog_read_idx(10, &e) == 0);
CHECK(e.type == OFFLOG_EVT_IOT_CONNECT);
CHECK(e.seq == 11); /* seq 跨 boot 连续 */
CHECK(e.boot_seq == 2);
printf("PASS test_power_loss_recovery\n");
}
/* 测试4: 扇区切换 — 写满 128 条后头扇区更新, 数据完整 */
static void test_sector_switch(void)
{
OfflogEvt e;
OfflogHead h;
uint32_t i;
reset_flash();
offlog_init();
for (i = 0; i < 128; i++) {
offlog_evt(OFFLOG_EVT_BOOT, NULL, 0);
}
/* 第 129 条触发扇区切换 */
offlog_evt(OFFLOG_EVT_TIME_ANCHOR, NULL, 0);
CHECK(offlog_count() == 129);
SPI_Flash_Read((uint8_t *)&h, OFFLOG_AREA_BASE, sizeof(h));
CHECK(h.magic[0] == OFFLOG_HEAD_MAGIC0);
CHECK(h.wr_sector == 2);
/* 头在扇区切换时刷新: wr_off = 新扇区起点 (尚未写入第 129 条) */
CHECK(h.wr_off == OFFLOG_DATA_BASE + 4096);
CHECK(offlog_read_idx(127, &e) == 0);
CHECK(e.type == OFFLOG_EVT_BOOT);
CHECK(e.seq == 128);
CHECK(offlog_read_idx(128, &e) == 0);
CHECK(e.type == OFFLOG_EVT_TIME_ANCHOR);
CHECK(e.seq == 129);
printf("PASS test_sector_switch\n");
}
/* 测试5: 清空 — 擦数据区 + LOG_CLEAR 审计 */
static void test_clear(void)
{
OfflogEvt e;
uint32_t i;
reset_flash();
offlog_init();
for (i = 0; i < 5; i++) {
offlog_evt(OFFLOG_EVT_IOT_READY, NULL, 0);
}
offlog_clear();
CHECK(offlog_count() == 1); /* 只剩审计 */
CHECK(offlog_read_idx(0, &e) == 0);
CHECK(e.type == OFFLOG_EVT_LOG_CLEAR);
CHECK(e.seq == 6); /* seq 不重置 */
printf("PASS test_clear\n");
}
/* 测试6: 掉电恢复跨扇区 — 写到扇区 2 中间掉电 */
static void test_power_loss_mid_sector(void)
{
OfflogEvt e;
uint32_t i;
reset_flash();
offlog_init();
for (i = 0; i < 130; i++) { /* 已跨入扇区 2 */
offlog_evt(OFFLOG_EVT_COIL, NULL, 0);
}
CHECK(offlog_count() == 130);
offlog_init(); /* 掉电重启 */
CHECK(offlog_boot_seq() == 2);
CHECK(offlog_count() == 130);
offlog_evt(OFFLOG_EVT_IOT_CONNECT, NULL, 0);
CHECK(offlog_count() == 131);
CHECK(offlog_read_idx(130, &e) == 0);
CHECK(e.seq == 131);
printf("PASS test_power_loss_mid_sector\n");
}
/* 测试7: TIME_ANCHOR 严格使用平台下发 unix_ts (不走 dev_time_now) */
static void test_time_anchor_exact(void)
{
OfflogEvt e;
reset_flash();
offlog_init();
offlog_time_anchor(1800000000UL); /* 平台下发值, 与 mock dev_time_now() 不同 */
CHECK(offlog_count() == 1);
CHECK(offlog_read_idx(0, &e) == 0);
CHECK(e.type == OFFLOG_EVT_TIME_ANCHOR);
CHECK(e.unix_ts == 1800000000UL); /* 严格等于传参 */
CHECK(e.flags & OFFLOG_UNIX_VALID_FLAG);
printf("PASS test_time_anchor_exact\n");
}
int main(void)
{
test_fresh_init();
test_ring_wrap();
test_power_loss_recovery();
test_sector_switch();
test_clear();
test_power_loss_mid_sector();
test_time_anchor_exact();
if (failures) {
printf("\n%d FAILURE(S)\n", failures);
return 1;
}
printf("\nALL PASS\n");
return 0;
}