xt-sdk 文档 v1.0.3
xt-sdk 嵌入式 SDK API 参考
载入中...
搜索中...
未找到
测试规范

版本:v1.0 | 日期:2026-07-03

本规范规定 xt-sdk 测试代码的编写标准。所有新增测试必须遵循本规范。

配套阅读:编码规范(§14 测试代码规范)、代码注释指南。


一、文件组织

1.1 文件头

测试文件头使用 @details 描述覆盖范围(必需),格式与编码规范 §1.2 一致:

/**
* @file xt_main.c
* @author your name (you@domain.com)
* @brief xt_vfs 单元测试套件——覆盖 init/deinit、mount/unmount、open/close 等 71 项测试
* @details 每个测试用例通过 setUp/tearDown 管理环境初始化和清理。基于 Unity 测试框架运行。
* @version 0.1
* @date 2026-07-03
*
* SPDX-FileCopyrightText: 2026 深圳市天工聚创科技有限公司
* SPDX-License-Identifier: Apache-2.0
*
*/

1.2 include 顺序

标准库 → Unity → 平台基础头 → 依赖模块 → 被测模块:

#include <string.h>
#include <stdio.h>
#include "unity.h"
#include "xtiny.h"
#include "xt_task.h"
#include "xt_vfs.h"

**规则**:

  • 标准库( <...> )在最前面
  • Unity 和平台基础头紧跟标准库
  • 被测模块头文件放在最后

1.3 全局变量

文件作用域的静态变量使用 s_ 前缀。回调间共享的状态使用 volatile :

static xt_vfs_config_t s_cfg;
static volatile int s_subscribe_count;
static volatile int s_unsubscribe_count;

二、setUp / tearDown

2.1 格式

setUp 和 tearDown 使用 @name 分组,格式固定:

/** @name setUp / tearDown */
/** @{ */
/**
* @brief 每个测试用例执行前初始化环境
*/
void setUp(void)
{
// 1. 重置状态
// 2. 初始化模块
// 3. 确认初始化成功
}
/**
* @brief 每个测试用例执行后清理
*/
void tearDown(void)
{
// 清理临时文件/资源
}
/** @} */

2.2 职责划分

函数 职责 说明
setUp 重置全局状态 → 初始化模块 → 断言 init 成功 确保每个测试从干净状态开始
tearDown 清理临时资源、反初始化 可以为空(当 setUp 已充分重置时)

2.3 关键规则

  • setUp 和 tearDown 不加 static (Unity 框架要求)
  • setUp 和 tearDown 放在辅助函数之后、测试分组之前
  • 如果模块需要 deinit,应在 tearDown 中调用或在 setUp 开头先清理旧实例

三、测试分组

3.1 @name 格式

测试按功能分组的统一格式:

/** @name 1. init/deinit (3 项测试) */
/** @{ */
/**
* @brief 正常初始化后 is_active 应为 true
*/
static void test_init_ok(void)
{
xt_vfs_err_t ret = xt_vfs_init(&s_cfg);
TEST_ASSERT_EQUAL(XT_VFS_ERR_OK, ret);
TEST_ASSERT_TRUE(xt_vfs_is_active());
xt_vfs_deinit();
}
/** @} */

**规则**:

  • 使用 N. 中文分组名 (N 项测试) 格式
  • 数字编号从 1 递增
  • @{ 和 @} 配对使用
  • 每个测试函数前必须有 @brief 注释

3.2 辅助函数

测试辅助函数也作为独立分组放在 setUp 之前:

/** @name 辅助函数 */
/** @{ */
static void _write_file(const char *path, const void *data, xt_vfs_size_t size)
{
/* ... */
}
/** @} */

四、测试函数命名

4.1 命名规则

test_ 前缀 + snake_case + 语义化描述:

  • test_<api>_<scenario>:如 test_init_ok、test_mount_ok
  • test_<scenario>_<expected>:如 test_generation_rejects_old_fd、test_double_init_returns_busy
  • 看函数名即能理解测试意图

4.2 示例

static void test_init_ok(void); // 正常初始化
static void test_double_init(void); // 重复初始化
static void test_open_read_write_then_read_back(void); // 读写回读
static void test_null_path_open(void); // NULL 路径错误处理
static void test_write_null_buffer(void); // NULL 缓冲区错误处理
static void test_fd_generation_rejects_old(void); // generation 防悬空

