11 - 编写第一个测试
·
🎯 学习目标
完成本章后,你将能够:
- ✅ 从零开始创建一个新的测试文件
- ✅ 编写包含多个子测试的完整测试
- ✅ 将测试添加到构建系统
- ✅ 编译并运行自己的测试
- ✅ 理解测试的最佳实践
📝 准备工作
前置知识
确保你已经:
- ✅ 完成环境搭建(第 3 章)
- ✅ 理解测试框架基础(第 4 章)
- ✅ 知道如何运行测试(第 5 章)
开发环境
# 确认 IGT 已编译
cd /path/to/igt-gpu-tools
ls -la build/
# 确认有写权限
ls -la tests/
🚀 第一步:创建测试文件
选择测试位置
根据测试类型选择目录:
tests/
├── core_*.c # DRM 核心测试
├── kms_*.c # KMS 显示测试
├── drm_*.c # 通用 DRM 测试
├── prime_*.c # PRIME 共享测试
├── i915/ # Intel 特定测试
├── amdgpu/ # AMD 特定测试
└── my_test.c # 你的测试 ✨
创建测试文件
让我们创建一个名为 my_first_test.c 的测试:
cd tests/
touch my_first_test.c
✍️ 第二步:编写基本测试结构
最小测试模板
/*
* Copyright © 2026 Your Name
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "igt.h"
IGT_TEST_DESCRIPTION("我的第一个 IGT 测试");
igt_simple_main
{
igt_info("Hello, IGT!\n");
igt_assert(true);
}
测试说明
关键元素:
- 版权声明:必须包含(MIT 许可证)
- 头文件:
#include "igt.h"- 包含所有 IGT 功能 - 测试描述:
IGT_TEST_DESCRIPTION()- 描述测试目的 - 主函数:
igt_simple_main- 最简单的测试入口
🧪 第三步:添加子测试
使用 igt_main 和子测试
#include "igt.h"
IGT_TEST_DESCRIPTION("演示子测试的使用");
igt_main
{
int fd;
igt_fixture {
fd = drm_open_driver(DRIVER_ANY);
igt_require(fd >= 0);
}
igt_subtest("basic") {
igt_info("运行基本测试\n");
igt_assert(fd >= 0);
}
igt_subtest("query-version") {
drmVersionPtr version;
version = drmGetVersion(fd);
igt_assert(version != NULL);
igt_info("驱动: %s\n", version->name);
igt_info("版本: %d.%d.%d\n",
version->version_major,
version->version_minor,
version->version_patchlevel);
drmFreeVersion(version);
}
igt_fixture {
close(fd);
}
}
结构说明
igt_main {
igt_fixture {
// 所有测试前的准备
}
igt_subtest("test-1") {
// 子测试 1
}
igt_subtest("test-2") {
// 子测试 2
}
igt_fixture {
// 所有测试后的清理
}
}
📦 完整示例:DRM 设备信息测试
让我们编写一个完整的实用测试:
my_first_test.c
/*
* Copyright © 2026 Your Name
* SPDX-License-Identifier: MIT
*/
#include "igt.h"
#include <fcntl.h>
#include <string.h>
IGT_TEST_DESCRIPTION("测试 DRM 设备信息查询功能");
/**
* SUBTEST: device-info
* Description: 查询并显示 DRM 设备基本信息
* Category: basic
* Run type: FULL
*/
/**
* SUBTEST: version-info
* Description: 查询并验证驱动版本信息
* Category: basic
* Run type: FULL
*/
/**
* SUBTEST: multiple-open
* Description: 测试多次打开关闭设备
* Category: stress
* Run type: FULL
*/
static void print_device_info(int fd)
{
drmVersionPtr version;
char *busid;
version = drmGetVersion(fd);
igt_assert(version);
igt_info("=== 设备信息 ===\n");
igt_info("驱动名称: %s\n", version->name);
igt_info("驱动描述: %s\n", version->desc);
igt_info("驱动日期: %s\n", version->date);
igt_info("驱动版本: %d.%d.%d\n",
version->version_major,
version->version_minor,
version->version_patchlevel);
busid = drmGetBusid(fd);
if (busid) {
igt_info("Bus ID: %s\n", busid);
drmFreeBusid(busid);
}
drmFreeVersion(version);
}
static void verify_version(int fd)
{
drmVersionPtr version;
version = drmGetVersion(fd);
igt_require(version);
/* 验证基本字段 */
igt_assert(version->name);
igt_assert(strlen(version->name) > 0);
igt_assert(version->name_len > 0);
/* 验证描述 */
igt_assert(version->desc);
igt_assert(strlen(version->desc) > 0);
/* 验证日期 */
igt_assert(version->date);
igt_assert(strlen(version->date) > 0);
/* 版本号应该合理 */
igt_assert(version->version_major >= 0);
igt_assert(version->version_minor >= 0);
igt_assert(version->version_patchlevel >= 0);
drmFreeVersion(version);
}
igt_main
{
int fd = -1;
igt_fixture {
fd = drm_open_driver(DRIVER_ANY);
igt_require(fd >= 0);
igt_info("成功打开 DRM 设备: fd=%d\n", fd);
}
igt_describe("查询并显示设备基本信息");
igt_subtest("device-info") {
print_device_info(fd);
}
igt_describe("验证驱动版本信息的完整性");
igt_subtest("version-info") {
verify_version(fd);
}
igt_describe("测试设备可以被多次打开和关闭");
igt_subtest("multiple-open") {
int test_fd;
for (int i = 0; i < 10; i++) {
test_fd = drm_open_driver(DRIVER_ANY);
igt_assert(test_fd >= 0);
/* 验证每次打开都能获取版本信息 */
verify_version(test_fd);
close(test_fd);
}
igt_info("成功完成 10 次打开/关闭循环\n");
}
igt_subtest_group {
igt_fixture {
igt_info("进入子测试组\n");
}
igt_describe("测试设备名称长度");
igt_subtest("name-length") {
drmVersionPtr version = drmGetVersion(fd);
igt_assert(version);
igt_assert(version->name_len > 0);
igt_assert(version->name_len < 256);
drmFreeVersion(version);
}
igt_describe("测试驱动描述长度");
igt_subtest("desc-length") {
drmVersionPtr version = drmGetVersion(fd);
igt_assert(version);
igt_assert(version->desc_len > 0);
igt_assert(version->desc_len < 1024);
drmFreeVersion(version);
}
}
igt_fixture {
close(fd);
igt_info("测试完成,已关闭设备\n");
}
}
代码亮点
1. 文档注释:
/**
* SUBTEST: device-info
* Description: 查询并显示 DRM 设备基本信息
* Category: basic
* Run type: FULL
*/
这些注释会被自动提取生成文档。
2. 辅助函数:
static void print_device_info(int fd)
static void verify_version(int fd)
将复杂逻辑封装成函数,提高可读性。
3. 子测试组:
igt_subtest_group {
igt_fixture { /* 组准备 */ }
igt_subtest("test-1") { }
igt_subtest("test-2") { }
}
组织相关的测试。
🔧 第四步:添加到构建系统
编辑 meson.build
在 tests/meson.build 中添加你的测试:
cd tests/
vim meson.build
找到测试列表部分,添加:
# 在适当的位置添加
test_progs = [
'core_auth',
'core_getclient',
# ... 其他测试 ...
'my_first_test', # ← 添加你的测试
]
完整的 meson.build 示例
如果你想更精确地控制:
# 在 tests/meson.build 中
my_first_test = executable(
'my_first_test',
'my_first_test.c',
dependencies: test_deps,
install: true,
install_dir: libexecdir / 'igt-gpu-tools',
)
test(
'my_first_test',
my_first_test,
is_parallel: false,
timeout: 60,
)
🏗️ 第五步:编译测试
重新配置构建
cd /path/to/igt-gpu-tools
# 重新配置(如果需要)
meson setup --reconfigure build/
# 或者直接编译
meson compile -C build/
只编译你的测试
# 使用 ninja 只编译特定目标
ninja -C build/ my_first_test
# 验证编译成功
ls -lh build/tests/my_first_test
编译输出
[1/1] Linking target tests/my_first_test
NOTICE: You are running this build from a shallow git checkout which may
make some operations unavailable. To fix this, please run
'git fetch --unshallow' and re-configure the build.
Build targets in project: 386
▶️ 第六步:运行测试
列出子测试
# 查看所有子测试
./build/tests/my_first_test --list-subtests
# 输出:
# device-info
# version-info
# multiple-open
# name-length
# desc-length
运行所有子测试
sudo ./build/tests/my_first_test
# 或指定子测试
sudo ./build/tests/my_first_test --run-subtest device-info
期望输出
IGT-Version: 1.28-gxxxx (x86_64) (Linux: 6.x.x)
Starting subtest: device-info
成功打开 DRM 设备: fd=3
=== 设备信息 ===
驱动名称: i915
驱动描述: Intel Graphics
驱动日期: 20230101
驱动版本: 1.6.0
Bus ID: pci:0000:00:02.0
Subtest device-info: SUCCESS (0.001s)
Starting subtest: version-info
Subtest version-info: SUCCESS (0.000s)
Starting subtest: multiple-open
成功完成 10 次打开/关闭循环
Subtest multiple-open: SUCCESS (0.002s)
Starting subtest: name-length
Subtest name-length: SUCCESS (0.000s)
Starting subtest: desc-length
Subtest desc-length: SUCCESS (0.000s)
测试完成,已关闭设备
📊 第七步:测试验证
验证测试正确性
1. 检查所有子测试都能运行:
for test in $(./build/tests/my_first_test --list-subtests); do
echo "测试: $test"
sudo ./build/tests/my_first_test --run-subtest $test
done
2. 测试失败场景:
添加一个故意失败的测试:
igt_subtest("intentional-failure") {
igt_info("这个测试应该失败\n");
igt_assert_eq(1, 2); // 故意失败
}
运行并观察输出:
sudo ./build/tests/my_first_test --run-subtest intentional-failure
# 输出:
# Starting subtest: intentional-failure
# 这个测试应该失败
# (my_first_test:12345) CRITICAL: Test assertion failure function main, file my_first_test.c:123:
# (my_first_test:12345) CRITICAL: Failed assertion: 1 == 2
# Subtest intentional-failure: FAIL (0.000s)
3. 测试跳过场景:
igt_subtest("skip-test") {
igt_require(false); // 条件不满足,跳过
igt_info("不会执行到这里\n");
}
🎨 实践练习
练习 1:Hello World 测试
创建 tests/hello_igt.c:
#include "igt.h"
IGT_TEST_DESCRIPTION("Hello IGT 测试");
igt_simple_main
{
igt_info("Hello, IGT World!\n");
igt_info("当前进程 PID: %d\n", getpid());
igt_info("运行用户 UID: %d\n", getuid());
}
任务:
- 添加到 meson.build
- 编译并运行
- 观察输出
练习 2:文件操作测试
创建一个测试读写临时文件:
#include "igt.h"
#include <fcntl.h>
#include <unistd.h>
IGT_TEST_DESCRIPTION("文件操作测试");
igt_main
{
char tmpfile[64];
int fd;
igt_subtest("create-file") {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/igt-test-%d", getpid());
fd = open(tmpfile, O_CREAT | O_RDWR, 0644);
igt_assert(fd >= 0);
igt_info("创建文件: %s\n", tmpfile);
close(fd);
}
igt_subtest("write-file") {
const char *data = "IGT Test Data\n";
ssize_t ret;
fd = open(tmpfile, O_WRONLY);
igt_assert(fd >= 0);
ret = write(fd, data, strlen(data));
igt_assert_eq(ret, strlen(data));
close(fd);
}
igt_subtest("read-file") {
char buffer[64] = {0};
ssize_t ret;
fd = open(tmpfile, O_RDONLY);
igt_assert(fd >= 0);
ret = read(fd, buffer, sizeof(buffer));
igt_assert(ret > 0);
igt_info("读取内容: %s", buffer);
close(fd);
}
igt_fixture {
unlink(tmpfile);
}
}
练习 3:KMS 测试
创建一个简单的 KMS 测试:
#include "igt.h"
#include "igt_kms.h"
IGT_TEST_DESCRIPTION("简单的 KMS 测试");
igt_main
{
igt_display_t display;
igt_output_t *output;
int fd;
igt_fixture {
fd = drm_open_driver_master(DRIVER_ANY);
igt_display_require(&display, fd);
}
igt_subtest("list-outputs") {
int count = 0;
for_each_connected_output(&display, output) {
drmModeModeInfo *mode;
mode = igt_output_get_mode(output);
igt_info("输出 %d: %s\n", count,
igt_output_name(output));
igt_info(" 模式: %dx%d@%d\n",
mode->hdisplay,
mode->vdisplay,
mode->vrefresh);
count++;
}
igt_info("总共 %d 个已连接的输出\n", count);
}
igt_fixture {
igt_display_fini(&display);
close(fd);
}
}
✅ 最佳实践
1. 命名约定
测试文件名:
core_* - DRM 核心测试
kms_* - KMS 显示测试
gem_* - GEM 内存管理(Intel)
amd_* - AMD 特定测试
子测试名:
basic - 基础功能
invalid-* - 无效参数测试
stress-* - 压力测试
suspend-* - 电源管理测试
2. 代码风格
遵循 Linux 内核风格:
// ✅ 好的风格
static void helper_function(int fd)
{
if (condition) {
do_something();
}
}
// ❌ 不好的风格
static void helperFunction(int fd){
if(condition){
do_something();
}
}
3. 错误处理
// ✅ 正确的错误处理
int fd = drm_open_driver(DRIVER_ANY);
igt_require(fd >= 0);
// ✅ 使用断言验证
drmVersionPtr ver = drmGetVersion(fd);
igt_assert(ver != NULL);
// ✅ 清理资源
drmFreeVersion(ver);
close(fd);
4. 日志输出
// 使用合适的日志级别
igt_debug("调试信息:细节较多\n");
igt_info("一般信息:正常输出\n");
igt_warn("警告信息:可能有问题\n");
igt_critical("严重错误:必须注意\n");
5. 测试隔离
igt_main
{
// 每个 fixture 独立
igt_subtest("test-1") {
int fd = drm_open_driver(DRIVER_ANY);
// 测试逻辑
close(fd);
}
igt_subtest("test-2") {
// test-2 不依赖 test-1 的状态
int fd = drm_open_driver(DRIVER_ANY);
// 测试逻辑
close(fd);
}
}
🐛 常见问题
问题 1:编译失败
错误:
undefined reference to `drm_open_driver'
解决:
# 确保 meson.build 包含正确的依赖
executable('my_test',
'my_test.c',
dependencies: test_deps, # ← 必须有这个
install: true,
)
问题 2:找不到测试
错误:
bash: ./build/tests/my_test: No such file or directory
解决:
# 检查是否添加到 test_progs
grep "my_test" tests/meson.build
# 重新编译
ninja -C build/ my_test
问题 3:权限问题
错误:
Could not open DRM device: Permission denied
解决:
# 使用 sudo 运行
sudo ./build/tests/my_test
# 或添加到 video 组
sudo usermod -a -G video $USER
# 注销后重新登录
问题 4:设备不支持
错误:
Test requirement not met in function main
解决:
// 添加更宽松的要求
igt_fixture {
fd = drm_open_driver(DRIVER_ANY); // 接受任意驱动
if (fd < 0) {
igt_skip("No DRM device available\n");
}
}
📚 下一步
恭喜!你已经学会了编写 IGT 测试。接下来:
👉 继续阅读:12-调试技巧 - 学习如何调试测试
或者:
- 研究现有测试:
tests/core_*.c - 尝试更复杂的测试:KMS、GEM 等
- 阅读 API 文档:
lib/igt_*.h
📎 参考资料
示例测试
tests/core_auth.c- 简单的核心测试tests/kms_addfb_basic.c- 基础 KMS 测试tests/template.c- 测试模板(如果存在)
构建系统
tests/meson.build- 测试构建配置meson_options.txt- 构建选项
代码风格
- Linux 内核编码风格:https://www.kernel.org/doc/html/latest/process/coding-style.html
- IGT 贡献指南:
CONTRIBUTING.md
API 文档
lib/igt_core.h- 核心测试框架lib/drmtest.h- DRM 测试辅助lib/igt_kms.h- KMS 测试框架
💡 提示
快速开始模板
#include "igt.h"
IGT_TEST_DESCRIPTION("测试描述");
igt_main
{
int fd;
igt_fixture {
fd = drm_open_driver(DRIVER_ANY);
}
igt_subtest("my-test") {
// 你的测试代码
}
igt_fixture {
close(fd);
}
}
调试技巧
# 启用调试输出
IGT_LOG_LEVEL=debug sudo ./build/tests/my_test
# 使用 gdb 调试
sudo gdb --args ./build/tests/my_test --run-subtest my-test
# 查看详细输出
sudo ./build/tests/my_test --debug
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)