Reputation: 47
I am looking for a unit testing framework that allows passing type and value parameters to test function at the same time.
Example: let's say I have a few template sorting functions:
template<class ITERATOR, class COMPARATOR = std::less<typename std::iterator_traits<ITERATOR>::value_type>>
void selectionSort(const ITERATOR first,const ITERATOR last, const COMPARATOR comparator = COMPARATOR ()){ ... };
template<class ITERATOR, class COMPARATOR = std::less<typename std::iterator_traits<ITERATOR>::value_type>>
void insertionSort(const ITERATOR first,const ITERATOR last, const COMPARATOR comparator = COMPARATOR ()) { ... }
and I want to test these functions with different input types as:
template<class T>
vector<T> makeRandomVector(int size){ ... }
vector<int> intVec = makeRandomVector<int>(10); //10 is vector size
someSort(intVec.begin(),intVec.end())
vector<MyCustomStruct> structVec = makeRandomVector<MyCustomStruct>(10);
someSort(structVec.begin(),structVec.end())
and with different size arguments for makeRandomVector
to test these function if vector size is 0,1,10 etc.
So I am looking for C++ unit testing framework that allows creating test that would accept type (int,MyCustomStruct) and value parameters (0,1,10) at the same time and perform the test for each element of the cartesian product of Types x Values.
Upvotes: 1
Views: 295
Reputation: 2880
A possible way is the typed test of GoogleTest as follows.
Please note that this is the solution for C++14 and over, and integer parameters just like your example {0, 1, 10}
.
Approach
Let us define the following struct Case
:
template<typename T, std::size_t I>
struct Case
{
using type = T;
static constexpr std::size_t size = I;
};
The basic idea of testing all combinations of types and sizes are using this struct to specify test types like this:
#include <gtest/gtest.h>
template <typename T>
class TypedTest : public ::testing::Test {};
using TestTypes = ::testing::Types<Case<int, 0>, Case<int, 1>, Case<int, 10>, Case<MyCustomStruct, 0>, ...>;
TYPED_TEST_CASE(TypedTest, TestTypes);
TYPED_TEST(TypedTest, sort)
{
auto vec = makeRandomVector<TypeParam::type>(TypeParam::size);
...
}
Combinatorics
Now our problem is how can we simply construct the all combinations of types and sizes.
I just answered the almost same question with this problem yesterday.
In the current case, all possible pairs of {int, MyCustomStruct}
and {0, 1, 10}
are labeled by one dimensional integers 0,1,...,5 like this (and max66's approach would be also possible):
0 -> (0/3, 0%3) = (0,0) -> std::tuple<int, 0>
1 -> (1/3, 1%3) = (0,1) -> std::tuple<int, 1>
2 -> (2/3, 2%3) = (0,2) -> std::tuple<int, 10>
3 -> (3/3, 3%3) = (1,0) -> std::tuple<MyCustomStruct, 0>
4 -> (4/3, 4%3) = (1,1) -> std::tuple<MyCustomStruct, 1>
5 -> (5/3, 5%3) = (1,2) -> std::tuple<MyCustomStruct, 10>
where 3
is the size of {0, 1, 10}
.
It is simple and straightforward to write the function which makes all possible combinations with this algorithm as follows.
For instance, Combinations<std::tuple<int, MyCustomStruct>, 0, 1, 10>
is equal to the type of std::tuple<Case<int,0>, Case<int,1>, Case<int,10>, Case<MyCustomStruct,0>, ...>
:
template<typename TupleType, typename TupleIdx, std::size_t I>
struct make_case
{
static constexpr std::size_t N = std::tuple_size<TupleIdx>::value;
using type = Case<typename std::tuple_element<I/N, TupleType>::type,
std::tuple_element<I%N, TupleIdx >::type::value>;
};
template <typename T1, typename T2, typename Is>
struct make_combinations;
template <typename TupleType, typename TupleIdx, std::size_t... Is>
struct make_combinations<TupleType, TupleIdx, std::index_sequence<Is...>>
{
using tuples = std::tuple<typename make_case<TupleType, TupleIdx, Is>::type...>;
};
template<typename TupleTypes, std::size_t ...Is>
using Combinations = typename make_combinations
<TupleTypes,
std::tuple<std::integral_constant<std::size_t, Is>...>,
std::make_index_sequence<(std::tuple_size<TupleTypes>::value)*(sizeof...(Is))>>
::tuples;
Testing
Applying answers of this post, we can stripe this tuple Combinations<...>
into a list of types and here I apply Nawaz's simple one.
Then all tests can be done as follows:
template<typename T>
struct Test;
template<typename ...T>
struct Test<std::tuple<T...>>
{
using Types = ::testing::Types<T...>;
};
template <typename T>
class TypedTest : public ::testing::Test {};
using TestTypes = Test<Combinations<std::tuple<int, MyCustomStruct>, 0, 1, 10>>::Types;
TYPED_TEST_CASE(TypedTest, TestTypes);
TYPED_TEST(TypedTest, sort)
{
auto vec = makeRandomVector<TypeParam::type>(TypeParam::size);
...
}
Upvotes: 3