Testing the numpy.i Typemaps

Introduction

numpy.i SWIG接口文件编写测试是一种组合性头痛。目前,支持12种不同的数据类型,每种类型具有74个不同的参数签名,支持“开箱即用”的总共888个类型映射。这些类型映射中的每一个反过来可能需要几个单元测试,以便验证正确和不正确输入的预期行为。目前,这将导致在进行 测试时在numpy/tools/swig

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

Testing Organization

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

Vector.h
Vector.cxx

其包含用于具有一维数组作为函数参数的各种函数的原型和代码。文件:

Vector.i

is a SWIG interface file that defines a python module Vector that wraps the functions in Vector.h while utilizing the typemaps in numpy.i to correctly handle the C arrays.

Makefile调用swig以生成Vector.pyVector_wrap.cxx,并执行setup.py脚本,它编译Vector_wrap.cxx并将扩展模块_Vector.so_Vector.dylib平台。此扩展模块和代理文件Vector.py都放置在build目录下的子目录中。

实际测试使用一个名为的Python脚本:

testVector.py

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

二维数组以完全相同的方式测试。以上描述适用,但使用Matrix替换Vector对于三维测试,将Tensor替换为Vector对于四维测试,用SuperTensor替换Vector对于平面原位数组测试,用Flat替换Vector对于以下描述,我们将引用Vector测试,但相同的信息适用于MatrixTensorSuperTensor

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

Testing Header Files

Vector.h是一个C ++头文件,它定义了一个名为TEST_FUNC_PROTOS的C宏,它接受两个参数:TYPE,这是一个数据类型名称unsigned int;和SNAME,这是相同数据类型的短名称,不含空格,例如uint此宏定义了几个具有前缀SNAME的函数原型,并且至少有一个参数是类型TYPE的数组。具有返回参数的函数返回TYPE值。

numpy.i支持的所有数据类型都将实现TEST_FUNC_PROTOS

  • 签名 char
  • 无符号 char
  • short
  • 无符号
  • int
  • 无符号 int
  • long
  • 无符号
  • long long
  • 无符号
  • float
  • double

Testing Source Files

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

Testing SWIG Interface Files

Vector.i是一个定义python模块VectorSWIG接口文件。它遵循本章中所述的使用numpy.i的约定。它定义了具有单个参数TYPESWIG%apply_numpy_typemaps它使用SWIG指令%apply将提供的类型映射应用于Vector.h中的参数签名。然后对由numpy.i支持的所有数据类型实现此宏。然后,它会在Vector.h中包含所有的函数原型,%include “Vector.h”使用numpy.i中的类型映射。

Testing Python Scripts

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

class VectorTestCase(unittest.TestCase):

但是,这个类不是直接运行的。相反,它用作几个其他Python类的基类,每个特定于特定数据类型。VectorTestCase类存储两个用于键入信息的字符串:

self.typeStr
Vector.hVector.cxx中使用的SNAME前缀之一匹配的字符串。例如,"double"
self.typeCode
表示numpy中的数据类型的短(通常为单字符)字符串,对应于self.typeStr例如,如果self.typeStr"double",则self.typeCode应为"d"

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

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

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

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

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

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