Search code examples
c++catch2

Catch2: Test all permutations of two lists of types


I need to write some unit tests in Catch2 where each test case should be executed for each possible permutation of two lists of types. I'd love something like

// Some types defined in my project
class A;
class B;

PERMUTATION_TEST_CASE ("Foo", (A, B), (float, double))
{
   TestTypeX x;
   TestTypeY y;
}

Where the test case would be executed 4 times with

TestTypeX = A, TestTypeY = float
TestTypeX = A, TestTypeY = double
TestTypeX = B, TestTypeY = float
TestTypeX = B, TestTypeY = double

Alternatively, something like this would also be possible

constexpr A a;
constexpr B b;

TEMPLATE_TEST_CASE ("Foo", float, double)
{
   auto x = GENERATE (a, b); // does not work because a and b have different types
   TestType y; 
}

To my knowledge, there is no such thing in catch2. There is , in type-parametrised-test-cases, TEMPLATE_TEST_CASE which solves the problem for a single list of types but not each permutation of two lists and there is TEMPLATE_PRODUCT_TEST_CASE which solves the problem in case of the first type being a template which is then instantiated with each type of the second list – which is also not what I need here.

Is there any suitable catch2 mechanism for that which I'm overlooking at the moment? I'm using Catch2 version 3.1.0.

My real-world requirements are much larger sets of combinations than this 2x2 example, so manually specifying all permutations would not be my favourite choice.


Solution

  • You can create the Cartesian product of type list, and then:

    using MyTypes = cross_product<type_list<A, B>, type_list<float, double>>::type;
    // using MyTypes = type_list<std::pair<A, float>, std::pair<A, double>,
    //                           std::pair<B, float>, std::pair<B, double>>;
    TEMPLATE_LIST_TEST_CASE("some_name", "[xxx]", MyTypes)
    {
        using TestTypeX = typename TestType::first_type;  // A or B
        using TestTypeY = typename TestType::second_type; // float or double
        // ...
    }
    

    In the same way, TEMPLATE_PRODUCT_TEST_CASE might be (ab)used with wrapper type:

    template <typename T>
    struct A_with
    {
        using first_type = A;
        using second_type = T;
    };
    template <typename T>
    struct B_with
    {
        using first_type = B;
        using second_type = T;
    };
    
    TEMPLATE_PRODUCT_TEST_CASE("some name", "[xxx]", (A_with, B_with), (float, double))
    {
        using TestTypeX = typename TestType::first_type;  // A or B
        using TestTypeY = typename TestType::second_type; // float or double
        // ...
    }