This is a follow-up question for An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++. As Toby Speight's answer mentioned, some self-checking unit tests are needed to verify the correctness of arithmetic_mean
function. There are numerous possible ways to construct test iterables and let's focused on std::vector
case here first. I am trying to implement a test_vectors_generator
which can generate vectors with specific pattern to help the testing tasks of arithmetic_mean
function. In the previous question std::array and std::vector Type Arbitrary Nested Iterable Generator Functions Implementation in C++, I tried to construct nested std::vector
which elements are all filled the same given value with n_dim_vector_generator
template function. Besides the case that all elements are with the same value, I've check the question like use std::fill to populate vector with increasing numbers which discusses the methods for filling sequential values into std::vector
so that the vector like {0, 1, 2, 3, ..., 99}
is easy to created with Oleksandr Karaberov's answer. I want to take a further step to create a set of vectors as follows easily with the given conditions start_num=0, end_num=1, step=1, element_count=3
.
{0, 0, 0}
{0, 0, 1}
{0, 1, 0}
{0, 1, 1}
{1, 0, 0}
{1, 0, 1}
{1, 1, 0}
{1, 1, 1}
The usage description
There are four parameters in test_vectors_generator
template function, the first one is a start iteration number of each element, the second one is a end iteration number of each element, the third one is a step size and the fourth one is the element count of each std::vector
. In other words, a series std::vector
can be created with test_vectors_generator(start_num, end_num, step_num, element_count)
. Another usage example is like test_vectors_generator(1, 3, 1, 3)
and its output is:
{1, 1, 1}
{1, 1, 2}
{1, 1, 3}
{1, 2, 1}
{1, 2, 2}
{1, 2, 3}
{1, 3, 1}
{1, 3, 2}
{1, 3, 3}
{2, 1, 1}
...
{3, 1, 1}
{3, 1, 2}
{3, 1, 3}
{3, 2, 1}
{3, 2, 2}
{3, 2, 3}
{3, 3, 1}
{3, 3, 2}
{3, 3, 3}
The experimental implementation
namespace ts
{
template<class T> requires (!is_iterable<T>)
constexpr auto test_vectors_generator(T start, T end, T step, std::size_t element_count)
{
if (element_count == 1)
{
std::list<std::vector<T>> output(((end - start) / step) + 1);
T i = 0; // incrementor
std::for_each(output.begin(), output.end(), [&](auto& item) { i+=step; item = std::vector<T>{ i }; });
return output;
}
else
{
std::list<std::vector<T>> output{};
auto test_vectors = test_vectors_generator(start, end, step, element_count - 1);
std::for_each(test_vectors.begin(), test_vectors.end(), [&](const auto item) {
for (T i = start; i <= end; i += step)
{
auto new_element = item;
new_element.push_back(i);
output.push_back(new_element);
}
});
return output;
}
}
}
The used is_iterable
concept:
template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};
Test cases
- Test cases of
test_vectors_generator
template function
With the previous question A recursive_print Function For Various Type Arbitrary Nested Iterable Implementation in C++, the contents of std::vector
can print out with recursive_print
template function. As the result,
typedef int TestType;
TestType start_num = 1;
TestType end_num = 3;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
recursive_print(vectors_for_test);
Level 0:
Level 1:
1
1
1
Level 1:
1
1
2
Level 1:
1
1
3
Level 1:
1
2
1
Level 1:
1
2
2
Level 1:
1
2
3
Level 1:
1
3
1
Level 1:
1
3
2
Level 1:
1
3
3
Level 1:
2
1
1
...
Level 1:
3
1
1
Level 1:
3
1
2
Level 1:
3
1
3
Level 1:
3
2
1
Level 1:
3
2
2
Level 1:
3
2
3
Level 1:
3
3
1
Level 1:
3
3
2
Level 1:
3
3
3
- Test cases for
arithmetic_mean
With Boost.Test tool, the arithmetic_mean
template function can be tested with the following code.
BOOST_AUTO_TEST_CASE(test_vectors_generator_char)
{
typedef char TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_int)
{
typedef int TestType;
TestType start_num = 1;
TestType end_num = 3;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_short)
{
typedef short TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_long)
{
typedef long TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_long_long_int)
{
typedef long long int TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_unsigned_char)
{
typedef unsigned char TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_float)
{
typedef float TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_double)
{
typedef double TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
BOOST_AUTO_TEST_CASE(test_vectors_generator_long_double)
{
typedef long double TestType;
TestType start_num = 1;
TestType end_num = 50;
TestType step_num = 1;
auto vectors_for_test = ts::test_vectors_generator(start_num, end_num, step_num, 3);
for (auto& each_test_vector : vectors_for_test)
{
// Generate expected_value
double expected_value = 0;
for (auto& each_item : each_test_vector)
{
expected_value += each_item;
}
expected_value = expected_value / static_cast<double>(each_test_vector.size());
BOOST_TEST(expected_value == arithmetic_mean(each_test_vector));
}
BOOST_TEST(true);
}
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++
What changes has been made in the code since last question?
Using Boost.Test and
test_vectors_generator
to testarithmetic_mean
template function.Why a new review is being asked for?
I am not sure if it is a good idea to create test cases for
arithmetic_mean
template function like this. I think that it's hard to test various configuration completely. If there is any further possible improvement, please let me know.