ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RIOT 构建系统测试工具验证:深入 `tests/build_system/test_tools` 的板级测试链路剖析

RIOT 构建系统测试工具验证:深入 `tests/build_system/test_tools` 的板级测试链路剖析 RIOT 构建系统测试工具验证深入tests/build_system/test_tools的板级测试链路剖析【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT导读本文聚焦 RIOT 操作系统仓库中 tests/build_system/test_tools/README.md 所定义的test_tools测试应用剖析它如何验证测试工具与板卡/测试环境集成这一核心前提。文章将逐一拆解该测试的四个 shell 命令true、shellping、toupper、getchar、make term终端链路的TERMPROG/TERMFLAGS配置机制以及01-run.py中针对本地回显、输出纯净度、空行发送等测试假设的验证方法。读完本文你将掌握 RIOT 中板卡测试前置验证的标准范式并能在自己的板卡或测试环境中复现同样的验证流程。一、test_tools测试的定位与目的test_tools是 RIOT 构建系统测试套件tests/build_system中的一个特殊应用其 README 开门见山This test is here to verify the test tools integration with your board and test setup. It verify the assumptions required for testing on the board behaviour throughmake term.即该测试不验证任何业务功能而是验证你的板卡 你的测试工具链能否满足 RIOT 自动化测试的基本前提。这些前提包括通过make term能否打开与板卡的终端会话测试运行时本地终端是否关闭了回显echo避免测试脚本把本地输入误判为设备输出板卡 shell 是否不输出多余的提示符prompt保证测试脚本读到的是纯净的设备输出是否能够向设备发送空行仅一个换行符而不触发本地终端重复上一次命令终端行编辑模式的影响。换言之test_tools是测试的测试meta-test它先于任何功能测试运行用来确认测试环境本身是可信的。这正是它被放置在 tests/build_system 目录下的原因——它属于构建系统与测试基础设施层面的自检。二、测试应用主体main.c中的四个自检命令测试应用的 C 代码位于 tests/build_system/test_tools/main.c。它并没有实现复杂逻辑而是注册了四个经过精心设计的 shell 命令让测试脚本可以通过终端交互来验证不同假设。2.1 编译期前提禁用 shell 回显与提示符main.c顶部有一段硬性约束#if !IS_ACTIVE(CONFIG_SHELL_NO_ECHO) || !IS_ACTIVE(CONFIG_SHELL_NO_PROMPT) #error This test assumes no shell echo or shell prompt #endif它要求编译时同时启用CONFIG_SHELL_NO_ECHO与CONFIG_SHELL_NO_PROMPT否则直接编译失败。这两个配置的默认值定义在 sys/include/shell.h 中#ifndef CONFIG_SHELL_NO_ECHO #define CONFIG_SHELL_NO_ECHO 0 #endif #ifndef CONFIG_SHELL_NO_PROMPT #define CONFIG_SHELL_NO_PROMPT 0 #endif默认都为 0即默认 shell 会回显、会打印提示符因此必须显式开启。这一设计用意很明确RIOT 的自动化测试脚本通过串口解析设备输出任何多余的回显字符或提示符都会污染解析结果。test_tools通过#error把这条约定从约定升级为编译期强制约束。2.2true什么都不做但成功返回static int cmd_true(int argc, char **argv) { (void)argc; (void)argv; return 0; } SHELL_COMMAND(true, do nothing, successfully, cmd_true);语义与 coreutils 的true完全一致接受任意参数恒返回 0。它在本测试中用于验证本地终端没有回显——测试脚本发送true this should not be echoed如果本地回显开启脚本会立即收到该字符串测试即失败详见 4.2 节。2.3shellpingshell 就绪握手static int cmd_shellping(int argc, char **argv) { (void)argc; (void)argv; puts(shellpong); return 0; } SHELL_COMMAND(shellping, Just print shellpong, cmd_shellping);shellping是 shell 的ping/pong握手发送shellping设备应答shellpong。这是测试脚本判断shell 是否就绪、串口链路是否通畅的标准手段。值得注意的是Makefile 中明确注释道# No need for test_utils_interactive_sync in this test since the test # synchronizes by itself through shellping command. DISABLE_MODULE test_utils_interactive_sync即本测试主动禁用了通用的test_utils_interactive_sync同步机制改由shellping自同步从而让测试脚本独立地验证这套握手逻辑本身是否可靠。2.4toupper输出纯净度验证static int cmd_toupper(int argc, char **argv) { if (argc ! 2) { puts(Invalid number of argument); printf(Usage: %s word\n, argv[0]); return 1; } size_t len strlen(argv[1]); for (size_t i 0; i len; i) { char c toupper((int)argv[1][i]); putchar(c); } putchar(\n); return 0; } SHELL_COMMAND(toupper, uppercase first argument, cmd_toupper);toupper把第一个参数转换为大写后输出源码中对char做了(int)显式转换以避免部分编译器对数组下标类型为 char的告警。它用于验证设备输出是否纯净测试脚本发送toupper lowercase后读到的下一行必须恰好是LOWERCASE中间不允许夹杂提示符、日志或其他终端输出。2.5getchar字符级输入验证static int cmd_getchar(int argc, char **argv) { (void)argc; (void)argv; printf(%s 0x%02x\n, argv[0], getchar()); return 0; } SHELL_COMMAND(getchar, Get one character and print the hex value, cmd_getchar);getchar读取一个字符并打印其十六进制值。它用于验证发送空行仅一个换行符0x0a的能力如果本地终端处于行编辑模式并重复上次命令那么发送空行后设备收到的将不是换行符测试脚本也就无法读到期望的getchar 0x0a。2.6 主循环int main(void) { puts(Running tests_tools application); char line_buf[SHELL_DEFAULT_BUFSIZE]; shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); return 0; }main启动后打印一条标志信息然后以 sys/include/shell.h 中定义的SHELL_DEFAULT_BUFSIZE128 字节为行缓冲调用shell_run(NULL, ...)进入标准 shell 交互循环。该测试没有自定义提示符NULL配合禁用的 prompt整个会话对测试脚本保持静默等待 精确应答。三、构建配置Makefile 与测试环境声明Makefile 完整内容如下DEVELHELP 0 include ../Makefile.build_system_common USEMODULE shell # No need for test_utils_interactive_sync in this test since the test # synchronizes by itself through shellping command. DISABLE_MODULE test_utils_interactive_sync # include sys/test_utils/dummy_thread USEMODULE dummy_thread # microbit qemu failing currently TEST_ON_CI_BLACKLIST microbit include $(RIOTBASE)/Makefile.include # Set the shell echo configuration via CFLAGS if not being controlled via Kconfig # Disable shell echo and prompt to not have them in the way for testing ifndef CONFIG_KCONFIG_USEMODULE_SHELL CFLAGS -DCONFIG_SHELL_NO_ECHO -DCONFIG_SHELL_NO_PROMPT endif关键配置点逐项说明配置项作用DEVELHELP 0关闭开发辅助输出保证输出纯净include ../Makefile.build_system_common引入构建系统测试公共片段Makefile.build_system_common其中以RIOTBASE ? $(CURDIR)/../../..定位仓库根并引入仓库根部的 Makefile.tests_commonUSEMODULE shell启用 RIOT shell 模块DISABLE_MODULE test_utils_interactive_sync禁用通用交互同步模块改由shellping自同步USEMODULE dummy_thread引入 sys/test_utils/dummy_thread 下的 dummy 线程为 shell 调度提供线程上下文TEST_ON_CI_BLACKLIST microbitmicrobit 板卡在当前 CI 的 QEMU 环境下列入黑名单存在已知失败CFLAGS -DCONFIG_SHELL_NO_ECHO -DCONFIG_SHELL_NO_PROMPT在未走 Kconfig 配置时直接通过 CFLAGS 宏定义强制关闭 shell 回显与提示符其中最后一段体现了 RIOT 的双配置路径若CONFIG_KCONFIG_USEMODULE_SHELL已定义即 shell 由 Kconfig 管理则回显/提示符配置应在 Kconfig 中设置否则回退到传统的 CFLAGS 宏注入方式。这两种方式最终都作用于 sys/include/shell.h 中CONFIG_SHELL_NO_ECHO/CONFIG_SHELL_NO_PROMPT的取值。此外 Makefile.ci 声明了 CI 内存受限板卡黑名单BOARD_INSUFFICIENT_MEMORY : \ nucleo-l011k4 \ #即nucleo-l011k4STM32L011K4仅 8 KB SRAM内存不足以运行本测试CI 中会跳过。四、Python 测试脚本验证测试前提的四步真正的验证逻辑在 tests/build_system/test_tools/tests/01-run.py。它基于pexpect与 RIOT 的testrunner框架编写通过run(testfunc)入口执行。4.1 等待 shell 就绪_wait_shell_readydef _wait_shell_ready(child, numtries5): Wait until the shell is ready by using shellping. for _ in range(numtries - 1): try: _shellping(child) except pexpect.TIMEOUT: pass else: break else: # This one should fail _shellping(child)脚本反复发送shellping并期待shellpong_shellping内部通过child.expect_exact(shellpong\r\n, timeouttimeout)精确匹配注意\r\n是串口行结束符。最多重试 5 次若全部超时最后一次故意让_shellping抛出超时异常从而使测试失败。这套先握手、后测试的机制替代了test_utils_interactive_sync验证 shell 与串口链路在测试开始时确实可用。4.2 验证无本地回显_test_no_local_echodef _test_no_local_echo(child): Verify that there is not local echo while testing. msg true this should not be echoed child.sendline(msg) res child.expect_exact([pexpect.TIMEOUT, msg], timeout1) assert res 0, There should have been a timeout and not match stdin发送true this should not be echoed后用expect_exact在超时与匹配到该字符串两个分支中二选一并断言必须命中超时。因为 shell 回显已被禁用设备不会把输入原样返回若本地终端或测试工具链开启了回显脚本就会看到输入字符串断言失败——这正是true命令存在的意义。4.3 验证输出纯净_test_clean_outputdef _test_clean_output(child): Verify that only what the node sends is received. child.sendline(toupper lowercase) retline child.readline() assert retline.strip() LOWERCASE发送toupper lowercase后读取下一行断言其恰好等于LOWERCASE。任何多余的提示符、日志或终端控制序列都会导致该断言失败从而保证测试工具链不会在设备输出中混入杂质。4.4 验证空行发送_test_sending_newlinedef _test_sending_newline(child): Verify that a empty line can be send to the node. The local terminal must NOT repeat the previous command. child.sendline(getchar) child.sendline() # send only one newline character child.expect_exact(getchar 0x0a\r\n)先发送getchar此时设备阻塞在getchar()等待一个字符随后单独发送一个空行仅\n即 0x0a。若本地终端在行编辑模式下会把空行解释为重复上一条命令则getchar收到的将是字符g0x67而非换行符脚本期待getchar 0x0a就会失败。这个用例直接检验了串口工具是否以raw模式不做本地行编辑工作。4.5 测试主流程testfuncdef testfunc(child): _wait_shell_ready(child) # Verify there is no local and remote echo as it is disabled _test_no_local_echo(child) # The node should still answer after the previous one _shellping(child) # Check that the output is clean without extra terminal output _test_clean_output(child) # It is possible to send an empty newline _test_sending_newline(child)四个步骤依次执行等待就绪 → 验证无回显 → 再次握手确认链路仍通畅 → 验证输出纯净 → 验证空行发送。任何一步失败都会让run(testfunc)以非零状态退出CI 随即判定该板卡的测试环境不合格。五、make term链路测试工具与板卡的连接层test_tools验证的核心对象是make term所建立的终端链路。RIOT 的 makefiles/tools/serial.inc.mk 根据板卡/工具链配置将TERMPROG终端程序与TERMFLAGS终端参数解析为实际命令常见的几种实现包括pyterm默认串口工具RIOT 自带位于 dist/tools/pytermTERMPROG ? $(RIOTTOOLS)/pyterm/pytermTERMFLAGS ? -p $(PORT) -b $(BAUD) -ln $(PYTERMLOGDIR) -rn $(PYTERMSESSION) $(PYTERMFLAGS)socat以echo0,raw等参数打开串口确保本地不做回显与行编辑picocom/pyserial-miniterm通过--eol LF、--nolock --imap lfcrlf等参数控制换行映射J-Link RTTterm-rtt、openocdterm-rtt、boottermbt等调试器通道分别由 makefiles/tools/jlink.inc.mk、makefiles/tools/openocd.inc.mk 等定义。QEMU/模拟器环境则由 makefiles/tools/qemu.inc.mk 与 makefiles/tools/renode.inc.mk 统一包装为term.sh把模拟器串口重定向到终端程序。从这些配置可以推断test_tools对测试环境的两条硬性要求终端程序必须关闭本地回显对应_test_no_local_echo否则所有基于expect_exact的输出匹配都会被本地回显污染终端程序必须关闭本地行编辑/历史重复对应_test_sending_newline保证发送空行就是字面上的换行符。因此当你在新板卡或新终端工具上运行 CI 测试前先跑一遍test_tools即可快速定位测试脚本失败是因为板卡逻辑问题还是终端工具链配置问题。六、如何运行与扩展6.1 本地运行在仓库根目录执行示例以native板卡为例串口工具为 pytermmake -C tests/build_system/test_tools flash term或直接由 testrunner 驱动make -C tests/build_system/test_tools testmake test会先编译烧录再通过make term建立会话并自动运行 tests/build_system/test_tools/tests/01-run.py。6.2 手动交互验证也可以手动体验各命令shellping → shellpong toupper hello → HELLO getchar → 等待一个字符并打印其十六进制值 true foo → 无输出静默成功6.3 应用到新板卡/新工具链若你的板卡内存较小先在 Makefile.ci 中确认是否已列入BOARD_INSUFFICIENT_MEMORY当前仅nucleo-l011k4若更换了串口终端程序重点检查其是否以 raw 模式运行、是否关闭本地回显若板卡在模拟器中运行如 microbit QEMU注意 Makefile 中的TEST_ON_CI_BLACKLIST机制当前已将microbit列入。七、小结为什么每个板卡都需要一次test_toolsRIOT 的自动化测试体系高度依赖make term与串口终端的纯净性测试脚本通过pexpect精确匹配设备输出任何本地回显、终端提示符、行编辑行为都会造成误判。test_tools用最小的应用一个 shell 四个命令把这条链路的所有前提一次性验证清楚并借助#error、CFLAGS宏注入与testrunner断言把约定固化为可执行的检查。从 tests/build_system/test_tools/main.c 的命令设计、Makefile 的模块裁剪到 tests/01-run.py 的四步断言再到 makefiles/tools/serial.inc.mk 的终端参数解析整条链路构成了一套可复用的测试环境体检范式——无论你是为 CI 接入新板卡还是排查测试总是超时/误匹配的疑难问题test_tools都是第一步的诊断工具。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表