为编写测试 numpy.i SWIG 接口文件是一个组合难题。目前,共支持12种不同的数据类型,每种数据类型有74种不同的参数签名,共支持888种“开箱即用”的类型映射。反过来,这些类型映射中的每一个都可能需要几个单元测试,以验证正确和不正确输入的预期行为。目前,当 make test 运行在 numpy/tools/swig 子目录。
numpy.i
make test
numpy/tools/swig
为了便于进行许多类似的单元测试,使用了一些高级编程技术,包括C和 SWIG 宏以及python继承。本文件旨在描述用于验证 numpy.i 排版图按预期工作。
对于一维、二维和三维数组,分别支持三个独立的测试框架。对于一维数组,有两个C++文件,一个标头和一个源,命名为:
Vector.h Vector.cxx
它包含各种函数的原型和代码,这些函数具有一维数组作为函数参数。文件:
Vector.i
是一个 SWIG 定义python模块的接口文件 Vector 将函数包装在 Vector.h 在 numpy.i 正确处理C数组。
Vector
Vector.h
这个 Makefile 电话 swig 生成 Vector.py 和 Vector_wrap.cxx 并执行 setup.py 编译的脚本 Vector_wrap.cxx 并将扩展模块连接在一起 _Vector.so 或 _Vector.dylib ,取决于平台。此扩展模块和代理文件 Vector.py 都放在 build 目录。
Makefile
swig
Vector.py
Vector_wrap.cxx
setup.py
_Vector.so
_Vector.dylib
build
实际的测试是使用名为::
testVector.py
使用标准的python库模块 unittest ,对中定义的每个函数执行多个测试 Vector.h 对于支持的每个数据类型。
unittest
二维阵列的测试方法完全相同。上述描述适用,但 Matrix 替代的 Vector . 对于三维测试,替换 Tensor 对于 Vector . 对于四维测试,替换 SuperTensor 对于 Vector . 对于平置阵列测试,替换 Flat 对于 Vector . 对于下面的描述,我们将参考 Vector 测试,但同样的信息也适用于 Matrix , Tensor 和 SuperTensor 测验。
Matrix
Tensor
SuperTensor
Flat
命令 make test 将确保生成所有测试软件,然后运行所有三个测试脚本。
Vector.h 是一个C++头文件,它定义了一个C宏调用 TEST_FUNC_PROTOS 这需要两个参数: TYPE ,它是数据类型名称,例如 unsigned int ;和 SNAME ,它是同一数据类型的简称,没有空格,例如 uint . 此宏定义了几个具有前缀的函数原型 SNAME 并且至少有一个参数是类型为的数组 TYPE . 那些具有返回参数的函数返回 TYPE 价值。
TEST_FUNC_PROTOS
TYPE
unsigned int
SNAME
uint
TEST_FUNC_PROTOS 然后为支持的所有数据类型实现 numpy.i :
signed char unsigned char short unsigned short int unsigned int long unsigned long long long unsigned long long float double
signed char
unsigned char
short
unsigned short
int
long
unsigned long
long long
unsigned long long
float
double
Vector.cxx 是一个C++源文件,它为每个指定的函数原型实现可编译代码。 Vector.h . 它定义了一个C宏 TEST_FUNCS 具有相同的论点,并且工作方式与 TEST_FUNC_PROTOS 确实在 Vector.h . TEST_FUNCS 为上述12种数据类型中的每一种实现。
Vector.cxx
TEST_FUNCS
Vector.i 是一个 SWIG 定义python模块的接口文件 Vector . 它遵循使用惯例 numpy.i 如本章所述。它定义了一个 SWIG 宏 %apply_numpy_typemaps 只有一个论点 TYPE . 它使用 SWIG 指令 %apply 将提供的类型映射应用于中找到的参数签名 Vector.h . 然后,此宏针对支持的所有数据类型实现 numpy.i . 然后做一个 %include "Vector.h" 将所有函数原型包装在 Vector.h 在 numpy.i .
%apply_numpy_typemaps
%apply
%include "Vector.h"
后 make 用于构建测试扩展模块, testVector.py 可以运行以执行测试。与其他使用 unittest 为了便于单元测试, testVector.py 定义继承自的类 unittest.TestCase ::
make
unittest.TestCase
class VectorTestCase(unittest.TestCase):
但是,这个类不是直接运行的。相反,它充当其他几个Python类的基类,每个类都特定于特定的数据类型。这个 VectorTestCase 类存储用于键入信息的两个字符串:
VectorTestCase
self.typeStr与其中一个匹配的字符串 SNAME 中使用的前缀 Vector.h 和 Vector.cxx . 例如, "double" . self.typeCode以numpy表示数据类型并对应于 self.typeStr . 例如,如果 self.typeStr 是 "double" 然后 self.typeCode 应该是 "d" .
与其中一个匹配的字符串 SNAME 中使用的前缀 Vector.h 和 Vector.cxx . 例如, "double" .
"double"
以numpy表示数据类型并对应于 self.typeStr . 例如,如果 self.typeStr 是 "double" 然后 self.typeCode 应该是 "d" .
self.typeStr
self.typeCode
"d"
每个测试由 VectorTestCase 类通过访问 Vector 模块字典:
length = Vector.__dict__[self.typeStr + "Length"]
在双精度测试的情况下,这将返回python函数 Vector.doubleLength .
Vector.doubleLength
然后,我们用一个简短的定义为每个支持的数据类型定义一个新的测试用例类,例如:
class doubleTestCase(VectorTestCase): def __init__(self, methodName="runTest"): VectorTestCase.__init__(self, methodName) self.typeStr = "double" self.typeCode = "d"
这12个类中的每一个都被收集到 unittest.TestSuite ,然后执行。错误和失败被汇总在一起,并作为exit参数返回。任何非零结果都表明至少有一个测试未通过。
unittest.TestSuite