- 嵌入式
- 系统编程
【免费下载链接】fprime
F´ - A flight software and embedded systems framework
导读
本文聚焦 F´(F Prime)飞行软件与嵌入式系统框架中 CMake 构建系统自身的单元测试体系(cmake-uts.md)。这套测试用于验证 CMake 系统的各项核心功能符合 CMake SDD 的设计需求,确保构建系统在自动化环境中长期可维护。读完本文,你将掌握 CMake 构建系统测试的运行方式、测试套件的组织架构、底层 CMake 调用封装原理,以及测试数据部署的构造方法,可直接在本地虚拟环境中复现并扩展这套测试。
测试目标:验证构建系统的“需求合规”与“可维护性”
F´ 的 CMake 构建系统承担着模块注册、依赖解析、自动代码生成(autocoder)、单元测试生成、部署生成等复杂职责。与普通项目测试不同,这里的被测对象是构建系统本身——它必须通过自动化手段保证:
- 需求合规:CMake 系统在运行过程中满足 CMake SDD 中规定的全部需求,例如模块、可执行程序、单元测试的正确注册与产出;
- 核心流程稳定:CMake 生成、构建、安装、字典生成等关键环节按预期工作;
- 可维护性:通过自动化系统持续回归验证,避免构建系统在演进过程中发生隐性破坏。
这些测试全部存放在仓库的 cmake/test 目录下,与运行时软件的单元测试(如 test_unittests.py 中构建的各_ut_exe)相互独立、互为补充:前者验证构建系统本身,后者验证被构建的软件模块。
实现方式:PyTest + subprocess 调用 CMake
原文档明确说明,CMake 构建系统测试通过PyTest实现,并借助 Python 标准库subprocess直接驱动cmake命令。选择这一方案的理由是:
- PyTest提供标准的测试框架能力(fixture、断言、参数化),实现代码精简、可读性强;
- subprocess以命令行方式运行 CMake,最贴近真实用户操作路径,能够暴露真实的命令行行为问题。
这一封装体现在 cmake.py 中,其核心函数构成了一条完整的“生成—构建—校验”调用链:
| 函数 | 职责 |
|---|---|
subprocess_helper(args, cwd) | 启动子进程并同时“tee”输出到控制台与捕获缓冲区;支持 pytest 的-s/--capture=no实时打印 |
run_cmake(source_directory, build_path, options) | 将 options 字典转换为-D<key>=<value>参数并执行cmake,在独立的临时构建目录中生成构建系统 |
run_make(build_directory, target) | 在生成好的构建目录中执行make <target> -j2,完成实际构建 |
assert_process_success(data_object, errors_ok) | 统一断言 CMake 生成与各 make target 的返回码、stdout/stderr,校验产物数据对象字段完整性 |
get_build(...) | 组合以上步骤,生成一个 session 级 pytest fixture,并在测试结束后清理临时构建/安装目录 |
subprocess_helper中使用了select.select对 stdout/stderr 做非阻塞读取,避免管道阻塞导致死锁;get_build则用tempfile.mkdtemp()创建隔离的构建目录,并在 fixture 收尾时通过shutil.rmtree清理,保证每次测试运行的确定性。
环境准备与运行方法
原文档给出了明确的运行前置条件与命令,这里结合仓库补充完整上下文:
前置条件:
- 按照 F´ 安装流程,在Python 虚拟环境(virtual environment)中运行 fprime;
- 系统必须已安装
cmake与make可执行文件(cmake.py 中直接以cmake、make命令调用); - 已安装
pytest。
运行命令:
fprime> cd cmake/test pytest执行后,PyTest 会依次收集 src 目录下的测试文件并运行。若需要实时查看子进程输出,可追加-s或--capture=no参数,此时subprocess_helper会将 CMake/make 的输出实时打印到终端。
测试套件结构:三类构建场景全覆盖
src 目录下共有 4 个 Python 模块,前三个文件分别构建不同的 CMake 场景,覆盖构建系统的主要功能面。
功能构建测试:test_feature.py
test_feature.py 针对 TestDeployment 展开,是覆盖面最广的一组测试,验证:
- 框架模块识别:检查
Fw_*、Os、Svc_CmdDispatcher等框架模块的静态库lib<module>.a是否生成在build/lib/<platform>/下(对应settings.FRAMEWORK_MODULES列表); - 外部库识别:验证通过
FPRIME_LIBRARY_LOCATIONS引入的两个测试库TestLibrary_TestComponent、TestLibrary2_TestComponent被正确构建; - 部署产出:确认可执行文件
TestDeployment出现在build/bin/<platform>/; - autocoder 集成:验证自定义 autocoder 生成的
test-ac-1、test-ac-2两个产物存在; - 自定义 target:确认
global-test、deployment-test、TestLibrary_TestComponent-test等 target 均被执行; - 安装流程:校验安装目录
install/<platform>/lib/static/下所有静态库与install/<platform>/bin/TestDeployment存在。
该测试的关键 CMake 参数如下,展示了如何在一个测试部署中注入框架路径、项目根与外部库:
cmake.get_build( "FEATURE_BUILD", settings.DATA_DIR / "TestDeployment", { "FPRIME_FRAMEWORK_PATH": settings.REF_APP_PATH.parent, "FPRIME_PROJECT_ROOT": settings.DATA_DIR, "FPRIME_LIBRARY_LOCATIONS": ";".join([ str(settings.DATA_DIR / "test-fprime-library"), str(settings.DATA_DIR / "test-fprime-library2"), ]), }, make_targets=["TestDeployment", "test", "TestDeployment_test", "TestLibrary_TestComponent_test"], )其中FPRIME_LIBRARY_LOCATIONS使用;分隔多个库清单路径,与 FPrime-Code.cmake 中遍历FPRIME_LIBRARY_LOCATIONS查找各*.cmake清单文件的逻辑一一对应。
单元测试构建:test_unittests.py
test_unittests.py 以 Ref 参考部署为被测对象,使用BUILD_TESTING=ON开启测试构建(参见 cmake-advanced.md 中关于构建类型的说明),构建目标为Ref与ut_exe,随后断言:
- 框架模块与标准模块(
settings.FRAMEWORK_MODULES + settings.STANDARD_MODULES)的静态库全部产出; Ref可执行文件以及全部 39 个单元测试可执行文件(如Fw_Types_ut_exe、Svc_CmdDispatcher_ut_exe、Os_ut_exe等)存在于build/bin/<platform>/;- 安装目录包含对应静态库、
Ref可执行文件,以及 F´ 命令/遥测字典文件RefTopologyAppDictionary.xml(位于install/<platform>/dict/)。
这组测试直接验证了register_fprime_ut等单元测试注册 API 的正确性——正如 API.cmake 所述,单元测试仅在BUILD_TESTING启用时生成,并使用<MODULE_NAME>_ut_exe作为默认可执行名,与测试中断言的ut_exe目标命名完全吻合。
共享库构建:test_ref_shared.py
test_ref_shared.py 通过BUILD_SHARED_LIBS=ON验证构建系统的共享库(动态库)路径,断言lib<module>.so(Linux)或lib<module>.dylib(macOS)在构建目录与安装目录中均正确产出,同时验证Ref可执行文件与字典文件的共享库安装形态。它与 cmake-intro.md 中描述的库类型配置相互印证。
共享配置:settings.py
settings.py 集中管理测试常量:
DATA_DIR:指向 cmake/test/data 测试数据目录;REF_APP_PATH:指向仓库根目录的 Ref 参考部署;FRAMEWORK_MODULES/STANDARD_MODULES/REF_MODULES:框架、标准、参考部署三组模块清单,供各测试断言产物时复用,避免魔法字符串散落各处。
测试数据部署:TestDeployment 与测试库
TestDeployment:功能测试的载体
TestDeployment/CMakeLists.txt 是功能测试的最小化部署,展示了 F´ CMake API 的标准用法:
cmake_minimum_required(VERSION 3.13) cmake_policy(SET CMP0048 NEW) project(TestDeployment VERSION 1.0.0 LANGUAGES C CXX) include("${FPRIME_FRAMEWORK_PATH}/cmake/FPrime.cmake") register_fprime_target("target/test") # 注册自定义 target 及支撑它的 autocoder include("${FPRIME_FRAMEWORK_PATH}/cmake/FPrime-Code.cmake") set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Main.cpp") set(MOD_DEPS Svc_CmdDispatcher TestLibrary_TestComponent TestLibrary2_TestComponent) register_fprime_deployment()其要点在于:MOD_DEPS同时声明了框架模块Svc_CmdDispatcher与两个外部测试库模块,从而在一个部署中同时验证框架依赖与库依赖的解析;register_fprime_target("target/test")则用于验证自定义 target 注册机制。其 Main.cpp 是一个空操作可执行程序(仅返回 0),因为该部署的存在意义是测试构建系统而非业务逻辑。
测试库清单:FPRIME_LIBRARY_LOCATIONS 的验证样本
- test-fprime-library.cmake:通过
add_fprime_subdirectory(".../TestLibrary/TestComponent")将测试组件加入构建; - test-fprime-library2.cmake:第二个库清单,用于验证多库清单
;分隔的解析。
add_fprime_subdirectory(定义于 API.cmake)是add_subdirectory的封装,它会自动计算组件的binary_dir以避免产物冲突,并保持以仓库根为基准的标准 include 路径——这正是功能测试能同时在构建目录中产出TestLibrary_TestComponent与TestLibrary2_TestComponent两个库的原因。
自定义 autocoder:验证 autocoder 扩展点
cmake/autocoder/test.cmake 演示了如何在测试中注入一个自定义 autocoder:
autocoder_setup_for_individual_sources()启用逐源文件处理;test_is_supported通过autocoder_support_by_suffix("TestComponent.fpp" ...)声明仅处理TestComponent.fpp文件;test_setup_autocode定义生成脚本,使用cmake -E touch在构建目录生成test-ac-1、test-ac-2两个标记文件。
功能测试中的test_feature_autocoder正是通过断言这两个文件存在,验证自定义 autocoder 被 CMake 正确发现并执行。
关键 CMake 选项与测试的对应关系
CMake 构建系统测试对以下选项尤其敏感,理解它们有助于解读测试行为:
| 选项 | 默认值 | 对测试的影响 |
|---|---|---|
BUILD_TESTING | OFF | 控制单元测试目标与ut_exe是否生成;test_unittests.py 显式开启该选项 |
BUILD_SHARED_LIBS | OFF | 控制静态库/共享库构建路径;test_ref_shared.py 显式开启 |
FPRIME_ENABLE_FRAMEWORK_UTS | ON | 控制是否加入框架自身 UT 目标;如 ci/tests/Ref.bash 所示,CI 对 Ref 部署会以-DFPRIME_ENABLE_FRAMEWORK_UTS=OFF关闭以聚焦部署自身的 UT |
FPRIME_ENABLE_AUTOCODER_UTS | ON | 与上述选项共同控制 autocoder 的 UT 是否生成,见 FPrime-Code.cmake 中的__FPRIME_NO_UT_GEN__开关逻辑 |
FPRIME_ENABLE_UTIL_TARGETS | ON | 控制check、coverage等 fprime-util 目标是否生成,定义于 options.cmake |
在 FPrime-Code.cmake 中可以看到框架 UT 的门控逻辑:当BUILD_TESTING与FPRIME_PRESCAN未定义时引入 googletest 与 STest;随后依据FPRIME_ENABLE_FRAMEWORK_UTS与FPRIME_ENABLE_AUTOCODER_UTS的组合决定__FPRIME_NO_UT_GEN__,最终控制框架各目录(Fw、Svc、Os、Drv、CFDP、Utils)中的 UT 目标是否注册。
测试在 CI 中的集成
这套 CMake 测试已经纳入仓库的持续集成流程。以 Ref.bash 为例,CI 脚本会设置CTEST_OUTPUT_ON_FAILURE=1、为 Ref 部署关闭框架 UT,并遍历fputil目标(generate、build、test 等)逐一执行。此外,fputil.bash 中可以看到 CI 同时运行部署test目录下的 pytest 集成测试,并配合timeout --kill-after=10s 180s pytest限定运行时长,出现内存泄漏时输出 valgrind 日志并判定失败——这与 CMake 构建系统测试“在自动化环境中持续回归”的目标一脉相承。
进一步阅读
- cmake-intro.md:CMake 构建系统入门,理解模块、部署与库的基础概念;
- cmake-api.md:CMake API 参考,包含
register_fprime_*系列函数的完整签名; - cmake-advanced.md:构建类型、安装流程与进阶用法;
- sdd.md:CMake 系统设计文档,其中 5.3 节给出了
BUILD_TESTING=ON下的单元测试构建示例,是本文所涉测试断言行为的权威依据。
- 嵌入式
- 系统编程
【免费下载链接】fprime
F´ - A flight software and embedded systems framework
相关推荐
F´(fprime)CMake 构建系统单元测试指南:基于 PyTest 的构建系统自动化验证
F´(fprime)CMake 构建系统单元测试指南:基于 PyTest 的构建系统自动化验证 本指南围绕 F´ 飞行软件框架的构建系统单元测试展开,介绍这套以
嵌入式系统编程F´ CMake 构建系统单元测试(PyTest)实战指南
F´ CMake 构建系统单元测试(PyTest)实战指南 F´(F Prime)是一个由 NASA JPL 主导的开源飞行软件与嵌入式系统框架。其 CMake
嵌入式系统编程F´ 框架 Autocoders/Python 测试套件:基于 CMake 的构建配置与 Pytest 运行指南
F´ 框架 Autocoders/Python 测试套件:基于 CMake 的构建配置与 Pytest 运行指南 导读 本文聚焦 F´(F Prime,飞行软件
嵌入式系统编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考