Тестирование typemap numpy.i
Введение
Написание тестов для файла интерфейса numpy.i SWIG является комбинаторной головной болью. В настоящее время поддерживается 12 различных типов данных, каждый с 74 различными сигнатурами аргументов, в общей сложности 888 typemap, поддерживаемые «из коробки». Каждый из этих typemap, в свою очередь, может потребовать несколько модульных тестов для проверки ожидаемого поведения как для правильных, так и для неправильных входных данных. В настоящее время это приводит к выполнению более 1000 отдельных модульных тестов при запуске make test в подкаталоге numpy/tools/swig.
Для облегчения такого количества аналогичных модульных тестов применяются некоторые высокоуровневые методы программирования, включая макросы C и SWIG, а также наследование Python. Цель данного документа – описать используемую инфраструктуру тестирования для проверки того, что typemap numpy.i работают как ожидается.
Организация тестирования
Поддерживаются три независимые тестовые среды для одномерных, двумерных и трехмерных массивов соответственно. Для одномерных массивов существуют два файла C++, заголовок и исходный код, с именами:
Vector.h Vector.cxx
в которых содержатся прототипы и код для различных функций, имеющих одномерные массивы в качестве аргументов функции. Файл:
Vector.i
является файлом интерфейса SWIG, который определяет модуль python Vector, оборачивающий функции в Vector.h, используя typemaps в numpy.i для правильной обработки массивов C.
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 %apply_numpy_typemaps, имеющий один аргумент TYPE. Он использует директиву SWIG %apply для применения указанных typemap к сигнатурам аргументов, найденным в Vector.h. Этот макрос затем реализуется для всех поддерживаемых типов данных numpy.i. Затем выполняется %include "Vector.h" для обертывания всех прототипов функций в Vector.h с использованием typemap в 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, который затем выполняется. Ошибки и сбои суммируются и возвращаются в качестве аргумента выхода. Любой ненулевой результат указывает на то, что по крайней мере один тест не пройден.
© 2005–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/swig.testing.html