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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.