// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wweak-vtables" # pragma clang diagnostic ignored "-Wpadded" #endif namespace { static const char* testStringForMatching() { return "this string contains 'abc' as a substring"; } static const char* testStringForMatching2() { return "some completely different text that contains one common word"; } static bool alwaysTrue( int ) { return true; } static bool alwaysFalse( int ) { return false; } #ifdef _MSC_VER # pragma warning( disable : 4702 ) // Unreachable code -- MSVC 19 (VS 2015) // sees right through the indirection #endif struct SpecialException : std::exception { SpecialException( int i_ ): i( i_ ) {} char const* what() const noexcept override { return "SpecialException::what"; } int i; }; struct DerivedException : std::exception { char const* what() const noexcept override { return "DerivedException::what"; } }; static void doesNotThrow() {} [[noreturn]] static void throwsSpecialException( int i ) { throw SpecialException{ i }; } [[noreturn]] static void throwsAsInt( int i ) { throw i; } [[noreturn]] static void throwsDerivedException() { throw DerivedException{}; } class ExceptionMatcher : public Catch::Matchers::MatcherBase { int m_expected; public: ExceptionMatcher( int i ): m_expected( i ) {} bool match( SpecialException const& se ) const override { return se.i == m_expected; } std::string describe() const override { std::ostringstream ss; ss << "special exception has value of " << m_expected; return ss.str(); } }; using namespace Catch::Matchers; #ifdef __DJGPP__ static float nextafter( float from, float to ) { return ::nextafterf( from, to ); } static double nextafter( double from, double to ) { return ::nextafter( from, to ); } #else using std::nextafter; #endif } // end unnamed namespace TEST_CASE( "String matchers", "[matchers]" ) { REQUIRE_THAT( testStringForMatching(), ContainsSubstring( "string" ) ); REQUIRE_THAT( testStringForMatching(), ContainsSubstring( "string", Catch::CaseSensitive::No ) ); CHECK_THAT( testStringForMatching(), ContainsSubstring( "abc" ) ); CHECK_THAT( testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) ); CHECK_THAT( testStringForMatching(), StartsWith( "this" ) ); CHECK_THAT( testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) ); CHECK_THAT( testStringForMatching(), EndsWith( "substring" ) ); CHECK_THAT( testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) ); } TEST_CASE( "Contains string matcher", "[.][failing][matchers]" ) { CHECK_THAT( testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) ); CHECK_THAT( testStringForMatching(), ContainsSubstring( "STRING" ) ); } TEST_CASE( "StartsWith string matcher", "[.][failing][matchers]" ) { CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) ); CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) ); } TEST_CASE( "EndsWith string matcher", "[.][failing][matchers]" ) { CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) ); CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) ); } TEST_CASE( "Equals string matcher", "[.][failing][matchers]" ) { CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) ); CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) ); } TEST_CASE( "Equals", "[matchers]" ) { CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) ); CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) ); } TEST_CASE( "Regex string matcher -- libstdc++-4.8 workaround", "[matchers][approvals]" ) { // DJGPP has similar problem with its regex support as libstdc++ 4.8 #ifndef __DJGPP__ REQUIRE_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a substring" ) ); REQUIRE_THAT( testStringForMatching(), Matches( "this string CONTAINS 'abc' as a substring", Catch::CaseSensitive::No ) ); REQUIRE_THAT( testStringForMatching(), Matches( "^this string contains 'abc' as a substring$" ) ); REQUIRE_THAT( testStringForMatching(), Matches( "^.* 'abc' .*$" ) ); REQUIRE_THAT( testStringForMatching(), Matches( "^.* 'ABC' .*$", Catch::CaseSensitive::No ) ); #endif REQUIRE_THAT( testStringForMatching2(), !Matches( "this string contains 'abc' as a substring" ) ); } TEST_CASE( "Regex string matcher", "[matchers][.failing]" ) { CHECK_THAT( testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) ); CHECK_THAT( testStringForMatching(), Matches( "contains 'abc' as a substring" ) ); CHECK_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a" ) ); } TEST_CASE( "Matchers can be (AllOf) composed with the && operator", "[matchers][operators][operator&&]" ) { CHECK_THAT( testStringForMatching(), ContainsSubstring( "string" ) && ContainsSubstring( "abc" ) && ContainsSubstring( "substring" ) && ContainsSubstring( "contains" ) ); } TEST_CASE( "Matchers can be (AnyOf) composed with the || operator", "[matchers][operators][operator||]" ) { CHECK_THAT( testStringForMatching(), ContainsSubstring( "string" ) || ContainsSubstring( "different" ) || ContainsSubstring( "random" ) ); CHECK_THAT( testStringForMatching2(), ContainsSubstring( "string" ) || ContainsSubstring( "different" ) || ContainsSubstring( "random" ) ); } TEST_CASE( "Matchers can be composed with both && and ||", "[matchers][operators][operator||][operator&&]" ) { CHECK_THAT( testStringForMatching(), ( ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ) && ContainsSubstring( "substring" ) ); } TEST_CASE( "Matchers can be composed with both && and || - failing", "[matchers][operators][operator||][operator&&][.failing]" ) { CHECK_THAT( testStringForMatching(), ( ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ) && ContainsSubstring( "random" ) ); } TEST_CASE( "Matchers can be negated (Not) with the ! operator", "[matchers][operators][not]" ) { CHECK_THAT( testStringForMatching(), !ContainsSubstring( "different" ) ); } TEST_CASE( "Matchers can be negated (Not) with the ! operator - failing", "[matchers][operators][not][.failing]" ) { CHECK_THAT( testStringForMatching(), !ContainsSubstring( "substring" ) ); } template struct CustomAllocator : private std::allocator { using size_type = size_t; using difference_type = ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using value_type = T; template struct rebind { using other = CustomAllocator; }; using propagate_on_container_move_assignment = std::true_type; using is_always_equal = std::true_type; CustomAllocator() = default; CustomAllocator( const CustomAllocator& other ): std::allocator( other ) {} template CustomAllocator( const CustomAllocator& ) {} ~CustomAllocator() = default; using std::allocator::allocate; using std::allocator::deallocate; }; TEST_CASE( "Vector matchers", "[matchers][vector]" ) { std::vector v; v.push_back( 1 ); v.push_back( 2 ); v.push_back( 3 ); std::vector v2; v2.push_back( 1 ); v2.push_back( 2 ); std::vector v3; v3.push_back( 1 ); v3.push_back( 2 ); v3.push_back( 3 ); std::vector v4; v4.push_back( 1 + 1e-8 ); v4.push_back( 2 + 1e-8 ); v4.push_back( 3 + 1e-8 ); std::vector> v5; v5.push_back( 1 ); v5.push_back( 2 ); v5.push_back( 3 ); std::vector> v6; v6.push_back( 1 ); v6.push_back( 2 ); std::vector empty; SECTION( "Contains (element)" ) { CHECK_THAT( v, VectorContains( 1 ) ); CHECK_THAT( v, VectorContains( 2 ) ); CHECK_THAT( v5, ( VectorContains>( 2 ) ) ); } SECTION( "Contains (vector)" ) { CHECK_THAT( v, Contains( v2 ) ); CHECK_THAT( v, Contains( { 1, 2 } ) ); CHECK_THAT( v5, ( Contains, CustomAllocator>( v2 ) ) ); v2.push_back( 3 ); // now exactly matches CHECK_THAT( v, Contains( v2 ) ); CHECK_THAT( v, Contains( empty ) ); CHECK_THAT( empty, Contains( empty ) ); CHECK_THAT( v5, ( Contains, CustomAllocator>( v2 ) ) ); CHECK_THAT( v5, Contains( v6 ) ); } SECTION( "Contains (element), composed" ) { CHECK_THAT( v, VectorContains( 1 ) && VectorContains( 2 ) ); } SECTION( "Equals" ) { // Same vector CHECK_THAT( v, Equals( v ) ); CHECK_THAT( empty, Equals( empty ) ); // Different vector with same elements CHECK_THAT( v, Equals( { 1, 2, 3 } ) ); v2.push_back( 3 ); CHECK_THAT( v, Equals( v2 ) ); CHECK_THAT( v5, ( Equals, CustomAllocator>( v2 ) ) ); v6.push_back( 3 ); CHECK_THAT( v5, Equals( v6 ) ); } SECTION( "UnorderedEquals" ) { CHECK_THAT( v, UnorderedEquals( v ) ); CHECK_THAT( v, UnorderedEquals( { 3, 2, 1 } ) ); CHECK_THAT( empty, UnorderedEquals( empty ) ); auto permuted = v; std::next_permutation( begin( permuted ), end( permuted ) ); REQUIRE_THAT( permuted, UnorderedEquals( v ) ); std::reverse( begin( permuted ), end( permuted ) ); REQUIRE_THAT( permuted, UnorderedEquals( v ) ); CHECK_THAT( v5, ( UnorderedEquals, CustomAllocator>( permuted ) ) ); auto v5_permuted = v5; std::next_permutation( begin( v5_permuted ), end( v5_permuted ) ); CHECK_THAT( v5_permuted, UnorderedEquals( v5 ) ); } } TEST_CASE( "Vector matchers that fail", "[matchers][vector][.][failing]" ) { std::vector v; v.push_back( 1 ); v.push_back( 2 ); v.push_back( 3 ); std::vector v2; v2.push_back( 1 ); v2.push_back( 2 ); std::vector v3; v3.push_back( 1 ); v3.push_back( 2 ); v3.push_back( 3 ); std::vector v4; v4.push_back( 1.1 ); v4.push_back( 2.1 ); v4.push_back( 3.1 ); std::vector empty; SECTION( "Contains (element)" ) { CHECK_THAT( v, VectorContains( -1 ) ); CHECK_THAT( empty, VectorContains( 1 ) ); } SECTION( "Contains (vector)" ) { CHECK_THAT( empty, Contains( v ) ); v2.push_back( 4 ); CHECK_THAT( v, Contains( v2 ) ); } SECTION( "Equals" ) { CHECK_THAT( v, Equals( v2 ) ); CHECK_THAT( v2, Equals( v ) ); CHECK_THAT( empty, Equals( v ) ); CHECK_THAT( v, Equals( empty ) ); } SECTION( "UnorderedEquals" ) { CHECK_THAT( v, UnorderedEquals( empty ) ); CHECK_THAT( empty, UnorderedEquals( v ) ); auto permuted = v; std::next_permutation( begin( permuted ), end( permuted ) ); permuted.pop_back(); CHECK_THAT( permuted, UnorderedEquals( v ) ); std::reverse( begin( permuted ), end( permuted ) ); CHECK_THAT( permuted, UnorderedEquals( v ) ); } } TEST_CASE( "Exception matchers that succeed", "[matchers][exceptions][!throws]" ) { CHECK_THROWS_MATCHES( throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 } ); REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 } ); } TEST_CASE( "Exception matchers that fail", "[matchers][exceptions][!throws][.failing]" ) { SECTION( "No exception" ) { CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ); REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } ); } SECTION( "Type mismatch" ) { CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } ); REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } ); } SECTION( "Contents are wrong" ) { CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } ); REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } ); } } TEST_CASE( "Floating point matchers: float", "[matchers][floating-point]" ) { SECTION( "Relative" ) { REQUIRE_THAT( 10.f, WithinRel( 11.1f, 0.1f ) ); REQUIRE_THAT( 10.f, !WithinRel( 11.2f, 0.1f ) ); REQUIRE_THAT( 1.f, !WithinRel( 0.f, 0.99f ) ); REQUIRE_THAT( -0.f, WithinRel( 0.f ) ); SECTION( "Some subnormal values" ) { auto v1 = std::numeric_limits::min(); auto v2 = v1; for ( int i = 0; i < 5; ++i ) { v2 = std::nextafter( v1, 0.f ); } REQUIRE_THAT( v1, WithinRel( v2 ) ); } } SECTION( "Margin" ) { REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0 ) ); REQUIRE_THAT( 0.f, WithinAbs( 1.f, 1 ) ); REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ); REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ); REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) ); REQUIRE_THAT( 11.f, !WithinAbs( 10.f, 0.5f ) ); REQUIRE_THAT( 10.f, !WithinAbs( 11.f, 0.5f ) ); REQUIRE_THAT( -10.f, WithinAbs( -10.f, 0.5f ) ); REQUIRE_THAT( -10.f, WithinAbs( -9.6f, 0.5f ) ); } SECTION( "ULPs" ) { REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) ); REQUIRE_THAT(-1.f, WithinULP( -1.f, 0 ) ); REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) ); REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) ); REQUIRE_THAT( 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) ); REQUIRE_THAT( 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) ); REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) ); REQUIRE_THAT( -0.f, WithinULP( 0.f, 0 ) ); } SECTION( "Composed" ) { REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) ); REQUIRE_THAT( 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) ); REQUIRE_THAT( 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) ); } SECTION( "Constructor validation" ) { REQUIRE_NOTHROW( WithinAbs( 1.f, 0.f ) ); REQUIRE_THROWS_AS( WithinAbs( 1.f, -1.f ), std::domain_error ); REQUIRE_NOTHROW( WithinULP( 1.f, 0 ) ); REQUIRE_THROWS_AS( WithinULP( 1.f, static_cast( -1 ) ), std::domain_error ); REQUIRE_NOTHROW( WithinRel( 1.f, 0.f ) ); REQUIRE_THROWS_AS( WithinRel( 1.f, -0.2f ), std::domain_error ); REQUIRE_THROWS_AS( WithinRel( 1.f, 1.f ), std::domain_error ); } } TEST_CASE( "Floating point matchers: double", "[matchers][floating-point]" ) { SECTION( "Relative" ) { REQUIRE_THAT( 10., WithinRel( 11.1, 0.1 ) ); REQUIRE_THAT( 10., !WithinRel( 11.2, 0.1 ) ); REQUIRE_THAT( 1., !WithinRel( 0., 0.99 ) ); REQUIRE_THAT( -0., WithinRel( 0. ) ); SECTION( "Some subnormal values" ) { auto v1 = std::numeric_limits::min(); auto v2 = v1; for ( int i = 0; i < 5; ++i ) { v2 = std::nextafter( v1, 0 ); } REQUIRE_THAT( v1, WithinRel( v2 ) ); } } SECTION( "Margin" ) { REQUIRE_THAT( 1., WithinAbs( 1., 0 ) ); REQUIRE_THAT( 0., WithinAbs( 1., 1 ) ); REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ); REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ); REQUIRE_THAT( 11., !WithinAbs( 10., 0.5 ) ); REQUIRE_THAT( 10., !WithinAbs( 11., 0.5 ) ); REQUIRE_THAT( -10., WithinAbs( -10., 0.5 ) ); REQUIRE_THAT( -10., WithinAbs( -9.6, 0.5 ) ); } SECTION( "ULPs" ) { REQUIRE_THAT( 1., WithinULP( 1., 0 ) ); REQUIRE_THAT( nextafter( 1., 2. ), WithinULP( 1., 1 ) ); REQUIRE_THAT( 0., WithinULP( nextafter( 0., 1. ), 1 ) ); REQUIRE_THAT( 1., WithinULP( nextafter( 1., 0. ), 1 ) ); REQUIRE_THAT( 1., !WithinULP( nextafter( 1., 2. ), 0 ) ); REQUIRE_THAT( 1., WithinULP( 1., 0 ) ); REQUIRE_THAT( -0., WithinULP( 0., 0 ) ); } SECTION( "Composed" ) { REQUIRE_THAT( 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) ); REQUIRE_THAT( 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) ); REQUIRE_THAT( 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) ); } SECTION( "Constructor validation" ) { REQUIRE_NOTHROW( WithinAbs( 1., 0. ) ); REQUIRE_THROWS_AS( WithinAbs( 1., -1. ), std::domain_error ); REQUIRE_NOTHROW( WithinULP( 1., 0 ) ); REQUIRE_NOTHROW( WithinRel( 1., 0. ) ); REQUIRE_THROWS_AS( WithinRel( 1., -0.2 ), std::domain_error ); REQUIRE_THROWS_AS( WithinRel( 1., 1. ), std::domain_error ); } } TEST_CASE( "Floating point matchers that are problematic in approvals", "[approvals][matchers][floating-point]" ) { REQUIRE_THAT( NAN, !WithinAbs( NAN, 0 ) ); REQUIRE_THAT( NAN, !( WithinAbs( NAN, 100 ) || WithinULP( NAN, 123 ) ) ); REQUIRE_THAT( NAN, !WithinULP( NAN, 123 ) ); REQUIRE_THAT( INFINITY, WithinRel( INFINITY ) ); REQUIRE_THAT( -INFINITY, !WithinRel( INFINITY ) ); REQUIRE_THAT( 1., !WithinRel( INFINITY ) ); REQUIRE_THAT( INFINITY, !WithinRel( 1. ) ); REQUIRE_THAT( NAN, !WithinRel( NAN ) ); REQUIRE_THAT( 1., !WithinRel( NAN ) ); REQUIRE_THAT( NAN, !WithinRel( 1. ) ); } TEST_CASE( "Arbitrary predicate matcher", "[matchers][generic]" ) { SECTION( "Function pointer" ) { REQUIRE_THAT( 1, Predicate( alwaysTrue, "always true" ) ); REQUIRE_THAT( 1, !Predicate( alwaysFalse, "always false" ) ); } SECTION( "Lambdas + different type" ) { REQUIRE_THAT( "Hello olleH", Predicate( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) ); REQUIRE_THAT( "This wouldn't pass", !Predicate( []( std::string const& str ) -> bool { return str.front() == str.back(); } ) ); } } TEST_CASE( "Regression test #1", "[matchers][vector]" ) { // At some point, UnorderedEqualsMatcher skipped // mismatched prefixed before doing the comparison itself std::vector actual = { 'a', 'b' }; std::vector expected = { 'c', 'b' }; CHECK_THAT( actual, !UnorderedEquals( expected ) ); } TEST_CASE( "Predicate matcher can accept const char*", "[matchers][compilation]" ) { REQUIRE_THAT( "foo", Predicate( []( const char* const& ) { return true; } ) ); } TEST_CASE( "Vector Approx matcher", "[matchers][approx][vector]" ) { using Catch::Matchers::Approx; SECTION( "Empty vector is roughly equal to an empty vector" ) { std::vector empty; REQUIRE_THAT( empty, Approx( empty ) ); } SECTION( "Vectors with elements" ) { std::vector v1( { 1., 2., 3. } ); SECTION( "A vector is approx equal to itself" ) { REQUIRE_THAT( v1, Approx( v1 ) ); REQUIRE_THAT( v1, Approx( { 1., 2., 3. } ) ); } std::vector v2( { 1.5, 2.5, 3.5 } ); SECTION( "Different length" ) { auto temp( v1 ); temp.push_back( 4 ); REQUIRE_THAT( v1, !Approx( temp ) ); } SECTION( "Same length, different elements" ) { REQUIRE_THAT( v1, !Approx( v2 ) ); REQUIRE_THAT( v1, Approx( v2 ).margin( 0.5 ) ); REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.5 ) ); REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 ) ); } } } TEST_CASE( "Vector Approx matcher -- failing", "[matchers][approx][vector][.failing]" ) { using Catch::Matchers::Approx; SECTION( "Empty and non empty vectors are not approx equal" ) { std::vector empty, t1( { 1, 2 } ); CHECK_THAT( empty, Approx( t1 ) ); } SECTION( "Just different vectors" ) { std::vector v1( { 2., 4., 6. } ), v2( { 1., 3., 5. } ); CHECK_THAT( v1, Approx( v2 ) ); } } TEST_CASE( "Exceptions matchers", "[matchers][exceptions][!throws]" ) { REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) ); REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) ); REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) ); REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, Message( "SpecialException::what" ) ); } struct CheckedTestingMatcher : Catch::Matchers::MatcherBase { mutable bool matchCalled = false; bool matchSucceeds = false; bool match( int const& ) const override { matchCalled = true; return matchSucceeds; } std::string describe() const override { return "CheckedTestingMatcher set to " + ( matchSucceeds ? std::string( "succeed" ) : std::string( "fail" ) ); } }; TEST_CASE( "Composed matchers shortcircuit", "[matchers][composed]" ) { // Check that if first returns false, second is not touched CheckedTestingMatcher first, second; SECTION( "MatchAllOf" ) { first.matchSucceeds = false; Detail::MatchAllOf matcher = Detail::MatchAllOf{} && first && second; CHECK_FALSE( matcher.match( 1 ) ); // These two assertions are the important ones REQUIRE( first.matchCalled ); REQUIRE( !second.matchCalled ); } // Check that if first returns true, second is not touched SECTION( "MatchAnyOf" ) { first.matchSucceeds = true; Detail::MatchAnyOf matcher = Detail::MatchAnyOf{} || first || second; CHECK( matcher.match( 1 ) ); // These two assertions are the important ones REQUIRE( first.matchCalled ); REQUIRE( !second.matchCalled ); } } struct CheckedTestingGenericMatcher : Catch::Matchers::MatcherGenericBase { mutable bool matchCalled = false; bool matchSucceeds = false; bool match( int const& ) const { matchCalled = true; return matchSucceeds; } std::string describe() const override { return "CheckedTestingGenericMatcher set to " + ( matchSucceeds ? std::string( "succeed" ) : std::string( "fail" ) ); } }; TEST_CASE( "Composed generic matchers shortcircuit", "[matchers][composed][generic]" ) { // Check that if first returns false, second is not touched CheckedTestingGenericMatcher first, second; SECTION( "MatchAllOf" ) { first.matchSucceeds = false; Detail::MatchAllOfGeneric matcher{ first, second }; CHECK_FALSE( matcher.match( 1 ) ); // These two assertions are the important ones REQUIRE( first.matchCalled ); REQUIRE( !second.matchCalled ); } // Check that if first returns true, second is not touched SECTION( "MatchAnyOf" ) { first.matchSucceeds = true; Detail::MatchAnyOfGeneric matcher{ first, second }; CHECK( matcher.match( 1 ) ); // These two assertions are the important ones REQUIRE( first.matchCalled ); REQUIRE( !second.matchCalled ); } } template struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase { EqualsRangeMatcher( Range const& range ): m_range{ range } {} template bool match( OtherRange const& other ) const { using std::begin; using std::end; return std::equal( begin( m_range ), end( m_range ), begin( other ), end( other ) ); } std::string describe() const override { return "Equals: " + Catch::rangeToString( m_range ); } private: Range const& m_range; }; template auto EqualsRange( const Range& range ) -> EqualsRangeMatcher { return EqualsRangeMatcher{ range }; } TEST_CASE( "Combining templated matchers", "[matchers][templated]" ) { std::array container{ { 1, 2, 3 } }; std::array a{ { 1, 2, 3 } }; std::vector b{ 0, 1, 2 }; std::list c{ 4, 5, 6 }; REQUIRE_THAT( container, EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) ); } TEST_CASE( "Combining templated and concrete matchers", "[matchers][templated]" ) { std::vector vec{ 1, 3, 5 }; std::array a{ { 5, 3, 1 } }; REQUIRE_THAT( vec, Predicate>( []( auto const& v ) { return std::all_of( v.begin(), v.end(), []( int elem ) { return elem % 2 == 1; } ); }, "All elements are odd" ) && !EqualsRange( a ) ); const std::string str = "foobar"; const std::array arr{ { 'f', 'o', 'o', 'b', 'a', 'r' } }; const std::array bad_arr{ { 'o', 'o', 'f', 'b', 'a', 'r' } }; using Catch::Matchers::EndsWith; using Catch::Matchers::StartsWith; REQUIRE_THAT( str, StartsWith( "foo" ) && EqualsRange( arr ) && EndsWith( "bar" ) ); REQUIRE_THAT( str, StartsWith( "foo" ) && !EqualsRange( bad_arr ) && EndsWith( "bar" ) ); REQUIRE_THAT( str, EqualsRange( arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) ); REQUIRE_THAT( str, !EqualsRange( bad_arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) ); REQUIRE_THAT( str, EqualsRange( bad_arr ) || ( StartsWith( "foo" ) && EndsWith( "bar" ) ) ); REQUIRE_THAT( str, ( StartsWith( "foo" ) && EndsWith( "bar" ) ) || EqualsRange( bad_arr ) ); } TEST_CASE( "Combining concrete matchers does not use templated matchers", "[matchers][templated]" ) { using Catch::Matchers::EndsWith; using Catch::Matchers::StartsWith; STATIC_REQUIRE( std::is_same>::value ); } struct MatcherA : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "equals: (int) 1 or (string) \"1\""; } bool match( int i ) const { return i == 1; } bool match( std::string s ) const { return s == "1"; } }; struct MatcherB : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "equals: (long long) 1"; } bool match( long long l ) const { return l == 1ll; } }; struct MatcherC : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "equals: (T) 1"; } template bool match( T t ) const { return t == T{ 1 }; } }; struct MatcherD : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "equals: true"; } bool match( bool b ) const { return b == true; } }; TEST_CASE( "Combining only templated matchers", "[matchers][templated]" ) { STATIC_REQUIRE( std::is_same>::value ); REQUIRE_THAT( 1, MatcherA() || MatcherB() ); STATIC_REQUIRE( std::is_same>::value ); REQUIRE_THAT( 1, MatcherA() && MatcherB() ); STATIC_REQUIRE( std::is_same< decltype( MatcherA() || !MatcherB() ), Catch::Matchers::Detail::MatchAnyOfGeneric< MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric>>::value ); REQUIRE_THAT( 1, MatcherA() || !MatcherB() ); } TEST_CASE( "Combining MatchAnyOfGeneric does not nest", "[matchers][templated]" ) { // MatchAnyOfGeneric LHS + some matcher RHS STATIC_REQUIRE( std::is_same< decltype( ( MatcherA() || MatcherB() ) || MatcherC() ), Catch::Matchers::Detail:: MatchAnyOfGeneric>::value ); REQUIRE_THAT( 1, ( MatcherA() || MatcherB() ) || MatcherC() ); // some matcher LHS + MatchAnyOfGeneric RHS STATIC_REQUIRE( std::is_same< decltype( MatcherA() || ( MatcherB() || MatcherC() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric>::value ); REQUIRE_THAT( 1, MatcherA() || ( MatcherB() || MatcherC() ) ); // MatchAnyOfGeneric LHS + MatchAnyOfGeneric RHS STATIC_REQUIRE( std::is_same< decltype( ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric>:: value ); REQUIRE_THAT( 1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) ); } TEST_CASE( "Combining MatchAllOfGeneric does not nest", "[matchers][templated]" ) { // MatchAllOfGeneric lhs + some matcher RHS STATIC_REQUIRE( std::is_same< decltype( ( MatcherA() && MatcherB() ) && MatcherC() ), Catch::Matchers::Detail:: MatchAllOfGeneric>::value ); REQUIRE_THAT( 1, ( MatcherA() && MatcherB() ) && MatcherC() ); // some matcher LHS + MatchAllOfGeneric RSH STATIC_REQUIRE( std::is_same< decltype( MatcherA() && ( MatcherB() && MatcherC() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric>::value ); REQUIRE_THAT( 1, MatcherA() && ( MatcherB() && MatcherC() ) ); // MatchAllOfGeneric LHS + MatchAllOfGeneric RHS STATIC_REQUIRE( std::is_same< decltype( ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric>:: value ); REQUIRE_THAT( 1, ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) ); } TEST_CASE( "Combining MatchNotOfGeneric does not nest", "[matchers][templated]" ) { STATIC_REQUIRE( std::is_same< decltype( !MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric>::value ); REQUIRE_THAT( 0, !MatcherA() ); STATIC_REQUIRE( std::is_same::value ); REQUIRE_THAT( 1, !!MatcherA() ); STATIC_REQUIRE( std::is_same< decltype( !!!MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric>::value ); REQUIRE_THAT( 0, !!!MatcherA() ); STATIC_REQUIRE( std::is_same::value ); REQUIRE_THAT( 1, !!!!MatcherA() ); } struct EvilAddressOfOperatorUsed : std::exception { EvilAddressOfOperatorUsed() {} const char* what() const noexcept override { return "overloaded address-of operator of matcher was used instead of " "std::addressof"; } }; struct EvilCommaOperatorUsed : std::exception { EvilCommaOperatorUsed() {} const char* what() const noexcept override { return "overloaded comma operator of matcher was used"; } }; struct EvilMatcher : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "equals: 45"; } bool match( int i ) const { return i == 45; } EvilMatcher const* operator&() const { throw EvilAddressOfOperatorUsed(); } int operator,( EvilMatcher const& ) const { throw EvilCommaOperatorUsed(); } }; TEST_CASE( "Overloaded comma or address-of operators are not used", "[matchers][templated]" ) { REQUIRE_THROWS_AS( ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed ); REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed ); REQUIRE_NOTHROW( EvilMatcher() || ( EvilMatcher() && !EvilMatcher() ) ); REQUIRE_NOTHROW( ( EvilMatcher() && EvilMatcher() ) || !EvilMatcher() ); } struct ImmovableMatcher : Catch::Matchers::MatcherGenericBase { ImmovableMatcher() = default; ImmovableMatcher( ImmovableMatcher const& ) = delete; ImmovableMatcher( ImmovableMatcher&& ) = delete; ImmovableMatcher& operator=( ImmovableMatcher const& ) = delete; ImmovableMatcher& operator=( ImmovableMatcher&& ) = delete; std::string describe() const override { return "always false"; } template bool match( T&& ) const { return false; } }; struct MatcherWasMovedOrCopied : std::exception { MatcherWasMovedOrCopied() {} const char* what() const noexcept override { return "attempted to copy or move a matcher"; } }; struct ThrowOnCopyOrMoveMatcher : Catch::Matchers::MatcherGenericBase { ThrowOnCopyOrMoveMatcher() = default; [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher const& ): Catch::Matchers::MatcherGenericBase() { throw MatcherWasMovedOrCopied(); } [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& ): Catch::Matchers::MatcherGenericBase() { throw MatcherWasMovedOrCopied(); } ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher const& ) { throw MatcherWasMovedOrCopied(); } ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher&& ) { throw MatcherWasMovedOrCopied(); } std::string describe() const override { return "always false"; } template bool match( T&& ) const { return false; } }; TEST_CASE( "Matchers are not moved or copied", "[matchers][templated][approvals]" ) { REQUIRE_NOTHROW( ( ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher() ) || !ThrowOnCopyOrMoveMatcher() ); } TEST_CASE( "Immovable matchers can be used", "[matchers][templated][approvals]" ) { REQUIRE_THAT( 123, ( ImmovableMatcher() && ImmovableMatcher() ) || !ImmovableMatcher() ); } struct ReferencingMatcher : Catch::Matchers::MatcherGenericBase { std::string describe() const override { return "takes reference"; } bool match( int& i ) const { return i == 22; } }; TEST_CASE( "Matchers can take references", "[matchers][templated][approvals]" ) { REQUIRE_THAT( 22, ReferencingMatcher{} ); } #ifdef __clang__ # pragma clang diagnostic pop #endif TEMPLATE_TEST_CASE( "#2152 - ULP checks between differently signed values were wrong", "[matchers][floating-point][ulp]", float, double ) { using Catch::Matchers::WithinULP; static constexpr TestType smallest_non_zero = std::numeric_limits::denorm_min(); CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) ); CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) ); }