approvals / ApprovalTests.cpp

Native ApprovalTests for C++ on Linux, Mac and Windows
https://approvaltestscpp.readthedocs.io/en/latest/
Apache License 2.0
318 stars 51 forks source link

CombinationApprovals snippet does not compile with single header #90

Closed claremacrae closed 3 years ago

claremacrae commented 4 years ago

Steps to reproduce:

#define APPROVALS_CATCH
#include "ApprovalTests.hpp"
-#include "catch.hpp"
+#include "catch2/catch.hpp"
#include "catch2/catch.hpp"
#include "ApprovalTests.v.7.0.0_hacked_catch_include.hpp"
#include "PairUtilities.h"

using namespace ApprovalTests;

// begin-snippet: YouCanVerifyCombinationsOf2
TEST_CASE("YouCanVerifyCombinationsOf2")
{
    std::vector<std::string> v{"hello", "world"};
    std::vector<int> numbers{1, 2, 3};
    CombinationApprovals::verifyAllCombinations(
        [](std::string s, int i) { return std::make_pair(s, i); }, v, numbers);
}
// end-snippet
cmake -G "Ninja" -DCMAKE_UNITY_BUILD=yes

It will fail to compile - as noted in https://github.com/approvals/ApprovalTests.cpp/issues/89#issuecomment-575170237

I believe that this is related to Catch's issue 1405 - linked from the comment above.

claremacrae commented 4 years ago

The build output is:

