Я ищу хороший способ использовать 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.)
Проблема с вашим макросом в том, что когда он раскрывается, он раскрывается до одной строки. Хотя я не знаю, как используется ваша тестовая среда, очевидно, что макрос делает что-то похожее на это:
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 ...);
// ...
@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>();
}