测试 numpy.i 类型映射#

介绍

numpy.i 为SWIG接口文件编写测试 是一件令人头痛的事情。目前,支持 12 种不同的数据类型,每种数据类型有 74 种不同的参数签名,总共支持“开箱即用”的 888 种类型映射。反过来,每个类型映射可能需要多次单元测试,以便验证正确和不正确输入的预期行为。目前,这会导致在子目录中运行时执行 1,000 多个单独的单元测试 。make testnumpy/tools/swig

为了促进许多类似的单元测试,采用了一些高级编程技术,包括 C 和SWIG宏以及 Python 继承。本文档的目的是描述用于验证numpy.i 类型映射是否按预期工作的测试基础设施。

测试组织#

支持三个独立的测试框架,分别针对一维、二维和三维数组。对于一维数组,有两个 C++ 文件,一个头文件和一个源文件,名称为:

Vector.h
Vector.cxx

包含各种以一维数组作为函数参数的函数的原型和代码。文件:

Vector.i

是一个SWIG接口文件,它定义了一个 python 模块Vector ,该模块包装了函数,Vector.h同时利用类型映射来numpy.i正确处理 C 数组。

调用生成和 ,并且还执行编译扩展模块 或并将Makefile其链接在一起的脚本,具体取决于平台。这个扩展模块和代理文件都放在该目录下的子目录中。swigVector.pyVector_wrap.cxxsetup.pyVector_wrap.cxx_Vector.so_Vector.dylibVector.pybuild

实际测试使用名为以下的 Python 脚本进行:

testVector.py

它使用标准 Python 库模块unittest,该模块对支持的每种数据类型定义的每个函数执行多项测试Vector.h

二维数组的测试方式完全相同。上述描述适用,但用Matrix代替 Vector。对于三维测试,替换TensorVector。对于四维测试,替换SuperTensorVector。对于平面就地阵列测试,请Flat 替换Vector.对于下面的描述,我们将引用 Vector测试,但相同的信息适用于MatrixTensorSuperTensor测试。

该命令将确保构建所有测试软件,然后运行所有三个测试脚本。make test

测试头文件#

Vector.h是一个 C++ 头文件,它定义了一个名为 的 C 宏, TEST_FUNC_PROTOS该宏带有两个参数:TYPE,它是一个数据类型名称,例如; and ,它是相同数据类型的短名称,不带空格,例如。该宏定义了多个具有前缀的函数原型 ,并且至少有一个参数为类型数组 。那些具有返回参数的函数会返回一个 值。unsigned intSNAMEuintSNAMETYPETYPE

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

测试源文件#

Vector.cxx是一个 C++ 源文件,它为 中指定的每个函数原型实现可编译代码Vector.h。它定义了一个 C 宏,该宏具有与 中TEST_FUNCS相同的参数和工作方式。 如上所述,对 12 种数据类型中的每一种进行了实现。TEST_FUNC_PROTOSVector.hTEST_FUNCS

测试 SWIG 接口文件#

Vector.i是一个定义 python 模块的 SWIGVector接口文件。它遵循numpy.i本章所述的使用约定。它定义了一个具有单个参数的SWIG宏 。它使用SWIG指令将提供的类型映射应用到 中找到的参数签名。然后针对 所支持的所有数据类型实现该宏 。然后,它使用 中的类型映射 来包装所有函数原型。%apply_numpy_typemapsTYPE%applyVector.hnumpy.i%include "Vector.h"Vector.hnumpy.i

测试 Python 脚本#

make用于构建测试扩展模块 后,testVector.py可以运行来执行测试。与用于unittest促进单元测试的其他脚本一样, testVector.py定义一个继承自的类 unittest.TestCase

class VectorTestCase(unittest.TestCase):

然而,这个类并不是直接运行的。相反,它充当其他几个 Python 类的基类,每个类都特定于一种特定的数据类型。该类VectorTestCase存储两个用于输入信息的字符串:

self.typeStr

与 和SNAME中使用的前缀 之一匹配的字符串。例如,。Vector.hVector.cxx"double"

自我类型代码

一个短(通常是单字符)字符串,表示 numpy 中的数据类型并对应于self.typeStr.例如,如果self.typeStr"double",那么 self.typeCode应该是"d"

类定义的每个测试都会VectorTestCase通过访问模块的字典来提取它尝试测试的 python 函数Vector

length = Vector.__dict__[self.typeStr + "Length"]

在双精度测试的情况下,这将返回 python function Vector.doubleLength

然后,我们为每个支持的数据类型定义一个新的测试用例类,并带有简短的定义,例如:

class doubleTestCase(VectorTestCase):
    def __init__(self, methodName="runTest"):
        VectorTestCase.__init__(self, methodName)
        self.typeStr  = "double"
        self.typeCode = "d"

这 12 个类中的每一个都被收集到一个 中unittest.TestSuite,然后执行。错误和失败会汇总在一起并作为退出参数返回。任何非零结果都表明至少有一项测试未通过。