6.3 测试框架 Atest
6.3 测试框架 Atest
📚 本节导读
学习时长: 约 45 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: C 语言宏定义、函数指针、结构体、第4章内核组件
🎯 学习目标
- 理解 Atest(And-Test)单元测试框架的设计理念与三级层次结构
- 掌握
atest_tc_entry_t测试用例入口结构体及其注册导出机制 - 掌握 12 种断言宏的使用场景与语义
- 掌握测试单元(TestUnit)与测试点(TestPoint)的执行流程
- 了解 Atest 的 Kconfig 配置项与时钟系统集成
- 能够编写完整的测试用例并进行测试结果统计
一、概述
Atest(And-Test)是 OneOS 内置的轻量级单元测试框架,专为嵌入式实时操作系统设计。它提供了一套完整的测试用例组织、注册、执行与统计机制,帮助开发者在目标平台上直接运行测试用例,验证内核组件和应用代码的正确性。
Atest 框架的核心设计特点:
- 三级层次结构:TestCase(TC,测试用例) -> TestUnit(TU,测试单元) -> TestPoint(TP,测试点),层次清晰,粒度可控
- 段导出机制:利用链接器段(section)
AtestTcTab,测试用例在编译时自动注册,无需手动调用注册函数 - 丰富的断言宏:支持整数、字符串、缓冲区、范围等多种类型的断言,覆盖常见测试场景
- 失败即停止:测试单元内任一测试点失败,立即终止当前测试用例,避免级联错误
- 优先级分类:支持 HIGH/MIDDLE/LOW 三级优先级,可按优先级选择性执行测试用例
- 双日志模式:支持 Dlog 日志系统输出(带颜色)或裸
os_kprintf控制台输出(红/黄/绿/默认色)
二、核心数据结构
2.1 测试用例优先级枚举 enum atest_tc_priority
测试用例支持三种优先级,测试执行时可按优先级排序或筛选(源码 atest.h 第68-73行):
enum atest_tc_priority
{
TC_PRIORITY_HIGH = 0, /* High priority testcase. */
TC_PRIORITY_MIDDLE, /* Middle priority testcase. */
TC_PRIORITY_LOW, /* Low priority testcase. */
TC_PRIORITY_CNT_MAX
};2.2 测试结果统计结构体 atest_stats_t
用于统计多个测试用例执行后的总体结果(源码 atest.h 第83-91行):
struct atest_stats
{
uint16_t tc_passed_num; /* Total number of testcases passed. */
uint16_t tc_failed_num; /* Total number of testcases failed. */
uint16_t tu_passed_num; /* Total number of test units passed. */
uint16_t tu_failed_num; /* Total number of test units failed. */
uint16_t tp_passed_num; /* Total number of test points passed. */
uint16_t tp_failed_num; /* Total number of test points failed. */
};
typedef struct atest_stats atest_stats_t;2.3 测试点统计结构体 atest_tp_stats_t
用于统计单个测试用例内的测试点执行结果(源码 atest.h 第101-105行):
struct atest_tp_stats
{
uint16_t tp_passed_num; /* Total number of test points passed in a single testcase. */
uint16_t tp_failed_num; /* Total number of test points failed in a single testcase. */
};
typedef struct atest_tp_stats atest_tp_stats_t;2.4 测试用例入口结构体 atest_tc_entry_t
每个测试用例编译时被导出到 AtestTcTab 段中,框架通过遍历该段收集所有测试用例(源码 atest.h 第115-123行):
struct atest_tc_entry
{
const char *name; /* Testcase name. */
os_err_t (*init)(void); /* Initialization before executing the testcase function. */
void (*tc)(void); /* Execute the testcase function. */
os_err_t (*cleanup)(void); /* Cleanup after executing the testcase function. */
enum atest_tc_priority priority; /* The priority of testcase */
};
typedef struct atest_tc_entry atest_tc_entry_t;字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
name | const char * | 测试用例名称,用于日志输出和结果展示 |
init | os_err_t (*)(void) | 初始化回调,返回 OS_SUCCESS 表示成功,否则测试用例不执行 |
tc | void (*)(void) | 测试用例主体函数,内部使用 ATEST_UNIT_RUN 运行测试单元 |
cleanup | os_err_t (*)(void) | 清理回调,测试用例执行完毕后调用(无论成功与否) |
priority | enum atest_tc_priority | 测试用例优先级 |
三、测试用例注册与执行
3.1 ATEST_TC_EXPORT —— 测试用例导出宏
将测试用例入口结构体导出到链接器段 AtestTcTab 中(源码 atest.h 第141-143行):
#define ATEST_TC_EXPORT(name, testcase, init, cleanup, priority) \
OS_USED static const atest_tc_entry_t gs_atest##testcase OS_SECTION( \
"AtestTcTab") = {#name, init, testcase, cleanup, priority};用法示例:
/* 声明测试用例函数 */
static os_err_t math_tc_init(void);
static void math_tc(void);
static os_err_t math_tc_cleanup(void);
/* 导出测试用例 */
ATEST_TC_EXPORT(math_test, math_tc, math_tc_init, math_tc_cleanup, TC_PRIORITY_HIGH);关键点:
- 变量名
gs_atest##testcase通过##拼接,确保唯一性 OS_SECTION("AtestTcTab")指示链接器将该变量放入指定段OS_USED防止编译器优化掉未显式引用的变量#name将参数名转为字符串,作为测试用例名称
3.2 ATEST_UNIT_RUN —— 测试单元执行宏
测试用例主体函数中使用此宏运行测试单元,失败后自动 return(源码 atest.h 第154-162行):
#define ATEST_UNIT_RUN(test_unit_func) \
do \
{ \
atest_unit_run(test_unit_func, #test_unit_func); \
if (atest_tp_stats_get()->tp_failed_num != 0) \
{ \
return; \
} \
} while (0)执行逻辑:
- 调用
atest_unit_run()执行测试单元函数,并统计测试点通过/失败数 - 检查
tp_failed_num,若不为0则立即从测试用例返回,不再执行后续测试单元 - 这种"失败即停止"策略避免了级联错误,便于定位问题根源
3.3 三级层次结构
TestCase (TC) 测试用例,最高层,一个 TC 可包含多个 TU
└── TestUnit (TU) 测试单元,中层,一个 TU 可包含多个 TP
└── TestPoint (TP) 测试点,最底层,一个断言即为一个 TP- TestCase:通过
ATEST_TC_EXPORT注册,包含init/tc/cleanup三个生命周期函数 - TestUnit:通过
ATEST_UNIT_RUN在tc函数中调用,通常对应一个独立的功能验证场景 - TestPoint:通过断言宏(如
tp_assert_true)在 TestUnit 函数中定义,每个断言是一个测试点
四、断言宏详解
Atest 提供 12 种断言宏,覆盖整数、字符串、缓冲区、范围等常见测试场景。所有断言宏通过统一的底层断言函数 atest_assert 或 atest_assert_string / atest_assert_buf 实现。完整源码(atest.h 第164-220行):
#define __atest_assert(value, msg) atest_assert(value, __FILE__, __LINE__, __func__, msg)
/* If @value is true, not assert, means passing. */
#define tp_assert_true(value) __atest_assert(value, "(" #value ") is false")
/* If @value is false, not assert, means passing. */
#define tp_assert_false(value) __atest_assert(!(value), "(" #value ") is true")
/* If @value is null, not assert, means passing. */
#define tp_assert_null(value) __atest_assert(OS_NULL == (const char *)(value), "(" #value ") is not null")
/* If @value is not null, not assert, means passing. */
#define tp_assert_not_null(value) __atest_assert(OS_NULL != (const char *)(value), "(" #value ") is null")
/* If @a equal to @b, not assert, means passing. Integer type test. */
#define tp_assert_integer_equal(a, b) __atest_assert((a) == (b), "(" #a ") not equal to (" #b ")")
/* If @a not equal to @b, not assert, means passing. Integer type test. */
#define tp_assert_integer_not_equal(a, b) __atest_assert((a) != (b), "(" #a ") equal to (" #b ")")
/* if @a equal to @b, not assert, means passing. String type test. */
#define tp_assert_str_equal(a, b) \
atest_assert_string((const char *)(a), (const char *)(b), OS_TRUE, __FILE__, __LINE__, __func__, "string not equal")
/* If @a not equal to @b, not assert, means passing. String type test. */
#define tp_assert_str_not_equal(a, b) \
atest_assert_string((const char *)(a), (const char *)(b), OS_FALSE, __FILE__, __LINE__, __func__, "string equal")
/* If @a equal to @b, not assert, means passing. Buffer type test. */
#define tp_assert_buf_equal(a, b, size) \
atest_assert_buf((const uint8_t *)(a), \
(const uint8_t *)(b), \
(size), \
OS_TRUE, \
__FILE__, \
__LINE__, \
__func__, \
"buf not equal")
/* If @a not equal to @b, not assert, means passing. Buffer type test. */
#define tp_assert_buf_not_equal(a, b, size) \
atest_assert_buf((const uint8_t *)(a), \
(const uint8_t *)(b), \
(size), \
OS_FALSE, \
__FILE__, \
__LINE__, \
__func__, \
"buf equal")
/* If @value is in range of @min and @max, not assert, means passing. */
#define tp_assert_in_range(value, min, max) \
__atest_assert(((value >= min) && (value <= max)), "(" #value ") not in range(" #min "," #max ")")
/* If @value is not in range of @min and @max, not assert, means passing. */
#define tp_assert_not_in_range(value, min, max) \
__atest_assert(!((value >= min) && (value <= max)), "(" #value ") in range(" #min "," #max ")")关键理解:
- 所有宏最终都落到
__atest_assert(value, msg)(真值/整数/范围)或atest_assert_string(字符串)或atest_assert_buf(缓冲区); - 宏通过
__FILE__、__LINE__、__func__自动携带出错位置,无需用户传入; - 错误消息用
#value把传入表达式字符串化,失败时直接打印表达式原文(如(os_sem_get_value(g_test_sem)) not equal to (1)); - 正/反断言共用同一断言函数,仅通过
equal参数(OS_TRUE/OS_FALSE)或!(...)取反区分。
4.1 真值断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_true(value) | value 为 true(非零) | 基本布尔断言 |
tp_assert_false(value) | value 为 false(零) | 内部对 value 取反后断言 |
tp_assert_true(ret == OS_SUCCESS); /* 检查返回值是否为成功 */
tp_assert_false(task == OS_NULL); /* 检查任务指针是否为空 */4.2 NULL 断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_null(value) | value 为 NULL | 将 value 强转为 const char * 后与 OS_NULL 比较 |
tp_assert_not_null(value) | value 不为 NULL | 同上,取反比较 |
tp_assert_null(os_sem_create("test", 0, OS_IPC_FLAG_FIFO)); /* 创建失败时应返回NULL */
tp_assert_not_null(malloc(128)); /* 内存分配成功时应返回非NULL */4.3 整数断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_integer_equal(a, b) | a == b | 整数相等断言 |
tp_assert_integer_not_equal(a, b) | a != b | 整数不相等断言 |
tp_assert_integer_equal(os_sem_get_value(sem), 5); /* 信号量值应为5 */
tp_assert_integer_not_equal(task->stat, OS_TASK_STAT_RUNNING); /* 任务不应处于运行态 */4.4 字符串断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_str_equal(a, b) | 字符串 a 与 b 内容相等 | 调用 atest_assert_string() 实现 |
tp_assert_str_not_equal(a, b) | 字符串 a 与 b 内容不相等 | equal 参数传 OS_FALSE |
tp_assert_str_equal(task->name, "task_test"); /* 任务名称应为 "task_test" */
tp_assert_str_not_equal(version, "v1.0.0"); /* 版本号不应为 "v1.0.0" */4.5 缓冲区断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_buf_equal(a, b, size) | 缓冲区 a 与 b 前 size 字节相等 | 调用 atest_assert_buf() 实现 |
tp_assert_buf_not_equal(a, b, size) | 缓冲区 a 与 b 前 size 字节不相等 | equal 参数传 OS_FALSE |
uint8_t expected[4] = {0xAA, 0xBB, 0xCC, 0xDD};
tp_assert_buf_equal(rx_buffer, expected, 4); /* 接收缓冲区应与期望值一致 */
tp_assert_buf_not_equal(tx_buffer, rx_buffer, 256); /* 发送和接收缓冲区应不同 */4.6 范围断言
| 宏 | 通过条件 | 备注 |
|---|---|---|
tp_assert_in_range(value, min, max) | value 在 [min, max] 范围内 | 闭区间判断 |
tp_assert_not_in_range(value, min, max) | value 不在 [min, max] 范围内 | 取反判断 |
tp_assert_in_range(adc_value, 0, 4095); /* ADC值应在12位范围内 */
tp_assert_not_in_range(temperature, -40, 125); /* 温度不应在正常范围,说明异常 */五、核心API详解
5.1 atest_unit_run
extern void atest_unit_run(atest_unit_func_t func, const char *unit_func_name);执行测试单元函数 func,并统计该单元内的测试点通过/失败情况。unit_func_name 为函数名,用于日志输出。
5.2 atest_tp_stats_get
extern atest_tp_stats_t *atest_tp_stats_get(void);获取当前测试用例的测试点统计指针。ATEST_UNIT_RUN 宏内部调用此函数来判断是否继续执行后续测试单元。
5.3 atest_assert —— 底层断言函数
extern void atest_assert(os_bool_t condition, const char *file, int32_t line, const char *func, const char *msg);所有真值/整数/范围断言宏的底层实现。当 condition 为 OS_FALSE 时,记录失败并输出文件名、行号、函数名和错误信息。
5.4 atest_assert_string —— 字符串断言
extern void atest_assert_string(const char *str_a,
const char *str_b,
os_bool_t equal,
const char *file,
int32_t line,
const char *func,
const char *msg);当 equal == OS_TRUE 时,str_a 与 str_b 不相等则断言失败;当 equal == OS_FALSE 时,str_a 与 str_b 相等则断言失败。
5.5 atest_assert_buf —— 缓冲区断言
extern void atest_assert_buf(const uint8_t *buff_a,
const uint8_t *buff_b,
os_size_t size,
os_bool_t equal,
const char *file,
int32_t line,
const char *func,
const char *msg);逐字节比较 buff_a 和 buff_b 的前 size 字节。equal 参数含义同 atest_assert_string。
六、日志集成
Atest 框架支持两种日志输出模式,通过 ATEST_USING_DLOG 配置项切换(源码 atest.h 第35-59行)。
6.1 双模式实现(完整源码)
Atest 通过 ATEST_USING_DLOG 在编译期切换日志输出模式(atest.h 第35-59行)。完整源码:
#ifdef ATEST_USING_DLOG
#define ATEST_LOG_E LOG_E
#define ATEST_LOG_W LOG_W
#define ATEST_LOG_I LOG_I
#define ATEST_LOG_D LOG_D
#define ATEST_LOG_RAW LOG_RAW
#define ATEST_LOG_HEX LOG_HEX
#else /* Not define ATEST_USING_DLOG */
#include <os_clock.h>
#define ATEST_LOG_E(tag, fmt, ...) \
os_kprintf("\033[31m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)
#define ATEST_LOG_W(tag, fmt, ...) \
os_kprintf("\033[33m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)
#define ATEST_LOG_I(tag, fmt, ...) \
os_kprintf("\033[32m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)
#define ATEST_LOG_D(tag, fmt, ...) \
os_kprintf("[%d]" fmt " [%s][%d]\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)
#endif /* ATEST_USING_DLOG */6.2 模式一:启用 Dlog(ATEST_USING_DLOG)
当 ATEST_USING_DLOG 定义为 y 时,ATEST_LOG_* 直接复用 Dlog 的 LOG_E/W/I/D/RAW/HEX 宏(见 6.1 设备驱动详解的 Dlog 章节),支持级别、tag、时间戳、颜色等完整 Dlog 特性。
6.3 模式二:未启用 Dlog(默认 os_kprintf)
当 ATEST_USING_DLOG 未定义(默认)时,使用 os_kprintf 带 ANSI 颜色输出:
| 宏 | 颜色 | 说明 |
|---|---|---|
ATEST_LOG_E | 红色 \033[31m | 错误日志(断言失败) |
ATEST_LOG_W | 黄色 \033[33m | 警告日志 |
ATEST_LOG_I | 绿色 \033[32m | 信息日志(测试通过) |
ATEST_LOG_D | 默认色 | 调试日志 |
所有日志格式均为:[tick值] 消息 [函数名][行号],其中 tick 值由 os_tick_get_value() 获取,便于在时间轴上定位测试事件。
关键理解:Dlog 模式下
ATEST_LOG_*是无参宏别名(#define ATEST_LOG_E LOG_E),由 Dlog 宏自身解析tag/fmt/...;os_kprintf模式则把ATEST_LOG_E(tag, fmt, ...)定义成带参宏,tag参数被丢弃、fmt直接拼入格式串,颜色码\033[31m等写死在宏内。
七、Kconfig 配置
Atest 的配置项定义在 components/atest/Kconfig 中,通过 menuconfig 图形化配置:
menu "Atest"
config OS_USING_ATEST
bool "Enable atest"
default y
select OS_USING_SHELL
if OS_USING_ATEST
config ATEST_USING_DLOG
bool "Using dlog"
default n
select OS_USING_DLOG
config ATEST_TASK_STACK_SIZE
int "The atest task stack size"
default 4096
config ATEST_TASK_PRIORITY
int "The atest task priority"
default 5 if OS_TASK_PRIORITY_8
default 10 if OS_TASK_PRIORITY_16
default 20 if OS_TASK_PRIORITY_32
default 40 if OS_TASK_PRIORITY_64
default 80 if OS_TASK_PRIORITY_128
default 160 if OS_TASK_PRIORITY_256
endif
endmenu配置项说明:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
OS_USING_ATEST | bool | y | 启用 Atest 测试框架,自动选择 OS_USING_SHELL |
ATEST_USING_DLOG | bool | n | 使用 Dlog 日志系统输出,启用后自动选择 OS_USING_DLOG |
ATEST_TASK_STACK_SIZE | int | 4096 | 测试任务栈大小(字节),可根据测试用例复杂度调整 |
ATEST_TASK_PRIORITY | int | 视优先级位数而定 | 测试任务优先级,与系统优先级位数联动 |
八、使用示例
8.1 完整测试用例示例
以下示例展示如何编写一个完整的测试用例,包含 init、tc、cleanup 三个生命周期函数:
#include <atest.h>
#include <os_sem.h>
/* 全局变量 */
static os_semaphore_id g_test_sem = OS_NULL;
/* 初始化函数:创建测试资源 */
static os_err_t sem_tc_init(void)
{
g_test_sem = os_sem_create("test_sem", 1, OS_IPC_FLAG_FIFO);
if (OS_NULL == g_test_sem)
{
return OS_FAILURE;
}
return OS_SUCCESS;
}
/* 测试单元1:信号量获取测试 */
static void sem_take_tu(void)
{
/* 测试点1:信号量初始值应为1 */
tp_assert_integer_equal(os_sem_get_value(g_test_sem), 1);
/* 测试点2:获取信号量应成功 */
tp_assert_true(os_sem_wait(g_test_sem, OS_NO_WAIT) == OS_SUCCESS);
/* 测试点3:获取后信号量值应为0 */
tp_assert_integer_equal(os_sem_get_value(g_test_sem), 0);
}
/* 测试单元2:信号量释放测试 */
static void sem_post_tu(void)
{
/* 测试点1:释放信号量应成功 */
tp_assert_true(os_sem_post(g_test_sem) == OS_SUCCESS);
/* 测试点2:释放后信号量值应为1 */
tp_assert_integer_equal(os_sem_get_value(g_test_sem), 1);
}
/* 测试用例主体函数 */
static void sem_tc(void)
{
ATEST_UNIT_RUN(sem_take_tu); /* 执行测试单元1 */
ATEST_UNIT_RUN(sem_post_tu); /* 执行测试单元2 */
}
/* 清理函数:销毁测试资源 */
static os_err_t sem_tc_cleanup(void)
{
if (os_sem_destroy(g_test_sem) != OS_SUCCESS)
{
return OS_FAILURE;
}
return OS_SUCCESS;
}
/* 导出测试用例 */
ATEST_TC_EXPORT(semaphore_test, sem_tc, sem_tc_init, sem_tc_cleanup, TC_PRIORITY_HIGH);8.2 断言使用示例
/* === 整数断言 === */
int result = os_task_create(&task, "worker", task_entry, OS_NULL, 1024, 5);
tp_assert_integer_equal(result, OS_SUCCESS); /* 任务创建应成功 */
/* === 字符串断言 === */
const char *name = os_task_get_name(&task);
tp_assert_str_equal(name, "worker"); /* 任务名称应匹配 */
/* === 缓冲区断言 === */
uint8_t tx_data[4] = {0x01, 0x02, 0x03, 0x04};
uint8_t rx_data[4];
os_memcpy(rx_data, tx_data, sizeof(tx_data)); /* 模拟接收:把 tx_data 拷入 rx_data */
tp_assert_buf_equal(rx_data, tx_data, 4); /* 收发数据应一致 */
/* === 范围断言 === */
uint32_t tick = os_tick_get_value();
tp_assert_in_range(tick, 0, 10000); /* tick值应在合理范围内 */
/* === NULL 断言 === */
void *ptr = os_malloc(256);
tp_assert_not_null(ptr); /* 内存分配应成功 */8.3 运行结果示例
测试用例执行时,Atest 框架会在控制台输出类似以下格式的日志:
[12345]========================= Run TestCase: semaphore_test =========================
[12346][passed] sem_take_tu - tp1: (os_sem_get_value(g_test_sem)) equal to (1)
[12347][passed] sem_take_tu - tp2: (os_sem_wait(g_test_sem, OS_NO_WAIT) == OS_SUCCESS) is true
[12348][passed] sem_take_tu - tp3: (os_sem_get_value(g_test_sem)) equal to (0)
[12349][passed] sem_post_tu - tp1: (os_sem_post(g_test_sem) == OS_SUCCESS) is true
[12350][passed] sem_post_tu - tp2: (os_sem_get_value(g_test_sem)) equal to (1)
[12351]========================= TestCase Result: PASSED =========================📝 本节小结
核心要点回顾
- 三级层次结构:TestCase(TC)→ TestUnit(TU)→ TestPoint(TP),粒度从粗到细;一个 TC 含多个 TU,一个 TU 含多个 TP(一个断言即一个 TP)。
- 段导出机制:
ATEST_TC_EXPORT用OS_SECTION("AtestTcTab")把atest_tc_entry_t编译时导出到专用段,运行时遍历该段即可收集全部测试用例,零注册函数调用。 - 测试用例生命周期:
init(初始化,失败则不执行)→tc(主体,内含ATEST_UNIT_RUN)→cleanup(清理,无论成败都执行)。 - 失败即停止:
ATEST_UNIT_RUN内部检查tp_failed_num != 0就return,避免级联错误。 - 12 种断言宏:真值/NULL/整数/字符串/缓冲区/范围各正反一对,全部落到
atest_assert/atest_assert_string/atest_assert_buf三个底层函数,并通过#value字符串化打印表达式原文。 - 双日志模式:
ATEST_USING_DLOG编译期切换 Dlog 宏别名(无参)或os_kprintf带颜色宏(带参、写死 ANSI 颜色码)。 - 优先级:
TC_PRIORITY_HIGH/MIDDLE/LOW,测试执行时可按优先级排序/筛选。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| 在源码中给结构体/宏加中文注释 | 违反"逐字一致"规范 → 英文注释保留,中文解释写正文 |
| 以为断言宏需要手动传文件/行号 | 宏内部已用 __FILE__/__LINE__/__func__ 自动填充 → 只需传被测表达式 |
混淆 ATEST_LOG_* 两种模式的参数 | Dlog 模式是无参宏别名、os_kprintf 模式是带参宏 → 用 tag/fmt 时以对应模式为准 |
忘写 ATEST_UNIT_RUN 失败返回 | 不检查 tp_failed_num 会导致失败用例继续跑后续单元 → 始终用 ATEST_UNIT_RUN 包裹 |
断言宏误用 = 赋值 | tp_assert_integer_equal(a, b) 内部 (a) == (b) → 传两个值,不是单表达式 |
自定义 atest_tc_entry 不导出段 | 直接定义变量不会自动注册 → 必须用 ATEST_TC_EXPORT 放进 AtestTcTab 段 |
忘记 cleanup 释放资源 | 断言失败提前 return 时靠 cleanup 收尾 → 所有测试用例都要写清理函数 |
下一步
- 6.4 栈回溯:了解 OneOS 的异常栈回溯(Stack Backtrace)机制,掌握在系统崩溃时解析调用栈、定位问题函数的调试方法。