五、测试函数内结构

5.1 三段式结构

每个测试函数按 Given / When / Then 三段组织:

/**
* @brief 正常打开文件后 is_active 应为 true,返回有效 fd
*/
static void test_open_ok(void)
{
// Given: 初始化环境,准备测试数据
const char *path = "/native/test_open_ok.txt";
xt_vfs_fd_t fd;
// When: 执行被测试操作
fd = xt_vfs_open(path, XT_VFS_O_CREAT | XT_VFS_O_RDWR | XT_VFS_O_TRUNC);
// Then: 验证结果
TEST_ASSERT_NOT_EQUAL(XT_VFS_INVALID_FD, fd);
// Cleanup
xt_vfs_close(fd);
xt_vfs_remove(path);
xt_printf("[LOG] test_open_ok: fd=%d\n", fd);
}

**规则**:

  • // Given :准备输入、初始化状态
  • // When :调用被测 API
  • // Then :断言验证,可有多组断言
  • // Cleanup :释放资源(如有)
  • 函数末尾可以用 xt_printf 输出关键变量值供调试(非强制)

5.2 @brief 写法

@brief 用一句话描述测试场景和期望结果,含被测 API 名:

  • 正常: 正常初始化后 is_active 应为 true
  • 错误: 传入 NULL 路径应返回 XT_VFS_INVALID_FD
  • 边界: 文件名恰好等于 XT_VFS_FILENAME_MAX 时应成功
  • 重入: 回调中 delete self 应安全释放

六、断言宏

6.1 常用断言

断言宏 用途 示例
TEST_ASSERT_EQUAL(expected, actual) 整数/枚举值比较 TEST_ASSERT_EQUAL(XT_EOK, ret)
TEST_ASSERT_NOT_EQUAL(expected, actual) 不等于,常用于 fd 有效性 TEST_ASSERT_NOT_EQUAL(XT_VFS_INVALID_FD, fd)
TEST_ASSERT_TRUE(condition) 布尔值为真 TEST_ASSERT_TRUE(xt_vfs_is_active())
TEST_ASSERT_FALSE(condition) 布尔值为假 TEST_ASSERT_FALSE(xt_vfs_is_active())
TEST_ASSERT_NULL(ptr) 指针为空 TEST_ASSERT_NULL(ptr)
TEST_ASSERT_NOT_NULL(ptr) 指针非空 TEST_ASSERT_NOT_NULL(ptr)
TEST_ASSERT_EQUAL_STRING(expected, actual) 字符串比较 TEST_ASSERT_EQUAL_STRING("hello", buf)
TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) 二进制比较 TEST_ASSERT_EQUAL_MEMORY(data, buf, 5)

6.2 整数范围断言

断言宏 用途
TEST_ASSERT_GREATER_THAN(threshold, actual) 大于
TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) 大于等于
TEST_ASSERT_LESS_THAN(threshold, actual) 小于
TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) 小于等于

6.3 选择原则


七、入口函数

7.1 标准格式

/**
* @brief Unity 测试入口
*
* 测试分组顺序:
* 1. init/deinit (3)
* 2. mount/unmount (5)
* 3. open/close (8)
* ...
*
* 共计 71 项测试。
*/
int xt_main(void)
{
UNITY_BEGIN();
/* 1. init/deinit */
RUN_TEST(test_init_ok);
RUN_TEST(test_double_init);
RUN_TEST(test_deinit_resets_state);
/* 2. mount/unmount */
RUN_TEST(test_mount_ok);
/* ... */
return UNITY_END();
}

**规则**:

  • 函数名固定为 xt_main(Unity 框架要求)
  • 注释中列出所有分组及项数、总计
  • RUN_TEST 按分组顺序排列,用 /* N. 分组名 */ 注释分隔
  • RUN_TEST 参数是函数名,不加 &

7.2 条件编译

某些测试可用 #if 包裹:

#if XT_VFS_USE_HEAP
/* 3. 堆模式 */
RUN_TEST(test_heap_alloc);
#endif

八、xmake.lua 模板

8.1 最小模板

local TARGET_NAME = "xt_main"
target(TARGET_NAME)
set_kind("static")
add_deps("xt_core")
add_deps("xt_unity")
add_files("xt_main.c")
target_end()

