1
\$\begingroup\$

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

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

A Godbolt link is here.

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 test arithmetic_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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.