F´(F Prime)框架 FPP 数组代码生成器单元测试解析:ArrayToStringTest 与 FormatTest 深度指南
2026/9/24 14:35:37 网站建设 项目流程

F´(F Prime)框架 FPP 数组代码生成器单元测试解析:ArrayToStringTest 与 FormatTest 深度指南

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprime

导读

本文围绕 F´ 飞控与嵌入式系统框架中 FppTest/array 目录展开,系统讲解 FPP(F Prime Prime)语言数组(array)类型代码生成器的两类单元测试:数组toString()/ostream运算符测试(ArrayToStringTest)与 FPP 格式字符串(format string)测试(FormatTest)。读完本文,你将掌握 FPP 数组类型的声明语法、默认值、格式字符串说明符({}{d}{o}{x}{.1e}等)的精确行为,以及如何通过fprime-util build --utfprime-util check一键构建并运行整套代码生成器回归测试。

一、目录定位:FPP 数组代码生成器的回归测试床

FppTest/array 是 F´ 仓库中 FppTest 测试项目的一个子目录。FppTest 项目本身定位为 "unit tests for the FPP autocoder",即针对 FPP 自动代码生成器(autocoder)的单元测试套件;array 子目录则专门聚焦于FPP 数组(array)类型代码生成器,验证从.fpp声明生成的 C++ 数组类是否符合预期。

该目录包含两组测试目标:

测试文件被测功能
ArrayToStringTest.cpp数组类型的toString()ostreamoperator<<)运算符
FormatTest.cppFPP 格式字符串(format string)说明符的展开行为

从目录结构看(list_files 确认),测试输入由 5 个 FPP 声明文件提供:array.fppenum.fppstring.fppstruct.fppformat.fpp,它们分别覆盖了数组元素为基本类型、枚举、字符串、结构体、以及带格式字符串的数组;main.cpp 作为测试入口实例化整套 gtest 用例。

二、环境前提与构建运行方式

按 README 与 FppTest/README.md 的要求,使用该测试目录需要两个前提:

  1. 已安装 F Prime 框架(本仓库即为 F´ 源码本身,需先完成框架安装与配置);
  2. 处于 F Prime Python 虚拟环境(venv)内——因为构建过程需要调用 FPP 自动代码生成器的 Python 工具链。

构建与运行只有两条命令:

# 构建单元测试(UT build) fprime-util build --ut # 运行单元测试 fprime-util check

其中--ut标志指示构建系统仅编译单元测试目标(unit-test build)。这一点在 CMakeLists.txt 中有明确注释:"However, only the UT build is allowed here."(此处只允许 UT 构建),说明该目录只服务于测试,不参与飞行软件的常规产物构建。

三、测试数据定义:FPP 数组声明语法一览

数组测试的输入全部由.fpp声明文件驱动,代码生成器据此产出*ArrayAc.hpp头文件(例如EnumArrayAc.hppStringArrayAc.hppFormatBoolArrayAc.hpp等,见 ArrayToStringTest.cpp 与 FormatTest.cpp 的 include 列表)。

3.1 基本类型与嵌套数组(array.fpp)

array Uint32 = [2] U32; array Uint32Array = [3] Uint32;
  • Uint32是长度为 2、元素类型为U32的数组;
  • Uint32Array是长度为 3、元素类型为另一个数组Uint32的嵌套数组,用于验证"数组的元素本身是数组"时代码生成器的行为。

3.2 枚举元素数组与默认值(enum.fpp)

enum E { A, B, C, } array Enum = [3] E default [ E.A, E.B, E.C ]

这里演示了 FPP 数组的两点核心能力:元素类型可以是枚举,并且可以通过default关键字为数组指定逐元素默认值列表[ E.A, E.B, E.C ])。默认值会在生成的默认构造函数中被使用(见第五节 ArrayTest.hpp 的Default用例)。

3.3 字符串元素数组(string.fpp)

array String = [3] string

声明长度为 3、元素为 F´ 字符串类型的数组。字符串元素在代码生成中对应Fw::ExternalString(见 main.cpp 中setTestVals<String>Fw::ExternalString特化),且生成类会额外暴露ELEMENT_BUFFER_SIZE常量用于为每个字符串元素分配缓冲区。

3.4 结构体元素数组(struct.fpp)

struct S { mU32: U32 mU32Arr: [3] U32 } array Struct = [3] S

数组元素可以是结构体,而结构体成员本身又可以是数组(mU32Arr: [3] U32),形成了"数组-结构体-数组"的复合嵌套,用于压力测试代码生成器的递归类型解析。

四、toString 与 ostream 运算符测试:ArrayToStringTest 深度解析

ArrayToStringTest.cpp 的目标是验证数组类型的两类字符串输出接口:toString()ostream运算符operator<<

4.1 测试框架:gtest 类型参数化

