/** * test_ble_offlog.c — dbn_ble_srv.c 脱机日志 3 case 的隔离验证 * * 直接从 dbn_ble_srv.c 提取 case 文本嵌入本文件, 测的是源文件真实代码。 * Mock: offlog_* / set_response_buf / PRINT / Buf_DBN_BLE 类型。 * * 编译: gcc -I../BLE/OnlyUpdateApp_Peripheral/APP/include \ * -o test_ble_offlog test_ble_offlog.c (需先跑 extract_offlog_cases.py) * 运行: ./test_ble_offlog */ #include #include #include /* ============ 类型/宏 (来自 dbn_ble_srv.h 编辑后, 避免 include BLE 栈) ============ */ #define MAGIC_BYTE_DBN_DEFAULT 0x8F #define CMD_DBN_OFFLOG_STAT 0x25 #define CMD_DBN_OFFLOG_QUERY 0x26 #define CMD_DBN_OFFLOG_CLEAR 0x27 #define MAX_BLE_TMP_BUF_LEN 132 #define MAX_BLE_DAT_BUF_LEN 132 #define MAX_BLE_DAT_RESPONSE_LEN (100 - 4) /* 单包数据上限 96B */ typedef struct _BLE_Notify_Buf_ { uint8_t flag; uint8_t len; uint8_t buf[100]; } BLE_Notify_Buf; typedef struct _BUF_DBN_BLE_ { uint8_t flag; uint8_t magic; uint8_t cmd; uint8_t dat_len ; uint8_t dat_offset; uint8_t pkg_amount; uint8_t pkg_seq; uint8_t dat[MAX_BLE_DAT_BUF_LEN]; } Buf_DBN_BLE; #include "offlog.h" #define PRINT(...) ((void)0) /* ============ 全局 (来自 dbn_ble_srv.c) ============ */ static uint8_t tmp_ble_buf[MAX_BLE_TMP_BUF_LEN]; static Buf_DBN_BLE g_buf_ble_response; /* ============ offlog mock ============ */ static uint32_t mock_count = 0; static uint32_t mock_seq_last = 0; static uint32_t mock_boot = 0; static uint8_t mock_enabled = 1; uint16_t offlog_count(void) { return (uint16_t)mock_count; } uint32_t offlog_seq_last(void) { return mock_seq_last; } uint32_t offlog_boot_seq(void) { return mock_boot; } uint8_t offlog_enabled(void) { return mock_enabled; } void offlog_clear(void) { mock_count = 1; mock_seq_last++; } /* 审计留痕: count 归 1, seq 继续 */ int offlog_read_idx(uint16_t idx, OfflogEvt *out) { uint32_t seq_first; if (idx >= mock_count) return -1; seq_first = mock_seq_last - mock_count + 1; memset(out, 0, sizeof(OfflogEvt)); out->magic = OFFLOG_EVT_MAGIC; out->type = (idx % 3 == 0) ? OFFLOG_EVT_BOOT : (idx % 3 == 1) ? OFFLOG_EVT_IOT_READY : OFFLOG_EVT_COIL; out->seq = seq_first + idx; out->boot_seq = 2; out->ts_ms = 1000 + idx * 100; out->unix_ts = 1784768575UL; if (idx % 3 == 2) { /* COIL: sub=1 ch=1 value=97 */ out->payload[0] = 1; out->payload[1] = 1; out->payload[2] = 0; out->payload[3] = 0; out->payload[4] = 0; out->payload[5] = 97; } return 0; } /* ============ set_response_buf mock: 只记录调用参数 ============ */ static uint8_t last_cmd = 0; static uint8_t last_len = 0; static uint8_t last_dat[160]; uint8_t set_response_buf(Buf_DBN_BLE *response_dst, uint8_t magic, uint8_t cmd, uint8_t *dat, uint8_t dat_len) { uint8_t i; memset(last_dat, 0, sizeof(last_dat)); last_cmd = cmd; last_len = dat_len; for (i = 0; i < dat_len; i++) last_dat[i] = dat[i]; return 0; } /* ============ 被测: 从 dbn_ble_srv.c 提取的 3 case (嵌入 switch) ============ */ static void run_case(uint8_t cmd, uint8_t *pkg, uint8_t len) { uint8_t _cmd = cmd; switch (_cmd) { #include "offlog_cases_embedded.c" default: break; } } /* ============ 断言 ============ */ static int failures = 0; #define CHECK(cond) do { \ if (!(cond)) { printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); failures++; } \ } while (0) static uint8_t pkg[16]; static void test_stat_ok(void) { mock_enabled = 1; mock_count = 1234; mock_seq_last = 1333; mock_boot = 2; memset(tmp_ble_buf, 0, sizeof(tmp_ble_buf)); run_case(0x25, NULL, 0); CHECK(last_cmd == 0x25); CHECK(last_len == 19); /* status=0 */ CHECK(last_dat[0] == 0x00); /* boot_seq=2 LE */ CHECK(last_dat[1] == 0x02 && last_dat[2] == 0x00); /* count=1234 LE */ CHECK(last_dat[3] == 0xD2 && last_dat[4] == 0x04 && last_dat[5] == 0x00 && last_dat[6] == 0x00); /* capacity=8064 LE */ CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x1F && last_dat[9] == 0x00 && last_dat[10] == 0x00); /* seq_first=100 LE */ CHECK(last_dat[11] == 0x64 && last_dat[12] == 0x00 && last_dat[13] == 0x00 && last_dat[14] == 0x00); /* seq_last=1333 LE */ CHECK(last_dat[15] == 0x35 && last_dat[16] == 0x05 && last_dat[17] == 0x00 && last_dat[18] == 0x00); printf("PASS test_stat_ok\n"); } static void test_stat_disabled(void) { mock_enabled = 0; run_case(0x25, NULL, 0); CHECK(last_cmd == 0x25); CHECK(last_len == 1); CHECK(last_dat[0] == 0x01); printf("PASS test_stat_disabled\n"); } static void test_query_ok(void) { /* 10 条: seq 11..20, 请求 start_seq=13 count=4 → 返回 seq 13,14,15,16 */ mock_enabled = 1; mock_count = 10; mock_seq_last = 20; mock_boot = 2; memset(pkg, 0, sizeof(pkg)); pkg[4] = 13; /* start_seq LE */ pkg[5] = 0; pkg[6] = 0; pkg[7] = 0; pkg[8] = 4; /* count */ memset(tmp_ble_buf, 0, sizeof(tmp_ble_buf)); run_case(0x26, pkg, 11); CHECK(last_cmd == 0x26); CHECK(last_len == 2 + 4 * 32); /* status + count + 4×32B */ CHECK(last_dat[0] == 0x00); /* status ok */ CHECK(last_dat[1] == 4); /* fetched 4 */ /* 记录0 (idx=2, seq=13): type=coil (mock: idx%3==2 → COIL), 仅查 magic+seq */ CHECK(last_dat[2] == OFFLOG_EVT_MAGIC); CHECK(last_dat[6] == 13); /* seq LE32 偏移4 */ CHECK(last_dat[7] == 0); /* 记录1 (idx=3, seq=14): type=boot */ CHECK(last_dat[2+32+1] == OFFLOG_EVT_BOOT); CHECK(last_dat[2+32+4] == 14); /* 记录2 (idx=4, seq=15): type=iot_ready, payload 空 */ CHECK(last_dat[2+64+1] == OFFLOG_EVT_IOT_READY); CHECK(last_dat[2+64+4] == 15); /* 记录3 (idx=5, seq=16): type=coil, payload sub=1 ch=1 value=97 */ CHECK(last_dat[2+96+1] == OFFLOG_EVT_COIL); CHECK(last_dat[2+96+4] == 16); CHECK(last_dat[2+96+20] == 1); /* payload[0] sub */ CHECK(last_dat[2+96+21] == 1); /* payload[1] ch */ CHECK(last_dat[2+96+25] == 97); /* payload[5] value LSB */ printf("PASS test_query_ok\n"); } static void test_query_range_error(void) { mock_enabled = 1; mock_count = 10; mock_seq_last = 20; mock_boot = 2; memset(pkg, 0, sizeof(pkg)); pkg[4] = 100; /* 越界 > seq_last */ pkg[8] = 4; run_case(0x26, pkg, 11); CHECK(last_cmd == 0x26); CHECK(last_len == 2); CHECK(last_dat[0] == 0x00); CHECK(last_dat[1] == 0); /* 空 records */ printf("PASS test_query_range_error\n"); } static void test_query_short_frame(void) { mock_enabled = 1; pkg[8] = 4; run_case(0x26, pkg, 9); /* len < 11 */ CHECK(last_cmd == 0x26); CHECK(last_len == 1); CHECK(last_dat[0] == 0x02); /* bad request */ printf("PASS test_query_short_frame\n"); } static void test_query_disabled(void) { mock_enabled = 0; pkg[8] = 4; run_case(0x26, pkg, 11); CHECK(last_cmd == 0x26); CHECK(last_len == 1); CHECK(last_dat[0] == 0x01); /* disabled */ printf("PASS test_query_disabled\n"); } static void test_clear(void) { mock_enabled = 1; mock_count = 5; mock_seq_last = 30; run_case(0x27, NULL, 0); CHECK(last_cmd == 0x27); CHECK(last_len == 1); CHECK(last_dat[0] == 0x00); /* offlog_clear mock: count 归 1, seq_last 递增 */ CHECK(mock_count == 1); CHECK(mock_seq_last == 31); printf("PASS test_clear\n"); } int main(void) { test_stat_ok(); test_stat_disabled(); test_query_ok(); test_query_range_error(); test_query_short_frame(); test_query_disabled(); test_clear(); if (failures == 0) printf("\nALL PASS\n"); else printf("\n%d FAILURES\n", failures); return failures ? 1 : 0; }