Протестируйте шаблонный класс C ++, используя Catch Framework

Я ищу хороший способ использовать Catch для тестирования шаблонного класса. У меня есть кое-что, что почти работает:

#define RUN_ALL(fn, params)  \
fn<uint8_t, bool>(params);  \
fn<uint8_t, char>(params);  \
fn<uint16_t, bool>(params); \
fn<uint16_t, char>(params); \
fn<uint32_t, bool>(params); \
fn<uint32_t, char>(params); \
fn<uint64_t, bool>(params); \
fn<uint64_t, char>(params);

template<typename A, typename B>
void test_number_one() {
REQUIRE(...)
}

TEST_CASE("Foo::Foo() works nicely", "[SmallGraph]") {
RUN_ALL(test_number_one)
}

Эта установка будет работать только до первого сбоя, что вполне нормально, поскольку весьма вероятно, что все 8 случаев будут одинаковыми. Однако было бы неплохо узнать, какой набор аргументов шаблона используется при возникновении сбоя. Моя идея сделать это:

#define RUN_ALL_P(fn, params)  \
INFO("Testing <uint8_t, bool>"); \
fn<uint8_t, bool>(params);  \
INFO("Testing <uint8_t, char>"); \
fn<uint8_t, char>(params);  \
INFO("Testing <uint16_t, bool>"); \
fn<uint16_t, bool>(params); \
...

Однако я не могу использовать более одного INFO в RUN_ALL, потому что при этом генерируется код с повторяющимся идентификатором.

FOO.cpp:270:3: error: redefinition of 'scopedMessage270'
RUN_ALL(test_number_one);

(RUN_ALL(test_number_one) появляется в строке 270.)

Любые идеи для обходного пути, который не требует, чтобы все тестовые функции имели одну и ту же сигнатуру?

(Я также приветствовал бы ссылки на статьи о тестировании кода шаблона с использованием CATCH, а также предложения о том, как искать такие статьи, не получая кучу результатов об общей обработке исключений — т. Е. Try / catch.)

0

Решение

Проблема с вашим макросом в том, что когда он раскрывается, он раскрывается до одной строки. Хотя я не знаю, как используется ваша тестовая среда, очевидно, что макрос делает что-то похожее на это:

struct M { M(char* msg) { puts(msg); } }; // just an example class...
#define INFO(m) M scopedMessage##__line__(msg)

Таким образом, вы получите несколько экземпляров scopedMessage270, если будете использовать макрос RUN_ALL в строке 270 …

Вы можете обойти эту проблему, заменив макрос шаблоном. К сожалению, вы не можете использовать это с шаблонными функциями, поэтому вам придется также создавать тестовые примеры шаблонных классов:

template <template <typename T, typename TT > class Test >
struct All
{
template <typename ... Parameters>
static void run(Parameters ... parameters)
{
Test<uint8_t, bool>::run(parameters ...);
Test<uint8_t, char>::run(parameters ...);
Test<uint16_t, bool>::run(parameters ...);
Test<uint16_t, char>::run(parameters ...);
Test<uint32_t, bool>::run(parameters ...);
Test<uint32_t, char>::run(parameters ...);
Test<uint64_t, bool>::run(parameters ...);
Test<uint64_t, char>::run(parameters ...);
}
};

template<typename A, typename B>
struct test_number_one
{
static void run()
{
// log test name
// run the test
}
};
template<typename A, typename B>
struct test_number_two
{
static void run(int n)
{
// log test name and parameter value
// run the test
}
};

int main(int argc, char* argv[])
{
All<test_number_one>::run();
All<test_number_two>::run(12);
All<test_number_two>::run(10);
}

Как и сейчас, внутри шаблона все строки кода остаются на отдельных строках, вы можете размещать их между любыми журналами так, как вам нравится:

template <typename ... Parameters>
static void run(Parameters ... parameters)
{
INFO("uint8_t, bool");
Test<uint8_t, bool>::run(parameters ...);
INFO("uint8_t, char");
Test<uint8_t, char>::run(parameters ...);
// ...
1

Другие решения

@Aconcagua абсолютно правильно. Мое решение аналогично, но использует функторы (как предложено @R Sahu —C ++ Одиночный указатель на функцию для всех экземпляров шаблона.)

template<template<typename, typename> class TestFunctor, typename... Parameters>
void testAllTypes(Parameters... parameters) {

INFO("Testing <uint8_t, bool>");
TestFunctor<uint8_t, bool>()(parameters...);
INFO("Testing <uint8_t, char>");
TestFunctor<uint8_t, char>()(parameters...);

// ...
}

template<typename A, typename B>
struct testDefaultConstructor {
void operator()() {
mallGraph<A, B> sg;
REQUIRE(sg.numVertices() == 0);
REQUIRE_FALSE(sg.edgecountIsValid());
REQUIRE_FALSE(sg.adjacencyMatrixIsValid());
}
};TEST_CASE("SmallGraph::SmallGraph() initializes instance data as expected", "[SmallGraph]") {
testAllTypes<testDefaultConstructor>();
}
1