☰
F´ 框架 CMake 构建系统单元测试指南:基于 PyTest 的自动化验证方案
2026/9/25 5:08:22 网站建设 项目流程
  • 嵌入式
  • 系统编程

【免费下载链接】fprime

F´ - A flight software and embedded systems framework

项目地址:https://gitcode.com/gh_mirrors/fpri/fprime
点击查看免费下载

导读

本文聚焦 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清理,保证每次测试运行的确定性。

环境准备与运行方法

原文档给出了明确的运行前置条件与命令,这里结合仓库补充完整上下文:

前置条件:

  1. 按照 F´ 安装流程,在Python 虚拟环境(virtual environment)中运行 fprime;
  2. 系统必须已安装cmake与make可执行文件(cmake.py 中直接以cmake、make命令调用);
  3. 已安装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_TESTINGOFF控制单元测试目标与ut_exe是否生成;test_unittests.py 显式开启该选项
BUILD_SHARED_LIBSOFF控制静态库/共享库构建路径;test_ref_shared.py 显式开启
FPRIME_ENABLE_FRAMEWORK_UTSON控制是否加入框架自身 UT 目标;如 ci/tests/Ref.bash 所示,CI 对 Ref 部署会以-DFPRIME_ENABLE_FRAMEWORK_UTS=OFF关闭以聚焦部署自身的 UT
FPRIME_ENABLE_AUTOCODER_UTSON与上述选项共同控制 autocoder 的 UT 是否生成,见 FPrime-Code.cmake 中的__FPRIME_NO_UT_GEN__开关逻辑
FPRIME_ENABLE_UTIL_TARGETSON控制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

项目地址:https://gitcode.com/gh_mirrors/fpri/fprime
点击查看免费下载

相关推荐

上一篇:终极指南:如何用biliTickerBuy轻松搞定B站会员购抢票难题
下一篇:探秘 CyberZHG Toolbox:一款强大的在线编程工具箱

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询