2022-01-29 00:03:43 +01:00
|
|
|
|
|
|
|
// 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
|
2017-02-21 15:19:09 +01:00
|
|
|
|
2020-01-20 23:24:04 +01:00
|
|
|
#include <catch2/catch_test_macros.hpp>
|
2021-07-26 22:01:04 +02:00
|
|
|
#include <catch2/catch_template_test_macros.hpp>
|
2020-02-20 13:57:24 +01:00
|
|
|
#include <catch2/matchers/catch_matchers_exception.hpp>
|
2020-12-13 15:41:12 +01:00
|
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
2020-03-18 15:57:11 +01:00
|
|
|
#include <catch2/matchers/catch_matchers_predicate.hpp>
|
2020-03-01 19:59:18 +01:00
|
|
|
#include <catch2/matchers/catch_matchers_string.hpp>
|
|
|
|
#include <catch2/matchers/catch_matchers_vector.hpp>
|
2020-03-18 15:57:11 +01:00
|
|
|
#include <catch2/matchers/catch_matchers_templated.hpp>
|
2017-02-21 15:19:09 +01:00
|
|
|
|
2017-12-07 17:07:25 +01:00
|
|
|
#include <algorithm>
|
2021-06-20 16:25:57 +02:00
|
|
|
#include <exception>
|
2020-01-20 23:24:04 +01:00
|
|
|
#include <cmath>
|
2020-02-16 11:19:10 +01:00
|
|
|
#include <list>
|
|
|
|
#include <sstream>
|
2017-11-07 19:01:10 +01:00
|
|
|
|
2017-09-07 16:51:33 +02:00
|
|
|
#ifdef __clang__
|
2021-06-20 16:25:57 +02:00
|
|
|
# pragma clang diagnostic push
|
|
|
|
# pragma clang diagnostic ignored "-Wweak-vtables"
|
|
|
|
# pragma clang diagnostic ignored "-Wpadded"
|
2017-09-07 16:51:33 +02:00
|
|
|
#endif
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
namespace {
|
2017-11-13 21:23:52 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
static const char* testStringForMatching() {
|
2017-11-15 08:48:21 +01:00
|
|
|
return "this string contains 'abc' as a substring";
|
2017-02-21 17:05:04 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
static const char* testStringForMatching2() {
|
2017-11-15 08:48:21 +01:00
|
|
|
return "some completely different text that contains one common word";
|
2017-02-21 17:05:04 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
static bool alwaysTrue( int ) { return true; }
|
|
|
|
static bool alwaysFalse( int ) { return false; }
|
2017-02-21 17:05:04 +01:00
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
#ifdef _MSC_VER
|
2021-06-20 16:25:57 +02:00
|
|
|
# pragma warning( disable : 4702 ) // Unreachable code -- MSVC 19 (VS 2015)
|
|
|
|
// sees right through the indirection
|
2017-11-15 08:48:21 +01:00
|
|
|
#endif
|
2017-02-21 17:05:04 +01:00
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
struct SpecialException : std::exception {
|
2021-06-20 16:25:57 +02:00
|
|
|
SpecialException( int i_ ): i( i_ ) {}
|
2017-02-21 17:05:04 +01:00
|
|
|
|
2018-05-12 20:37:13 +02:00
|
|
|
char const* what() const noexcept override {
|
|
|
|
return "SpecialException::what";
|
|
|
|
}
|
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
int i;
|
|
|
|
};
|
2017-02-21 17:05:04 +01:00
|
|
|
|
2019-10-13 20:37:07 +02:00
|
|
|
struct DerivedException : std::exception {
|
|
|
|
char const* what() const noexcept override {
|
|
|
|
return "DerivedException::what";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
static void doesNotThrow() {}
|
2017-02-21 17:05:04 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
[[noreturn]] static void throwsSpecialException( int i ) {
|
|
|
|
throw SpecialException{ i };
|
2017-02-21 17:05:04 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
[[noreturn]] static void throwsAsInt( int i ) { throw i; }
|
2017-06-05 19:14:00 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
[[noreturn]] static void throwsDerivedException() {
|
2019-10-13 20:37:07 +02:00
|
|
|
throw DerivedException{};
|
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
class ExceptionMatcher
|
|
|
|
: public Catch::Matchers::MatcherBase<SpecialException> {
|
2017-11-15 08:48:21 +01:00
|
|
|
int m_expected;
|
2021-06-20 16:25:57 +02:00
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
public:
|
2021-06-20 16:25:57 +02:00
|
|
|
ExceptionMatcher( int i ): m_expected( i ) {}
|
2017-06-05 19:14:00 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
bool match( SpecialException const& se ) const override {
|
2017-11-15 08:48:21 +01:00
|
|
|
return se.i == m_expected;
|
|
|
|
}
|
2017-06-05 19:14:00 +02:00
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
std::string describe() const override {
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << "special exception has value of " << m_expected;
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
};
|
2017-06-05 19:14:00 +02:00
|
|
|
|
2017-11-15 08:48:21 +01:00
|
|
|
using namespace Catch::Matchers;
|
2017-06-05 19:14:00 +02:00
|
|
|
|
2018-03-02 14:39:01 +01:00
|
|
|
#ifdef __DJGPP__
|
2021-06-20 16:25:57 +02:00
|
|
|
static float nextafter( float from, float to ) {
|
|
|
|
return ::nextafterf( from, to );
|
2018-03-02 14:39:01 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
static double nextafter( double from, double to ) {
|
|
|
|
return ::nextafter( from, to );
|
2018-03-02 14:39:01 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
using std::nextafter;
|
|
|
|
#endif
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
} // end unnamed namespace
|
|
|
|
|
|
|
|
TEST_CASE( "String matchers", "[matchers]" ) {
|
2021-09-23 23:28:59 +02:00
|
|
|
REQUIRE_THAT( testStringForMatching(), ContainsSubstring( "string" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( testStringForMatching(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "string", Catch::CaseSensitive::No ) );
|
|
|
|
CHECK_THAT( testStringForMatching(), ContainsSubstring( "abc" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
CHECK_THAT( testStringForMatching(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "aBC", Catch::CaseSensitive::No ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
|
|
|
|
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(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "not there", Catch::CaseSensitive::No ) );
|
|
|
|
CHECK_THAT( testStringForMatching(), ContainsSubstring( "STRING" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-11-10 18:14:42 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "string" ) && ContainsSubstring( "abc" ) &&
|
|
|
|
ContainsSubstring( "substring" ) && ContainsSubstring( "contains" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Matchers can be (AnyOf) composed with the || operator",
|
|
|
|
"[matchers][operators][operator||]" ) {
|
|
|
|
CHECK_THAT( testStringForMatching(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ||
|
|
|
|
ContainsSubstring( "random" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
CHECK_THAT( testStringForMatching2(),
|
2021-09-23 23:28:59 +02:00
|
|
|
ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ||
|
|
|
|
ContainsSubstring( "random" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Matchers can be composed with both && and ||",
|
|
|
|
"[matchers][operators][operator||][operator&&]" ) {
|
|
|
|
CHECK_THAT( testStringForMatching(),
|
2021-09-23 23:28:59 +02:00
|
|
|
( ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ) &&
|
|
|
|
ContainsSubstring( "substring" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Matchers can be composed with both && and || - failing",
|
|
|
|
"[matchers][operators][operator||][operator&&][.failing]" ) {
|
|
|
|
CHECK_THAT( testStringForMatching(),
|
2021-09-23 23:28:59 +02:00
|
|
|
( ContainsSubstring( "string" ) || ContainsSubstring( "different" ) ) &&
|
|
|
|
ContainsSubstring( "random" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Matchers can be negated (Not) with the ! operator",
|
|
|
|
"[matchers][operators][not]" ) {
|
2021-09-23 23:28:59 +02:00
|
|
|
CHECK_THAT( testStringForMatching(), !ContainsSubstring( "different" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Matchers can be negated (Not) with the ! operator - failing",
|
|
|
|
"[matchers][operators][not][.failing]" ) {
|
2021-09-23 23:28:59 +02:00
|
|
|
CHECK_THAT( testStringForMatching(), !ContainsSubstring( "substring" ) );
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T> struct CustomAllocator : private std::allocator<T> {
|
|
|
|
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 <typename U> struct rebind { using other = CustomAllocator<U>; };
|
|
|
|
|
|
|
|
using propagate_on_container_move_assignment = std::true_type;
|
|
|
|
using is_always_equal = std::true_type;
|
|
|
|
|
|
|
|
CustomAllocator() = default;
|
|
|
|
|
|
|
|
CustomAllocator( const CustomAllocator& other ):
|
|
|
|
std::allocator<T>( other ) {}
|
|
|
|
|
|
|
|
template <typename U> CustomAllocator( const CustomAllocator<U>& ) {}
|
|
|
|
|
|
|
|
~CustomAllocator() = default;
|
|
|
|
|
|
|
|
using std::allocator<T>::allocate;
|
|
|
|
using std::allocator<T>::deallocate;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_CASE( "Vector matchers", "[matchers][vector]" ) {
|
|
|
|
std::vector<int> v;
|
|
|
|
v.push_back( 1 );
|
|
|
|
v.push_back( 2 );
|
|
|
|
v.push_back( 3 );
|
|
|
|
|
|
|
|
std::vector<int> v2;
|
|
|
|
v2.push_back( 1 );
|
|
|
|
v2.push_back( 2 );
|
|
|
|
|
|
|
|
std::vector<double> v3;
|
|
|
|
v3.push_back( 1 );
|
|
|
|
v3.push_back( 2 );
|
|
|
|
v3.push_back( 3 );
|
|
|
|
|
|
|
|
std::vector<double> v4;
|
|
|
|
v4.push_back( 1 + 1e-8 );
|
|
|
|
v4.push_back( 2 + 1e-8 );
|
|
|
|
v4.push_back( 3 + 1e-8 );
|
|
|
|
|
|
|
|
std::vector<int, CustomAllocator<int>> v5;
|
|
|
|
v5.push_back( 1 );
|
|
|
|
v5.push_back( 2 );
|
|
|
|
v5.push_back( 3 );
|
|
|
|
|
|
|
|
std::vector<int, CustomAllocator<int>> v6;
|
|
|
|
v6.push_back( 1 );
|
|
|
|
v6.push_back( 2 );
|
|
|
|
|
|
|
|
std::vector<int> empty;
|
|
|
|
|
|
|
|
SECTION( "Contains (element)" ) {
|
|
|
|
CHECK_THAT( v, VectorContains( 1 ) );
|
|
|
|
CHECK_THAT( v, VectorContains( 2 ) );
|
|
|
|
CHECK_THAT( v5, ( VectorContains<int, CustomAllocator<int>>( 2 ) ) );
|
2017-11-10 18:14:42 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
SECTION( "Contains (vector)" ) {
|
|
|
|
CHECK_THAT( v, Contains( v2 ) );
|
|
|
|
CHECK_THAT( v, Contains<int>( { 1, 2 } ) );
|
|
|
|
CHECK_THAT( v5,
|
|
|
|
( Contains<int, std::allocator<int>, CustomAllocator<int>>(
|
|
|
|
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<int, std::allocator<int>, CustomAllocator<int>>(
|
|
|
|
v2 ) ) );
|
|
|
|
CHECK_THAT( v5, Contains( v6 ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
SECTION( "Contains (element), composed" ) {
|
|
|
|
CHECK_THAT( v, VectorContains( 1 ) && VectorContains( 2 ) );
|
2017-11-10 18:14:42 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
SECTION( "Equals" ) {
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
// Same vector
|
|
|
|
CHECK_THAT( v, Equals( v ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
CHECK_THAT( empty, Equals( empty ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
// Different vector with same elements
|
|
|
|
CHECK_THAT( v, Equals<int>( { 1, 2, 3 } ) );
|
|
|
|
v2.push_back( 3 );
|
|
|
|
CHECK_THAT( v, Equals( v2 ) );
|
2019-01-14 15:31:09 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
CHECK_THAT(
|
|
|
|
v5,
|
|
|
|
( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) );
|
2019-01-14 15:31:09 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
v6.push_back( 3 );
|
|
|
|
CHECK_THAT( v5, Equals( v6 ) );
|
|
|
|
}
|
|
|
|
SECTION( "UnorderedEquals" ) {
|
|
|
|
CHECK_THAT( v, UnorderedEquals( v ) );
|
|
|
|
CHECK_THAT( v, UnorderedEquals<int>( { 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<int, std::allocator<int>, CustomAllocator<int>>(
|
|
|
|
permuted ) ) );
|
|
|
|
|
|
|
|
auto v5_permuted = v5;
|
|
|
|
std::next_permutation( begin( v5_permuted ), end( v5_permuted ) );
|
|
|
|
CHECK_THAT( v5_permuted, UnorderedEquals( v5 ) );
|
|
|
|
}
|
|
|
|
}
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
TEST_CASE( "Vector matchers that fail", "[matchers][vector][.][failing]" ) {
|
|
|
|
std::vector<int> v;
|
|
|
|
v.push_back( 1 );
|
|
|
|
v.push_back( 2 );
|
|
|
|
v.push_back( 3 );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::vector<int> v2;
|
|
|
|
v2.push_back( 1 );
|
|
|
|
v2.push_back( 2 );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::vector<double> v3;
|
|
|
|
v3.push_back( 1 );
|
|
|
|
v3.push_back( 2 );
|
|
|
|
v3.push_back( 3 );
|
2017-12-07 17:07:25 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::vector<double> v4;
|
|
|
|
v4.push_back( 1.1 );
|
|
|
|
v4.push_back( 2.1 );
|
|
|
|
v4.push_back( 3.1 );
|
2017-12-07 17:07:25 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::vector<int> empty;
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
|
|
|
}
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
SECTION( "Equals" ) {
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2017-12-06 15:37:13 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
auto permuted = v;
|
|
|
|
std::next_permutation( begin( permuted ), end( permuted ) );
|
|
|
|
permuted.pop_back();
|
|
|
|
CHECK_THAT( permuted, UnorderedEquals( v ) );
|
2019-10-13 11:56:50 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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<float>::min();
|
|
|
|
auto v2 = v1;
|
|
|
|
for ( int i = 0; i < 5; ++i ) {
|
|
|
|
v2 = std::nextafter( v1, 0.f );
|
2017-12-06 15:37:13 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( v1, WithinRel( v2 ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
SECTION( "Margin" ) {
|
|
|
|
REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0 ) );
|
|
|
|
REQUIRE_THAT( 0.f, WithinAbs( 1.f, 1 ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) );
|
|
|
|
REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2021-07-26 22:01:04 +02:00
|
|
|
REQUIRE_THAT(-1.f, WithinULP( -1.f, 0 ) );
|
2017-12-06 15:37:13 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2019-10-13 11:56:50 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 );
|
2017-11-15 08:48:21 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_NOTHROW( WithinULP( 1.f, 0 ) );
|
|
|
|
REQUIRE_THROWS_AS( WithinULP( 1.f, static_cast<uint64_t>( -1 ) ),
|
|
|
|
std::domain_error );
|
2019-06-14 20:16:12 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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<double>::min();
|
|
|
|
auto v2 = v1;
|
|
|
|
for ( int i = 0; i < 5; ++i ) {
|
|
|
|
v2 = std::nextafter( v1, 0 );
|
2018-04-03 23:28:14 +02:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( v1, WithinRel( v2 ) );
|
2018-04-03 23:28:14 +02:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
SECTION( "Margin" ) {
|
|
|
|
REQUIRE_THAT( 1., WithinAbs( 1., 0 ) );
|
|
|
|
REQUIRE_THAT( 0., WithinAbs( 1., 1 ) );
|
2018-04-03 23:28:14 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) );
|
|
|
|
REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) );
|
2018-09-28 15:30:00 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2018-09-28 15:30:00 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2018-11-04 00:00:36 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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 );
|
2019-01-14 15:31:09 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
REQUIRE_NOTHROW( WithinULP( 1., 0 ) );
|
2019-01-14 15:31:09 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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<int>( alwaysTrue, "always true" ) );
|
|
|
|
REQUIRE_THAT( 1, !Predicate<int>( alwaysFalse, "always false" ) );
|
|
|
|
}
|
|
|
|
SECTION( "Lambdas + different type" ) {
|
|
|
|
REQUIRE_THAT( "Hello olleH",
|
|
|
|
Predicate<std::string>(
|
|
|
|
[]( 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>( []( 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<char> actual = { 'a', 'b' };
|
|
|
|
std::vector<char> 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 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<double> empty;
|
|
|
|
REQUIRE_THAT( empty, Approx( empty ) );
|
|
|
|
}
|
|
|
|
SECTION( "Vectors with elements" ) {
|
|
|
|
std::vector<double> v1( { 1., 2., 3. } );
|
|
|
|
SECTION( "A vector is approx equal to itself" ) {
|
|
|
|
REQUIRE_THAT( v1, Approx( v1 ) );
|
|
|
|
REQUIRE_THAT( v1, Approx<double>( { 1., 2., 3. } ) );
|
2019-10-13 20:37:07 +02:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
std::vector<double> v2( { 1.5, 2.5, 3.5 } );
|
|
|
|
SECTION( "Different length" ) {
|
|
|
|
auto temp( v1 );
|
|
|
|
temp.push_back( 4 );
|
|
|
|
REQUIRE_THAT( v1, !Approx( temp ) );
|
2020-06-14 21:48:08 +02:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
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 ) );
|
2020-07-26 21:22:13 +02:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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<double> empty, t1( { 1, 2 } );
|
|
|
|
CHECK_THAT( empty, Approx( t1 ) );
|
|
|
|
}
|
|
|
|
SECTION( "Just different vectors" ) {
|
|
|
|
std::vector<double> 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<int> {
|
|
|
|
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" ) );
|
|
|
|
}
|
|
|
|
};
|
2020-06-14 21:48:08 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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;
|
2020-06-14 21:48:08 +02:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
Detail::MatchAllOf<int> matcher =
|
|
|
|
Detail::MatchAllOf<int>{} && first && second;
|
|
|
|
CHECK_FALSE( matcher.match( 1 ) );
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
// 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;
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
Detail::MatchAnyOf<int> matcher =
|
|
|
|
Detail::MatchAnyOf<int>{} || first || second;
|
|
|
|
CHECK( matcher.match( 1 ) );
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
// These two assertions are the important ones
|
|
|
|
REQUIRE( first.matchCalled );
|
|
|
|
REQUIRE( !second.matchCalled );
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct CheckedTestingGenericMatcher : Catch::Matchers::MatcherGenericBase {
|
|
|
|
mutable bool matchCalled = false;
|
|
|
|
bool matchSucceeds = false;
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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" ) );
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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;
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
Detail::MatchAllOfGeneric<CheckedTestingGenericMatcher,
|
|
|
|
CheckedTestingGenericMatcher>
|
|
|
|
matcher{ first, second };
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
CHECK_FALSE( matcher.match( 1 ) );
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
// 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<CheckedTestingGenericMatcher,
|
|
|
|
CheckedTestingGenericMatcher>
|
|
|
|
matcher{ first, second };
|
|
|
|
CHECK( matcher.match( 1 ) );
|
|
|
|
|
|
|
|
// These two assertions are the important ones
|
|
|
|
REQUIRE( first.matchCalled );
|
|
|
|
REQUIRE( !second.matchCalled );
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
template <typename Range>
|
|
|
|
struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase {
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
EqualsRangeMatcher( Range const& range ): m_range{ range } {}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
template <typename OtherRange> bool match( OtherRange const& other ) const {
|
|
|
|
using std::begin;
|
|
|
|
using std::end;
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
return std::equal(
|
|
|
|
begin( m_range ), end( m_range ), begin( other ), end( other ) );
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::string describe() const override {
|
|
|
|
return "Equals: " + Catch::rangeToString( m_range );
|
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
private:
|
|
|
|
Range const& m_range;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Range>
|
|
|
|
auto EqualsRange( const Range& range ) -> EqualsRangeMatcher<Range> {
|
|
|
|
return EqualsRangeMatcher<Range>{ range };
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Combining templated matchers", "[matchers][templated]" ) {
|
|
|
|
std::array<int, 3> container{ { 1, 2, 3 } };
|
|
|
|
|
|
|
|
std::array<int, 3> a{ { 1, 2, 3 } };
|
|
|
|
std::vector<int> b{ 0, 1, 2 };
|
|
|
|
std::list<int> c{ 4, 5, 6 };
|
|
|
|
|
|
|
|
REQUIRE_THAT( container,
|
|
|
|
EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE( "Combining templated and concrete matchers",
|
|
|
|
"[matchers][templated]" ) {
|
|
|
|
std::vector<int> vec{ 1, 3, 5 };
|
|
|
|
|
|
|
|
std::array<int, 3> a{ { 5, 3, 1 } };
|
|
|
|
|
|
|
|
REQUIRE_THAT( vec,
|
|
|
|
Predicate<std::vector<int>>(
|
|
|
|
[]( 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<char, 6> arr{ { 'f', 'o', 'o', 'b', 'a', 'r' } };
|
|
|
|
const std::array<char, 6> 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<decltype( StartsWith( "foo" ) ||
|
|
|
|
( StartsWith( "bar" ) && EndsWith( "bar" ) &&
|
|
|
|
!EndsWith( "foo" ) ) ),
|
|
|
|
Catch::Matchers::Detail::MatchAnyOf<std::string>>::value );
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MatcherA : Catch::Matchers::MatcherGenericBase {
|
|
|
|
std::string describe() const override {
|
|
|
|
return "equals: (int) 1 or (float) 1.0f";
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
bool match( int i ) const { return i == 1; }
|
|
|
|
bool match( float f ) const { return f == 1.0f; }
|
|
|
|
};
|
|
|
|
|
|
|
|
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 <typename T> 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<decltype( MatcherA() || MatcherB() ),
|
|
|
|
Catch::Matchers::Detail::
|
|
|
|
MatchAnyOfGeneric<MatcherA, MatcherB>>::value );
|
|
|
|
|
|
|
|
REQUIRE_THAT( 1, MatcherA() || MatcherB() );
|
|
|
|
|
|
|
|
STATIC_REQUIRE(
|
|
|
|
std::is_same<decltype( MatcherA() && MatcherB() ),
|
|
|
|
Catch::Matchers::Detail::
|
|
|
|
MatchAllOfGeneric<MatcherA, MatcherB>>::value );
|
|
|
|
|
|
|
|
REQUIRE_THAT( 1, MatcherA() && MatcherB() );
|
|
|
|
|
|
|
|
STATIC_REQUIRE(
|
|
|
|
std::is_same<
|
|
|
|
decltype( MatcherA() || !MatcherB() ),
|
|
|
|
Catch::Matchers::Detail::MatchAnyOfGeneric<
|
|
|
|
MatcherA,
|
|
|
|
Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>>>::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<MatcherA, MatcherB, MatcherC>>::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<MatcherA, MatcherB, MatcherC>>::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<MatcherA, MatcherB, MatcherC, MatcherD>>::
|
|
|
|
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<MatcherA, MatcherB, MatcherC>>::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<MatcherA, MatcherB, MatcherC>>::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<MatcherA, MatcherB, MatcherC, MatcherD>>::
|
|
|
|
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<MatcherA>>::value );
|
|
|
|
|
|
|
|
REQUIRE_THAT( 0, !MatcherA() );
|
|
|
|
|
|
|
|
STATIC_REQUIRE(
|
|
|
|
std::is_same<decltype( !!MatcherA() ), MatcherA const&>::value );
|
|
|
|
|
|
|
|
REQUIRE_THAT( 1, !!MatcherA() );
|
|
|
|
|
|
|
|
STATIC_REQUIRE(
|
|
|
|
std::is_same<
|
|
|
|
decltype( !!!MatcherA() ),
|
|
|
|
Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value );
|
|
|
|
|
|
|
|
REQUIRE_THAT( 0, !!!MatcherA() );
|
|
|
|
|
|
|
|
STATIC_REQUIRE(
|
|
|
|
std::is_same<decltype( !!!!MatcherA() ), MatcherA const&>::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";
|
|
|
|
}
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct EvilCommaOperatorUsed : std::exception {
|
|
|
|
EvilCommaOperatorUsed() {}
|
|
|
|
const char* what() const noexcept override {
|
|
|
|
return "overloaded comma operator of matcher was used";
|
|
|
|
}
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct EvilMatcher : Catch::Matchers::MatcherGenericBase {
|
|
|
|
std::string describe() const override { return "equals: 45"; }
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
bool match( int i ) const { return i == 45; }
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
EvilMatcher const* operator&() const { throw EvilAddressOfOperatorUsed(); }
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
int operator,( EvilMatcher const& ) const { throw EvilCommaOperatorUsed(); }
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
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() );
|
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct ImmovableMatcher : Catch::Matchers::MatcherGenericBase {
|
|
|
|
ImmovableMatcher() = default;
|
|
|
|
ImmovableMatcher( ImmovableMatcher const& ) = delete;
|
|
|
|
ImmovableMatcher( ImmovableMatcher&& ) = delete;
|
|
|
|
ImmovableMatcher& operator=( ImmovableMatcher const& ) = delete;
|
|
|
|
ImmovableMatcher& operator=( ImmovableMatcher&& ) = delete;
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::string describe() const override { return "always false"; }
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
template <typename T> bool match( T&& ) const { return false; }
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct MatcherWasMovedOrCopied : std::exception {
|
|
|
|
MatcherWasMovedOrCopied() {}
|
|
|
|
const char* what() const noexcept override {
|
|
|
|
return "attempted to copy or move a matcher";
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct ThrowOnCopyOrMoveMatcher : Catch::Matchers::MatcherGenericBase {
|
|
|
|
ThrowOnCopyOrMoveMatcher() = default;
|
|
|
|
[[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher const& ):
|
|
|
|
Catch::Matchers::MatcherGenericBase() {
|
|
|
|
throw MatcherWasMovedOrCopied();
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
[[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& ):
|
|
|
|
Catch::Matchers::MatcherGenericBase() {
|
|
|
|
throw MatcherWasMovedOrCopied();
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher const& ) {
|
|
|
|
throw MatcherWasMovedOrCopied();
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
2021-06-20 16:25:57 +02:00
|
|
|
ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher&& ) {
|
|
|
|
throw MatcherWasMovedOrCopied();
|
2020-02-16 11:19:10 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
std::string describe() const override { return "always false"; }
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
template <typename T> bool match( T&& ) const { return false; }
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
TEST_CASE( "Matchers are not moved or copied",
|
|
|
|
"[matchers][templated][approvals]" ) {
|
|
|
|
REQUIRE_NOTHROW(
|
|
|
|
( ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher() ) ||
|
|
|
|
!ThrowOnCopyOrMoveMatcher() );
|
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
TEST_CASE( "Immovable matchers can be used",
|
|
|
|
"[matchers][templated][approvals]" ) {
|
|
|
|
REQUIRE_THAT( 123,
|
|
|
|
( ImmovableMatcher() && ImmovableMatcher() ) ||
|
|
|
|
!ImmovableMatcher() );
|
|
|
|
}
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
struct ReferencingMatcher : Catch::Matchers::MatcherGenericBase {
|
|
|
|
std::string describe() const override { return "takes reference"; }
|
|
|
|
bool match( int& i ) const { return i == 22; }
|
|
|
|
};
|
2020-02-16 11:19:10 +01:00
|
|
|
|
2021-06-20 16:25:57 +02:00
|
|
|
TEST_CASE( "Matchers can take references",
|
|
|
|
"[matchers][templated][approvals]" ) {
|
|
|
|
REQUIRE_THAT( 22, ReferencingMatcher{} );
|
|
|
|
}
|
2017-11-10 18:14:42 +01:00
|
|
|
|
2017-09-07 16:51:33 +02:00
|
|
|
#ifdef __clang__
|
2021-06-20 16:25:57 +02:00
|
|
|
# pragma clang diagnostic pop
|
2017-09-07 16:51:33 +02:00
|
|
|
#endif
|
2021-07-26 22:01:04 +02:00
|
|
|
|
|
|
|
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<TestType>::denorm_min();
|
|
|
|
|
|
|
|
CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) );
|
|
|
|
CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) );
|
|
|
|
}
|