Тестирование typemaps numpy.i#
Введение#
Написание тестов для numpy.i SWIG
интерфейсный файл — это комбинаторная головная боль. В настоящее время поддерживаются 12 различных
типов данных, каждый с 74 различными сигнатурами аргументов,
всего 888 типовых карт, поддерживаемых "из коробки". Каждая из этих
типовых карт, в свою очередь, может потребовать нескольких модульных тестов для проверки
ожидаемого поведения как для правильных, так и для неправильных входных данных. В настоящее время
это приводит к выполнению более 1000 отдельных модульных тестов, когда
make test запускается в numpy/tools/swig подкаталог.
Для облегчения этого множества похожих модульных тестов используются некоторые высокоуровневые
программные техники, включая C и SWIG макросы,
а также наследование в Python. Цель этого документа — описать
инфраструктуру тестирования, используемую для проверки того, что numpy.i
typemaps работают как ожидалось.
Организация тестирования#
Поддерживаются три независимые тестовые структуры для одномерных, двумерных и трехмерных массивов соответственно. Для одномерных массивов есть два файла C++, заголовочный и исходный, с именами:
Vector.h
Vector.cxx
содержащие прототипы и код для различных функций, которые имеют одномерные массивы в качестве аргументов. Файл:
Vector.i
является SWIG интерфейсный файл, определяющий модуль Python Vector
который оборачивает функции в Vector.h используя типовые карты
в numpy.i для корректной обработки C-массивов.
The Makefile вызовы swig для генерации Vector.py и
Vector_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 тестов, но та же информация применима к Matrix,
Tensor и SuperTensor тесты.
Команда make test обеспечит сборку всего тестового программного обеспечения и затем запуск всех трех тестовых скриптов.
Тестирование заголовочных файлов#
Vector.h является заголовочным файлом C++, который определяет макрос C, называемый
TEST_FUNC_PROTOS который принимает два аргумента: TYPE, что является именем типа данных, таким как unsigned int; и SNAME, что является коротким именем для того же типа данных без пробелов, например, uint. Этот макрос определяет несколько прототипов функций с префиксом
SNAME и иметь хотя бы один аргумент, который является массивом типа
TYPE. Те функции, которые имеют возвращаемые аргументы, возвращают
TYPE значение.
TEST_FUNC_PROTOS затем реализуется для всех типов данных, поддерживаемых numpy.i:
signed charunsigned charshortunsigned shortintunsigned intlongunsigned longlong longunsigned long longfloatdouble
Тестирование исходных файлов#
Vector.cxx является исходным файлом C++, который реализует компилируемый код
для каждого из прототипов функций, указанных в Vector.h. Он
определяет макрос C TEST_FUNCS которая имеет те же аргументы и работает таким же образом, как TEST_FUNC_PROTOS делает в Vector.h.
TEST_FUNCS реализован для каждого из 12 типов данных, как указано выше.
Тестирование файлов интерфейса SWIG#
Vector.i является SWIG интерфейсный файл, определяющий модуль Python
VectorОн следует соглашениям для использования numpy.i как описано в этой главе. Он определяет SWIG macro
%apply_numpy_typemaps который имеет один аргумент TYPE.
Он использует SWIG директива %apply применить предоставленные
типомапы к сигнатурам аргументов, найденным в Vector.h. Этот макрос
затем реализуется для всех типов данных, поддерживаемых
numpy.i. Затем выполняется %include "Vector.h" чтобы обернуть все
прототипы функций в Vector.h используя типовые карты в
numpy.i.
Тестирование скриптов Python#
После make используется для сборки тестовых модулей расширений,
testVector.py может быть запущен для выполнения тестов. Как и с другими
скриптами, которые используют unittest для облегчения модульного тестирования,
testVector.py определяет класс, который наследуется от
unittest.TestCase:
class VectorTestCase(unittest.TestCase):
Однако этот класс не запускается напрямую. Скорее, он служит базовым
классом для нескольких других классов Python, каждый из которых специфичен для
определенного типа данных. VectorTestCase класс хранит две строки
для информации о типах:
- self.typeStr
Строка, соответствующая одному из
SNAMEпрефиксы, используемые вVector.hиVector.cxx. Например,"double".- self.typeCode
Короткая (обычно однобуквенная) строка, которая представляет тип данных в numpy и соответствует
self.typeStr. Например, еслиself.typeStrявляется"double", затемself.typeCodeдолжен быть"d".
Каждый тест, определённый VectorTestCase класс извлекает тестируемую python-функцию, обращаясь к Vector словарь модуля:
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, который затем выполняется. Ошибки и сбои суммируются и возвращаются как аргумент выхода. Любой ненулевой результат указывает, что хотя бы один тест не пройден.