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