测试使用 gtest 的type-parameterized testsTYPED_TEST_SUITE/TYPED_TEST),一次性覆盖四类数组元素:

using ArrayTypes = ::testing::Types< Enum, String, Struct, Uint32Array >; TYPED_TEST_SUITE(ArrayToStringTest, ArrayTypes);
  • Enum:枚举元素数组;
  • String:字符串元素数组;
  • Struct:结构体元素数组;
  • Uint32Array:嵌套数组(数组元素为数组)。

SetUp()通过FppTest::Array::setTestVals<ArrayType>(testVals)填充非平凡的测试值——这些特化定义在 main.cpp 中,枚举值用STest::Pick::startLength随机挑选,字符串与结构体值则调用FppTest::Utils辅助函数生成。

4.2 断言策略:与手工拼接的流输出逐字符比对

核心测试逻辑非常直观:将数组对象写入std::stringstream,再与手工按"元素逐个输出、首尾加括号"的参考流比对:

TYPED_TEST(ArrayToStringTest, ToString) { TypeParam a(this->testVals); std::stringstream buf1, buf2; buf1 << a; // 被测:ostream 运算符 buf2 << "[ "; // 参考:手工拼接 for (U32 i = 0; i < TypeParam::SIZE; i++) { buf2 << this->testVals[i] << " "; } buf2 << "]"; ASSERT_STREQ(buf1.str().c_str(), buf2.str().c_str()); }

由此可以确认生成数组类的标准文本输出格式

[ 元素0 元素1 元素2 ]

即以[开头、每个元素后跟一个空格、以]结尾。每个元素自身的文本表示则由元素类型决定(枚举输出常量名、字符串输出其字符内容、结构体输出其成员序列等),并逐层递归到toString()实现。该用例同时覆盖了toString()operator<<,因为二者在生成代码中共享同一套元素序列化逻辑。

五、FPP 格式字符串测试:FormatTest 深度解析

FormatTest.cpp 是 array 目录中信息量最大的测试文件,验证 FPP 数组声明中format关键字的全部说明符行为。其输入定义在 format.fpp,共 14 个带格式字符串的数组声明。

5.1 FPP 格式字符串语法

array FormatBool = [3] bool format "a {} b" array FormatU8 = [3] U8 format "a {} b" array FormatU16Dec= [3] U16 format "a {d} b" array FormatU32Oct= [3] U32 format "a {o} b" array FormatU64Hex= [3] U64 format "a {x} b" array FormatF32e = [3] F32 format "a {.1e} b" array FormatF32f = [3] F32 format "a {.2f} b" array FormatF64g = [3] F64 format "a {.3g} b" array FormatString= [3] string format "% {}" array FormatChar = [3] U8 format "a {c} b"

格式字符串由普通文本与花括号占位符{...}组成,占位符控制元素如何被格式化后嵌入字符串。结合 FormatTest.cpp 中每个用例的参考实现(使用std::decstd::octstd::hexstd::scientificstd::fixedstd::setprecision等流操纵符),可以归纳出完整的说明符语义表:

说明符语义参考实现(测试代码)示例数组声明
{}默认格式直接输出元素FormatBoolFormatU8FormatI8
{d}十进制(decimal)std::decFormatU16DecFormatI16Dec
{o}八进制(octal)std::octFormatU32OctFormatI32Oct
{x}十六进制(hex)std::hexFormatU64HexFormatI64Hex
{c}字符(char)U8直接输出FormatChar
{.1e}科学计数法,1 位精度std::setprecision(1) << std::scientificFormatF32e
{.2f}定点,2 位小数std::setprecision(2) << std::fixedFormatF32f
{.3g}通用格式,3 位有效数字std::setprecision(3)FormatF64g
% {}字符串前置%前缀输出"% " << str << " "FormatString

5.2 测试用例与断言方式

FormatTest是一个普通 gtest fixture(TEST_F),每个说明符对应一个独立用例,被测与参考输出分别写入buf1buf2两个stringstream,最后ASSERT_STREQ逐字符比对:

TEST_F(FormatTest, U32Oct) { U32 testVals[FormatU32Oct::SIZE] = {0, 100, std::numeric_limits<U32>::max()}; FormatU32Oct a(testVals); buf1 << a; for (U32 i = 0; i < FormatU32Oct::SIZE; i++) { buf2 << "a " << std::oct << testVals[i] << " b "; } buf2 << "]"; ASSERT_STREQ(buf1.str().c_str(), buf2.str().c_str()); }

值得注意的测试设计细节:

  • 边界值覆盖:无符号类型使用{0, 100, std::numeric_limits<U8/U16/U32/U64>::max()}(最小值、中间值、最大值);有符号类型使用{min, 0, max};浮点类型使用{min, 0.0, max}。这保证了格式代码在数值边界上行为正确。
  • 小整数提升U8/I8用例中参考代码将元素强制转换为U16/I16再输出((U16) testVals[i]),避免char被当作字符打印,验证了生成代码在整数提升上的正确处理。
  • 字符串用例FormatString用例通过Fw::ExternalString::setBuffer为每个元素分配缓冲区并填充测试字符串,验证% {}前缀与字符串元素输出的组合。

5.3 格式字符串的实战价值

format是 FPP 数组类型面向日志、遥测、人机交互的核心特性:开发者可以在类型声明层面统一约定"数组元素如何显示"(十进制、八进制、十六进制、科学计数、定点小数等),生成代码自动在toString()/operator<<中应用,避免在每个组件中手工格式化。FormatTest 正是这批格式约定的"行为契约"。

六、背后的完整测试矩阵:typed_tests/ArrayTest.hpp 与 main.cpp

ArrayToStringTest.cpp 与 FormatTest.cpp 只是该目录的两个专属测试文件;通过 main.cpp 还实例化了一套通用的、模板化的数组行为测试套件 ArrayTest.hpp,对EnumStringStructUint32Array四类数组做全面回归:

用例(REGISTER_TYPED_TEST_SUITE_P验证内容
DefaultSIZE常量等于 3;默认构造后各元素等于默认值(含default [...]声明的默认值)
Constructors数组构造(从元素数组)、单元素构造(填充全部元素)、多元素构造(逐个传入)、拷贝构造
SubscriptOpoperator[]的读写能力
AssignmentOp数组整体赋值、拷贝赋值、自赋值、单元素赋值
EqualityOp==!=运算符
Serialization通过Fw::SerialBuffer序列化/反序列化往返一致;缓冲区过小(serializedSize-1)时序列化失败

其中序列化用例还验证了SERIALIZED_SIZE常量与元素类型感知的序列化大小计算:对于字符串数组,main.cpp 特化了getSerializedSize<::String>,逐元素累加a[i].serializedSize(),而非简单使用固定值——这是字符串数组与定长类型数组在序列化语义上的关键区别。

测试入口 main.cpp 还做了两类关键特化:

  1. 默认值/测试值特化:为Enum指定setDefaultValsE.A, E.B, E.C)与随机化的setTestVals;为String管理静态缓冲区并调用FppTest::Utils::setString;为Struct/Uint32Array生成非零随机值。
  2. 多元素构造特化getMultiElementConstructedArray演示了Enum(a[0], a[1], a[2])形式的多元素构造函数语法。

主函数通过STest::Random::seed()初始化随机源,保证每次运行测试值的多样性。

七、构建集成:CMakeLists.txt 的 UT 布局

CMakeLists.txt 展示了 F´ 模块化构建体系(register_fprime_module/register_fprime_ut)在纯测试目录中的用法:

set(SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/array.fpp" "${CMAKE_CURRENT_LIST_DIR}/enum.fpp" "${CMAKE_CURRENT_LIST_DIR}/string.fpp" "${CMAKE_CURRENT_LIST_DIR}/struct.fpp" "${CMAKE_CURRENT_LIST_DIR}/format.fpp" ) register_fprime_module() set(UT_MOD_DEPS Fw/Test STest) set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/ArrayToStringTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/FormatTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/../utils/Utils.cpp" "${CMAKE_CURRENT_LIST_DIR}/main.cpp" ) register_fprime_ut()

关键点:

  • .fpp源文件通过register_fprime_module()注册,触发 FPP 自动代码生成器在构建期生成*ArrayAc.hpp
  • 单元测试依赖Fw/Test(提供Fw::ExternalStringFw::SerialBuffer等基础设施)与STest(提供随机数/选择工具);
  • 测试源码经register_fprime_ut()注册为 UT 目标,且该目录只允许 UT 构建(注释"only the UT build is allowed here");
  • 测试还复用了上层FppTest/utils/Utils.cpp的辅助函数,体现跨目录共享测试设施的组织方式。

八、总结与延伸阅读

FppTest/array是 F´ 仓库中验证FPP 数组代码生成器正确性的最小但完整的回归测试样例:它用 5 个声明文件覆盖了数组元素的全部主要类型(基本类型、枚举、字符串、结构体、嵌套数组),用两个专属测试文件锁定了toString()/ostream输出格式与 14 种格式字符串说明符的行为,再通过模板化的 ArrayTest.hpp 与 main.cpp 特化补全构造函数、赋值、比较、序列化的全量契约。对开发者而言,它同时是一份可直接运行的测试模板和一份"FPP 数组 + 格式字符串"的权威行为规范参考。

若需继续深入,可参考:

  • FppTest/array/format.fpp —— 格式字符串的完整声明样例;
  • FppTest/array/ArrayToStringTest.cpp 与 FormatTest.cpp —— 行为契约的断言实现;
  • FppTest/typed_tests/ArrayTest.hpp —— 可复用的数组类型模板测试套件;
  • FppTest/README.md —— FppTest 整体构建与运行说明。

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprime

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

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

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

立即咨询