8.2 需额外依赖时

target(TARGET_NAME)
set_kind("static")
add_deps("xt_core")
add_deps("xt_unity")
add_deps("xt_vfs") -- 被测模块
add_deps("xt_vfs_native") -- 测试用的后端
add_files("xt_main.c")
target_end()

8.3 构建与运行

cd tests/<模块名>
xt --target windows/simulator fullclean build
xt --target windows/simulator run

九、测试覆盖场景

9.1 场景分类

测试应覆盖以下场景类型,按优先级排列:

优先级 场景类型 说明 示例
必须 正常路径 标准输入的标准行为 test_init_ok、test_write_then_read_back
必须 错误处理 NULL 参数、无效句柄、不存在的资源、未初始化 test_null_path_open、test_close_invalid_fd
必须 边界条件 空文件、零长度、路径边界、最大/最小值 test_read_empty_file、test_start_int32max
推荐 幂等/重复 重复 init、重复 subscribe、重复 stop test_double_init、test_stop_idempotent
推荐 重入安全 回调中操作自身/其他对象、嵌套调用 test_cb_delete_self、test_cb_start_other
推荐 资源耗尽 表满、slot 复用 test_open_file_table_full、test_mount_table_full
推荐 状态转换 状态机正确性 test_is_running_false_after_stop
按需 防悬空/UAF generation 校验、bitmap 清除 test_generation_rejects_old_fd
按需 并发/隔离 多后端隔离、多事件隔离 test_two_backends_independent
按需 回绕安全 整数回绕、tick 溢出 test_remain_wraparound
按需 深度/容量限制 嵌套限制、队列溢出 test_nested_publish_depth_limit

9.2 递进式测试策略

  1. 先测**生命周期**(init → deinit)
  2. 再测**基本功能**(open/read/write/close)
  3. 然后测**边界和错误**(NULL、空、满、无效值)
  4. 最后测**重入和复杂场景**(回调中操作、嵌套、并发)

十、多配置自动化测试

10.1 run_tests.py 模式

通过 Python 脚本驱动多配置 xmake 测试(参考 tests/xt_vfs/run_tests.py ):

configs = [
{"desc": "默认全功能", "env": {}},
{"desc": "静态内存", "env": {"XT_VFS_USE_HEAP": "0"}},
{"desc": "最小裸机", "env": {"XT_VFS_USE_HEAP": "0", "XT_VFS_ENABLE_THREAD_SAFE": "0"}},
# ...
]
for config in configs:
set_env(config["env"])
run("fullclean build")
run("run")

配合 xmake.lua 中的 set_config 使用:

if os.getenv("XT_VFS_USE_HEAP") then
add_defines("XT_VFS_USE_HEAP=" .. os.getenv("XT_VFS_USE_HEAP"))
end

十一、doxygen snippet 标记

11.1 用途

当 T3 文档需要引用测试代码中的反模式示例时,用 //! [tag] 标记代码片段:

// 在测试代码中标记:
//! [anti_pattern_callback_stop]
void bad_callback(void *arg) {
xt_timer2_t *timer = (xt_timer2_t *)arg;
xt_timer2_stop(timer, true); // 错误:回调中同步释放
}
//! [anti_pattern_callback_stop]

11.2 在文档中引用

\snippet tests/xt_timer2/xt_main.c anti_pattern_callback_stop

标记名使用 snake_case,清晰描述 snippet 的用途。


十二、回调测试模式

12.1 前向声明回调

回调中需要引用静态函数时,在文件顶部前向声明:

static void on_subscribe(void *param, void *user_data);
static void on_event_a(void *param, void *user_data);

12.2 跨回调计数

使用 static volatile 变量在回调间传递计数信息:

static volatile int s_subscribe_count;
static volatile int s_callback_count;
static void on_event(void *param, void *user_data)
{
s_callback_count++;
xt_printf("[LOG] on_event: count=%d\n", s_callback_count);
}

12.3 常见回调测试场景

场景 测试要点
回调删除自身 delete(self) 后不访问已释放内存
回调操作其他对象 delete(other) 或 start(other) 的隔离性
回调中重新订阅 订阅后当前轮次不被调用(快照一致性)
回调嵌套 嵌套深度限制和位图隔离

十三、相关文档

  • 编码规范 §14 — 测试文件语法模板(文件头、