lambdageneratorgoogletestc++23c++-coroutine

How to use C++ Google Test ::testing::ValuesIn with std::generator lambda?


class SumPairsTestFixture : public RangeTestFixture2<size_t, size_t, size_t>, public testing::TestWithParam<tuple<size_t, size_t, vector<size_t>>>
{
public:
    void SetUp() override
    {
        RangeTestFixture2::SetUp(get<0>(GetParam()), get<1>(GetParam()), get<2>(GetParam()));
    }
    size_t SumPairsTest()
    {
        return _rangeObj.SumPairs(_param, _data);
    }
};
TEST_P(SumPairsTestFixture, SumPairsTests)
{
    ASSERT_EQ(this->_expected, this->SumPairsTest());
}
INSTANTIATE_TEST_SUITE_P(
    SumPairsTests,
    SumPairsTestFixture,
    ::testing::Combine(
        ::testing::Values(make_tuple(2, 8, vector<size_t>{1, 2, 3, 4, 5, 6, 5}),
                          make_tuple(3, 12, vector<size_t>{5, 7, 9, 13, 11, 6, 6, 3, 3}),
                          // make_tuple(4, 10, vector<size_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
                          ::testing::ValuesIn([]() -> generator<size_t>
                                              {
    co_yield 4;
    co_yield 10;
    co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
);

Errors:

FAILED: test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o 
/usr/bin/g++ -DUNICODE -DX64 -D_CONSOLE -D_DEBUG -D_UNICODE  -g -std=gnu++23 -Winvalid-pch -include /usr/src/DataStructuresAlgorithms/build/test/CMakeFiles/DataStructuresAlgorithmsTests.dir/cmake_pch.hxx -MD -MT test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o -MF test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o.d -o test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o -c /usr/src/DataStructuresAlgorithms/test/RangeTests.cpp
In file included from /usr/include/gtest/gtest.h:67,
                 from /usr/src/DataStructuresAlgorithms/test/pch.h:2,
                 from /usr/src/DataStructuresAlgorithms/build/test/CMakeFiles/DataStructuresAlgorithmsTests.dir/cmake_pch.hxx:5,
                 from <command-line>:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp: In function ‘testing::internal::ParamGenerator<std::tuple<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > > > gtest_SumPairsTestsSumPairsTestFixture_EvalGenerator_()’:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:64:70: error: no matching function for call to ‘ValuesIn(std::generator<long unsigned int>)’
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/include/gtest/gtest-param-test.h:309:58: note: candidate: ‘template<class Container> testing::internal::ParamGenerator<typename Container::value_type> testing::ValuesIn(const Container&)’
  309 | internal::ParamGenerator<typename Container::value_type> ValuesIn(
      |                                                          ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:309:58: note:   template argument deduction/substitution failed:
/usr/include/gtest/gtest-param-test.h: In substitution of ‘template<class Container> testing::internal::ParamGenerator<typename Container::value_type> testing::ValuesIn(const Container&) [with Container = std::generator<long unsigned int>]’:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:52:1:   required from here
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/include/gtest/gtest-param-test.h:309:58: error: no type named ‘value_type’ in ‘class std::generator<long unsigned int>’
  309 | internal::ParamGenerator<typename Container::value_type> ValuesIn(
      |                                                          ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:297:1: note: candidate: ‘template<class ForwardIterator> testing::internal::ParamGenerator<typename std::iterator_traits<_Iterator>::value_type> testing::ValuesIn(ForwardIterator, ForwardIterator)’
  297 | ValuesIn(ForwardIterator begin, ForwardIterator end) {
      | ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:297:1: note:   candidate expects 2 arguments, 1 provided
/usr/include/gtest/gtest-param-test.h:304:29: note: candidate: ‘template<class T, long unsigned int N> testing::internal::ParamGenerator<T> testing::ValuesIn(const T (&)[N])’
  304 | internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
      |                             ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:304:29: note:   template argument deduction/substitution failed:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:64:70: note:   mismatched types ‘const T [N]’ and ‘std::generator<long unsigned int>’
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp: In function ‘std::string gtest_SumPairsTestsSumPairsTestFixture_EvalGenerateName_(const testing::TestParamInfo<std::tuple<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > > >&)’:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:64:70: error: no matching function for call to ‘ValuesIn(std::generator<long unsigned int>)’
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/include/gtest/gtest-param-test.h:309:58: note: candidate: ‘template<class Container> testing::internal::ParamGenerator<typename Container::value_type> testing::ValuesIn(const Container&)’
  309 | internal::ParamGenerator<typename Container::value_type> ValuesIn(
      |                                                          ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:309:58: note:   template argument deduction/substitution failed:
/usr/include/gtest/gtest-param-test.h: In substitution of ‘template<class Container> testing::internal::ParamGenerator<typename Container::value_type> testing::ValuesIn(const Container&) [with Container = std::generator<long unsigned int>]’:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:52:1:   required from here
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/include/gtest/gtest-param-test.h:309:58: error: no type named ‘value_type’ in ‘class std::generator<long unsigned int>’
  309 | internal::ParamGenerator<typename Container::value_type> ValuesIn(
      |                                                          ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:297:1: note: candidate: ‘template<class ForwardIterator> testing::internal::ParamGenerator<typename std::iterator_traits<_Iterator>::value_type> testing::ValuesIn(ForwardIterator, ForwardIterator)’
  297 | ValuesIn(ForwardIterator begin, ForwardIterator end) {
      | ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:297:1: note:   candidate expects 2 arguments, 1 provided
/usr/include/gtest/gtest-param-test.h:304:29: note: candidate: ‘template<class T, long unsigned int N> testing::internal::ParamGenerator<T> testing::ValuesIn(const T (&)[N])’
  304 | internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
      |                             ^~~~~~~~
/usr/include/gtest/gtest-param-test.h:304:29: note:   template argument deduction/substitution failed:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:64:70: note:   mismatched types ‘const T [N]’ and ‘std::generator<long unsigned int>’
   64 |                                                   ::testing::ValuesIn([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                                                           {
      |                                                                                           ~
   66 |         co_yield 4;
      |         ~~~~~~~~~~~                                                   
   67 |         co_yield 10;
      |         ~~~~~~~~~~~~                                                  
   68 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }())))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:52:1: error: template argument 1 is invalid
   52 | INSTANTIATE_TEST_SUITE_P(

I tried PiotrNycz's suggestion and hit the following error:

FAILED: test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o 
/usr/bin/g++ -DUNICODE -DX64 -D_CONSOLE -D_DEBUG -D_UNICODE  -g -std=gnu++23 -Winvalid-pch -include /usr/src/DataStructuresAlgorithms/build/test/CMakeFiles/DataStructuresAlgorithmsTests.dir/cmake_pch.hxx -MD -MT test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o -MF test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o.d -o test/CMakeFiles/DataStructuresAlgorithmsTests.dir/RangeTests.cpp.o -c /usr/src/DataStructuresAlgorithms/test/RangeTests.cpp
In file included from /usr/include/gtest/gtest-param-test.h:181,
                 from /usr/include/gtest/gtest.h:67,
                 from /usr/src/DataStructuresAlgorithms/test/pch.h:2,
                 from /usr/src/DataStructuresAlgorithms/build/test/CMakeFiles/DataStructuresAlgorithmsTests.dir/cmake_pch.hxx:5,
                 from <command-line>:
/usr/include/gtest/internal/gtest-param-util.h: In instantiation of ‘testing::internal::CartesianProductHolder<Gen>::operator testing::internal::ParamGenerator<std::tuple<_Args1 ...> >() const [with T = {long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> >}; Gen = {testing::internal::ValueArray<std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, testing::internal::ParamGenerator<long unsigned int> >}]’:
/usr/src/DataStructuresAlgorithms/test/RangeTests.cpp:53:1:   required from here
   61 |         ::testing::Combine(
      |         ~~~~~~~~~~~~~~~~~~^
   62 |                 ::testing::Values(make_tuple(2, 8, vector<size_t>{1, 2, 3, 4, 5, 6, 5}),
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   63 |                                                   make_tuple(3, 12, vector<size_t>{5, 7, 9, 13, 11, 6, 6, 3, 3}),
      |                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   64 |                                                   // make_tuple(4, 10, vector<size_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
      |                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   65 |                                                   ::testing::ValuesIn(vector<size_t>([]() -> generator<size_t>
      |                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   66 |                                                                                                                                                  {
      |                                                                                                                                                  ~
   67 |         co_yield 4;
      |         ~~~~~~~~~~~        
   68 |         co_yield 10;
      |         ~~~~~~~~~~~~       
   69 |         co_yield ranges::elements_of(ranges::iota_view(0, 10)); }() | ranges::to<vector<size_t>>()))))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/gtest/internal/gtest-param-util.h:948:9: error: no matching function for call to ‘testing::internal::CartesianProductGenerator<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > >::CartesianProductGenerator(const std::tuple<testing::internal::ValueArray<std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, testing::internal::ParamGenerator<long unsigned int> > >&)’
  948 |         new CartesianProductGenerator<T...>(generators_));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/gtest/internal/gtest-param-util.h:830:3: note: candidate: ‘testing::internal::CartesianProductGenerator<T>::CartesianProductGenerator(const std::tuple<testing::internal::ParamGenerator<T>...>&) [with T = {long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> >}]’
  830 |   CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/gtest/internal/gtest-param-util.h:830:69: note:   no known conversion for argument 1 from ‘const std::tuple<testing::internal::ValueArray<std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, testing::internal::ParamGenerator<long unsigned int> > >’ to ‘const std::tuple<testing::internal::ParamGenerator<long unsigned int>, testing::internal::ParamGenerator<long unsigned int>, testing::internal::ParamGenerator<std::vector<long unsigned int, std::allocator<long unsigned int> > > >&’
  830 |   CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
      |                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
/usr/include/gtest/internal/gtest-param-util.h:825:7: note: candidate: ‘testing::internal::CartesianProductGenerator<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > >::CartesianProductGenerator(const testing::internal::CartesianProductGenerator<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > >&)’
  825 | class CartesianProductGenerator
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/gtest/internal/gtest-param-util.h:825:7: note:   no known conversion for argument 1 from ‘const std::tuple<testing::internal::ValueArray<std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, std::tuple<int, int, std::vector<long unsigned int, std::allocator<long unsigned int> > >, testing::internal::ParamGenerator<long unsigned int> > >’ to ‘const testing::internal::CartesianProductGenerator<long unsigned int, long unsigned int, std::vector<long unsigned int, std::allocator<long unsigned int> > >&’

Solution

  • testing::ValueIn does not accept std::generator because:

    1. Lack of value_type member type of std::generator
    2. Lack of begin/end that are const. std::generator has non const versions of these functions -- which is ok, since these functions modify generator state
    3. The begin and end must returns the same type -- which is not true -- end() returns sentinel type.

    But, what is more important ::testing::ValuesIn will try to copy entire input range to its internal vector -- so we can just transform to vector -> so use extra | std::ranges::to<std::vector>()

    ::testing::ValuesIn([]() -> std::generator<size_t>
            {
                co_yield 4;
                co_yield 10;
                co_yield std::ranges::elements_of(std::ranges::iota_view(0, 10)); 
            }() | std::ranges::to<std::vector>());
    
    

    Answer to updated question:

    Combine and ValuesIn are not needed - not sure why you added them.

    The other most important thing is that coroutine generator is not magic tool - co_yield cannot just change the type of return value making a tuple -- you need to make a last tuple by hand:

    class TestFixture : public testing::TestWithParam<tuple<size_t, size_t, vector<size_t>>>
    {
    public:
    };
    TEST_P(TestFixture, test)
    {
    }
    INSTANTIATE_TEST_SUITE_P(
        tests,
        TestFixture,
            ::testing::Values(make_tuple(2, 8, vector<size_t>{1, 2, 3, 4, 5, 6, 5}),
                              make_tuple(3, 12, vector<size_t>{5, 7, 9, 13, 11, 6, 6, 3, 3}),
                              // make_tuple(4, 10, vector<size_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
                              make_tuple(4, 10,  []() -> generator<size_t>
                                                  {
    //    co_yield 4;
    //    co_yield 10;
        co_yield ranges::elements_of(ranges::iota_view(0, 10)); }() | ranges::to<vector>() ))
    );
    
    

    But frankly I do not see much usage of coroutine here - you can make it even simpler and just do: ranges::iota_view(...) | ranges::to<vector>(), see:

    class TestFixture : public testing::TestWithParam<tuple<size_t, size_t, vector<size_t>>>
    {
    public:
    };
    TEST_P(TestFixture, test)
    {
    }
    INSTANTIATE_TEST_SUITE_P(
        tests,
        TestFixture,
            ::testing::Values(make_tuple(2, 8, vector<size_t>{1, 2, 3, 4, 5, 6, 5}),
                              make_tuple(3, 12, vector<size_t>{5, 7, 9, 13, 11, 6, 6, 3, 3}),
                              // make_tuple(4, 10, vector<size_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
                              make_tuple(4, 10,  ranges::iota_view(0, 10) | ranges::to<vector<size_t>>() ))
    );