Apache Arrow MATLAB 接口测试指南:从本地 runtests 到 CI 与代码覆盖率
【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址: https://gitcode.com/gh_mirrors/arrow12/arrow
Apache Arrow 项目为 MATLAB 提供了一套基于 C++ 底层库的接口实现(源码位于matlab/目录),而本指南所依据的 测试指南文档 正是围绕该接口的测试实践而写的。本文系统讲解如何为 MATLAB 接口编写并运行单元测试:从本地运行runtests的完整命令,到基于matlab.unittest.TestCase的测试类编写规范、测试文件组织规则、GitHub Actions 中的 MATLAB CI 工作流,再到基于ReportCoverageFor的代码覆盖率检查,并结合仓库内真实测试文件(如tArray.m、tStringArray.m、tTabularInternal.m)深入剖析其设计模式。读完本文,你将掌握 Apache Arrow MATLAB 接口测试的完整方法论,能够独立编写、组织并验证高质量的测试用例。
一、概述与测试目标
matlab目录是 Apache Arrow 仓库中面向 MATLAB 语言的绑定实现。它的核心价值在于让 MATLAB 用户可以:
- 在 MATLAB 数组类型与 Arrow
Array类型之间进行转换(例如double↔Float64Array、string↔StringArray); - 在 MATLAB
table与arrow.tabular.RecordBatch之间互相转换; - 创建 Arrow
Field、Schema与Type对象; - 读写 Feather V1 文件。
由于 MATLAB 接口是包裹在 C++ 之上的 MEX 扩展(接口底层实现位于 matlab/src/cpp/arrow/matlab,包括 array、buffer、c、io、tabular、type 等 proxy 目录),任何改动都可能同时触及 MATLAB 层与 C++ 层,因此测试是保证该接口质量的关键环节。本指南的目标就是为在仓库matlab目录下测试功能提供一套明确、可复用的指引。
二、前置条件
要为 MATLAB 接口添加并运行测试,本地环境需要安装以下软件:
- MATLAB:运行测试所依赖的宿主环境,测试通过 MATLAB 命令窗口中的
runtests驱动。 - MATLAB Interface to Apache Arrow:即本仓库
matlab目录所构建出的接口产物。构建方式可参考 matlab/README.md:使用 CMake 执行cmake -S . -B build与cmake --build build --config Release --target install完成编译安装,安装目录会被加入 MATLAB Search Path。
注意:根据 matlab/README.md 的状态说明,该 MATLAB 接口仍处于活跃开发阶段,应视为实验性(experimental)功能。这意味着测试工作尤为重要,也意味着测试需要随接口演进持续更新。
三、在本地运行测试
本地运行测试非常简单:启动 MATLAB,然后cd到matlab/test下感兴趣测试文件所在目录,再调用runtests命令即可。
% 运行单个测试文件 >> runtests(testFileName) % 例如: runtests("tArray.m") % 递归运行某个测试目录下的所有测试 >> runtests(testFolderName, IncludeSubfolders = true) % 例如: runtests('matlab\test', IncludeSubfolders = true)两种用法分别适用于“调试单个测试类”和“跑全量回归”两种场景:
- 单个测试文件:参数为测试文件名(字符串),MATLAB 会定位到该文件并执行其中的全部测试方法,输出详细的通过/失败/被跳过的信息。
- 整个测试目录:传入文件夹路径并设置
IncludeSubfolders为true,可递归发现子目录下所有以t开头的测试文件。这正好契合仓库matlab/test的目录结构(如test/arrow/array、test/arrow/tabular、test/arrow/type等)。
关于runtests的完整参数说明(包括下文会用到的ReportCoverageFor和IncludeSubfolders),可以参考 MathWorks 官方文档对runtests的说明。
四、如何编写测试
4.1 测试框架与最小示例
仓库内所有 MATLAB 接口测试都基于MATLAB Class-Based Unit Testing Framework,即测试类必须继承自matlab.unittest.TestCase。文档给出了一个简洁可运行的示例tStringArray:
classdef tStringArray < matlab.unittest.TestCase methods(Test) function TestBasicStringArray(testCase) % Verify that an `arrow.array.StringArray` can be created from % a basic MATLAB `string` array using the `arrow.array` gateway % construction function. % Create a basic MATLAB `string` array. matlabArray = ["A" ,"B", "C"]; % Create an `arrow.array.StringArray` from the MATLAB `string` % array by using the `arrow.array` gateway construction function. arrowArray = arrow.array(matlabArray); % Verify the class of `arrowArray` is `arrow.array.StringArray`. testCase.verifyEqual(string(class(arrowArray)), "arrow.array.StringArray"); % Verify `arrowArray` can be converted back into a MATLAB `string` array. testCase.verifyEqual(arrowArray.toMATLAB, ["A"; "B"; "C"]); end end end这个示例体现了测试编写的最小骨架:
- 测试类以
t开头命名(如tStringArray),继承matlab.unittest.TestCase; - 测试方法放在
methods(Test)块内,方法名即测试名; - 每个测试方法接收
testCase参数,通过testCase.verifyEqual等验证方法断言结果。
需要指出,当前仓库中该测试文件的实际实现比文档示例更为丰富,位于 matlab/test/arrow/array/tStringArray.m。仓库版本采用properties声明测试类的构造函数引用与转换函数引用(ArrowArrayConstructorFcn、MatlabConversionFcn等),使测试代码可复用于任意数组类型,这正体现了下文“测试最佳实践”中“使用抽象、复用模式”的原则。toMATLAB方法是仓库接口中所有 Array 类共有的转换入口,其源码位于 matlab/src/matlab/+arrow/+array/Array.m。
4.2 测试最佳实践
文档明确列出了编写测试时应遵循的六条最佳实践:
- 为测试用例使用描述性名称(descriptive names);
- 每个测试用例只聚焦验证一种软件“行为”;
- 同时使用“预期输入”和“非预期输入”测试;
- 在每个测试用例开头添加注释,说明该用例在验证什么;
- 像对待普通代码一样对待测试代码:使用清晰的变量名、编写辅助函数、善用抽象;
- 向已有测试类新增用例时,遵循既有模式。
这些实践在仓库测试文件中均有具体体现。以 matlab/test/arrow/array/tArray.m 为例:
properties(TestParameter) MATLABDataArrayTypePair = { ... {[true false], "arrow.array.BooleanArray"}, ... {int8([1 2]), "arrow.array.Int8Array"}, ... {single([1 2]), "arrow.array.Float32Array"}, ... {[1 2], "arrow.array.Float64Array"}, ... {datetime(2022,1,1), "arrow.array.TimestampArray"}, ... {["A" "B"], "arrow.array.StringArray"}, ... {{[1, 2, 3], [4, 5]}, "arrow.array.ListArray"}}; end methods(Test) function ArrowArrayOutputType(testCase, MATLABDataArrayTypePair) matlabArray = MATLABDataArrayTypePair{1}; expectedClassName = MATLABDataArrayTypePair{2}; arrowArray = arrow.array(matlabArray); actualClassName = string(class(arrowArray)); testCase.verifyEqual(actualClassName, expectedClassName); end ... end该文件还展示了“非预期输入”的测试方式——用testCase.verifyError(fcn, errID)验证错误标识符:
function UnsupportedMATLABTypeError(testCase) % Verify arrow.array throws an error with the identifier % "arrow:array:UnsupportedMATLABType" if the input array is not one % we support converting into an Arrow array. matlabArray = calmonths(12); fcn = @() arrow.array(matlabArray); errID = "arrow:array:UnsupportedMATLABType"; testCase.verifyError(fcn, errID); end此外,tArray.m还通过InferNullsDefault、InferNullsTrue、InferNullsFalse、ValidNameValuePair四个用例系统验证了arrow.array的InferNulls与Valid两个名称-值参数的语义——这展示了“每个用例聚焦一个行为”的实践。更多示例可以直接在matlab/test目录下查阅。
五、测试用例设计指南
5.1 面向真实工作流
新增测试时,最低要求是确保真实世界的工作流(real-world workflows)按预期工作。也就是说,测试不应停留在孤立的单元级断言,而应覆盖从 MATLAB 数组 → Arrow 对象 → 回到 MATLAB 数组的完整往返路径。仓库中 matlab/test/arrow/io/csv/tRoundTrip.m、matlab/test/arrow/io/feather/tRoundTrip.m 等以RoundTrip命名的测试正是这类端到端流程的典型代表。
5.2 无法在接口层测试时的替代方案:直接驱动 C++ Proxy
如果某个改动难以在 MATLAB 接口层测试(例如需要验证 C++Proxy方法的内部行为),可以从 MATLAB 测试用例中手动创建一个Proxy实例并直接调用其相关方法。
文档专门指出了参考实现:matlab/test/arrow/tabular/tTabularInternal.m。该文件中的测试直接通过TabularObjectWithAllTypes.Proxy获取底层 proxy 对象,然后调用proxy.getRowAsString(struct(Index=int64(1)))验证行字符串化结果,甚至用testCase.verifyError(fcn, "arrow:tabular:GetRowAsStringFailed")验证无效索引(如Index=0或Index=4)会抛出预期错误。这种方式把测试触角延伸到 MATLAB 与 C++ 的边界,是接口层测试的有效补充。
六、测试组织与目录结构
所有 MATLAB 接口测试都位于matlab/test目录下。为保证“源码文件 ↔ 测试文件”的可查性,仓库遵循以下两条组织规则:
- “近似平行”的目录结构:测试目录与源码目录结构一一对应。例如,测试目录 matlab/test/arrow/array 对应源码目录 matlab/src/matlab/+arrow/+array(MATLAB 包目录
+arrow/+array)。 - 一个测试文件对应一个源文件:例如 matlab/test/arrow/array/tArray.m 是 matlab/src/matlab/+arrow/+array/Array.m 的测试文件。
注:在特定场景下允许偏离上述规则。例如某个类非常复杂、包含多种差异较大的功能(仓库本身倾向于避免这种情况)时,可以将其测试拆分为多个“聚焦”的测试文件——一个专测类的显示(display)、一个专测属性(properties)、一个专测方法(methods)。仓库中 matlab/test/arrow/array/tArrayDisplay.m 即为这种“聚焦”模式的实例:它专门针对数组显示功能,用TestParameter系统化覆盖空数组、单元素数组、多元素数组、含单个 null、含多个 null 等显示场景。
七、持续集成(CI)工作流
Apache Arrow 项目以 GitHub Actions 作为主要 CI 平台。任何修改 MATLAB 接口代码的 Pull Request 都会自动触发 MATLAB CI 工作流,该工作流会运行matlab/test目录下的全部测试。
CI 工作流的真实配置位于仓库根目录下的 .github/workflows/matlab.yml。从该配置可以看出其关键设计:
- 触发条件:
push与pull_request事件,且路径过滤为.github/workflows/matlab.yml、ci/scripts/matlab*.sh、matlab/**与cpp/src/arrow/**——即只有改动 MATLAB 接口或其依赖的 C++ 核心代码时才触发。 - 平台矩阵:包括 Ubuntu 20.04、macOS(AMD64 与 ARM64)、Windows 2022 三个操作系统。注释解释了为何将 Ubuntu 锁定在 20.04:Ubuntu 22.04 自带的 GLIBCXX 与 MATLAB R2023a 捆绑的 GLIBCXX 存在二进制兼容问题,且 20.04 与社区成员本地使用的 Debian 11 兼容性更好。
- MATLAB 版本:通过
matlab-actions/setup-matlab@v2安装R2024a。 - 构建步骤:调用 ci/scripts/matlab_build.sh(该脚本以 Ninja 为生成器、以
matlab/install为安装前缀执行 CMake 构建与安装)。 - 测试步骤:通过
matlab-actions/run-tests@v2运行,select-by-folder: matlab/test指定测试目录,strict: true启用严格模式;同时通过环境变量MATLABPATH: matlab/install/arrow_matlab将安装目录加入 MATLAB Search Path。 - 并发控制:
concurrency配置为同一 PR 的新提交自动取消正在运行的工作流,避免资源浪费。
评审者通常要求 MATLAB CI 工作流成功通过后才会考虑合并 PR。如果你在排查 CI 失败时遇到困难,可以向评审者或其他社区成员求助。
八、代码覆盖率目标
对 MATLAB 接口的任何改动,都应尽力添加测试以覆盖所有被改动的代码行、条件分支与决策分支。提交 PR 前,请检查改动代码的覆盖率;如果方便,可以在 PR 描述中显式说明覆盖率情况。
虽然仓库追求高覆盖率,但也承认部分代码无法合理测试(例如针对枚举值的switch条件中不可达的分支)。这是一种务实的覆盖率哲学:覆盖率是质量手段而非目的。
8.1 如何生成代码覆盖率报告
要求:MATLAB R2023b 或更高版本。
生成覆盖率报告需要给runtests命令提供ReportCoverageFor名称-值参数。在生成报告前,务必先将你的源文件目录加入 MATLAB Search Path(因为runtests需要定位源文件来计算覆盖率):
>> addpath( genpath(<your local arrow/matlab>) ) % `genpath` 用于包含所有子目录并加入 MATLAB search path >> runtests(testFilePath/testFolderPath, 'ReportCoverageFor', sourceFilePath/sourceFolderPath, 'IncludeSubfolders', true/false);文档给出的完整示例——运行matlab/test下全部测试,并为matlab/src/matlab下所有文件生成覆盖率报告:
>> addpath(genpath("C:\TryCodeCoverage\arrow\matlab")) >> runtests('C:\TryCodeCoverage\arrow\matlab\test', 'ReportCoverageFor', 'C:\TryCodeCoverage\arrow\matlab\src\matlab\', 'IncludeSubfolders', true);该命令执行后,MATLAB 会运行测试并输出一份 HTML 覆盖率报告,按文件列出行覆盖、语句覆盖等信息,帮助定位未覆盖的代码区域。
8.2 覆盖率结果排查技巧
如果runtests配合ReportCoverageFor得到的覆盖率结果令人困惑或错误,很可能是缓存或其他问题导致的。一种有效的工作区做法是:在源文件中设置断点(breakpoint),然后重新运行测试。这一步骤可以验证源文件是否确实被测试执行了——如果断点被命中,说明覆盖率数据缺失并非“代码未被执行”,而是报告机制本身的问题。
九、与接口架构相呼应的测试纵深
了解 MATLAB 接口的底层架构有助于编写更有针对性的测试。从源码结构看,MATLAB 接口采用Proxy 模式:MATLAB 层对象(如arrow.array.StringArray)持有指向 C++ Proxy 对象的句柄,所有实际操作最终通过 MEX gateway 转发到 C++ 层执行。相关关键路径包括:
- matlab/src/matlab/+arrow/+array/Array.m:MATLAB 层 Array 基类;
- matlab/src/cpp/arrow/matlab/mex/gateway.cc:MEX 网关入口;
- matlab/src/cpp/arrow/matlab/proxy/factory.cc 与 factory.h:Proxy 工厂;
- matlab/src/cpp/arrow/matlab/array/proxy:各数组类型的 C++ Proxy 实现;
- matlab/test/arrow/gateway/tGateway.m:针对 gateway 错误条件的测试(如未知 Proxy 类名、非法 TimeUnit、非法 UTF-16 时区字符串),这些错误路径无法从常规
arrow.array.*接口触发,属于“从 MATLAB 测试用例直接驱动 Proxy”策略的典型应用。
理解这一分层后,你便能判断某个改动应放在接口层测试还是 Proxy 层测试,从而写出既覆盖真实工作流、又触及底层错误路径的高质量测试。
十、小结
Apache Arrow MATLAB 接口的测试体系可以概括为“一条主线、两条组织规则、三道质量关卡”:
- 一条主线:全部测试基于
matlab.unittest.TestCase类框架,遵循“一行为一用例”“预期/非预期输入并测”“注释说明验证目标”等最佳实践; - 两条组织规则:测试目录与源码目录近似平行、一个测试文件对应一个源文件,必要时拆分为聚焦测试文件;
- 三道质量关卡:本地
runtests手工验证 → GitHub Actions MATLAB CI 自动回归(覆盖 Ubuntu/macOS/Windows 三平台)→ReportCoverageFor覆盖率检查作为 PR 前的质量门槛。
无论你是为 MATLAB 接口修复 bug、新增数据类型支持,还是完善现有测试,都可以围绕本指南所讲的流程,先在 matlab/test 目录中参照既有测试模式编写用例,再通过本地runtests与覆盖率报告验证,最后提交触发 CI,确保改动以可验证、可持续的方式合入仓库。
【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址: https://gitcode.com/gh_mirrors/arrow12/arrow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考