2016-07-20 8 views
0

Ich bin auf der Suche nach einer guten Möglichkeit, Catch zu verwenden, um eine Vorlagenklasse zu testen. Ich habe etwas, das fast funktioniert:Test C++ Template-Klasse mit Catch-Framework

#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) 
} 

Diese Einstellung nur bis zum ersten Ausfall führen wird, was in Ordnung ist, weil es sehr wahrscheinlich ist, dass alle acht Fälle die gleiche Art und Weise fehlschlagen. Es wäre jedoch hilfreich zu wissen, welche Gruppe von Vorlagenargumenten bei einem Fehler verwendet wird. Meine Idee ist es, dies zu tun:

#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); \ 
... 

Allerdings habe ich nicht mehr als eine INFO in RUN_ALL weil so Code generiert mit einer doppelten Kennung tun können.

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

(RUN_ALL(test_number_one) erscheint auf der Leitung 270.)

Alle Ideen für eine Abhilfe, die nicht alle Testfunktionen auf die gleiche Signatur erfordert?

(Ich würde auch gerne Hinweise auf Artikel zum Testen von Vorlagencode mit CATCH, sowie Vorschläge, wie man nach solchen Artikeln suchen, ohne eine Reihe von Ergebnissen über die allgemeine Handhabung der Ausnahme zu erhalten - dh versuchen/fangen.)

+0

Kurze Antwort: Verwenden Sie functors: http://stackoverflow.com/questions/38494276/c-single-function- pointer-for-all-template-instances – Zack

Antwort

1

Das Problem mit Ihrem Makro ist, dass, wenn es erweitert wird, es zu einer einzigen Zeile erweitert wird. Obwohl ich im Einsatz wissen, dass Ihr Test-Framework nicht, ist es offensichtlich, dass das Makro etwas tut, vergleichbar dies:

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

So werden Sie mehrere scopedMessage270 Instanzen erhalten, wenn Sie Ihr Makro RUN_ALL in Zeile 270 verwenden .. .

Sie können das Problem umgehen, indem Sie das Makro durch eine Vorlage ersetzen. Leider kann man dies nicht mit Template-Funktionen verwenden, so dass Sie Ihre Testfälle Template-Klassen vornehmen müssen, auch:

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); 
} 

Wie jetzt, in der Vorlage, alle Codezeilen auf getrennten Leitungen bleiben, können Sie legen in zwischen jeder Protokollierung, wie Sie möchten:

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 ist absolut richtig. Meine Lösung ist ähnlich, verwendet aber functors (Wie @R Sahu vorgeschlagen --- C++ Single function pointer for all template instances)

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>(); 
} 
+0

Abgesehen von der Möglichkeit, als stateful Funktionszeiger in Templates verwendet zu werden, gibt es unter der Haube keinen wirklichen Unterschied zwischen einer operator() und einer anderen Funktion ... Ihr Punkt: die äußere Template-Funktion ist schöner als meine Template-Klasse ... – Aconcagua