[1/2] Building CXX object approval_tests_cpp_proj/build/examples/build_failure_test/CMakeFiles/build_failure_test.dir/CombinationTests.cpp.o
FAILED: approval_tests_cpp_proj/build/examples/build_failure_test/CMakeFiles/build_failure_test.dir/CombinationTests.cpp.o 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++  -DCATCH_CONFIG_FAST_COMPILE -DDUMMY_TEST_DEFINE -I/Users/clare/Documents/develop/cpp/Catch2/single_include -g -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk   -Wall -Wextra -Wpedantic -Werror -std=gnu++1z -MD -MT approval_tests_cpp_proj/build/examples/build_failure_test/CMakeFiles/build_failure_test.dir/CombinationTests.cpp.o -MF approval_tests_cpp_proj/build/examples/build_failure_test/CMakeFiles/build_failure_test.dir/CombinationTests.cpp.o.d -o approval_tests_cpp_proj/build/examples/build_failure_test/CMakeFiles/build_failure_test.dir/CombinationTests.cpp.o -c /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:2:
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/ApprovalTests.v.7.0.0_hacked_catch_include.hpp:2467:24: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
        out << ") => " << converter(input1, inputs...) << '\n';
                       ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/ApprovalTests.v.7.0.0_hacked_catch_include.hpp:923:12: note: in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::Detail::serialize<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:13:9)>::operator()<std::__1::basic_string<char>, int>' requested here
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
           ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/ApprovalTests.v.7.0.0_hacked_catch_include.hpp:1047:17: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::apply<ApprovalTests::CombinationApprovals::Detail::serialize<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:13:9)>, std::__1::tuple<std::__1::basic_string<char>, int> >' requested here
        Detail::apply(std::forward<F>(f), Detail::transform<Detail::dereference_iterator>(its));
                ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/ApprovalTests.v.7.0.0_hacked_catch_include.hpp:2476:23: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::cartesian_product<ApprovalTests::CombinationApprovals::Detail::serialize<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:13:9)>, std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
    CartesianProduct::cartesian_product(Detail::serialize<Converter>{s, std::forward<Converter>(converter)}, input0, inputs...);
                      ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/ApprovalTests.v.7.0.0_hacked_catch_include.hpp:2484:5: note: in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::verifyAllCombinations<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:13:9), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
    verifyAllCombinations(DefaultReporter(), std::forward<Converter>(converter), inputs...);
    ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:12:27: note: in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::verifyAllCombinations<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/CombinationTests.cpp:13:9), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
    CombinationApprovals::verifyAllCombinations(
                          ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/examples/build_failure_test/PairUtilities.h:9:26: note: 'operator<<' should be declared prior to the call site
    inline std::ostream& operator<<(std::ostream& os,
                         ^
1 error generated.
ninja: build stopped: subcommand failed.
claremacrae commented 4 years ago

This is a minimal example that fails to compile with XCode:

#include "catch2/catch.hpp"
#include "ApprovalTests/CombinationApprovals.h"
#include "PairUtilities.h"

using namespace ApprovalTests;

TEST_CASE("YouCanVerifyCombinationsOf2")
{
    std::vector<std::string> v{"hello", "world"};
    std::vector<int> numbers{1, 2, 3};
    CombinationApprovals::verifyAllCombinations(
        [](std::string s, int i) { return std::make_pair(s, i); }, v, numbers);
}

Output is:

/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:40:36: 
error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
                    out << ") => " << converter(input1, inputs...) << '\n';
                                   ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:57:24:
 note: in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::Detail::serialize<(
     lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:12:9)>::operator()<std::__1::basic_string<char>, int>' requested here
                return std::forward<F>(f)(
                       ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:215:25:
 note: in instantiation of function template specialization 
 'ApprovalTests::CartesianProduct::Detail::apply<ApprovalTests::CombinationApprovals::Detail::serialize<(
     lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:12:9)>,
      std::__1::tuple<std::__1::basic_string<char>, int> >' requested here
                Detail::apply(
                        ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:52:31:
 note: in instantiation of function template specialization 
 'ApprovalTests::CartesianProduct::cartesian_product<ApprovalTests::CombinationApprovals::Detail::serialize<(
     lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:12:9)>,
      std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
            CartesianProduct::cartesian_product(
                              ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:65:13:
 note: in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::verifyAllCombinations<(
     lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:12:9),
      std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
            verifyAllCombinations(DefaultReporter(),
            ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:11:27: note:
 in instantiation of function template specialization 'ApprovalTests::CombinationApprovals::verifyAllCombinations<(
     lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/CombinationTests.cpp:12:9),
      std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >, std::__1::vector<int, std::__1::allocator<int> > >' requested here
    CombinationApprovals::verifyAllCombinations(
                          ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/Catch2_Tests/PairUtilities.h:9:26: note: 'operator<<' should be declared prior to the call site
    inline std::ostream& operator<<(std::ostream& os,
                         ^
1 error generated.
make[3]: *** [tests/Catch2_Tests/CMakeFiles/Catch2_Tests.dir/CombinationTests.cpp.o] Error 1
make[2]: *** [tests/Catch2_Tests/CMakeFiles/Catch2_Tests.dir/all] Error 2
make[1]: *** [tests/Catch2_Tests/CMakeFiles/Catch2_Tests.dir/rule] Error 2
make: *** [Catch2_Tests] Error 2
claremacrae commented 4 years ago

This is a minimal example that fails to compile with XCode:


#include "catch2/catch.hpp"
#include "ApprovalTests/CombinationApprovals.h"
#include "PairUtilities.h"

If I switch the 2nd and 3rd header, it works fine:

#include "catch2/catch.hpp"
#include "PairUtilities.h"
#include "ApprovalTests/CombinationApprovals.h"

Is this actually reasonable behaviour?

Or do we need to do something like in https://github.com/catchorg/Catch2/pull/1405

claremacrae commented 4 years ago

I wonder if this is a rediscovery of https://github.com/approvals/ApprovalTests.cpp/issues/6

claremacrae commented 4 years ago

Notes for future us:

One issue is that we are not releasing the PairUtilities.h function - so the snippet code, as published, is expected not to work.

We could work around that by adding this code - but adding code to std is not legal, so it's still not a full workaround:

namespace std
{
    std::ostream& operator<<(std::ostream& os,
                             const std::pair<std::string, int>& pair)
    {
        os << "(" << pair.first << ", " << pair.second << ")";
        return os;
    }
}
isidore commented 3 years ago

The code above is no longer in the documentation