aboutsummaryrefslogtreecommitdiffstats
path: root/tests/SelfTest/UsageTests
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
commitbc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch)
tree1e629e7b46b1d9972a973bc93fd100bcebd395be /tests/SelfTest/UsageTests
downloadnihil-vendor/catch2.tar.gz
nihil-vendor/catch2.tar.bz2
Diffstat (limited to 'tests/SelfTest/UsageTests')
-rw-r--r--tests/SelfTest/UsageTests/Approx.tests.cpp218
-rw-r--r--tests/SelfTest/UsageTests/BDD.tests.cpp106
-rw-r--r--tests/SelfTest/UsageTests/Benchmark.tests.cpp173
-rw-r--r--tests/SelfTest/UsageTests/Class.tests.cpp159
-rw-r--r--tests/SelfTest/UsageTests/Compilation.tests.cpp525
-rw-r--r--tests/SelfTest/UsageTests/Condition.tests.cpp334
-rw-r--r--tests/SelfTest/UsageTests/Decomposition.tests.cpp41
-rw-r--r--tests/SelfTest/UsageTests/EnumToString.tests.cpp108
-rw-r--r--tests/SelfTest/UsageTests/Exception.tests.cpp204
-rw-r--r--tests/SelfTest/UsageTests/Generators.tests.cpp323
-rw-r--r--tests/SelfTest/UsageTests/Matchers.tests.cpp1144
-rw-r--r--tests/SelfTest/UsageTests/MatchersRanges.tests.cpp936
-rw-r--r--tests/SelfTest/UsageTests/Message.tests.cpp312
-rw-r--r--tests/SelfTest/UsageTests/Misc.tests.cpp560
-rw-r--r--tests/SelfTest/UsageTests/Skip.tests.cpp100
-rw-r--r--tests/SelfTest/UsageTests/ToStringByte.tests.cpp23
-rw-r--r--tests/SelfTest/UsageTests/ToStringChrono.tests.cpp51
-rw-r--r--tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp200
-rw-r--r--tests/SelfTest/UsageTests/ToStringOptional.tests.cpp35
-rw-r--r--tests/SelfTest/UsageTests/ToStringPair.tests.cpp38
-rw-r--r--tests/SelfTest/UsageTests/ToStringTuple.tests.cpp54
-rw-r--r--tests/SelfTest/UsageTests/ToStringVariant.tests.cpp99
-rw-r--r--tests/SelfTest/UsageTests/ToStringVector.tests.cpp94
-rw-r--r--tests/SelfTest/UsageTests/ToStringWhich.tests.cpp186
-rw-r--r--tests/SelfTest/UsageTests/Tricky.tests.cpp380
-rw-r--r--tests/SelfTest/UsageTests/VariadicMacros.tests.cpp29
26 files changed, 6432 insertions, 0 deletions
diff --git a/tests/SelfTest/UsageTests/Approx.tests.cpp b/tests/SelfTest/UsageTests/Approx.tests.cpp
new file mode 100644
index 0000000..c3d41cb
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Approx.tests.cpp
@@ -0,0 +1,218 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_approx.hpp>
+
+#include <cmath>
+
+using Catch::Approx;
+
+namespace {
+ static double divide(double a, double b) {
+ return a / b;
+ }
+
+ class StrongDoubleTypedef {
+ double d_ = 0.0;
+
+ public:
+ explicit StrongDoubleTypedef(double d) : d_(d) {}
+ explicit operator double() const { return d_; }
+ };
+
+ static std::ostream& operator<<(std::ostream& os, StrongDoubleTypedef td) {
+ return os << "StrongDoubleTypedef(" << static_cast<double>(td) << ")";
+ }
+} // end unnamed namespace
+
+using namespace Catch::literals;
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "A comparison that uses literals instead of the normal constructor", "[Approx]" ) {
+ double d = 1.23;
+
+ REQUIRE( d == 1.23_a );
+ REQUIRE( d != 1.22_a );
+ REQUIRE( -d == -1.23_a );
+
+ REQUIRE( d == 1.2_a .epsilon(.1) );
+ REQUIRE( d != 1.2_a .epsilon(.001) );
+ REQUIRE( d == 1_a .epsilon(.3) );
+}
+
+TEST_CASE( "Some simple comparisons between doubles", "[Approx]" ) {
+ double d = 1.23;
+
+ REQUIRE( d == Approx( 1.23 ) );
+ REQUIRE( d != Approx( 1.22 ) );
+ REQUIRE( d != Approx( 1.24 ) );
+
+ REQUIRE( d == 1.23_a );
+ REQUIRE( d != 1.22_a );
+
+ REQUIRE( Approx( d ) == 1.23 );
+ REQUIRE( Approx( d ) != 1.22 );
+ REQUIRE( Approx( d ) != 1.24 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Approximate comparisons with different epsilons", "[Approx]" ) {
+ double d = 1.23;
+
+ REQUIRE( d != Approx( 1.231 ) );
+ REQUIRE( d == Approx( 1.231 ).epsilon( 0.1 ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Less-than inequalities with different epsilons", "[Approx]" ) {
+ double d = 1.23;
+
+ REQUIRE( d <= Approx( 1.24 ) );
+ REQUIRE( d <= Approx( 1.23 ) );
+ REQUIRE_FALSE( d <= Approx( 1.22 ) );
+ REQUIRE( d <= Approx( 1.22 ).epsilon(0.1) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Greater-than inequalities with different epsilons", "[Approx]" ) {
+ double d = 1.23;
+
+ REQUIRE( d >= Approx( 1.22 ) );
+ REQUIRE( d >= Approx( 1.23 ) );
+ REQUIRE_FALSE( d >= Approx( 1.24 ) );
+ REQUIRE( d >= Approx( 1.24 ).epsilon(0.1) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Approximate comparisons with floats", "[Approx]" ) {
+ REQUIRE( 1.23f == Approx( 1.23f ) );
+ REQUIRE( 0.0f == Approx( 0.0f ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Approximate comparisons with ints", "[Approx]" ) {
+ REQUIRE( 1 == Approx( 1 ) );
+ REQUIRE( 0 == Approx( 0 ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Approximate comparisons with mixed numeric types", "[Approx]" ) {
+ const double dZero = 0;
+ const double dSmall = 0.00001;
+ const double dMedium = 1.234;
+
+ REQUIRE( 1.0f == Approx( 1 ) );
+ REQUIRE( 0 == Approx( dZero) );
+ REQUIRE( 0 == Approx( dSmall ).margin( 0.001 ) );
+ REQUIRE( 1.234f == Approx( dMedium ) );
+ REQUIRE( dMedium == Approx( 1.234f ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE( "Use a custom approx", "[Approx][custom]" ) {
+ double d = 1.23;
+
+ Approx approx = Approx::custom().epsilon( 0.01 );
+
+ REQUIRE( d == approx( 1.23 ) );
+ REQUIRE( d == approx( 1.22 ) );
+ REQUIRE( d == approx( 1.24 ) );
+ REQUIRE( d != approx( 1.25 ) );
+
+ REQUIRE( approx( d ) == 1.23 );
+ REQUIRE( approx( d ) == 1.22 );
+ REQUIRE( approx( d ) == 1.24 );
+ REQUIRE( approx( d ) != 1.25 );
+}
+
+TEST_CASE( "Approximate PI", "[Approx][PI]" ) {
+ REQUIRE( divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) );
+ REQUIRE( divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_CASE( "Absolute margin", "[Approx]" ) {
+ REQUIRE( 104.0 != Approx(100.0) );
+ REQUIRE( 104.0 == Approx(100.0).margin(5) );
+ REQUIRE( 104.0 == Approx(100.0).margin(4) );
+ REQUIRE( 104.0 != Approx(100.0).margin(3) );
+ REQUIRE( 100.3 != Approx(100.0) );
+ REQUIRE( 100.3 == Approx(100.0).margin(0.5) );
+}
+
+TEST_CASE("Approx with exactly-representable margin", "[Approx]") {
+ CHECK( 0.25f == Approx(0.0f).margin(0.25f) );
+
+ CHECK( 0.0f == Approx(0.25f).margin(0.25f) );
+ CHECK( 0.5f == Approx(0.25f).margin(0.25f) );
+
+ CHECK( 245.0f == Approx(245.25f).margin(0.25f) );
+ CHECK( 245.5f == Approx(245.25f).margin(0.25f) );
+}
+
+TEST_CASE("Approx setters validate their arguments", "[Approx]") {
+ REQUIRE_NOTHROW(Approx(0).margin(0));
+ REQUIRE_NOTHROW(Approx(0).margin(1234656));
+
+ REQUIRE_THROWS_AS(Approx(0).margin(-2), std::domain_error);
+
+ REQUIRE_NOTHROW(Approx(0).epsilon(0));
+ REQUIRE_NOTHROW(Approx(0).epsilon(1));
+
+ REQUIRE_THROWS_AS(Approx(0).epsilon(-0.001), std::domain_error);
+ REQUIRE_THROWS_AS(Approx(0).epsilon(1.0001), std::domain_error);
+}
+
+TEST_CASE("Default scale is invisible to comparison", "[Approx]") {
+ REQUIRE(101.000001 != Approx(100).epsilon(0.01));
+ REQUIRE(std::pow(10, -5) != Approx(std::pow(10, -7)));
+}
+
+TEST_CASE("Epsilon only applies to Approx's value", "[Approx]") {
+ REQUIRE(101.01 != Approx(100).epsilon(0.01));
+}
+
+TEST_CASE("Assorted miscellaneous tests", "[Approx][approvals]") {
+ REQUIRE(INFINITY == Approx(INFINITY));
+ REQUIRE(-INFINITY != Approx(INFINITY));
+ REQUIRE(1 != Approx(INFINITY));
+ REQUIRE(INFINITY != Approx(1));
+ REQUIRE(NAN != Approx(NAN));
+ REQUIRE_FALSE(NAN == Approx(NAN));
+}
+
+TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" )
+{
+ StrongDoubleTypedef td(10.0);
+
+ REQUIRE(td == Approx(10.0));
+ REQUIRE(Approx(10.0) == td);
+
+ REQUIRE(td != Approx(11.0));
+ REQUIRE(Approx(11.0) != td);
+
+ REQUIRE(td <= Approx(10.0));
+ REQUIRE(td <= Approx(11.0));
+ REQUIRE(Approx(10.0) <= td);
+ REQUIRE(Approx(9.0) <= td);
+
+ REQUIRE(td >= Approx(9.0));
+ REQUIRE(td >= Approx(td));
+ REQUIRE(Approx(td) >= td);
+ REQUIRE(Approx(11.0) >= td);
+
+}
+
+TEST_CASE("Approx::operator() is const correct", "[Approx][.approvals]") {
+ const Approx ap = Approx(0.0).margin(0.01);
+
+ // As long as this compiles, the test should be considered passing
+ REQUIRE(1.0 == ap(1.0));
+}
diff --git a/tests/SelfTest/UsageTests/BDD.tests.cpp b/tests/SelfTest/UsageTests/BDD.tests.cpp
new file mode 100644
index 0000000..5ac8605
--- /dev/null
+++ b/tests/SelfTest/UsageTests/BDD.tests.cpp
@@ -0,0 +1,106 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+
+namespace {
+
+ static bool itDoesThis() { return true; }
+
+ static bool itDoesThat() { return true; }
+
+ // a trivial fixture example to support SCENARIO_METHOD tests
+ struct Fixture {
+ Fixture(): d_counter( 0 ) {}
+
+ int counter() { return d_counter++; }
+
+ int d_counter;
+ };
+
+}
+
+
+SCENARIO("Do that thing with the thing", "[Tags]") {
+ GIVEN("This stuff exists") {
+ // make stuff exist
+ AND_GIVEN("And some assumption") {
+ // Validate assumption
+ WHEN("I do this") {
+ // do this
+ THEN("it should do this") {
+ REQUIRE(itDoesThis());
+ AND_THEN("do that") {
+ REQUIRE(itDoesThat());
+ }
+ }
+ }
+ }
+ }
+}
+
+SCENARIO( "Vector resizing affects size and capacity",
+ "[vector][bdd][size][capacity]" ) {
+ GIVEN( "an empty vector" ) {
+ std::vector<int> v;
+ REQUIRE( v.size() == 0 );
+
+ WHEN( "it is made larger" ) {
+ v.resize( 10 );
+ THEN( "the size and capacity go up" ) {
+ REQUIRE( v.size() == 10 );
+ REQUIRE( v.capacity() >= 10 );
+
+ AND_WHEN( "it is made smaller again" ) {
+ v.resize( 5 );
+ THEN(
+ "the size goes down but the capacity stays the same" ) {
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ }
+ }
+ }
+
+ WHEN( "we reserve more space" ) {
+ v.reserve( 10 );
+ THEN( "The capacity is increased but the size remains the same" ) {
+ REQUIRE( v.capacity() >= 10 );
+ REQUIRE( v.size() == 0 );
+ }
+ }
+ }
+}
+
+SCENARIO("This is a really long scenario name to see how the list command deals with wrapping",
+ "[very long tags][lots][long][tags][verbose]"
+ "[one very long tag name that should cause line wrapping writing out using the list command]"
+ "[anotherReallyLongTagNameButThisOneHasNoObviousWrapPointsSoShouldSplitWithinAWordUsingADashCharacter]") {
+ GIVEN("A section name that is so long that it cannot fit in a single console width") {
+ WHEN("The test headers are printed as part of the normal running of the scenario") {
+ THEN("The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent") {
+ SUCCEED("boo!");
+ }
+ }
+ }
+}
+
+SCENARIO_METHOD(Fixture,
+ "BDD tests requiring Fixtures to provide commonly-accessed data or methods",
+ "[bdd][fixtures]") {
+ const int before(counter());
+ GIVEN("No operations precede me") {
+ REQUIRE(before == 0);
+ WHEN("We get the count") {
+ const int after(counter());
+ THEN("Subsequently values are higher") {
+ REQUIRE(after > before);
+ }
+ }
+ }
+}
diff --git a/tests/SelfTest/UsageTests/Benchmark.tests.cpp b/tests/SelfTest/UsageTests/Benchmark.tests.cpp
new file mode 100644
index 0000000..c489eaa
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Benchmark.tests.cpp
@@ -0,0 +1,173 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+// Adapted from donated nonius code.
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/benchmark/catch_benchmark.hpp>
+#include <catch2/benchmark/catch_constructor.hpp>
+#include <catch2/generators/catch_generators_range.hpp>
+
+#include <map>
+
+namespace {
+ std::uint64_t Fibonacci(std::uint64_t number) {
+ return number < 2 ? number : Fibonacci(number - 1) + Fibonacci(number - 2);
+ }
+}
+
+TEST_CASE("Benchmark Fibonacci", "[!benchmark]") {
+ CHECK(Fibonacci(0) == 0);
+ // some more asserts..
+ CHECK(Fibonacci(5) == 5);
+ // some more asserts..
+
+ REQUIRE( Fibonacci( 20 ) == 6'765 );
+ BENCHMARK( "Fibonacci 20" ) {
+ return Fibonacci(20);
+ };
+
+ REQUIRE( Fibonacci( 25 ) == 75'025 );
+ BENCHMARK( "Fibonacci 25" ) {
+ return Fibonacci(25);
+ };
+
+ BENCHMARK("Fibonacci 30") {
+ return Fibonacci(30);
+ };
+
+ BENCHMARK("Fibonacci 35") {
+ return Fibonacci(35);
+ };
+}
+
+TEST_CASE("Benchmark containers", "[!benchmark]") {
+ static const int size = 100;
+
+ std::vector<int> v;
+ std::map<int, int> m;
+
+ SECTION("without generator") {
+ BENCHMARK("Load up a vector") {
+ v = std::vector<int>();
+ for (int i = 0; i < size; ++i)
+ v.push_back(i);
+ };
+ REQUIRE(v.size() == size);
+
+ // test optimizer control
+ BENCHMARK("Add up a vector's content") {
+ uint64_t add = 0;
+ for (int i = 0; i < size; ++i)
+ add += v[i];
+ return add;
+ };
+
+ BENCHMARK("Load up a map") {
+ m = std::map<int, int>();
+ for (int i = 0; i < size; ++i)
+ m.insert({ i, i + 1 });
+ };
+ REQUIRE(m.size() == size);
+
+ BENCHMARK("Reserved vector") {
+ v = std::vector<int>();
+ v.reserve(size);
+ for (int i = 0; i < size; ++i)
+ v.push_back(i);
+ };
+ REQUIRE(v.size() == size);
+
+ BENCHMARK("Resized vector") {
+ v = std::vector<int>();
+ v.resize(size);
+ for (int i = 0; i < size; ++i)
+ v[i] = i;
+ };
+ REQUIRE(v.size() == size);
+
+ int array[size] {};
+ BENCHMARK("A fixed size array that should require no allocations") {
+ for (int i = 0; i < size; ++i)
+ array[i] = i;
+ };
+ int sum = 0;
+ for (int val : array)
+ sum += val;
+ REQUIRE(sum > size);
+
+ SECTION("XYZ") {
+
+ BENCHMARK_ADVANCED("Load up vector with chronometer")(Catch::Benchmark::Chronometer meter) {
+ std::vector<int> k;
+ meter.measure([&](int idx) {
+ k = std::vector<int>();
+ for (int i = 0; i < size; ++i)
+ k.push_back(idx);
+ });
+ REQUIRE(k.size() == size);
+ };
+
+ int runs = 0;
+ BENCHMARK("Fill vector indexed", benchmarkIndex) {
+ v = std::vector<int>();
+ v.resize(size);
+ for (int i = 0; i < size; ++i)
+ v[i] = benchmarkIndex;
+ runs = benchmarkIndex;
+ };
+
+ for (int val : v) {
+ REQUIRE(val == runs);
+ }
+ }
+ }
+
+ SECTION("with generator") {
+ auto generated = GENERATE(range(0, 10));
+ BENCHMARK("Fill vector generated") {
+ v = std::vector<int>();
+ v.resize(size);
+ for (int i = 0; i < size; ++i)
+ v[i] = generated;
+ };
+ for (int val : v) {
+ REQUIRE(val == generated);
+ }
+ }
+
+ SECTION("construct and destroy example") {
+ BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
+ std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
+ meter.measure([&](int i) { storage[i].construct("thing"); });
+ };
+
+ BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
+ std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
+ for(auto&& o : storage)
+ o.construct("thing");
+ meter.measure([&](int i) { storage[i].destruct(); });
+ };
+ }
+}
+
+TEST_CASE("Skip benchmark macros", "[!benchmark]") {
+ std::vector<int> v;
+ BENCHMARK("fill vector") {
+ v.emplace_back(1);
+ v.emplace_back(2);
+ v.emplace_back(3);
+ };
+ REQUIRE(v.size() == 0);
+
+ std::size_t counter{0};
+ BENCHMARK_ADVANCED("construct vector")(Catch::Benchmark::Chronometer meter) {
+ std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
+ meter.measure([&](int i) { storage[i].construct("thing"); counter++; });
+ };
+ REQUIRE(counter == 0);
+}
diff --git a/tests/SelfTest/UsageTests/Class.tests.cpp b/tests/SelfTest/UsageTests/Class.tests.cpp
new file mode 100644
index 0000000..75510f1
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Class.tests.cpp
@@ -0,0 +1,159 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
+#include <array>
+
+namespace {
+
+ class TestClass {
+ std::string s;
+
+ public:
+ TestClass(): s( "hello" ) {}
+
+ void succeedingCase() { REQUIRE( s == "hello" ); }
+ void failingCase() { REQUIRE( s == "world" ); }
+ };
+
+ struct Fixture {
+ Fixture(): m_a( 1 ) {}
+
+ int m_a;
+ };
+
+ struct Persistent_Fixture {
+ mutable int m_a = 0;
+ };
+
+ template <typename T> struct Template_Fixture {
+ Template_Fixture(): m_a( 1 ) {}
+
+ T m_a;
+ };
+
+ template <typename T> struct Template_Fixture_2 {
+ Template_Fixture_2() = default;
+
+ T m_a;
+ };
+
+ template <typename T> struct Template_Foo {
+ size_t size() { return 0; }
+ };
+
+ template <typename T, size_t V> struct Template_Foo_2 {
+ size_t size() { return V; }
+ };
+
+ template <int V> struct Nttp_Fixture { int value = V; };
+
+} // end unnamed namespace
+
+METHOD_AS_TEST_CASE( TestClass::succeedingCase, "A METHOD_AS_TEST_CASE based test run that succeeds", "[class]" )
+METHOD_AS_TEST_CASE( TestClass::failingCase, "A METHOD_AS_TEST_CASE based test run that fails", "[.][class][failing]" )
+
+TEST_CASE_METHOD( Fixture, "A TEST_CASE_METHOD based test run that succeeds", "[class]" )
+{
+ REQUIRE( m_a == 1 );
+}
+
+TEST_CASE_PERSISTENT_FIXTURE( Persistent_Fixture, "A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds", "[class]" )
+{
+ SECTION( "First partial run" ) {
+ REQUIRE( m_a++ == 0 );
+ }
+
+ SECTION( "Second partial run" ) {
+ REQUIRE( m_a == 1 );
+ }
+}
+
+TEMPLATE_TEST_CASE_METHOD(Template_Fixture, "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds", "[class][template]", int, float, double) {
+ REQUIRE( Template_Fixture<TestType>::m_a == 1 );
+}
+
+TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][nttp]",((int V), V), 1, 3, 6) {
+ REQUIRE(Nttp_Fixture<V>::value > 0);
+}
+
+TEMPLATE_PRODUCT_TEST_CASE_METHOD(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds","[class][template][product]",(std::vector,Template_Foo),(int,float))
+{
+ REQUIRE( Template_Fixture_2<TestType>::m_a.size() == 0 );
+}
+
+TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][product][nttp]", ((typename T, size_t S), T, S),(std::array, Template_Foo_2), ((int,2), (float,6)))
+{
+ REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() >= 2);
+}
+
+using MyTypes = std::tuple<int, char, double>;
+TEMPLATE_LIST_TEST_CASE_METHOD(Template_Fixture, "Template test case method with test types specified inside std::tuple", "[class][template][list]", MyTypes)
+{
+ REQUIRE( Template_Fixture<TestType>::m_a == 1 );
+}
+
+// We should be able to write our tests within a different namespace
+namespace Inner
+{
+ TEST_CASE_METHOD( Fixture, "A TEST_CASE_METHOD based test run that fails", "[.][class][failing]" )
+ {
+ REQUIRE( m_a == 2 );
+ }
+
+ TEST_CASE_PERSISTENT_FIXTURE( Persistent_Fixture, "A TEST_CASE_PERSISTENT_FIXTURE based test run that fails", "[.][class][failing]" )
+ {
+ SECTION( "First partial run" ) {
+ REQUIRE( m_a++ == 0 );
+ }
+
+ SECTION( "Second partial run" ) {
+ REQUIRE( m_a == 0 );
+ }
+ }
+
+ TEMPLATE_TEST_CASE_METHOD(Template_Fixture,"A TEMPLATE_TEST_CASE_METHOD based test run that fails", "[.][class][template][failing]", int, float, double)
+ {
+ REQUIRE( Template_Fixture<TestType>::m_a == 2 );
+ }
+
+ TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails", "[.][class][template][nttp][failing]", ((int V), V), 1, 3, 6) {
+ REQUIRE(Nttp_Fixture<V>::value == 0);
+ }
+
+ TEMPLATE_PRODUCT_TEST_CASE_METHOD(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails","[.][class][template][product][failing]",(std::vector,Template_Foo),(int,float))
+ {
+ REQUIRE( Template_Fixture_2<TestType>::m_a.size() == 1 );
+ }
+
+ TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails", "[.][class][template][product][nttp][failing]", ((typename T, size_t S), T, S), (std::array, Template_Foo_2), ((int, 2), (float, 6)))
+ {
+ REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() < 2);
+ }
+} // namespace
+
+
+// We want a class in nested namespace so we can test JUnit's classname normalization.
+namespace {
+ namespace A {
+ namespace B {
+ class TestClass {};
+ }
+ }
+} // namespace
+
+TEST_CASE_METHOD( A::B::TestClass,
+ "A TEST_CASE_METHOD testing junit classname normalization",
+ "[class][approvals]" ) {
+ SUCCEED();
+}
diff --git a/tests/SelfTest/UsageTests/Compilation.tests.cpp b/tests/SelfTest/UsageTests/Compilation.tests.cpp
new file mode 100644
index 0000000..7a6a1f2
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Compilation.tests.cpp
@@ -0,0 +1,525 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <helpers/type_with_lit_0_comparisons.hpp>
+
+#include <array>
+#include <type_traits>
+
+// Setup for #1403 -- look for global overloads of operator << for classes
+// in a different namespace.
+#include <ostream>
+
+namespace foo {
+ struct helper_1403 {
+ bool operator==(helper_1403) const { return true; }
+ };
+}
+
+namespace bar {
+ template <typename... Ts>
+ struct TypeList {};
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#endif
+static std::ostream& operator<<(std::ostream& out, foo::helper_1403 const&) {
+ return out << "[1403 helper]";
+}
+///////////////////////////////
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_range.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
+#include <cstring>
+
+// Comparison operators can return non-booleans.
+// This is unusual, but should be supported.
+struct logic_t {
+ logic_t operator< (logic_t) const { return {}; }
+ logic_t operator<=(logic_t) const { return {}; }
+ logic_t operator> (logic_t) const { return {}; }
+ logic_t operator>=(logic_t) const { return {}; }
+ logic_t operator==(logic_t) const { return {}; }
+ logic_t operator!=(logic_t) const { return {}; }
+ explicit operator bool() const { return true; }
+};
+
+
+static void throws_int(bool b) {
+ if (b) {
+ throw 1;
+ }
+}
+
+template<typename T>
+bool templated_tests(T t) {
+ int a = 3;
+ REQUIRE(a == t);
+ CHECK(a == t);
+ REQUIRE_THROWS(throws_int(true));
+ CHECK_THROWS_AS(throws_int(true), int);
+ REQUIRE_NOTHROW(throws_int(false));
+ REQUIRE_THAT("aaa", Catch::Matchers::EndsWith("aaa"));
+ return true;
+}
+
+struct A {};
+
+static std::ostream &operator<<(std::ostream &o, const A &) { return o << 0; }
+
+struct B : private A {
+ bool operator==(int) const { return true; }
+};
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+#ifdef __GNUC__
+// Note that because -~GCC~-, this warning cannot be silenced temporarily, by pushing diagnostic stack...
+// Luckily it is firing in test files and thus can be silenced for the whole file, without losing much.
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+B f();
+
+std::ostream g();
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+template <typename, typename>
+struct Fixture_1245 {};
+
+// This is a minimal example for an issue we have found in 1.7.0
+struct dummy_809 {
+ int i;
+};
+
+template<typename T>
+bool operator==(const T& val, dummy_809 f) {
+ return val == f.i;
+}
+
+TEST_CASE("#809") {
+ dummy_809 f;
+ f.i = 42;
+ REQUIRE(42 == f);
+}
+
+
+// ------------------------------------------------------------------
+// Changes to REQUIRE_THROWS_AS made it stop working in a template in
+// an unfixable way (as long as C++03 compatibility is being kept).
+// To prevent these from happening in the future, this needs to compile
+
+ TEST_CASE("#833") {
+ REQUIRE(templated_tests<int>(3));
+ }
+
+
+// Test containing example where original stream insertable check breaks compilation
+TEST_CASE("#872") {
+ A dummy;
+ CAPTURE(dummy);
+ B x;
+ REQUIRE (x == 4);
+}
+
+TEST_CASE("#1027: Bitfields can be captured") {
+ struct Y {
+ uint32_t v : 1;
+ };
+ Y y{ 0 };
+ REQUIRE(y.v == 0);
+ REQUIRE(0 == y.v);
+}
+
+// Comparison operators can return non-booleans.
+// This is unusual, but should be supported.
+TEST_CASE("#1147") {
+ logic_t t1, t2;
+ REQUIRE(t1 == t2);
+ REQUIRE(t1 != t2);
+ REQUIRE(t1 < t2);
+ REQUIRE(t1 > t2);
+ REQUIRE(t1 <= t2);
+ REQUIRE(t1 >= t2);
+}
+
+// unsigned array
+TEST_CASE("#1238") {
+ unsigned char uarr[] = "123";
+ CAPTURE(uarr);
+ signed char sarr[] = "456";
+ CAPTURE(sarr);
+
+ REQUIRE(std::memcmp(uarr, "123", sizeof(uarr)) == 0);
+ REQUIRE(std::memcmp(sarr, "456", sizeof(sarr)) == 0);
+}
+
+TEST_CASE_METHOD((Fixture_1245<int, int>), "#1245", "[compilation]") {
+ SUCCEED();
+}
+
+TEST_CASE("#1403", "[compilation]") {
+ ::foo::helper_1403 h1, h2;
+ REQUIRE(h1 == h2);
+}
+
+TEST_CASE("Optionally static assertions", "[compilation]") {
+ STATIC_REQUIRE( std::is_void<void>::value );
+ STATIC_REQUIRE_FALSE( std::is_void<int>::value );
+ STATIC_CHECK( std::is_void<void>::value );
+ STATIC_CHECK_FALSE( std::is_void<int>::value );
+}
+
+TEST_CASE("#1548", "[compilation]") {
+ using namespace bar;
+ REQUIRE(std::is_same<TypeList<int>, TypeList<int>>::value);
+}
+
+ // #925
+ using signal_t = void (*) (void*);
+
+ struct TestClass {
+ signal_t testMethod_uponComplete_arg = nullptr;
+ };
+
+ namespace utility {
+ inline static void synchronizing_callback( void * ) { }
+ }
+
+#if defined (_MSC_VER)
+#pragma warning(push)
+// The function pointer comparison below triggers warning because of
+// calling conventions
+#pragma warning(disable:4244)
+#endif
+ TEST_CASE("#925: comparing function pointer to function address failed to compile", "[!nonportable]" ) {
+ TestClass test;
+ REQUIRE(utility::synchronizing_callback != test.testMethod_uponComplete_arg);
+ }
+#if defined (_MSC_VER)
+#pragma warning(pop)
+#endif
+
+TEST_CASE( "#1319: Sections can have description (even if it is not saved",
+ "[compilation]" ) {
+ SECTION( "SectionName", "This is a long form section description" ) {
+ SUCCEED();
+ }
+}
+
+TEST_CASE("Lambdas in assertions") {
+ REQUIRE([]() { return true; }());
+}
+
+namespace {
+ struct HasBitOperators {
+ int value;
+
+ friend HasBitOperators operator| (HasBitOperators lhs, HasBitOperators rhs) {
+ return { lhs.value | rhs.value };
+ }
+ friend HasBitOperators operator& (HasBitOperators lhs, HasBitOperators rhs) {
+ return { lhs.value & rhs.value };
+ }
+ friend HasBitOperators operator^ (HasBitOperators lhs, HasBitOperators rhs) {
+ return { lhs.value ^ rhs.value };
+ }
+ explicit operator bool() const {
+ return !!value;
+ }
+
+ friend std::ostream& operator<<(std::ostream& out, HasBitOperators val) {
+ out << "Val: " << val.value;
+ return out;
+ }
+ };
+}
+
+TEST_CASE("Assertion macros support bit operators and bool conversions", "[compilation][bitops]") {
+ HasBitOperators lhs{ 1 }, rhs{ 2 };
+ REQUIRE(lhs | rhs);
+ REQUIRE_FALSE(lhs & rhs);
+ REQUIRE(HasBitOperators{ 1 } & HasBitOperators{ 1 });
+ REQUIRE(lhs ^ rhs);
+ REQUIRE_FALSE(lhs ^ lhs);
+}
+
+namespace {
+ struct ImmovableType {
+ ImmovableType() = default;
+
+ ImmovableType(ImmovableType const&) = delete;
+ ImmovableType& operator=(ImmovableType const&) = delete;
+ ImmovableType(ImmovableType&&) = delete;
+ ImmovableType& operator=(ImmovableType&&) = delete;
+
+ friend bool operator==(ImmovableType const&, ImmovableType const&) {
+ return true;
+ }
+ };
+}
+
+TEST_CASE("Immovable types are supported in basic assertions", "[compilation][.approvals]") {
+ REQUIRE(ImmovableType{} == ImmovableType{});
+}
+
+namespace adl {
+
+struct always_true {
+ explicit operator bool() const { return true; }
+};
+
+#define COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(op) \
+template <class T, class U> \
+auto operator op (T&&, U&&) { \
+ return always_true{}; \
+}
+
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(==)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(!=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(|)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(&)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(^)
+
+#undef COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR
+
+}
+
+TEST_CASE("ADL universal operators don't hijack expression deconstruction", "[compilation][.approvals]") {
+ REQUIRE(adl::always_true{});
+ REQUIRE(0 == adl::always_true{});
+ REQUIRE(0 != adl::always_true{});
+ REQUIRE(0 < adl::always_true{});
+ REQUIRE(0 > adl::always_true{});
+ REQUIRE(0 <= adl::always_true{});
+ REQUIRE(0 >= adl::always_true{});
+ REQUIRE(0 | adl::always_true{});
+ REQUIRE(0 & adl::always_true{});
+ REQUIRE(0 ^ adl::always_true{});
+}
+
+TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as pointer conversion are supported",
+ "[compilation][approvals]" ) {
+ REQUIRE( TypeWithLit0Comparisons{} < 0 );
+ REQUIRE_FALSE( 0 < TypeWithLit0Comparisons{} );
+ REQUIRE( TypeWithLit0Comparisons{} <= 0 );
+ REQUIRE_FALSE( 0 <= TypeWithLit0Comparisons{} );
+
+ REQUIRE( TypeWithLit0Comparisons{} > 0 );
+ REQUIRE_FALSE( 0 > TypeWithLit0Comparisons{} );
+ REQUIRE( TypeWithLit0Comparisons{} >= 0 );
+ REQUIRE_FALSE( 0 >= TypeWithLit0Comparisons{} );
+
+ REQUIRE( TypeWithLit0Comparisons{} == 0 );
+ REQUIRE_FALSE( 0 == TypeWithLit0Comparisons{} );
+ REQUIRE( TypeWithLit0Comparisons{} != 0 );
+ REQUIRE_FALSE( 0 != TypeWithLit0Comparisons{} );
+}
+
+// These tests require `consteval` to propagate through `constexpr` calls
+// which is a late DR against C++20.
+#if defined( CATCH_CPP20_OR_GREATER ) && defined( __cpp_consteval ) && \
+ __cpp_consteval >= 202211L
+// Can't have internal linkage to avoid warnings
+void ZeroLiteralErrorFunc();
+namespace {
+ struct ZeroLiteralConsteval {
+ template <class T, std::enable_if_t<std::is_same_v<T, int>, int> = 0>
+ consteval ZeroLiteralConsteval( T zero ) noexcept {
+ if ( zero != 0 ) { ZeroLiteralErrorFunc(); }
+ }
+ };
+
+ // Should only be constructible from literal 0. Uses the propagating
+ // consteval constructor trick (currently used by MSVC, might be used
+ // by libc++ in the future as well).
+ struct TypeWithConstevalLit0Comparison {
+# define DEFINE_COMP_OP( op ) \
+ constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \
+ ZeroLiteralConsteval ) { \
+ return true; \
+ } \
+ constexpr friend bool operator op( ZeroLiteralConsteval, \
+ TypeWithConstevalLit0Comparison ) { \
+ return false; \
+ } \
+ /* std::orderings only have these for ==, but we add them for all \
+ operators so we can test all overloads for decomposer */ \
+ constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \
+ TypeWithConstevalLit0Comparison ) { \
+ return true; \
+ }
+
+ DEFINE_COMP_OP( < )
+ DEFINE_COMP_OP( <= )
+ DEFINE_COMP_OP( > )
+ DEFINE_COMP_OP( >= )
+ DEFINE_COMP_OP( == )
+ DEFINE_COMP_OP( != )
+
+#undef DEFINE_COMP_OP
+ };
+
+} // namespace
+
+namespace Catch {
+ template <>
+ struct capture_by_value<TypeWithConstevalLit0Comparison> : std::true_type {};
+}
+
+TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as consteval check are supported",
+ "[compilation][approvals]" ) {
+ REQUIRE( TypeWithConstevalLit0Comparison{} < 0 );
+ REQUIRE_FALSE( 0 < TypeWithConstevalLit0Comparison{} );
+ REQUIRE( TypeWithConstevalLit0Comparison{} <= 0 );
+ REQUIRE_FALSE( 0 <= TypeWithConstevalLit0Comparison{} );
+
+ REQUIRE( TypeWithConstevalLit0Comparison{} > 0 );
+ REQUIRE_FALSE( 0 > TypeWithConstevalLit0Comparison{} );
+ REQUIRE( TypeWithConstevalLit0Comparison{} >= 0 );
+ REQUIRE_FALSE( 0 >= TypeWithConstevalLit0Comparison{} );
+
+ REQUIRE( TypeWithConstevalLit0Comparison{} == 0 );
+ REQUIRE_FALSE( 0 == TypeWithConstevalLit0Comparison{} );
+ REQUIRE( TypeWithConstevalLit0Comparison{} != 0 );
+ REQUIRE_FALSE( 0 != TypeWithConstevalLit0Comparison{} );
+}
+
+// We check all comparison ops to test, even though orderings, the primary
+// motivation for this functionality, only have self-comparison (and thus
+// have the ambiguity issue) for `==` and `!=`.
+TEST_CASE( "Comparing const instances of type registered with capture_by_value",
+ "[regression][approvals][compilation]" ) {
+ SECTION("Type with consteval-int constructor") {
+ auto const const_Lit0Type_1 = TypeWithConstevalLit0Comparison{};
+ auto const const_Lit0Type_2 = TypeWithConstevalLit0Comparison{};
+ REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 );
+ }
+ SECTION("Type with constexpr-int constructor") {
+ auto const const_Lit0Type_1 = TypeWithLit0Comparisons{};
+ auto const const_Lit0Type_2 = TypeWithLit0Comparisons{};
+ REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 );
+ REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 );
+ }
+}
+
+#endif // C++20 consteval
+
+
+namespace {
+ struct MultipleImplicitConstructors {
+ MultipleImplicitConstructors( double ) {}
+ MultipleImplicitConstructors( int64_t ) {}
+ bool operator==( MultipleImplicitConstructors ) const { return true; }
+ bool operator!=( MultipleImplicitConstructors ) const { return true; }
+ bool operator<( MultipleImplicitConstructors ) const { return true; }
+ bool operator<=( MultipleImplicitConstructors ) const { return true; }
+ bool operator>( MultipleImplicitConstructors ) const { return true; }
+ bool operator>=( MultipleImplicitConstructors ) const { return true; }
+ };
+}
+TEST_CASE("#2571 - tests compile types that have multiple implicit constructors from lit 0",
+ "[compilation][approvals]") {
+ MultipleImplicitConstructors mic1( 0.0 );
+ MultipleImplicitConstructors mic2( 0.0 );
+ REQUIRE( mic1 == mic2 );
+ REQUIRE( mic1 != mic2 );
+ REQUIRE( mic1 < mic2 );
+ REQUIRE( mic1 <= mic2 );
+ REQUIRE( mic1 > mic2 );
+ REQUIRE( mic1 >= mic2 );
+}
+
+#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
+// This test does not test all the related codepaths, but it is the original
+// reproducer
+TEST_CASE( "Comparing const std::weak_ordering instances must compile",
+ "[compilation][approvals][regression]" ) {
+ auto const const_ordering_1 = std::weak_ordering::less;
+ auto const const_ordering_2 = std::weak_ordering::less;
+ auto plain_ordering_1 = std::weak_ordering::less;
+ REQUIRE( const_ordering_1 == plain_ordering_1 );
+ REQUIRE( const_ordering_1 == const_ordering_2 );
+ REQUIRE( plain_ordering_1 == const_ordering_1 );
+}
+#endif
+
+// Reproduce issue with yaml-cpp iterators, where the `const_iterator`
+// for Node type has `const T` as the value_type. This is wrong for
+// multitude of reasons, but there might be other libraries in the wild
+// that share this issue, and the workaround needed to support
+// `from_range(iter, iter)` helper with those libraries is easy enough.
+class HasBadIterator {
+ std::array<int, 10> m_arr{};
+
+public:
+ class iterator {
+ const int* m_ptr = nullptr;
+
+ public:
+ iterator( const int* ptr ): m_ptr( ptr ) {}
+
+ using difference_type = std::ptrdiff_t;
+ using value_type = const int;
+ using pointer = const int*;
+ using reference = const int&;
+ using iterator_category = std::input_iterator_tag;
+
+ iterator& operator++() {
+ ++m_ptr;
+ return *this;
+ }
+
+ iterator operator++( int ) {
+ auto ret( *this );
+ ++( *this );
+ return ret;
+ }
+
+ friend bool operator==( iterator lhs, iterator rhs ) {
+ return lhs.m_ptr == rhs.m_ptr;
+ }
+ friend bool operator!=( iterator lhs, iterator rhs ) {
+ return !( lhs == rhs );
+ }
+
+ int operator*() const { return *m_ptr; }
+ };
+
+ iterator cbegin() const { return { m_arr.data() }; }
+ iterator cend() const { return { m_arr.data() + m_arr.size() }; }
+};
+
+TEST_CASE("from_range(iter, iter) supports const_iterators", "[generators][from-range][approvals]") {
+ using namespace Catch::Generators;
+
+ HasBadIterator data;
+ auto gen = from_range(data.cbegin(), data.cend());
+ (void)gen;
+}
diff --git a/tests/SelfTest/UsageTests/Condition.tests.cpp b/tests/SelfTest/UsageTests/Condition.tests.cpp
new file mode 100644
index 0000000..211dd3b
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Condition.tests.cpp
@@ -0,0 +1,334 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+// Wdouble-promotion is not supported until 3.8
+# if (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ > 7)
+# pragma clang diagnostic ignored "-Wdouble-promotion"
+# endif
+#endif
+
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+using Catch::Approx;
+
+#include <string>
+#include <limits>
+#include <cstdint>
+
+namespace {
+
+ struct TestData {
+ int int_seven = 7;
+ std::string str_hello = "hello";
+ float float_nine_point_one = 9.1f;
+ double double_pi = 3.1415926535;
+ };
+
+ static const char* returnsConstNull() { return nullptr; }
+ static char* returnsNull() { return nullptr; }
+
+} // end unnamed namespace
+
+// The "failing" tests all use the CHECK macro, which continues if the specific test fails.
+// This allows us to see all results, even if an earlier check fails
+
+// Equality tests
+TEST_CASE( "Equality checks that should succeed" )
+{
+ TestData data;
+
+ REQUIRE( data.int_seven == 7 );
+ REQUIRE( data.float_nine_point_one == Approx( 9.1f ) );
+ REQUIRE( data.double_pi == Approx( 3.1415926535 ) );
+ REQUIRE( data.str_hello == "hello" );
+ REQUIRE( "hello" == data.str_hello );
+ REQUIRE( data.str_hello.size() == 5 );
+
+ double x = 1.1 + 0.1 + 0.1;
+ REQUIRE( x == Approx( 1.3 ) );
+}
+
+TEST_CASE( "Equality checks that should fail", "[.][failing][!mayfail]" )
+{
+ TestData data;
+
+ CHECK( data.int_seven == 6 );
+ CHECK( data.int_seven == 8 );
+ CHECK( data.int_seven == 0 );
+ CHECK( data.float_nine_point_one == Approx( 9.11f ) );
+ CHECK( data.float_nine_point_one == Approx( 9.0f ) );
+ CHECK( data.float_nine_point_one == Approx( 1 ) );
+ CHECK( data.float_nine_point_one == Approx( 0 ) );
+ CHECK( data.double_pi == Approx( 3.1415 ) );
+ CHECK( data.str_hello == "goodbye" );
+ CHECK( data.str_hello == "hell" );
+ CHECK( data.str_hello == "hello1" );
+ CHECK( data.str_hello.size() == 6 );
+
+ double x = 1.1 + 0.1 + 0.1;
+ CHECK( x == Approx( 1.301 ) );
+}
+
+// Needed to test junit reporter's handling of mayfail test cases and sections
+TEST_CASE("Mayfail test case with nested sections", "[!mayfail]") {
+ SECTION("A") {
+ SECTION("1") { FAIL(); }
+ SECTION("2") { FAIL(); }
+ }
+ SECTION("B") {
+ SECTION("1") { FAIL(); }
+ SECTION("2") { FAIL(); }
+ }
+}
+
+
+TEST_CASE( "Inequality checks that should succeed" )
+{
+ TestData data;
+
+ REQUIRE( data.int_seven != 6 );
+ REQUIRE( data.int_seven != 8 );
+ REQUIRE( data.float_nine_point_one != Approx( 9.11f ) );
+ REQUIRE( data.float_nine_point_one != Approx( 9.0f ) );
+ REQUIRE( data.float_nine_point_one != Approx( 1 ) );
+ REQUIRE( data.float_nine_point_one != Approx( 0 ) );
+ REQUIRE( data.double_pi != Approx( 3.1415 ) );
+ REQUIRE( data.str_hello != "goodbye" );
+ REQUIRE( data.str_hello != "hell" );
+ REQUIRE( data.str_hello != "hello1" );
+ REQUIRE( data.str_hello.size() != 6 );
+}
+
+TEST_CASE( "Inequality checks that should fail", "[.][failing][!shouldfail]" )
+{
+ TestData data;
+
+ CHECK( data.int_seven != 7 );
+ CHECK( data.float_nine_point_one != Approx( 9.1f ) );
+ CHECK( data.double_pi != Approx( 3.1415926535 ) );
+ CHECK( data.str_hello != "hello" );
+ CHECK( data.str_hello.size() != 5 );
+}
+
+// Ordering comparison tests
+TEST_CASE( "Ordering comparison checks that should succeed" )
+{
+ TestData data;
+
+ REQUIRE( data.int_seven < 8 );
+ REQUIRE( data.int_seven > 6 );
+ REQUIRE( data.int_seven > 0 );
+ REQUIRE( data.int_seven > -1 );
+
+ REQUIRE( data.int_seven >= 7 );
+ REQUIRE( data.int_seven >= 6 );
+ REQUIRE( data.int_seven <= 7 );
+ REQUIRE( data.int_seven <= 8 );
+
+ REQUIRE( data.float_nine_point_one > 9 );
+ REQUIRE( data.float_nine_point_one < 10 );
+ REQUIRE( data.float_nine_point_one < 9.2 );
+
+ REQUIRE( data.str_hello <= "hello" );
+ REQUIRE( data.str_hello >= "hello" );
+
+ REQUIRE( data.str_hello < "hellp" );
+ REQUIRE( data.str_hello < "zebra" );
+ REQUIRE( data.str_hello > "hellm" );
+ REQUIRE( data.str_hello > "a" );
+}
+
+TEST_CASE( "Ordering comparison checks that should fail", "[.][failing]" )
+{
+ TestData data;
+
+ CHECK( data.int_seven > 7 );
+ CHECK( data.int_seven < 7 );
+ CHECK( data.int_seven > 8 );
+ CHECK( data.int_seven < 6 );
+ CHECK( data.int_seven < 0 );
+ CHECK( data.int_seven < -1 );
+
+ CHECK( data.int_seven >= 8 );
+ CHECK( data.int_seven <= 6 );
+
+ CHECK( data.float_nine_point_one < 9 );
+ CHECK( data.float_nine_point_one > 10 );
+ CHECK( data.float_nine_point_one > 9.2 );
+
+ CHECK( data.str_hello > "hello" );
+ CHECK( data.str_hello < "hello" );
+ CHECK( data.str_hello > "hellp" );
+ CHECK( data.str_hello > "z" );
+ CHECK( data.str_hello < "hellm" );
+ CHECK( data.str_hello < "a" );
+
+ CHECK( data.str_hello >= "z" );
+ CHECK( data.str_hello <= "a" );
+}
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+
+// Comparisons with int literals
+TEST_CASE( "Comparisons with int literals don't warn when mixing signed/ unsigned" )
+{
+ int i = 1;
+ unsigned int ui = 2;
+ long l = 3;
+ unsigned long ul = 4;
+ char c = 5;
+ unsigned char uc = 6;
+
+ REQUIRE( i == 1 );
+ REQUIRE( ui == 2 );
+ REQUIRE( l == 3 );
+ REQUIRE( ul == 4 );
+ REQUIRE( c == 5 );
+ REQUIRE( uc == 6 );
+
+ REQUIRE( 1 == i );
+ REQUIRE( 2 == ui );
+ REQUIRE( 3 == l );
+ REQUIRE( 4 == ul );
+ REQUIRE( 5 == c );
+ REQUIRE( 6 == uc );
+
+ REQUIRE( (std::numeric_limits<uint32_t>::max)() > ul );
+}
+
+// Disable warnings about sign conversions for the next two tests
+// (as we are deliberately invoking them)
+// - Currently only disabled for GCC/ LLVM. Should add VC++ too
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+#ifdef _MSC_VER
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+TEST_CASE( "comparisons between int variables" )
+{
+ long long_var = 1L;
+ unsigned char unsigned_char_var = 1;
+ unsigned short unsigned_short_var = 1;
+ unsigned int unsigned_int_var = 1;
+ unsigned long unsigned_long_var = 1L;
+
+ REQUIRE( long_var == unsigned_char_var );
+ REQUIRE( long_var == unsigned_short_var );
+ REQUIRE( long_var == unsigned_int_var );
+ REQUIRE( long_var == unsigned_long_var );
+}
+
+TEST_CASE( "comparisons between const int variables" )
+{
+ const unsigned char unsigned_char_var = 1;
+ const unsigned short unsigned_short_var = 1;
+ const unsigned int unsigned_int_var = 1;
+ const unsigned long unsigned_long_var = 1L;
+
+ REQUIRE( unsigned_char_var == 1 );
+ REQUIRE( unsigned_short_var == 1 );
+ REQUIRE( unsigned_int_var == 1 );
+ REQUIRE( unsigned_long_var == 1 );
+}
+
+TEST_CASE( "Comparisons between unsigned ints and negative signed ints match c++ standard behaviour" )
+{
+ CHECK( ( -1 > 2u ) );
+ CHECK( -1 > 2u );
+
+ CHECK( ( 2u < -1 ) );
+ CHECK( 2u < -1 );
+
+ const int minInt = (std::numeric_limits<int>::min)();
+ CHECK( ( minInt > 2u ) );
+ CHECK( minInt > 2u );
+}
+
+TEST_CASE( "Comparisons between ints where one side is computed" )
+{
+ CHECK( 54 == 6*9 );
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+TEST_CASE( "Pointers can be compared to null" )
+{
+ TestData* p = nullptr;
+ TestData* pNULL = nullptr;
+
+ REQUIRE( p == nullptr );
+ REQUIRE( p == pNULL );
+
+ TestData data;
+ p = &data;
+
+ REQUIRE( p != nullptr );
+
+ const TestData* cp = p;
+ REQUIRE( cp != nullptr );
+
+ const TestData* const cpc = p;
+ REQUIRE( cpc != nullptr );
+
+ REQUIRE( returnsNull() == nullptr );
+ REQUIRE( returnsConstNull() == nullptr );
+
+ REQUIRE( nullptr != p );
+}
+
+// Not (!) tests
+// The problem with the ! operator is that it has right-to-left associativity.
+// This means we can't isolate it when we decompose. The simple REQUIRE( !false ) form, therefore,
+// cannot have the operand value extracted. The test will work correctly, and the situation
+// is detected and a warning issued.
+// An alternative form of the macros (CHECK_FALSE and REQUIRE_FALSE) can be used instead to capture
+// the operand value.
+TEST_CASE( "'Not' checks that should succeed" )
+{
+ bool falseValue = false;
+
+ REQUIRE( false == false );
+ REQUIRE( true == true );
+ REQUIRE( !false );
+ REQUIRE_FALSE( false );
+
+ REQUIRE( !falseValue );
+ REQUIRE_FALSE( falseValue );
+
+ REQUIRE( !(1 == 2) );
+ REQUIRE_FALSE( 1 == 2 );
+}
+
+TEST_CASE( "'Not' checks that should fail", "[.][failing]" )
+{
+ bool trueValue = true;
+
+ CHECK( false != false );
+ CHECK( true != true );
+ CHECK( !true );
+ CHECK_FALSE( true );
+
+ CHECK( !trueValue );
+ CHECK_FALSE( trueValue );
+
+ CHECK( !(1 == 1) );
+ CHECK_FALSE( 1 == 1 );
+}
diff --git a/tests/SelfTest/UsageTests/Decomposition.tests.cpp b/tests/SelfTest/UsageTests/Decomposition.tests.cpp
new file mode 100644
index 0000000..e92f740
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Decomposition.tests.cpp
@@ -0,0 +1,41 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <iostream>
+#include <cstdio>
+
+namespace {
+
+struct truthy {
+ truthy(bool b):m_value(b){}
+ operator bool() const {
+ return false;
+ }
+ bool m_value;
+};
+
+std::ostream& operator<<(std::ostream& o, truthy) {
+ o << "Hey, its truthy!";
+ return o;
+}
+
+} // end anonymous namespace
+
+#include <catch2/catch_test_macros.hpp>
+
+TEST_CASE( "Reconstruction should be based on stringification: #914" , "[Decomposition][failing][.]") {
+ CHECK(truthy(false));
+}
+
+TEST_CASE("#1005: Comparing pointer to int and long (NULL can be either on various systems)", "[Decomposition][approvals]") {
+ FILE* fptr = nullptr;
+ REQUIRE( fptr == 0 );
+ REQUIRE_FALSE( fptr != 0 );
+ REQUIRE( fptr == 0l );
+ REQUIRE_FALSE( fptr != 0l );
+}
diff --git a/tests/SelfTest/UsageTests/EnumToString.tests.cpp b/tests/SelfTest/UsageTests/EnumToString.tests.cpp
new file mode 100644
index 0000000..268a7ca
--- /dev/null
+++ b/tests/SelfTest/UsageTests/EnumToString.tests.cpp
@@ -0,0 +1,108 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/internal/catch_enum_values_registry.hpp>
+
+
+namespace {
+// Enum without user-provided stream operator
+enum Enum1 { Enum1Value0, Enum1Value1 };
+
+// Enum with user-provided stream operator
+enum Enum2 { Enum2Value0, Enum2Value1 };
+
+std::ostream& operator<<( std::ostream& os, Enum2 v ) {
+ return os << "E2{" << static_cast<int>(v) << "}";
+}
+} // end anonymous namespace
+
+TEST_CASE( "toString(enum)", "[toString][enum]" ) {
+ Enum1 e0 = Enum1Value0;
+ CHECK( ::Catch::Detail::stringify(e0) == "0" );
+ Enum1 e1 = Enum1Value1;
+ CHECK( ::Catch::Detail::stringify(e1) == "1" );
+}
+
+TEST_CASE( "toString(enum w/operator<<)", "[toString][enum]" ) {
+ Enum2 e0 = Enum2Value0;
+ CHECK( ::Catch::Detail::stringify(e0) == "E2{0}" );
+ Enum2 e1 = Enum2Value1;
+ CHECK( ::Catch::Detail::stringify(e1) == "E2{1}" );
+}
+
+// Enum class without user-provided stream operator
+namespace {
+enum class EnumClass1 { EnumClass1Value0, EnumClass1Value1 };
+
+// Enum class with user-provided stream operator
+enum class EnumClass2 { EnumClass2Value0, EnumClass2Value1 };
+
+std::ostream& operator<<( std::ostream& os, EnumClass2 e2 ) {
+ switch( static_cast<int>( e2 ) ) {
+ case static_cast<int>( EnumClass2::EnumClass2Value0 ):
+ return os << "E2/V0";
+ case static_cast<int>( EnumClass2::EnumClass2Value1 ):
+ return os << "E2/V1";
+ default:
+ return os << "Unknown enum value " << static_cast<int>( e2 );
+ }
+}
+
+} // end anonymous namespace
+
+TEST_CASE( "toString(enum class)", "[toString][enum][enumClass]" ) {
+ EnumClass1 e0 = EnumClass1::EnumClass1Value0;
+ CHECK( ::Catch::Detail::stringify(e0) == "0" );
+ EnumClass1 e1 = EnumClass1::EnumClass1Value1;
+ CHECK( ::Catch::Detail::stringify(e1) == "1" );
+}
+
+
+TEST_CASE( "toString(enum class w/operator<<)", "[toString][enum][enumClass]" ) {
+ EnumClass2 e0 = EnumClass2::EnumClass2Value0;
+ CHECK( ::Catch::Detail::stringify(e0) == "E2/V0" );
+ EnumClass2 e1 = EnumClass2::EnumClass2Value1;
+ CHECK( ::Catch::Detail::stringify(e1) == "E2/V1" );
+
+ auto e3 = static_cast<EnumClass2>(10);
+ CHECK( ::Catch::Detail::stringify(e3) == "Unknown enum value 10" );
+}
+
+enum class EnumClass3 { Value1, Value2, Value3, Value4 };
+
+CATCH_REGISTER_ENUM( EnumClass3, EnumClass3::Value1, EnumClass3::Value2, EnumClass3::Value3 )
+
+
+TEST_CASE( "Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM" ) {
+ using Catch::Detail::stringify;
+ REQUIRE( stringify( EnumClass3::Value1 ) == "Value1" );
+ REQUIRE( stringify( EnumClass3::Value2 ) == "Value2" );
+ REQUIRE( stringify( EnumClass3::Value3 ) == "Value3" );
+ REQUIRE( stringify( EnumClass3::Value4 ) == "{** unexpected enum value **}" );
+
+ EnumClass3 ec3 = EnumClass3::Value2;
+ REQUIRE( stringify( ec3 ) == "Value2" );
+}
+
+namespace Bikeshed {
+ enum class Colours { Red, Green, Blue };
+}
+
+// Important!: This macro must appear at top level scope - not inside a namespace
+// You can fully qualify the names, or use a using if you prefer
+CATCH_REGISTER_ENUM( Bikeshed::Colours,
+ Bikeshed::Colours::Red,
+ Bikeshed::Colours::Green,
+ Bikeshed::Colours::Blue )
+
+TEST_CASE( "Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM" ) {
+ using Catch::Detail::stringify;
+ REQUIRE( stringify( Bikeshed::Colours::Red ) == "Red" );
+ REQUIRE( stringify( Bikeshed::Colours::Blue ) == "Blue" );
+}
diff --git a/tests/SelfTest/UsageTests/Exception.tests.cpp b/tests/SelfTest/UsageTests/Exception.tests.cpp
new file mode 100644
index 0000000..7c6b0c8
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Exception.tests.cpp
@@ -0,0 +1,204 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_translate_exception.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
+#include <string>
+#include <stdexcept>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4702) // Unreachable code -- unconditional throws and so on
+#endif
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+#pragma clang diagnostic ignored "-Wunreachable-code-return"
+#endif
+
+namespace {
+
+ int thisThrows() {
+ throw std::domain_error("expected exception");
+ return 1;
+ }
+
+ int thisDoesntThrow() {
+ return 0;
+ }
+
+ class CustomException {
+ public:
+ explicit CustomException(const std::string& msg)
+ : m_msg(msg) {}
+
+ std::string const& getMessage() const {
+ return m_msg;
+ }
+
+ private:
+ std::string m_msg;
+ };
+
+ class CustomStdException : public std::exception {
+ public:
+ explicit CustomStdException(const std::string& msg)
+ : m_msg(msg) {}
+ ~CustomStdException() noexcept override = default;
+
+ CustomStdException( CustomStdException const& ) = default;
+ CustomStdException& operator=( CustomStdException const& ) = default;
+
+ std::string const& getMessage() const {
+ return m_msg;
+ }
+
+ private:
+ std::string m_msg;
+ };
+
+ [[noreturn]] void throwCustom() {
+ throw CustomException("custom exception - not std");
+ }
+
+}
+
+TEST_CASE( "When checked exceptions are thrown they can be expected or unexpected", "[!throws]" ) {
+ REQUIRE_THROWS_AS( thisThrows(), std::domain_error );
+ REQUIRE_NOTHROW( thisDoesntThrow() );
+ REQUIRE_THROWS( thisThrows() );
+}
+
+TEST_CASE( "Expected exceptions that don't throw or unexpected exceptions fail the test", "[.][failing][!throws]" ) {
+ CHECK_THROWS_AS( thisThrows(), std::string );
+ CHECK_THROWS_AS( thisDoesntThrow(), std::domain_error );
+ CHECK_NOTHROW( thisThrows() );
+}
+
+TEST_CASE( "When unchecked exceptions are thrown directly they are always failures", "[.][failing][!throws]" ) {
+ throw std::domain_error( "unexpected exception" );
+}
+
+TEST_CASE( "An unchecked exception reports the line of the last assertion", "[.][failing][!throws]" ) {
+ CHECK( 1 == 1 );
+ throw std::domain_error( "unexpected exception" );
+}
+
+TEST_CASE( "When unchecked exceptions are thrown from sections they are always failures", "[.][failing][!throws]" ) {
+ SECTION( "section name" ) {
+ throw std::domain_error("unexpected exception");
+ }
+}
+
+TEST_CASE( "When unchecked exceptions are thrown from functions they are always failures", "[.][failing][!throws]" ) {
+ CHECK( thisThrows() == 0 );
+}
+
+TEST_CASE( "When unchecked exceptions are thrown during a REQUIRE the test should abort fail", "[.][failing][!throws]" ) {
+ REQUIRE( thisThrows() == 0 );
+ FAIL( "This should never happen" );
+}
+
+TEST_CASE( "When unchecked exceptions are thrown during a CHECK the test should continue", "[.][failing][!throws]" ) {
+ try {
+ CHECK(thisThrows() == 0);
+ }
+ catch(...) {
+ FAIL( "This should never happen" );
+ }
+}
+
+TEST_CASE( "When unchecked exceptions are thrown, but caught, they do not affect the test", "[!throws]" ) {
+ try {
+ throw std::domain_error( "unexpected exception" );
+ }
+ catch(...) {} // NOLINT(bugprone-empty-catch)
+}
+
+
+CATCH_TRANSLATE_EXCEPTION( CustomException const& ex ) {
+ return ex.getMessage();
+}
+
+CATCH_TRANSLATE_EXCEPTION( CustomStdException const& ex ) {
+ return ex.getMessage();
+}
+
+CATCH_TRANSLATE_EXCEPTION( double const& ex ) {
+ return Catch::Detail::stringify( ex );
+}
+
+TEST_CASE("Non-std exceptions can be translated", "[.][failing][!throws]" ) {
+ throw CustomException( "custom exception" );
+}
+
+TEST_CASE("Custom std-exceptions can be custom translated", "[.][failing][!throws]" ) {
+ throw CustomStdException( "custom std exception" );
+}
+
+TEST_CASE( "Custom exceptions can be translated when testing for nothrow", "[.][failing][!throws]" ) {
+ REQUIRE_NOTHROW( throwCustom() );
+}
+
+TEST_CASE( "Custom exceptions can be translated when testing for throwing as something else", "[.][failing][!throws]" ) {
+ REQUIRE_THROWS_AS( throwCustom(), std::exception );
+}
+
+TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) {
+ throw double( 3.14 ); // NOLINT(readability-redundant-casting): the type is important here, so we want to be explicit
+}
+
+TEST_CASE("Thrown string literals are translated", "[.][failing][!throws]") {
+ throw "For some reason someone is throwing a string literal!";
+}
+
+TEST_CASE("thrown std::strings are translated", "[.][failing][!throws]") {
+ throw std::string{ "Why would you throw a std::string?" };
+}
+
+
+TEST_CASE( "Exception messages can be tested for", "[!throws]" ) {
+ using namespace Catch::Matchers;
+ SECTION( "exact match" )
+ REQUIRE_THROWS_WITH( thisThrows(), "expected exception" );
+ SECTION( "different case" )
+ REQUIRE_THROWS_WITH( thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) );
+ SECTION( "wildcarded" ) {
+ REQUIRE_THROWS_WITH( thisThrows(), StartsWith( "expected" ) );
+ REQUIRE_THROWS_WITH( thisThrows(), EndsWith( "exception" ) );
+ REQUIRE_THROWS_WITH( thisThrows(), ContainsSubstring( "except" ) );
+ REQUIRE_THROWS_WITH( thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) );
+ }
+}
+
+TEST_CASE( "Mismatching exception messages failing the test", "[.][failing][!throws]" ) {
+ REQUIRE_THROWS_WITH( thisThrows(), "expected exception" );
+ REQUIRE_THROWS_WITH( thisThrows(), "should fail" );
+ REQUIRE_THROWS_WITH( thisThrows(), "expected exception" );
+}
+
+TEST_CASE( "#748 - captures with unexpected exceptions", "[.][failing][!throws][!shouldfail]" ) {
+ int answer = 42;
+ CAPTURE( answer );
+ // the message should be printed on the first two sections but not on the third
+ SECTION( "outside assertions" ) {
+ thisThrows();
+ }
+ SECTION( "inside REQUIRE_NOTHROW" ) {
+ REQUIRE_NOTHROW( thisThrows() );
+ }
+ SECTION( "inside REQUIRE_THROWS" ) {
+ REQUIRE_THROWS( thisThrows() );
+ }
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
diff --git a/tests/SelfTest/UsageTests/Generators.tests.cpp b/tests/SelfTest/UsageTests/Generators.tests.cpp
new file mode 100644
index 0000000..f04cf4f
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Generators.tests.cpp
@@ -0,0 +1,323 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generator_exception.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+#include <catch2/generators/catch_generators_range.hpp>
+
+#include <cstring>
+
+
+// Generators and sections can be nested freely
+TEST_CASE("Generators -- simple", "[generators]") {
+ auto i = GENERATE(1, 2, 3);
+ SECTION("one") {
+ auto j = GENERATE(values({ -3, -2, -1 }));
+ REQUIRE(j < i);
+ }
+
+ SECTION("two") {
+ // You can also explicitly set type for generators via Catch::Generators::as
+ auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
+ REQUIRE(4u * i > str.size());
+ }
+}
+
+// You can create a cartesian-product of generators by creating multiple ones
+TEST_CASE("3x3x3 ints", "[generators]") {
+ auto x = GENERATE(1, 2, 3);
+ auto y = GENERATE(4, 5, 6);
+ auto z = GENERATE(7, 8, 9);
+ // These assertions will be run 27 times (3x3x3)
+ CHECK(x < y);
+ CHECK(y < z);
+ REQUIRE(x < z);
+}
+
+// You can also create data tuples
+TEST_CASE("tables", "[generators]") {
+ // Note that this will not compile with libstdc++ older than libstdc++6
+ // See https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
+ // for possible workarounds
+ // auto data = GENERATE(table<char const*, int>({
+ // {"first", 5},
+ // {"second", 6},
+ // {"third", 5},
+ // {"etc...", 6}
+ // }));
+
+ // Workaround for the libstdc++ bug mentioned above
+ using tuple_type = std::tuple<char const*, int>;
+ auto data = GENERATE(table<char const*, int>({
+ tuple_type{"first", 5},
+ tuple_type{"second", 6},
+ tuple_type{"third", 5},
+ tuple_type{"etc...", 6}
+ }));
+
+ REQUIRE(strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)));
+}
+
+
+#ifdef __cpp_structured_bindings
+
+// Structured bindings make the table utility much nicer to use
+TEST_CASE( "strlen2", "[approvals][generators]" ) {
+ using tuple_type = std::tuple<std::string, int>; // see above workaround
+ auto [test_input, expected] =
+ GENERATE( table<std::string, size_t>( { tuple_type{ "one", 3 },
+ tuple_type{ "two", 3 },
+ tuple_type{ "three", 5 },
+ tuple_type{ "four", 4 } } ) );
+
+ REQUIRE( test_input.size() == expected );
+}
+#endif
+
+
+// An alternate way of doing data tables without structured bindings
+struct Data { std::string str; size_t len; };
+
+TEST_CASE( "strlen3", "[generators]" ) {
+ auto data = GENERATE( values<Data>({
+ {"one", 3},
+ {"two", 3},
+ {"three", 5},
+ {"four", 4}
+ }));
+
+ REQUIRE( data.str.size() == data.len );
+}
+
+
+
+#ifdef __cpp_structured_bindings
+
+// Based on example from https://docs.cucumber.io/gherkin/reference/#scenario-outline
+// (thanks to https://github.com/catchorg/Catch2/issues/850#issuecomment-399504851)
+
+// Note that GIVEN, WHEN, and THEN now forward onto DYNAMIC_SECTION instead of SECTION.
+// DYNAMIC_SECTION takes its name as a stringstream-style expression, so can be formatted using
+// variables in scope - such as the generated variables here. This reads quite nicely in the
+// test name output (the full scenario description).
+
+static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
+
+SCENARIO("Eating cucumbers", "[generators][approvals]") {
+ using tuple_type = std::tuple<int, int, int>;
+ auto [start, eat, left] = GENERATE( table<int, int, int>(
+ { tuple_type{ 12, 5, 7 }, tuple_type{ 20, 5, 15 } } ) );
+
+ GIVEN( "there are " << start << " cucumbers" )
+ WHEN( "I eat " << eat << " cucumbers" )
+ THEN( "I should have " << left << " cucumbers" ) {
+ REQUIRE( eatCucumbers( start, eat ) == left );
+ }
+}
+#endif
+
+// There are also some generic generator manipulators
+TEST_CASE("Generators -- adapters", "[generators][generic]") {
+ // TODO: This won't work yet, introduce GENERATE_VAR?
+ //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 });
+ SECTION("Filtering by predicate") {
+ SECTION("Basic usage") {
+ // This filters out all odd (false) numbers, giving [2, 4, 6]
+ auto i = GENERATE(filter([] (int val) { return val % 2 == 0; }, values({ 1, 2, 3, 4, 5, 6 })));
+ REQUIRE(i % 2 == 0);
+ }
+ SECTION("Throws if there are no matching values") {
+ using namespace Catch::Generators;
+ REQUIRE_THROWS_AS(filter([] (int) {return false; }, value(1)), Catch::GeneratorException);
+ }
+ }
+ SECTION("Shortening a range") {
+ // This takes the first 3 elements from the values, giving back [1, 2, 3]
+ auto i = GENERATE(take(3, values({ 1, 2, 3, 4, 5, 6 })));
+ REQUIRE(i < 4);
+ }
+ SECTION("Transforming elements") {
+ SECTION("Same type") {
+ // This doubles values [1, 2, 3] into [2, 4, 6]
+ auto i = GENERATE(map([] (int val) { return val * 2; }, values({ 1, 2, 3 })));
+ REQUIRE(i % 2 == 0);
+ }
+ SECTION("Different type") {
+ // This takes a generator that returns ints and maps them into strings
+ auto i = GENERATE(map<std::string>([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 })));
+ REQUIRE(i.size() == 1);
+ }
+ SECTION("Different deduced type") {
+ // This takes a generator that returns ints and maps them into strings
+ auto i = GENERATE(map([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 })));
+ REQUIRE(i.size() == 1);
+ }
+ }
+ SECTION("Repeating a generator") {
+ // This will return values [1, 2, 3, 1, 2, 3]
+ auto j = GENERATE(repeat(2, values({ 1, 2, 3 })));
+ REQUIRE(j > 0);
+ }
+ SECTION("Chunking a generator into sized pieces") {
+ SECTION("Number of elements in source is divisible by chunk size") {
+ auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3, 3 })));
+ REQUIRE(chunk2.size() == 2);
+ REQUIRE(chunk2.front() == chunk2.back());
+ }
+ SECTION("Number of elements in source is not divisible by chunk size") {
+ auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3 })));
+ REQUIRE(chunk2.size() == 2);
+ REQUIRE(chunk2.front() == chunk2.back());
+ REQUIRE(chunk2.front() < 3);
+ }
+ SECTION("Chunk size of zero") {
+ auto chunk2 = GENERATE(take(3, chunk(0, value(1))));
+ REQUIRE(chunk2.size() == 0);
+ }
+ SECTION("Throws on too small generators") {
+ using namespace Catch::Generators;
+ REQUIRE_THROWS_AS(chunk(2, value(1)), Catch::GeneratorException);
+ }
+ }
+}
+
+// Note that because of the non-reproducibility of distributions,
+// anything involving the random generators cannot be part of approvals
+TEST_CASE("Random generator", "[generators][approvals]") {
+ SECTION("Infer int from integral arguments") {
+ auto val = GENERATE(take(4, random(0, 1)));
+ STATIC_REQUIRE(std::is_same<decltype(val), int>::value);
+ REQUIRE(0 <= val);
+ REQUIRE(val <= 1);
+ }
+ SECTION("Infer double from double arguments") {
+ auto val = GENERATE(take(4, random(0., 1.)));
+ STATIC_REQUIRE(std::is_same<decltype(val), double>::value);
+ REQUIRE(0. <= val);
+ REQUIRE(val < 1);
+ }
+}
+
+
+TEST_CASE("Nested generators and captured variables", "[generators]") {
+ // Workaround for old libstdc++
+ using record = std::tuple<int, int>;
+ // Set up 3 ranges to generate numbers from
+ auto extent = GENERATE(table<int, int>({
+ record{3, 7},
+ record{-5, -3},
+ record{90, 100}
+ }));
+
+ auto from = std::get<0>(extent);
+ auto to = std::get<1>(extent);
+
+ auto values = GENERATE_COPY(range(from, to));
+ REQUIRE(values > -6);
+}
+
+namespace {
+ size_t call_count = 0;
+ size_t test_count = 0;
+ std::vector<int> make_data() {
+ return { 1, 3, 5, 7, 9, 11 };
+ }
+ std::vector<int> make_data_counted() {
+ ++call_count;
+ return make_data();
+ }
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+TEST_CASE("Copy and then generate a range", "[generators]") {
+ SECTION("from var and iterators") {
+ static auto data = make_data();
+
+ // It is important to notice that a generator is only initialized
+ // **once** per run. What this means is that modifying data will not
+ // modify the underlying generator.
+ auto elem = GENERATE_REF(from_range(data.begin(), data.end()));
+ REQUIRE(elem % 2 == 1);
+ }
+ SECTION("From a temporary container") {
+ auto elem = GENERATE(from_range(make_data_counted()));
+ ++test_count;
+ REQUIRE(elem % 2 == 1);
+ }
+ SECTION("Final validation") {
+ REQUIRE(call_count == 1);
+ REQUIRE(make_data().size() == test_count);
+ }
+}
+
+#if defined( __clang__ )
+# pragma clang diagnostic pop
+#endif
+
+TEST_CASE("#1913 - GENERATE inside a for loop should not keep recreating the generator", "[regression][generators]") {
+ static int counter = 0;
+ for (int i = 0; i < 3; ++i) {
+ int _ = GENERATE(1, 2);
+ (void)_;
+ ++counter;
+ }
+ // There should be at most 6 (3 * 2) counter increments
+ REQUIRE(counter < 7);
+}
+
+TEST_CASE("#1913 - GENERATEs can share a line", "[regression][generators]") {
+ int i = GENERATE(1, 2); int j = GENERATE(3, 4);
+ REQUIRE(i != j);
+}
+
+namespace {
+ class test_generator : public Catch::Generators::IGenerator<int> {
+ public:
+ [[noreturn]] explicit test_generator() {
+ // removing the following line will cause the program to terminate
+ // gracefully.
+ throw Catch::GeneratorException( "failure to init" );
+ }
+
+ auto get() const -> int const& override {
+ static constexpr int value = 1;
+ return value;
+ }
+
+ auto next() -> bool override { return false; }
+ };
+
+ static auto make_test_generator()
+ -> Catch::Generators::GeneratorWrapper<int> {
+ return { new test_generator() };
+ }
+
+} // namespace
+
+TEST_CASE( "#2615 - Throwing in constructor generator fails test case but does not abort",
+ "[!shouldfail][regression][generators]" ) {
+ // this should fail the test case, but not abort the application
+ auto sample = GENERATE( make_test_generator() );
+ // this assertion shouldn't trigger
+ REQUIRE( sample == 0 );
+}
+
+TEST_CASE( "GENERATE can combine literals and generators", "[generators]" ) {
+ auto i = GENERATE( 2,
+ 4,
+ take( 2,
+ filter( []( int val ) { return val % 2 == 0; },
+ random( -100, 100 ) ) ) );
+ REQUIRE( i % 2 == 0 );
+}
diff --git a/tests/SelfTest/UsageTests/Matchers.tests.cpp b/tests/SelfTest/UsageTests/Matchers.tests.cpp
new file mode 100644
index 0000000..7c4501c
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Matchers.tests.cpp
@@ -0,0 +1,1144 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_exception.hpp>
+#include <catch2/matchers/catch_matchers_floating_point.hpp>
+#include <catch2/matchers/catch_matchers_predicate.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+#include <catch2/matchers/catch_matchers_vector.hpp>
+#include <catch2/matchers/catch_matchers_templated.hpp>
+
+#include <algorithm>
+#include <exception>
+#include <cmath>
+#include <list>
+#include <sstream>
+
+#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<SpecialException> {
+ 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 <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 ) ) );
+ }
+ 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 ) );
+ }
+ 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<int>( { 1, 2, 3 } ) );
+ v2.push_back( 3 );
+ CHECK_THAT( v, Equals( v2 ) );
+
+ CHECK_THAT(
+ v5,
+ ( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) );
+
+ 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 ) );
+ }
+}
+
+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 );
+
+ 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.1 );
+ v4.push_back( 2.1 );
+ v4.push_back( 3.1 );
+
+ std::vector<int> 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 ) );
+ }
+}
+
+namespace {
+ struct SomeType {
+ int i;
+ friend bool operator==( SomeType lhs, SomeType rhs ) {
+ return lhs.i == rhs.i;
+ }
+ };
+} // end anonymous namespace
+
+TEST_CASE( "Vector matcher with elements without !=", "[matchers][vector][approvals]" ) {
+ std::vector<SomeType> lhs, rhs;
+ lhs.push_back( { 1 } );
+ lhs.push_back( { 2 } );
+ rhs.push_back( { 1 } );
+ rhs.push_back( { 1 } );
+
+ REQUIRE_THAT( lhs, !Equals(rhs) );
+}
+
+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 );
+ }
+ 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<uint64_t>( -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 );
+ }
+ SECTION( "IsNaN" ) {
+ REQUIRE_THAT( 1., !IsNaN() );
+ }
+}
+
+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 );
+ }
+ 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 );
+ }
+ SECTION("IsNaN") {
+ REQUIRE_THAT( 1., !IsNaN() );
+ }
+}
+
+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. ) );
+ REQUIRE_THAT( NAN, IsNaN() );
+ REQUIRE_THAT( static_cast<double>(NAN), IsNaN() );
+}
+
+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. } ) );
+ }
+ 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 ) );
+ }
+ 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<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" ) );
+}
+
+TEST_CASE( "Exception message can be matched", "[matchers][exceptions][!throws]" ) {
+ REQUIRE_THROWS_MATCHES( throwsDerivedException(),
+ DerivedException,
+ MessageMatches( StartsWith( "Derived" ) ) );
+ REQUIRE_THROWS_MATCHES( throwsDerivedException(),
+ DerivedException,
+ MessageMatches( EndsWith( "::what" ) ) );
+ REQUIRE_THROWS_MATCHES( throwsDerivedException(),
+ DerivedException,
+ MessageMatches( !StartsWith( "::what" ) ) );
+ REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ),
+ SpecialException,
+ MessageMatches( StartsWith( "Special" ) ) );
+}
+
+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" ) );
+ }
+};
+
+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<int> matcher =
+ Detail::MatchAllOf<int>{} && 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<int> matcher =
+ Detail::MatchAnyOf<int>{} || 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<CheckedTestingGenericMatcher,
+ CheckedTestingGenericMatcher>
+ 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<CheckedTestingGenericMatcher,
+ CheckedTestingGenericMatcher>
+ matcher{ first, second };
+ CHECK( matcher.match( 1 ) );
+
+ // These two assertions are the important ones
+ REQUIRE( first.matchCalled );
+ REQUIRE( !second.matchCalled );
+ }
+}
+
+template <typename Range>
+struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase {
+
+ EqualsRangeMatcher( Range const& range ): m_range{ range } {}
+
+ template <typename OtherRange> 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 <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 (string) \"1\"";
+ }
+ bool match( int i ) const { return i == 1; }
+ bool match( std::string const& 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 <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 {
+ const char* what() const noexcept override {
+ return "overloaded address-of operator of matcher was used instead of "
+ "std::addressof";
+ }
+};
+
+struct EvilCommaOperatorUsed : std::exception {
+ 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 <typename T> bool match( T&& ) const { return false; }
+};
+
+struct MatcherWasMovedOrCopied : std::exception {
+ 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& other ):
+ Catch::Matchers::MatcherGenericBase( other ) {
+ throw MatcherWasMovedOrCopied();
+ }
+ // NOLINTNEXTLINE(performance-noexcept-move-constructor)
+ [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& other ):
+ Catch::Matchers::MatcherGenericBase( CATCH_MOVE(other) ) {
+ throw MatcherWasMovedOrCopied();
+ }
+ ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher const& ) {
+ throw MatcherWasMovedOrCopied();
+ }
+ // NOLINTNEXTLINE(performance-noexcept-move-constructor)
+ ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher&& ) {
+ throw MatcherWasMovedOrCopied();
+ }
+
+ std::string describe() const override { return "always false"; }
+
+ template <typename T> 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<TestType>::denorm_min();
+
+ CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) );
+ CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) );
+}
diff --git a/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp b/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp
new file mode 100644
index 0000000..4f906b9
--- /dev/null
+++ b/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp
@@ -0,0 +1,936 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_container_properties.hpp>
+#include <catch2/matchers/catch_matchers_contains.hpp>
+#include <catch2/matchers/catch_matchers_range_equals.hpp>
+#include <catch2/matchers/catch_matchers_floating_point.hpp>
+#include <catch2/matchers/catch_matchers_quantifiers.hpp>
+#include <catch2/matchers/catch_matchers_predicate.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
+#include <helpers/range_test_helpers.hpp>
+
+#include <cmath>
+#include <list>
+#include <map>
+#include <type_traits>
+#include <vector>
+#include <memory>
+
+struct MoveOnlyTestElement {
+ int num = 0;
+ MoveOnlyTestElement(int n) :num(n) {}
+
+ MoveOnlyTestElement(MoveOnlyTestElement&& rhs) = default;
+ MoveOnlyTestElement& operator=(MoveOnlyTestElement&& rhs) = default;
+
+ friend bool operator==(MoveOnlyTestElement const& lhs, MoveOnlyTestElement const& rhs) {
+ return lhs.num == rhs.num;
+ }
+
+ friend std::ostream& operator<<(std::ostream& out, MoveOnlyTestElement const& elem) {
+ out << elem.num;
+ return out;
+ }
+};
+
+TEST_CASE("Basic use of the Contains range matcher", "[matchers][templated][contains]") {
+ using Catch::Matchers::Contains;
+
+ SECTION("Different argument ranges, same element type, default comparison") {
+ std::array<int, 3> a{ { 1,2,3 } };
+ std::vector<int> b{ 0,1,2 };
+ std::list<int> c{ 4,5,6 };
+
+ // A contains 1
+ REQUIRE_THAT(a, Contains(1));
+ // B contains 1
+ REQUIRE_THAT(b, Contains(1));
+ // C does not contain 1
+ REQUIRE_THAT(c, !Contains(1));
+ }
+
+ SECTION("Different argument ranges, same element type, custom comparison") {
+ std::array<int, 3> a{ { 1,2,3 } };
+ std::vector<int> b{ 0,1,2 };
+ std::list<int> c{ 4,5,6 };
+
+ auto close_enough = [](int lhs, int rhs) { return std::abs(lhs - rhs) <= 1; };
+
+ // A contains 1, which is "close enough" to 0
+ REQUIRE_THAT(a, Contains(0, close_enough));
+ // B contains 0 directly
+ REQUIRE_THAT(b, Contains(0, close_enough));
+ // C does not contain anything "close enough" to 0
+ REQUIRE_THAT(c, !Contains(0, close_enough));
+ }
+
+ SECTION("Different element type, custom comparisons") {
+ std::array<std::string, 3> a{ { "abc", "abcd" , "abcde" } };
+
+ REQUIRE_THAT(a, Contains(4, [](auto&& lhs, size_t sz) {
+ return lhs.size() == sz;
+ }));
+ }
+
+ SECTION("Can handle type that requires ADL-found free function begin and end") {
+ unrelated::needs_ADL_begin<int> in{1, 2, 3, 4, 5};
+
+ REQUIRE_THAT(in, Contains(1));
+ REQUIRE_THAT(in, !Contains(8));
+ }
+
+ SECTION("Initialization with move only types") {
+ std::array<MoveOnlyTestElement, 3> in{ { MoveOnlyTestElement{ 1 }, MoveOnlyTestElement{ 2 }, MoveOnlyTestElement{ 3 } } };
+
+ REQUIRE_THAT(in, Contains(MoveOnlyTestElement{ 2 }));
+ REQUIRE_THAT(in, !Contains(MoveOnlyTestElement{ 9 }));
+ }
+
+ SECTION("Matching using matcher") {
+ std::array<double, 4> in{ {1, 2, 3} };
+
+ REQUIRE_THAT(in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5)));
+ }
+}
+
+namespace {
+
+ struct has_empty {
+ bool empty() const { return false; }
+ };
+
+} // end unnamed namespace
+
+TEST_CASE("Basic use of the Empty range matcher", "[matchers][templated][empty]") {
+ using Catch::Matchers::IsEmpty;
+ SECTION("Simple, std-provided containers") {
+ std::array<int, 0> empty_array{};
+ std::array<double, 1> non_empty_array{};
+ REQUIRE_THAT(empty_array, IsEmpty());
+ REQUIRE_THAT(non_empty_array, !IsEmpty());
+
+ std::vector<std::string> empty_vec;
+ std::vector<char> non_empty_vec{ 'a', 'b', 'c' };
+ REQUIRE_THAT(empty_vec, IsEmpty());
+ REQUIRE_THAT(non_empty_vec, !IsEmpty());
+
+ std::list<std::list<std::list<int>>> inner_lists_are_empty;
+ inner_lists_are_empty.push_back({});
+ REQUIRE_THAT(inner_lists_are_empty, !IsEmpty());
+ REQUIRE_THAT(inner_lists_are_empty.front(), IsEmpty());
+ }
+ SECTION("Type with empty") {
+ REQUIRE_THAT(has_empty{}, !IsEmpty());
+ }
+ SECTION("Type requires ADL found empty free function") {
+ REQUIRE_THAT(unrelated::ADL_empty{}, IsEmpty());
+ }
+}
+
+namespace {
+ class LessThanMatcher final : public Catch::Matchers::MatcherBase<size_t> {
+ size_t m_target;
+ public:
+ explicit LessThanMatcher(size_t target):
+ m_target(target)
+ {}
+
+ bool match(size_t const& size) const override {
+ return size < m_target;
+ }
+
+ std::string describe() const override {
+ return "is less than " + std::to_string(m_target);
+ }
+ };
+
+ LessThanMatcher Lt(size_t sz) {
+ return LessThanMatcher{ sz };
+ }
+
+ struct has_size {
+ size_t size() const {
+ return 13;
+ }
+ };
+
+} // end unnamed namespace
+
+TEST_CASE("Usage of the SizeIs range matcher", "[matchers][templated][size]") {
+ using Catch::Matchers::SizeIs;
+ SECTION("Some with stdlib containers") {
+ std::vector<int> empty_vec;
+ REQUIRE_THAT(empty_vec, SizeIs(0));
+ REQUIRE_THAT(empty_vec, !SizeIs(2));
+ REQUIRE_THAT(empty_vec, SizeIs(Lt(2)));
+
+ std::array<int, 2> arr{};
+ REQUIRE_THAT(arr, SizeIs(2));
+ REQUIRE_THAT(arr, SizeIs( Lt(3)));
+ REQUIRE_THAT(arr, !SizeIs(!Lt(3)));
+
+ std::map<int, int> map{ {1, 1}, {2, 2}, {3, 3} };
+ REQUIRE_THAT(map, SizeIs(3));
+ }
+ SECTION("Type requires ADL found size free function") {
+ REQUIRE_THAT(unrelated::ADL_size{}, SizeIs(12));
+ }
+ SECTION("Type has size member") {
+ REQUIRE_THAT(has_size{}, SizeIs(13));
+ }
+}
+
+
+TEST_CASE("Usage of AllMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AllMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{{
+ {{ 0, 1, 2, 3, 5 }},
+ {{ 4,-3,-2, 5, 0 }},
+ {{ 0, 0, 0, 5, 0 }},
+ {{ 0,-5, 0, 5, 0 }},
+ {{ 1, 0, 0,-1, 5 }}
+ }};
+
+ REQUIRE_THAT(data, AllMatch(SizeIs(5)));
+ REQUIRE_THAT(data, !AllMatch(Contains(0) && Contains(1)));
+ }
+
+ SECTION("Type requires ADL found begin and end") {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, AllMatch( Predicate<int>( []( int elem ) {
+ return elem < 6;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto allMatch = AllMatch(Predicate<int>([](int elem) {
+ return elem < 10;
+ }));
+ REQUIRE_THAT(mocked, allMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE(mocked.m_derefed[3]);
+ REQUIRE(mocked.m_derefed[4]);
+ }
+ SECTION("Short-circuited") {
+ auto allMatch = AllMatch(Predicate<int>([](int elem) {
+ return elem < 3;
+ }));
+ REQUIRE_THAT(mocked, !allMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE_FALSE(mocked.m_derefed[3]);
+ REQUIRE_FALSE(mocked.m_derefed[4]);
+ }
+ }
+}
+
+TEST_CASE("Usage of AnyMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AnyMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{ {
+ {{ 0, 1, 2, 3, 5 }},
+ {{ 4,-3,-2, 5, 0 }},
+ {{ 0, 0, 0, 5, 0 }},
+ {{ 0,-5, 0, 5, 0 }},
+ {{ 1, 0, 0,-1, 5 }}
+ } };
+
+ REQUIRE_THAT(data, AnyMatch(SizeIs(5)));
+ REQUIRE_THAT(data, !AnyMatch(Contains(0) && Contains(10)));
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, AnyMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto anyMatch = AnyMatch(
+ Predicate<int>( []( int elem ) { return elem > 10; } ) );
+ REQUIRE_THAT( mocked, !anyMatch );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION("Short-circuited") {
+ auto anyMatch = AnyMatch(
+ Predicate<int>( []( int elem ) { return elem < 3; } ) );
+ REQUIRE_THAT( mocked, anyMatch );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE_FALSE( mocked.m_derefed[1] );
+ REQUIRE_FALSE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE("Usage of NoneMatch range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::NoneMatch;
+ using Catch::Matchers::Predicate;
+
+ SECTION("Basic usage") {
+ using Catch::Matchers::Contains;
+ using Catch::Matchers::SizeIs;
+
+ std::array<std::array<int, 5>, 5> data{ {
+ {{ 0, 1, 2, 3, 5 }},
+ {{ 4,-3,-2, 5, 0 }},
+ {{ 0, 0, 0, 5, 0 }},
+ {{ 0,-5, 0, 5, 0 }},
+ {{ 1, 0, 0,-1, 5 }}
+ } };
+
+ REQUIRE_THAT(data, NoneMatch(SizeIs(6)));
+ REQUIRE_THAT(data, !NoneMatch(Contains(0) && Contains(1)));
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<int> needs_adl{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( needs_adl, NoneMatch( Predicate<int>( []( int elem ) {
+ return elem > 6;
+ } ) ) );
+ }
+
+ SECTION("Shortcircuiting") {
+ with_mocked_iterator_access<int> mocked{ 1, 2, 3, 4, 5 };
+ SECTION("All are read") {
+ auto noneMatch = NoneMatch(
+ Predicate<int>([](int elem) { return elem > 10; }));
+ REQUIRE_THAT(mocked, noneMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE(mocked.m_derefed[1]);
+ REQUIRE(mocked.m_derefed[2]);
+ REQUIRE(mocked.m_derefed[3]);
+ REQUIRE(mocked.m_derefed[4]);
+ }
+ SECTION("Short-circuited") {
+ auto noneMatch = NoneMatch(
+ Predicate<int>([](int elem) { return elem < 3; }));
+ REQUIRE_THAT(mocked, !noneMatch);
+ REQUIRE(mocked.m_derefed[0]);
+ REQUIRE_FALSE(mocked.m_derefed[1]);
+ REQUIRE_FALSE(mocked.m_derefed[2]);
+ REQUIRE_FALSE(mocked.m_derefed[3]);
+ REQUIRE_FALSE(mocked.m_derefed[4]);
+ }
+ }
+}
+
+namespace {
+ struct ConvertibleToBool
+ {
+ bool v;
+
+ explicit operator bool() const
+ {
+ return v;
+ }
+ };
+}
+
+namespace Catch {
+ template <>
+ struct StringMaker<ConvertibleToBool> {
+ static std::string
+ convert( ConvertibleToBool const& convertible_to_bool ) {
+ return ::Catch::Detail::stringify( convertible_to_bool.v );
+ }
+ };
+} // namespace Catch
+
+TEST_CASE("Usage of AllTrue range matcher", "[matchers][templated][quantifiers]") {
+ using Catch::Matchers::AllTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "Empty evaluates to true" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "One false evaluates to false" ) {
+ std::array<bool, 5> const data{ { true, true, false, true, true } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, AllTrue() );
+ }
+ SECTION( "One false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { false }, { true }, { true } } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, !AllTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ true, true, true, true, true };
+ REQUIRE_THAT( mocked, AllTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ true, true, false, true, true };
+ REQUIRE_THAT( mocked, !AllTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of NoneTrue range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::NoneTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to false" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "Empty evaluates to true" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ SECTION( "One true evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, true, false, false } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "All false evaluates to true" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "One true evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { true }, { false }, { false } } };
+ REQUIRE_THAT( data, !NoneTrue() );
+ }
+ SECTION( "All false evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, NoneTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, false, false, false };
+ REQUIRE_THAT( mocked, NoneTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, true, true, true };
+ REQUIRE_THAT( mocked, !NoneTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of AnyTrue range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::AnyTrue;
+
+ SECTION( "Basic usage" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<bool, 5> const data{ { true, true, true, true, true } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "Empty evaluates to false" ) {
+ std::array<bool, 0> const data{};
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ SECTION( "One true evaluates to true" ) {
+ std::array<bool, 5> const data{
+ { false, false, true, false, false } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<bool, 5> const data{
+ { false, false, false, false, false } };
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ }
+
+ SECTION( "Contained type is convertible to bool" ) {
+ SECTION( "All true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { true }, { true }, { true }, { true }, { true } } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "One true evaluates to true" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { true }, { false }, { false } } };
+ REQUIRE_THAT( data, AnyTrue() );
+ }
+ SECTION( "All false evaluates to false" ) {
+ std::array<ConvertibleToBool, 5> const data{
+ { { false }, { false }, { false }, { false }, { false } } };
+ REQUIRE_THAT( data, !AnyTrue() );
+ }
+ }
+
+ SECTION( "Shortcircuiting" ) {
+ SECTION( "All are read" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, false, false, true };
+ REQUIRE_THAT( mocked, AnyTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE( mocked.m_derefed[3] );
+ REQUIRE( mocked.m_derefed[4] );
+ }
+ SECTION( "Short-circuited" ) {
+ with_mocked_iterator_access<bool> const mocked{
+ false, false, true, true, true };
+ REQUIRE_THAT( mocked, AnyTrue() );
+ REQUIRE( mocked.m_derefed[0] );
+ REQUIRE( mocked.m_derefed[1] );
+ REQUIRE( mocked.m_derefed[2] );
+ REQUIRE_FALSE( mocked.m_derefed[3] );
+ REQUIRE_FALSE( mocked.m_derefed[4] );
+ }
+ }
+}
+
+TEST_CASE("All/Any/None True matchers support types with ADL begin",
+ "[approvals][matchers][quantifiers][templated]") {
+ using Catch::Matchers::AllTrue;
+ using Catch::Matchers::NoneTrue;
+ using Catch::Matchers::AnyTrue;
+
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ true, true, true, true, true };
+ REQUIRE_THAT( needs_adl, AllTrue() );
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ false, false, false, false, false };
+ REQUIRE_THAT( needs_adl, NoneTrue() );
+ }
+
+ SECTION( "Type requires ADL found begin and end" ) {
+ unrelated::needs_ADL_begin<bool> const needs_adl{
+ false, false, true, false, false };
+ REQUIRE_THAT( needs_adl, AnyTrue() );
+ }
+}
+
+// Range loop iterating over range with different types for begin and end is a
+// C++17 feature, and GCC refuses to compile such code unless the lang mode is
+// set to C++17 or later.
+#if defined(CATCH_CPP17_OR_GREATER)
+
+TEST_CASE( "The quantifier range matchers support types with different types returned from begin and end",
+ "[matchers][templated][quantifiers][approvals]" ) {
+ using Catch::Matchers::AllMatch;
+ using Catch::Matchers::AllTrue;
+ using Catch::Matchers::AnyMatch;
+ using Catch::Matchers::AnyTrue;
+ using Catch::Matchers::NoneMatch;
+ using Catch::Matchers::NoneTrue;
+
+ using Catch::Matchers::Predicate;
+
+ SECTION( "AllAnyNoneMatch" ) {
+ has_different_begin_end_types<int> diff_types{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( diff_types, !AllMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+
+ REQUIRE_THAT( diff_types, AnyMatch( Predicate<int>( []( int elem ) {
+ return elem < 2;
+ } ) ) );
+
+ REQUIRE_THAT( diff_types, !NoneMatch( Predicate<int>( []( int elem ) {
+ return elem < 3;
+ } ) ) );
+ }
+ SECTION( "AllAnyNoneTrue" ) {
+ has_different_begin_end_types<bool> diff_types{ false, false, true, false, false };
+
+ REQUIRE_THAT( diff_types, !AllTrue() );
+ REQUIRE_THAT( diff_types, AnyTrue() );
+ REQUIRE_THAT( diff_types, !NoneTrue() );
+ }
+}
+
+TEST_CASE( "RangeEquals supports ranges with different types returned from begin and end",
+ "[matchers][templated][range][approvals] ") {
+ using Catch::Matchers::RangeEquals;
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ has_different_begin_end_types<int> diff_types{ 1, 2, 3, 4, 5 };
+ std::array<int, 5> arr1{ { 1, 2, 3, 4, 5 } }, arr2{ { 2, 3, 4, 5, 6 } };
+
+ REQUIRE_THAT( diff_types, RangeEquals( arr1 ) );
+ REQUIRE_THAT( diff_types, RangeEquals( arr2, []( int l, int r ) {
+ return l + 1 == r;
+ } ) );
+ REQUIRE_THAT( diff_types, UnorderedRangeEquals( diff_types ) );
+}
+
+TEST_CASE( "RangeContains supports ranges with different types returned from "
+ "begin and end",
+ "[matchers][templated][range][approvals]" ) {
+ using Catch::Matchers::Contains;
+
+ has_different_begin_end_types<size_t> diff_types{ 1, 2, 3, 4, 5 };
+ REQUIRE_THAT( diff_types, Contains( size_t( 3 ) ) );
+ REQUIRE_THAT( diff_types, Contains( LessThanMatcher( size_t( 4 ) ) ) );
+}
+
+#endif
+
+TEST_CASE( "Usage of RangeEquals range matcher", "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::RangeEquals;
+
+ // In these tests, the types are always the same - type conversion is in the next section
+ SECTION( "Basic usage" ) {
+ SECTION( "Empty container matches empty container" ) {
+ const std::vector<int> empty_vector;
+ CHECK_THAT( empty_vector, RangeEquals( empty_vector ) );
+ }
+ SECTION( "Empty container does not match non-empty container" ) {
+ const std::vector<int> empty_vector;
+ const std::vector<int> non_empty_vector{ 1 };
+ CHECK_THAT( empty_vector, !RangeEquals( non_empty_vector ) );
+ // ...and in reverse
+ CHECK_THAT( non_empty_vector, !RangeEquals( empty_vector ) );
+ }
+ SECTION( "Two equal 1-length non-empty containers" ) {
+ const std::array<int, 1> non_empty_array{ { 1 } };
+ CHECK_THAT( non_empty_array, RangeEquals( non_empty_array ) );
+ }
+ SECTION( "Two equal-sized, equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ CHECK_THAT( array_a, RangeEquals( array_a ) );
+ }
+ SECTION( "Two equal-sized, non-equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ const std::array<int, 3> array_b{ { 2, 2, 3 } };
+ const std::array<int, 3> array_c{ { 1, 2, 2 } };
+ CHECK_THAT( array_a, !RangeEquals( array_b ) );
+ CHECK_THAT( array_a, !RangeEquals( array_c ) );
+ }
+ SECTION( "Two non-equal-sized, non-empty containers (with same first "
+ "elements)" ) {
+ const std::vector<int> vector_a{ 1, 2, 3 };
+ const std::vector<int> vector_b{ 1, 2, 3, 4 };
+ CHECK_THAT( vector_a, !RangeEquals( vector_b ) );
+ }
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_a_plus_1{ { 2, 3, 4 } };
+ CHECK_THAT( vector_a, RangeEquals( vector_a_plus_1, close_enough ) );
+ }
+ SECTION( "Two non-equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_b{ { 3, 3, 4 } };
+ CHECK_THAT( vector_a, !RangeEquals( vector_b, close_enough ) );
+ }
+ }
+
+ SECTION( "Ranges that need ADL begin/end" ) {
+ unrelated::needs_ADL_begin<int> const
+ needs_adl1{ 1, 2, 3, 4, 5 },
+ needs_adl2{ 1, 2, 3, 4, 5 },
+ needs_adl3{ 2, 3, 4, 5, 6 };
+
+ REQUIRE_THAT( needs_adl1, RangeEquals( needs_adl2 ) );
+ REQUIRE_THAT( needs_adl1, RangeEquals( needs_adl3, []( int l, int r ) {
+ return l + 1 == r;
+ } ) );
+ }
+
+ SECTION( "Compare against std::initializer_list" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+
+ REQUIRE_THAT( array_a, RangeEquals( { 1, 2, 3 } ) );
+ REQUIRE_THAT( array_a, RangeEquals( { 2, 4, 6 }, []( int l, int r ) {
+ return l * 2 == r;
+ } ) );
+ }
+
+ SECTION("Check short-circuiting behaviour") {
+ with_mocked_iterator_access<int> const mocked1{ 1, 2, 3, 4 };
+
+ SECTION( "Check short-circuits on failure" ) {
+ std::array<int, 4> arr{ { 1, 2, 4, 4 } };
+
+ REQUIRE_THAT( mocked1, !RangeEquals( arr ) );
+ REQUIRE( mocked1.m_derefed[0] );
+ REQUIRE( mocked1.m_derefed[1] );
+ REQUIRE( mocked1.m_derefed[2] );
+ REQUIRE_FALSE( mocked1.m_derefed[3] );
+ }
+ SECTION("All elements are checked on success") {
+ std::array<int, 4> arr{ { 1, 2, 3, 4 } };
+
+ REQUIRE_THAT( mocked1, RangeEquals( arr ) );
+ REQUIRE( mocked1.m_derefed[0] );
+ REQUIRE( mocked1.m_derefed[1] );
+ REQUIRE( mocked1.m_derefed[2] );
+ REQUIRE( mocked1.m_derefed[3] );
+ }
+ }
+}
+
+TEST_CASE( "Usage of UnorderedRangeEquals range matcher",
+ "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ // In these tests, the types are always the same - type conversion is in the
+ // next section
+ SECTION( "Basic usage" ) {
+ SECTION( "Empty container matches empty container" ) {
+ const std::vector<int> empty_vector;
+ CHECK_THAT( empty_vector, UnorderedRangeEquals( empty_vector ) );
+ }
+ SECTION( "Empty container does not match non-empty container" ) {
+ const std::vector<int> empty_vector;
+ const std::vector<int> non_empty_vector{ 1 };
+ CHECK_THAT( empty_vector,
+ !UnorderedRangeEquals( non_empty_vector ) );
+ // ...and in reverse
+ CHECK_THAT( non_empty_vector,
+ !UnorderedRangeEquals( empty_vector ) );
+ }
+ SECTION( "Two equal 1-length non-empty containers" ) {
+ const std::array<int, 1> non_empty_array{ { 1 } };
+ CHECK_THAT( non_empty_array,
+ UnorderedRangeEquals( non_empty_array ) );
+ }
+ SECTION( "Two equal-sized, equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ CHECK_THAT( array_a, UnorderedRangeEquals( array_a ) );
+ }
+ SECTION( "Two equal-sized, non-equal, non-empty containers" ) {
+ const std::array<int, 3> array_a{ { 1, 2, 3 } };
+ const std::array<int, 3> array_b{ { 2, 2, 3 } };
+ CHECK_THAT( array_a, !UnorderedRangeEquals( array_b ) );
+ }
+ SECTION( "Two non-equal-sized, non-empty containers" ) {
+ const std::vector<int> vector_a{ 1, 2, 3 };
+ const std::vector<int> vector_b{ 1, 2, 3, 4 };
+ CHECK_THAT( vector_a, !UnorderedRangeEquals( vector_b ) );
+ }
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 10, 20 } };
+ const std::vector<int> vector_a_plus_1{ { 11, 21, 2 } };
+ CHECK_THAT( vector_a,
+ UnorderedRangeEquals( vector_a_plus_1, close_enough ) );
+ }
+ SECTION( "Two non-equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 10, 21 } };
+ const std::vector<int> vector_b{ { 11, 21, 3 } };
+ CHECK_THAT( vector_a,
+ !UnorderedRangeEquals( vector_b, close_enough ) );
+ }
+ }
+
+
+ SECTION( "Ranges that need ADL begin/end" ) {
+ unrelated::needs_ADL_begin<int> const
+ needs_adl1{ 1, 2, 3, 4, 5 },
+ needs_adl2{ 1, 2, 3, 4, 5 };
+
+ REQUIRE_THAT( needs_adl1, UnorderedRangeEquals( needs_adl2 ) );
+ }
+
+ SECTION( "Compare against std::initializer_list" ) {
+ const std::array<int, 3> array_a{ { 1, 10, 20 } };
+
+ REQUIRE_THAT( array_a, UnorderedRangeEquals( { 10, 20, 1 } ) );
+ REQUIRE_THAT( array_a,
+ UnorderedRangeEquals( { 11, 21, 2 }, []( int l, int r ) {
+ return std::abs( l - r ) <= 1;
+ } ) );
+ }
+}
+
+/**
+ * Return true if the type given has a random access iterator type.
+ */
+template <typename Container>
+static constexpr bool ContainerIsRandomAccess( const Container& ) {
+ using array_iter_category = typename std::iterator_traits<
+ typename Container::iterator>::iterator_category;
+
+ return std::is_base_of<std::random_access_iterator_tag,
+ array_iter_category>::value;
+}
+
+TEST_CASE( "Type conversions of RangeEquals and similar",
+ "[matchers][templated][quantifiers]" ) {
+ using Catch::Matchers::RangeEquals;
+ using Catch::Matchers::UnorderedRangeEquals;
+
+ // In these test, we can always test RangeEquals and
+ // UnorderedRangeEquals in the same way, since we're mostly
+ // testing the template type deductions (and RangeEquals
+ // implies UnorderedRangeEquals)
+
+ SECTION( "Container conversions" ) {
+ SECTION( "Two equal containers of different container types" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const int c_array[3] = { 1, 2, 3 };
+ CHECK_THAT( array_int_a, RangeEquals( c_array ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( c_array ) );
+ }
+ SECTION( "Two equal containers of different container types "
+ "(differ in array N)" ) {
+ const std::array<int, 3> array_int_3{ { 1, 2, 3 } };
+ const std::array<int, 4> array_int_4{ { 1, 2, 3, 4 } };
+ CHECK_THAT( array_int_3, !RangeEquals( array_int_4 ) );
+ CHECK_THAT( array_int_3, !UnorderedRangeEquals( array_int_4 ) );
+ }
+ SECTION( "Two equal containers of different container types and value "
+ "types" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const std::vector<int> vector_char_a{ 1, 2, 3 };
+ CHECK_THAT( array_int_a, RangeEquals( vector_char_a ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( vector_char_a ) );
+ }
+ SECTION( "Two equal containers, one random access, one not" ) {
+ const std::array<int, 3> array_int_a{ { 1, 2, 3 } };
+ const std::list<int> list_char_a{ 1, 2, 3 };
+
+ // Verify these types really are different in random access nature
+ STATIC_REQUIRE( ContainerIsRandomAccess( array_int_a ) !=
+ ContainerIsRandomAccess( list_char_a ) );
+
+ CHECK_THAT( array_int_a, RangeEquals( list_char_a ) );
+ CHECK_THAT( array_int_a, UnorderedRangeEquals( list_char_a ) );
+ }
+ }
+
+ SECTION( "Value type" ) {
+ SECTION( "Two equal containers of different value types" ) {
+ const std::vector<int> vector_int_a{ 1, 2, 3 };
+ const std::vector<char> vector_char_a{ 1, 2, 3 };
+ CHECK_THAT( vector_int_a, RangeEquals( vector_char_a ) );
+ CHECK_THAT( vector_int_a, UnorderedRangeEquals( vector_char_a ) );
+ }
+ SECTION( "Two non-equal containers of different value types" ) {
+ const std::vector<int> vector_int_a{ 1, 2, 3 };
+ const std::vector<char> vector_char_b{ 1, 2, 2 };
+ CHECK_THAT( vector_int_a, !RangeEquals( vector_char_b ) );
+ CHECK_THAT( vector_int_a, !UnorderedRangeEquals( vector_char_b ) );
+ }
+ }
+
+ SECTION( "Ranges with begin that needs ADL" ) {
+ unrelated::needs_ADL_begin<int> a{ 1, 2, 3 }, b{ 3, 2, 1 };
+ REQUIRE_THAT( a, !RangeEquals( b ) );
+ REQUIRE_THAT( a, UnorderedRangeEquals( b ) );
+ }
+
+ SECTION( "Custom predicate" ) {
+
+ auto close_enough = []( int lhs, int rhs ) {
+ return std::abs( lhs - rhs ) <= 1;
+ };
+
+ SECTION( "Two equal non-empty containers (close enough)" ) {
+ const std::vector<int> vector_a{ { 1, 2, 3 } };
+ const std::array<char, 3> array_a_plus_1{ { 2, 3, 4 } };
+ CHECK_THAT( vector_a,
+ RangeEquals( array_a_plus_1, close_enough ) );
+ CHECK_THAT( vector_a,
+ UnorderedRangeEquals( array_a_plus_1, close_enough ) );
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/SelfTest/UsageTests/Message.tests.cpp b/tests/SelfTest/UsageTests/Message.tests.cpp
new file mode 100644
index 0000000..7626e00
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Message.tests.cpp
@@ -0,0 +1,312 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <iostream>
+
+TEST_CASE( "INFO and WARN do not abort tests", "[messages][.]" ) {
+ INFO( "this is a " << "message" ); // This should output the message if a failure occurs
+ WARN( "this is a " << "warning" ); // This should always output the message but then continue
+}
+
+TEST_CASE( "#1455 - INFO and WARN can start with a linebreak", "[messages][.]" ) {
+ // Previously these would be hidden from the console reporter output,
+ // because it would fail at properly reflowing the text
+ INFO( "\nThis info message starts with a linebreak" );
+ WARN( "\nThis warning message starts with a linebreak" );
+}
+
+TEST_CASE( "SUCCEED counts as a test pass", "[messages]" ) {
+ SUCCEED( "this is a " << "success" );
+}
+
+TEST_CASE( "INFO gets logged on failure", "[failing][messages][.]" ) {
+ INFO( "this message should be logged" );
+ INFO( "so should this" );
+ int a = 2;
+ REQUIRE( a == 1 );
+}
+
+TEST_CASE( "INFO gets logged on failure, even if captured before successful assertions", "[failing][messages][.]" ) {
+ INFO( "this message may be logged later" );
+ int a = 2;
+ CHECK( a == 2 );
+
+ INFO( "this message should be logged" );
+
+ CHECK( a == 1 );
+
+ INFO( "and this, but later" );
+
+ CHECK( a == 0 );
+
+ INFO( "but not this" );
+
+ CHECK( a == 2 );
+}
+
+TEST_CASE( "FAIL aborts the test", "[failing][messages][.]" ) {
+ FAIL( "This is a " << "failure" ); // This should output the message and abort
+ WARN( "We should never see this");
+}
+
+TEST_CASE( "FAIL_CHECK does not abort the test", "[failing][messages][.]" ) {
+ FAIL_CHECK( "This is a " << "failure" ); // This should output the message then continue
+ WARN( "This message appears in the output");
+}
+
+TEST_CASE( "FAIL does not require an argument", "[failing][messages][.]" ) {
+ FAIL();
+}
+
+TEST_CASE( "SUCCEED does not require an argument", "[messages][.]" ) {
+ SUCCEED();
+}
+
+TEST_CASE( "Output from all sections is reported", "[failing][messages][.]" ) {
+ SECTION( "one" ) {
+ FAIL( "Message from section one" );
+ }
+
+ SECTION( "two" ) {
+ FAIL( "Message from section two" );
+ }
+}
+
+TEST_CASE( "Standard output from all sections is reported", "[messages][.]" ) {
+ SECTION( "one" ) {
+ std::cout << "Message from section one\n";
+ }
+
+ SECTION( "two" ) {
+ std::cout << "Message from section two\n";
+ }
+}
+
+TEST_CASE( "Standard error is reported and redirected", "[messages][.][approvals]" ) {
+ SECTION( "std::cerr" ) {
+ std::cerr << "Write to std::cerr\n";
+ }
+ SECTION( "std::clog" ) {
+ std::clog << "Write to std::clog\n";
+ }
+ SECTION( "Interleaved writes to cerr and clog" ) {
+ std::cerr << "Inter";
+ std::clog << "leaved";
+ std::cerr << ' ';
+ std::clog << "writes";
+ std::cerr << " to error";
+ std::clog << " streams\n" << std::flush;
+ }
+}
+
+TEST_CASE( "INFO is reset for each loop", "[messages][failing][.]" ) {
+ for( int i=0; i<100; i++ )
+ {
+ INFO( "current counter " << i );
+ CAPTURE( i );
+ REQUIRE( i < 10 );
+ }
+}
+
+TEST_CASE( "The NO_FAIL macro reports a failure but does not fail the test", "[messages]" ) {
+ CHECK_NOFAIL( 1 == 2 );
+}
+
+TEST_CASE( "just info", "[info][isolated info][messages]" ) {
+ INFO( "this should never be seen" );
+}
+TEST_CASE( "just failure", "[fail][isolated info][.][messages]" ) {
+ FAIL( "Previous info should not be seen" );
+}
+
+
+TEST_CASE( "sends information to INFO", "[.][failing]" ) {
+ INFO( "hi" );
+ int i = 7;
+ CAPTURE( i );
+ REQUIRE( false );
+}
+
+TEST_CASE( "Pointers can be converted to strings", "[messages][.][approvals]" ) {
+ int p;
+ WARN( "actual address of p: " << &p );
+ WARN( "toString(p): " << ::Catch::Detail::stringify( &p ) );
+}
+
+template <typename T>
+static void unscoped_info( T msg ) {
+ UNSCOPED_INFO( msg );
+}
+
+TEST_CASE( "just unscoped info", "[unscoped][info]" ) {
+ unscoped_info( "this should NOT be seen" );
+ unscoped_info( "this also should NOT be seen" );
+}
+
+TEST_CASE( "just failure after unscoped info", "[failing][.][unscoped][info]" ) {
+ FAIL( "previous unscoped info SHOULD not be seen" );
+}
+
+TEST_CASE( "print unscoped info if passing unscoped info is printed", "[unscoped][info]" ) {
+ unscoped_info( "this MAY be seen IF info is printed for passing assertions" );
+ REQUIRE( true );
+}
+
+TEST_CASE( "prints unscoped info on failure", "[failing][.][unscoped][info]" ) {
+ unscoped_info( "this SHOULD be seen" );
+ unscoped_info( "this SHOULD also be seen" );
+ REQUIRE( false );
+ unscoped_info( "but this should NOT be seen" );
+}
+
+TEST_CASE( "not prints unscoped info from previous failures", "[failing][.][unscoped][info]" ) {
+ unscoped_info( "this MAY be seen only for the FIRST assertion IF info is printed for passing assertions" );
+ REQUIRE( true );
+ unscoped_info( "this MAY be seen only for the SECOND assertion IF info is printed for passing assertions" );
+ REQUIRE( true );
+ unscoped_info( "this SHOULD be seen" );
+ REQUIRE( false );
+}
+
+TEST_CASE( "prints unscoped info only for the first assertion", "[failing][.][unscoped][info]" ) {
+ unscoped_info( "this SHOULD be seen only ONCE" );
+ CHECK( false );
+ CHECK( true );
+ unscoped_info( "this MAY also be seen only ONCE IF info is printed for passing assertions" );
+ CHECK( true );
+ CHECK( true );
+}
+
+TEST_CASE( "stacks unscoped info in loops", "[failing][.][unscoped][info]" ) {
+ UNSCOPED_INFO("Count 1 to 3...");
+ for (int i = 1; i <= 3; i++) {
+ unscoped_info(i);
+ }
+ CHECK( false );
+
+ UNSCOPED_INFO("Count 4 to 6...");
+ for (int i = 4; i <= 6; i++) {
+ unscoped_info(i);
+ }
+ CHECK( false );
+}
+
+TEST_CASE( "mix info, unscoped info and warning", "[unscoped][info]" ) {
+ INFO("info");
+ unscoped_info("unscoped info");
+ WARN("and warn may mix");
+ WARN("they are not cleared after warnings");
+}
+
+TEST_CASE( "CAPTURE can deal with complex expressions", "[messages][capture]" ) {
+ int a = 1;
+ int b = 2;
+ int c = 3;
+ CAPTURE( a, b, c, a + b, a+b, c > b, a == 1 );
+ SUCCEED();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // In (1, 2), the "1" is unused ...
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-value" // All the comma operators are side-effect free
+#endif
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4709) // comma in indexing operator
+#endif
+
+template <typename T1, typename T2>
+struct helper_1436 {
+ helper_1436(T1 t1_, T2 t2_):
+ t1{ t1_ },
+ t2{ t2_ }
+ {}
+ T1 t1;
+ T2 t2;
+};
+
+template <typename T1, typename T2>
+std::ostream& operator<<(std::ostream& out, helper_1436<T1, T2> const& helper) {
+ out << "{ " << helper.t1 << ", " << helper.t2 << " }";
+ return out;
+}
+
+// Clang and gcc have different names for this warning, and clang also
+// warns about an unused value. This warning must be disabled for C++20.
+#if defined(__GNUG__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wcomma-subscript"
+#elif defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wunknown-warning-option"
+#pragma clang diagnostic ignored "-Wdeprecated-comma-subscript"
+#pragma clang diagnostic ignored "-Wunused-value"
+#endif
+
+namespace {
+ template <typename T>
+ struct custom_index_op {
+ constexpr custom_index_op( std::initializer_list<T> ) {}
+ constexpr T operator[]( size_t ) { return T{}; }
+#if defined( __cpp_multidimensional_subscript ) && \
+ __cpp_multidimensional_subscript >= 202110L
+ constexpr T operator[]( size_t, size_t, size_t ) const noexcept {
+ return T{};
+ }
+#endif
+ };
+}
+
+TEST_CASE("CAPTURE can deal with complex expressions involving commas", "[messages][capture]") {
+ CAPTURE(custom_index_op<int>{1, 2, 3}[0, 1, 2],
+ custom_index_op<int>{1, 2, 3}[(0, 1)],
+ custom_index_op<int>{1, 2, 3}[0]);
+ CAPTURE((helper_1436<int, int>{12, -12}),
+ (helper_1436<int, int>(-12, 12)));
+ CAPTURE( (1, 2), (2, 3) );
+ SUCCEED();
+}
+
+#ifdef __GNUG__
+#pragma GCC diagnostic pop
+#endif
+
+TEST_CASE("CAPTURE parses string and character constants", "[messages][capture]") {
+ CAPTURE(("comma, in string", "escaped, \", "), "single quote in string,',", "some escapes, \\,\\\\");
+ CAPTURE("some, ), unmatched, } prenheses {[<");
+ CAPTURE('"', '\'', ',', '}', ')', '(', '{');
+ SUCCEED();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+TEST_CASE( "INFO and UNSCOPED_INFO can stream multiple arguments",
+ "[messages][info][.failing]" ) {
+ INFO( "This info"
+ << " has multiple"
+ << " parts." );
+ UNSCOPED_INFO( "This unscoped info"
+ << " has multiple"
+ << " parts." );
+ FAIL( "Show infos!" );
+}
diff --git a/tests/SelfTest/UsageTests/Misc.tests.cpp b/tests/SelfTest/UsageTests/Misc.tests.cpp
new file mode 100644
index 0000000..3697f06
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Misc.tests.cpp
@@ -0,0 +1,560 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/internal/catch_config_wchar.hpp>
+#include <catch2/internal/catch_windows_h_proxy.hpp>
+
+
+#include <iostream>
+#include <cerrno>
+#include <limits>
+#include <array>
+#include <tuple>
+
+namespace {
+
+ static const char* makeString(bool makeNull) {
+ return makeNull ? nullptr : "valid string";
+ }
+ static bool testCheckedIf(bool flag) {
+ CHECKED_IF(flag)
+ return true;
+ else
+ return false;
+ }
+ static bool testCheckedElse(bool flag) {
+ CHECKED_ELSE(flag)
+ return false;
+
+ return true;
+ }
+
+ static unsigned int Factorial(unsigned int number) {
+ return number > 1 ? Factorial(number - 1) * number : 1;
+ }
+
+ static int f() {
+ return 1;
+ }
+
+ static void manuallyRegisteredTestFunction() {
+ SUCCEED("was called");
+ }
+
+ struct AutoTestReg {
+ AutoTestReg() {
+ REGISTER_TEST_CASE(manuallyRegisteredTestFunction, "ManuallyRegistered");
+ }
+ };
+
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+ static AutoTestReg autoTestReg;
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ template<typename T>
+ struct Foo {
+ size_t size() { return 0; }
+ };
+
+ template<typename T, size_t S>
+ struct Bar {
+ size_t size() { return S; }
+ };
+
+}
+
+TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) {
+ int a = 1;
+ int b = 2;
+
+ SECTION( "doesn't equal" ) {
+ REQUIRE( a != b );
+ REQUIRE( b != a );
+ }
+
+ SECTION( "not equal" ) {
+ REQUIRE( a != b);
+ }
+}
+
+TEST_CASE( "nested SECTION tests", "[.][sections][failing]" ) {
+ int a = 1;
+ int b = 2;
+
+ SECTION( "doesn't equal" ) {
+ REQUIRE( a != b );
+ REQUIRE( b != a );
+
+ SECTION( "not equal" ) {
+ REQUIRE( a != b);
+ }
+ }
+}
+
+TEST_CASE( "more nested SECTION tests", "[sections][failing][.]" ) {
+ int a = 1;
+ int b = 2;
+
+ SECTION( "doesn't equal" ) {
+ SECTION( "equal" ) {
+ REQUIRE( a == b );
+ }
+
+ SECTION( "not equal" ) {
+ REQUIRE( a != b );
+ }
+ SECTION( "less than" ) {
+ REQUIRE( a < b );
+ }
+ }
+}
+
+TEST_CASE( "even more nested SECTION tests", "[sections]" ) {
+ SECTION( "c" ) {
+ SECTION( "d (leaf)" ) {
+ SUCCEED(); // avoid failing due to no tests
+ }
+
+ SECTION( "e (leaf)" ) {
+ SUCCEED(); // avoid failing due to no tests
+ }
+ }
+
+ SECTION( "f (leaf)" ) {
+ SUCCEED(); // avoid failing due to no tests
+ }
+}
+
+TEST_CASE( "looped SECTION tests", "[.][failing][sections]" ) {
+ int a = 1;
+
+ for( int b = 0; b < 10; ++b ) {
+ DYNAMIC_SECTION( "b is currently: " << b ) {
+ CHECK( b > a );
+ }
+ }
+}
+
+TEST_CASE( "looped tests", "[.][failing]" ) {
+ static const int fib[] = { 1, 1, 2, 3, 5, 8, 13, 21 };
+
+ for( std::size_t i=0; i < sizeof(fib)/sizeof(int); ++i ) {
+ INFO( "Testing if fib[" << i << "] (" << fib[i] << ") is even" );
+ CHECK( ( fib[i] % 2 ) == 0 );
+ }
+}
+
+TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) {
+ std::cout << "A string sent directly to stdout\n" << std::flush;
+ std::cerr << "A string sent directly to stderr\n" << std::flush;
+ std::clog << "A string sent to stderr via clog\n" << std::flush;
+}
+
+TEST_CASE( "null strings" ) {
+ REQUIRE( makeString( false ) != static_cast<char*>(nullptr));
+ REQUIRE( makeString( true ) == static_cast<char*>(nullptr));
+}
+
+TEST_CASE( "checkedIf" ) {
+ REQUIRE( testCheckedIf( true ) );
+}
+
+TEST_CASE( "checkedIf, failing", "[failing][.]" ) {
+ REQUIRE( testCheckedIf( false ) );
+}
+
+TEST_CASE( "checkedElse" ) {
+ REQUIRE( testCheckedElse( true ) );
+}
+
+TEST_CASE( "checkedElse, failing", "[failing][.]" ) {
+ REQUIRE( testCheckedElse( false ) );
+}
+
+TEST_CASE("Testing checked-if", "[checked-if]") {
+ CHECKED_IF(true) {
+ SUCCEED();
+ }
+ CHECKED_IF(false) {
+ FAIL();
+ }
+ CHECKED_ELSE(true) {
+ FAIL();
+ }
+ CHECKED_ELSE(false) {
+ SUCCEED();
+ }
+}
+
+TEST_CASE("Testing checked-if 2", "[checked-if][!shouldfail]") {
+ CHECKED_IF(true) {
+ FAIL();
+ }
+ // If the checked if is not entered, this passes and the test
+ // fails, because of the [!shouldfail] tag.
+ SUCCEED();
+}
+
+TEST_CASE("Testing checked-if 3", "[checked-if][!shouldfail]") {
+ CHECKED_ELSE(false) {
+ FAIL();
+ }
+ // If the checked false is not entered, this passes and the test
+ // fails, because of the [!shouldfail] tag.
+ SUCCEED();
+}
+
+[[noreturn]]
+TEST_CASE("Testing checked-if 4", "[checked-if][!shouldfail]") {
+ CHECKED_ELSE(true) {}
+ throw std::runtime_error("Uncaught exception should fail!");
+}
+
+[[noreturn]]
+TEST_CASE("Testing checked-if 5", "[checked-if][!shouldfail]") {
+ CHECKED_ELSE(false) {}
+ throw std::runtime_error("Uncaught exception should fail!");
+}
+
+TEST_CASE( "xmlentitycheck" ) {
+ SECTION( "embedded xml: <test>it should be possible to embed xml characters, such as <, \" or &, or even whole <xml>documents</xml> within an attribute</test>" ) {
+ SUCCEED(); // We need this here to stop it failing due to no tests
+ }
+ SECTION( "encoded chars: these should all be encoded: &&&\"\"\"<<<&\"<<&\"" ) {
+ SUCCEED(); // We need this here to stop it failing due to no tests
+ }
+}
+
+TEST_CASE( "send a single char to INFO", "[failing][.]" ) {
+ INFO(3);
+ REQUIRE(false);
+}
+
+TEST_CASE( "Factorials are computed", "[factorial]" ) {
+ REQUIRE( Factorial(0) == 1 );
+ REQUIRE( Factorial(1) == 1 );
+ REQUIRE( Factorial(2) == 2 );
+ REQUIRE( Factorial(3) == 6 );
+ REQUIRE( Factorial(10) == 3628800 );
+}
+
+TEST_CASE( "An empty test with no assertions", "[empty]" ) {}
+
+TEST_CASE( "Nice descriptive name", "[tag1][tag2][tag3][.]" ) {
+ WARN( "This one ran" );
+}
+TEST_CASE( "first tag", "[tag1]" ) {}
+TEST_CASE( "second tag", "[tag2]" ) {}
+
+TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
+
+ std::vector<int> v( 5 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+
+ SECTION( "resizing bigger changes size and capacity" ) {
+ v.resize( 10 );
+
+ REQUIRE( v.size() == 10 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ SECTION( "resizing smaller changes size but not capacity" ) {
+ v.resize( 0 );
+
+ REQUIRE( v.size() == 0 );
+ REQUIRE( v.capacity() >= 5 );
+
+ SECTION( "We can use the 'swap trick' to reset the capacity" ) {
+ std::vector<int> empty;
+ empty.swap( v );
+
+ REQUIRE( v.capacity() == 0 );
+ }
+ }
+ SECTION( "reserving bigger changes capacity but not size" ) {
+ v.reserve( 10 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ SECTION( "reserving smaller does not change size or capacity" ) {
+ v.reserve( 0 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+ }
+}
+
+TEMPLATE_TEST_CASE( "TemplateTest: vectors can be sized and resized", "[vector][template]", int, float, std::string, (std::tuple<int,float>) ) {
+
+ std::vector<TestType> v( 5 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+
+ SECTION( "resizing bigger changes size and capacity" ) {
+ v.resize( 10 );
+
+ REQUIRE( v.size() == 10 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ SECTION( "resizing smaller changes size but not capacity" ) {
+ v.resize( 0 );
+
+ REQUIRE( v.size() == 0 );
+ REQUIRE( v.capacity() >= 5 );
+
+ SECTION( "We can use the 'swap trick' to reset the capacity" ) {
+ std::vector<TestType> empty;
+ empty.swap( v );
+
+ REQUIRE( v.capacity() == 0 );
+ }
+ }
+ SECTION( "reserving bigger changes capacity but not size" ) {
+ v.reserve( 10 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ SECTION( "reserving smaller does not change size or capacity" ) {
+ v.reserve( 0 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+ }
+}
+
+TEMPLATE_TEST_CASE_SIG("TemplateTestSig: vectors can be sized and resized", "[vector][template][nttp]", ((typename TestType, int V), TestType, V), (int,5), (float,4), (std::string,15), ((std::tuple<int, float>), 6)) {
+
+ std::vector<TestType> v(V);
+
+ REQUIRE(v.size() == V);
+ REQUIRE(v.capacity() >= V);
+
+ SECTION("resizing bigger changes size and capacity") {
+ v.resize(2 * V);
+
+ REQUIRE(v.size() == 2 * V);
+ REQUIRE(v.capacity() >= 2 * V);
+ }
+ SECTION("resizing smaller changes size but not capacity") {
+ v.resize(0);
+
+ REQUIRE(v.size() == 0);
+ REQUIRE(v.capacity() >= V);
+
+ SECTION("We can use the 'swap trick' to reset the capacity") {
+ std::vector<TestType> empty;
+ empty.swap(v);
+
+ REQUIRE(v.capacity() == 0);
+ }
+ }
+ SECTION("reserving bigger changes capacity but not size") {
+ v.reserve(2 * V);
+
+ REQUIRE(v.size() == V);
+ REQUIRE(v.capacity() >= 2 * V);
+ }
+ SECTION("reserving smaller does not change size or capacity") {
+ v.reserve(0);
+
+ REQUIRE(v.size() == V);
+ REQUIRE(v.capacity() >= V);
+ }
+}
+
+TEMPLATE_PRODUCT_TEST_CASE("A Template product test case", "[template][product]", (std::vector, Foo), (int, float)) {
+ TestType x;
+ REQUIRE(x.size() == 0);
+}
+
+TEMPLATE_PRODUCT_TEST_CASE_SIG("A Template product test case with array signature", "[template][product][nttp]", ((typename T, size_t S), T, S), (std::array, Bar), ((int, 9), (float, 42))) {
+ TestType x;
+ REQUIRE(x.size() > 0);
+}
+
+TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities", "[template][product]", std::tuple, (int, (int, double), (int, double, float))) {
+ REQUIRE(std::tuple_size<TestType>::value >= 1);
+}
+
+using MyTypes = std::tuple<int, char, float>;
+TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std::tuple", "[template][list]", MyTypes)
+{
+ REQUIRE(std::is_arithmetic<TestType>::value);
+}
+
+struct NonDefaultConstructibleType {
+ NonDefaultConstructibleType() = delete;
+};
+
+using MyNonDefaultConstructibleTypes = std::tuple<NonDefaultConstructibleType, float>;
+TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside non-default-constructible std::tuple", "[template][list]", MyNonDefaultConstructibleTypes)
+{
+ REQUIRE(std::is_trivially_copyable<TestType>::value);
+}
+
+struct NonCopyableAndNonMovableType {
+ NonCopyableAndNonMovableType() = default;
+
+ NonCopyableAndNonMovableType(NonCopyableAndNonMovableType const &) = delete;
+ NonCopyableAndNonMovableType(NonCopyableAndNonMovableType &&) = delete;
+ auto operator=(NonCopyableAndNonMovableType const &) -> NonCopyableAndNonMovableType & = delete;
+ auto operator=(NonCopyableAndNonMovableType &&) -> NonCopyableAndNonMovableType & = delete;
+};
+
+using NonCopyableAndNonMovableTypes = std::tuple<NonCopyableAndNonMovableType, float>;
+TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside non-copyable and non-movable std::tuple", "[template][list]", NonCopyableAndNonMovableTypes)
+{
+ REQUIRE(std::is_default_constructible<TestType>::value);
+}
+
+// https://github.com/philsquared/Catch/issues/166
+TEST_CASE("A couple of nested sections followed by a failure", "[failing][.]") {
+ SECTION("Outer")
+ SECTION("Inner")
+ SUCCEED("that's not flying - that's failing in style");
+
+ FAIL("to infinity and beyond");
+}
+
+TEST_CASE("not allowed", "[!throws]") {
+ // This test case should not be included if you run with -e on the command line
+ SUCCEED();
+}
+
+TEST_CASE( "Tabs and newlines show in output", "[.][whitespace][failing]" ) {
+
+ // Based on issue #242
+ std::string s1 = "if ($b == 10) {\n\t\t$a\t= 20;\n}";
+ std::string s2 = "if ($b == 10) {\n\t$a = 20;\n}\n";
+ CHECK( s1 == s2 );
+}
+
+
+#if defined(CATCH_CONFIG_WCHAR)
+TEST_CASE( "toString on const wchar_t const pointer returns the string contents", "[toString]" ) {
+ const wchar_t * const s = L"wide load";
+ std::string result = ::Catch::Detail::stringify( s );
+ CHECK( result == "\"wide load\"" );
+}
+
+TEST_CASE( "toString on const wchar_t pointer returns the string contents", "[toString]" ) {
+ const wchar_t * s = L"wide load";
+ std::string result = ::Catch::Detail::stringify( s );
+ CHECK( result == "\"wide load\"" );
+}
+
+TEST_CASE( "toString on wchar_t const pointer returns the string contents", "[toString]" ) {
+ auto const s = const_cast<wchar_t*>( L"wide load" );
+ std::string result = ::Catch::Detail::stringify( s );
+ CHECK( result == "\"wide load\"" );
+}
+
+TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) {
+ auto s = const_cast<wchar_t*>( L"wide load" );
+ std::string result = ::Catch::Detail::stringify( s );
+ CHECK( result == "\"wide load\"" );
+}
+#endif // CATCH_CONFIG_WCHAR
+
+TEST_CASE( "long long" ) {
+ constexpr long long l = std::numeric_limits<long long>::max();
+
+ REQUIRE( l == std::numeric_limits<long long>::max() );
+}
+
+TEST_CASE( "This test 'should' fail but doesn't", "[.][failing][!shouldfail]" ) {
+ SUCCEED( "oops!" );
+}
+
+TEST_CASE( "# A test name that starts with a #" ) {
+ SUCCEED( "yay" );
+}
+
+TEST_CASE( "#835 -- errno should not be touched by Catch2", "[.][failing][!shouldfail]" ) {
+ errno = 1;
+ // Check that reporting failed test doesn't change errno.
+ CHECK(f() == 0);
+ // We want to avoid expanding `errno` macro in assertion, because
+ // we capture the expression after macro expansion, and would have
+ // to normalize the ways different platforms spell `errno`.
+ const auto errno_after = errno;
+ REQUIRE(errno_after == 1);
+}
+
+TEST_CASE( "#961 -- Dynamically created sections should all be reported", "[.]" ) {
+ for (char i = '0'; i < '5'; ++i) {
+ SECTION(std::string("Looped section ") + i) {
+ SUCCEED( "Everything is OK" );
+ }
+ }
+}
+
+TEST_CASE( "#1175 - Hidden Test", "[.]" ) {
+ // Just for checking that hidden test is not listed by default
+ SUCCEED();
+}
+
+TEMPLATE_TEST_CASE_SIG("#1954 - 7 arg template test case sig compiles", "[regression][.compilation]",
+ ((int Tnx, int Tnu, int Tny, int Tph, int Tch, int Tineq, int Teq), Tnx, Tnu, Tny, Tph, Tch, Tineq, Teq),
+ (1, 1, 1, 1, 1, 0, 0), (5, 1, 1, 1, 1, 0, 0), (5, 3, 1, 1, 1, 0, 0)) {
+ SUCCEED();
+}
+
+TEST_CASE("Same test name but with different tags is fine", "[.approvals][some-tag]") {}
+TEST_CASE("Same test name but with different tags is fine", "[.approvals][other-tag]") {}
+
+// MinGW doesn't support __try, and Clang has only very partial support
+#if defined(_MSC_VER)
+void throw_and_catch()
+{
+ __try {
+ RaiseException(0xC0000005, 0, 0, NULL);
+ }
+ __except (1)
+ {
+
+ }
+}
+
+
+TEST_CASE("Validate SEH behavior - handled", "[approvals][FatalConditionHandler][CATCH_PLATFORM_WINDOWS]")
+{
+ // Validate that Catch2 framework correctly handles tests raising and handling SEH exceptions.
+ throw_and_catch();
+}
+
+void throw_no_catch()
+{
+ RaiseException(0xC0000005, 0, 0, NULL);
+}
+
+TEST_CASE("Validate SEH behavior - unhandled", "[.approvals][FatalConditionHandler][CATCH_PLATFORM_WINDOWS]")
+{
+ // Validate that Catch2 framework correctly handles tests raising and not handling SEH exceptions.
+ throw_no_catch();
+}
+
+static LONG CALLBACK dummyExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+TEST_CASE("Validate SEH behavior - no crash for stack unwinding", "[approvals][!throws][!shouldfail][FatalConditionHandler][CATCH_PLATFORM_WINDOWS]")
+{
+ // Trigger stack unwinding with SEH top-level filter changed and validate the test fails expectedly with no application crash
+ SetUnhandledExceptionFilter(dummyExceptionFilter);
+ throw 1;
+}
+
+#endif // _MSC_VER
diff --git a/tests/SelfTest/UsageTests/Skip.tests.cpp b/tests/SelfTest/UsageTests/Skip.tests.cpp
new file mode 100644
index 0000000..661795e
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Skip.tests.cpp
@@ -0,0 +1,100 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_range.hpp>
+
+#include <iostream>
+
+TEST_CASE( "tests can be skipped dynamically at runtime", "[skipping]" ) {
+ SKIP();
+ FAIL( "this is not reached" );
+}
+
+TEST_CASE( "skipped tests can optionally provide a reason", "[skipping]" ) {
+ const int answer = 43;
+ SKIP( "skipping because answer = " << answer );
+ FAIL( "this is not reached" );
+}
+
+TEST_CASE( "sections can be skipped dynamically at runtime", "[skipping]" ) {
+ SECTION( "not skipped" ) { SUCCEED(); }
+ SECTION( "skipped" ) { SKIP(); }
+ SECTION( "also not skipped" ) { SUCCEED(); }
+}
+
+TEST_CASE( "nested sections can be skipped dynamically at runtime",
+ "[skipping]" ) {
+ SECTION( "A" ) { std::cout << "a"; }
+ SECTION( "B" ) {
+ SECTION( "B1" ) { std::cout << "b1"; }
+ SECTION( "B2" ) { SKIP(); }
+ }
+ std::cout << "!\n";
+}
+
+TEST_CASE( "dynamic skipping works with generators", "[skipping]" ) {
+ const int answer = GENERATE( 41, 42, 43 );
+ if ( answer != 42 ) { SKIP( "skipping because answer = " << answer ); }
+ SUCCEED();
+}
+
+TEST_CASE( "failed assertions before SKIP cause test case to fail",
+ "[skipping][!shouldfail]" ) {
+ CHECK( 3 == 4 );
+ SKIP();
+}
+
+TEST_CASE( "a succeeding test can still be skipped",
+ "[skipping][!shouldfail]" ) {
+ SUCCEED();
+ SKIP();
+}
+
+TEST_CASE( "failing in some unskipped sections causes entire test case to fail",
+ "[skipping][!shouldfail]" ) {
+ SECTION( "skipped" ) { SKIP(); }
+ SECTION( "not skipped" ) { FAIL(); }
+}
+
+TEST_CASE( "failing for some generator values causes entire test case to fail",
+ "[skipping][!shouldfail]" ) {
+ int i = GENERATE( 1, 2, 3, 4 );
+ if ( i % 2 == 0 ) {
+ SKIP();
+ } else {
+ FAIL();
+ }
+}
+
+namespace {
+ class test_skip_generator : public Catch::Generators::IGenerator<int> {
+ public:
+ explicit test_skip_generator() { SKIP( "This generator is empty" ); }
+
+ auto get() const -> int const& override {
+ static constexpr int value = 1;
+ return value;
+ }
+
+ auto next() -> bool override { return false; }
+ };
+
+ static auto make_test_skip_generator()
+ -> Catch::Generators::GeneratorWrapper<int> {
+ return { new test_skip_generator() };
+ }
+
+} // namespace
+
+TEST_CASE( "Empty generators can SKIP in constructor", "[skipping]" ) {
+ // The generator signals emptiness with `SKIP`
+ auto sample = GENERATE( make_test_skip_generator() );
+ // This assertion would fail, but shouldn't trigger
+ REQUIRE( sample == 0 );
+}
diff --git a/tests/SelfTest/UsageTests/ToStringByte.tests.cpp b/tests/SelfTest/UsageTests/ToStringByte.tests.cpp
new file mode 100644
index 0000000..624abbf
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringByte.tests.cpp
@@ -0,0 +1,23 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+
+TEST_CASE( "std::byte -> toString", "[toString][byte][approvals]" ) {
+ using type = std::byte;
+ REQUIRE( "0" == ::Catch::Detail::stringify( type{ 0 } ) );
+}
+
+TEST_CASE( "std::vector<std::byte> -> toString", "[toString][byte][approvals]" ) {
+ using type = std::vector<std::byte>;
+ REQUIRE( "{ 0, 1, 2 }" == ::Catch::Detail::stringify( type{ std::byte{0}, std::byte{1}, std::byte{2} } ) );
+}
+
+#endif // CATCH_INTERNAL_CONFIG_CPP17_BYTE
diff --git a/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp b/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp
new file mode 100644
index 0000000..744b899
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp
@@ -0,0 +1,51 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <chrono>
+#include <cstdint>
+
+TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") {
+ // No literals because we still support c++11
+ auto hour = std::chrono::hours(1);
+ auto minute = std::chrono::minutes(1);
+ auto seconds = std::chrono::seconds(60);
+ auto micro = std::chrono::microseconds(1);
+ auto milli = std::chrono::milliseconds(1);
+ auto nano = std::chrono::nanoseconds(1);
+ REQUIRE(minute == seconds);
+ REQUIRE(hour != seconds);
+ REQUIRE(micro != milli);
+ REQUIRE(nano != micro);
+}
+
+TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[toString][chrono]") {
+ std::chrono::duration<int64_t, std::ratio<30>> half_minute(1);
+ std::chrono::duration<int64_t, std::ratio<1, 1000000000000>> pico_second(1);
+ std::chrono::duration<int64_t, std::ratio<1, 1000000000000000>> femto_second(1);
+ std::chrono::duration<int64_t, std::ratio<1, 1000000000000000000>> atto_second(1);
+ REQUIRE(half_minute != femto_second);
+ REQUIRE(pico_second != atto_second);
+}
+
+TEST_CASE("Stringifying std::chrono::time_point<system_clock>", "[toString][chrono]") {
+ auto now = std::chrono::system_clock::now();
+ auto later = now + std::chrono::minutes(2);
+ REQUIRE(now != later);
+}
+
+TEST_CASE("Stringifying std::chrono::time_point<Clock>", "[toString][chrono][!nonportable]") {
+ auto now = std::chrono::high_resolution_clock::now();
+ auto later = now + std::chrono::minutes(2);
+ REQUIRE(now != later);
+
+ auto now2 = std::chrono::steady_clock::now();
+ auto later2 = now2 + std::chrono::minutes(2);
+ REQUIRE(now2 != later2);
+}
diff --git a/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp b/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp
new file mode 100644
index 0000000..78c0c80
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp
@@ -0,0 +1,200 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#include <catch2/catch_test_macros.hpp>
+
+#include <map>
+#include <set>
+
+TEST_CASE( "Character pretty printing" ){
+ SECTION("Specifically escaped"){
+ CHECK(::Catch::Detail::stringify('\t') == "'\\t'");
+ CHECK(::Catch::Detail::stringify('\n') == "'\\n'");
+ CHECK(::Catch::Detail::stringify('\r') == "'\\r'");
+ CHECK(::Catch::Detail::stringify('\f') == "'\\f'");
+ }
+ SECTION("General chars"){
+ CHECK(::Catch::Detail::stringify( ' ' ) == "' '" );
+ CHECK(::Catch::Detail::stringify( 'A' ) == "'A'" );
+ CHECK(::Catch::Detail::stringify( 'z' ) == "'z'" );
+ }
+ SECTION("Low ASCII"){
+ CHECK(::Catch::Detail::stringify( '\0' ) == "0" );
+ CHECK(::Catch::Detail::stringify( static_cast<char>(2) ) == "2" );
+ CHECK(::Catch::Detail::stringify( static_cast<char>(5) ) == "5" );
+ }
+}
+
+
+TEST_CASE( "Capture and info messages" ) {
+ SECTION("Capture should stringify like assertions") {
+ int i = 2;
+ CAPTURE(i);
+ REQUIRE(true);
+ }
+ SECTION("Info should NOT stringify the way assertions do") {
+ int i = 3;
+ INFO(i);
+ REQUIRE(true);
+ }
+}
+
+TEST_CASE( "std::map is convertible string", "[toString]" ) {
+
+ SECTION( "empty" ) {
+ std::map<std::string, int> emptyMap;
+
+ REQUIRE( Catch::Detail::stringify( emptyMap ) == "{ }" );
+ }
+
+ SECTION( "single item" ) {
+ std::map<std::string, int> map = { { "one", 1 } };
+
+ REQUIRE( Catch::Detail::stringify( map ) == "{ { \"one\", 1 } }" );
+ }
+
+ SECTION( "several items" ) {
+ std::map<std::string, int> map = {
+ { "abc", 1 },
+ { "def", 2 },
+ { "ghi", 3 }
+ };
+
+ REQUIRE( Catch::Detail::stringify( map ) == "{ { \"abc\", 1 }, { \"def\", 2 }, { \"ghi\", 3 } }" );
+ }
+}
+
+TEST_CASE( "std::set is convertible string", "[toString]" ) {
+
+ SECTION( "empty" ) {
+ std::set<std::string> emptySet;
+
+ REQUIRE( Catch::Detail::stringify( emptySet ) == "{ }" );
+ }
+
+ SECTION( "single item" ) {
+ std::set<std::string> set = { "one" };
+
+ REQUIRE( Catch::Detail::stringify( set ) == "{ \"one\" }" );
+ }
+
+ SECTION( "several items" ) {
+ std::set<std::string> set = { "abc", "def", "ghi" };
+
+ REQUIRE( Catch::Detail::stringify( set ) == "{ \"abc\", \"def\", \"ghi\" }" );
+ }
+}
+
+TEST_CASE("Static arrays are convertible to string", "[toString]") {
+ SECTION("Single item") {
+ int singular[1] = { 1 };
+ REQUIRE(Catch::Detail::stringify(singular) == "{ 1 }");
+ }
+ SECTION("Multiple") {
+ int arr[3] = { 3, 2, 1 };
+ REQUIRE(Catch::Detail::stringify(arr) == "{ 3, 2, 1 }");
+ }
+ SECTION("Non-trivial inner items") {
+ std::vector<std::string> arr[2] = { {"1:1", "1:2", "1:3"}, {"2:1", "2:2"} };
+ REQUIRE(Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })");
+ }
+}
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+
+TEST_CASE("String views are stringified like other strings", "[toString][approvals]") {
+ std::string_view view{"abc"};
+ CHECK(Catch::Detail::stringify(view) == R"("abc")");
+
+ std::string_view arr[] { view };
+ CHECK(Catch::Detail::stringify(arr) == R"({ "abc" })");
+}
+
+#endif
+
+TEST_CASE("Precision of floating point stringification can be set", "[toString][floatingPoint]") {
+ SECTION("Floats") {
+ using sm = Catch::StringMaker<float>;
+ const auto oldPrecision = sm::precision;
+
+ const float testFloat = 1.12345678901234567899f;
+ sm::precision = 5;
+ auto str1 = sm::convert( testFloat );
+ // "1." prefix = 2 chars, f suffix is another char
+ CHECK(str1.size() == 3 + 5);
+
+ sm::precision = 10;
+ auto str2 = sm::convert(testFloat);
+ REQUIRE(str2.size() == 3 + 10);
+ sm::precision = oldPrecision;
+ }
+ SECTION("Double") {
+ using sm = Catch::StringMaker<double>;
+ const auto oldPrecision = sm::precision;
+
+ const double testDouble = 1.123456789012345678901234567899;
+ sm::precision = 5;
+ auto str1 = sm::convert(testDouble);
+ // "1." prefix = 2 chars
+ CHECK(str1.size() == 2 + 5);
+
+ sm::precision = 15;
+ auto str2 = sm::convert(testDouble);
+ REQUIRE(str2.size() == 2 + 15);
+
+ sm::precision = oldPrecision;
+ }
+}
+
+namespace {
+
+struct WhatException : std::exception {
+ char const* what() const noexcept override {
+ return "This exception has overridden what() method";
+ }
+ ~WhatException() override;
+};
+
+struct OperatorException : std::exception {
+ ~OperatorException() override;
+};
+
+std::ostream& operator<<(std::ostream& out, OperatorException const&) {
+ out << "OperatorException";
+ return out;
+}
+
+struct StringMakerException : std::exception {
+ ~StringMakerException() override;
+};
+
+} // end anonymous namespace
+
+namespace Catch {
+template <>
+struct StringMaker<StringMakerException> {
+ static std::string convert(StringMakerException const&) {
+ return "StringMakerException";
+ }
+};
+}
+
+// Avoid -Wweak-tables
+WhatException::~WhatException() = default;
+OperatorException::~OperatorException() = default;
+StringMakerException::~StringMakerException() = default;
+
+
+
+
+TEST_CASE("Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified", "[toString][exception]") {
+ REQUIRE(::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method");
+ REQUIRE(::Catch::Detail::stringify(OperatorException{}) == "OperatorException");
+ REQUIRE(::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException");
+}
diff --git a/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp b/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp
new file mode 100644
index 0000000..3671771
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp
@@ -0,0 +1,35 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+#include <catch2/catch_test_macros.hpp>
+
+#if defined(CATCH_CONFIG_CPP17_OPTIONAL)
+
+TEST_CASE( "std::optional<int> -> toString", "[toString][optional][approvals]" ) {
+ using type = std::optional<int>;
+ REQUIRE( "{ }" == ::Catch::Detail::stringify( type{} ) );
+ REQUIRE( "0" == ::Catch::Detail::stringify( type{ 0 } ) );
+}
+
+TEST_CASE( "std::optional<std::string> -> toString", "[toString][optional][approvals]" ) {
+ using type = std::optional<std::string>;
+ REQUIRE( "{ }" == ::Catch::Detail::stringify( type{} ) );
+ REQUIRE( "\"abc\"" == ::Catch::Detail::stringify( type{ "abc" } ) );
+}
+
+TEST_CASE( "std::vector<std::optional<int> > -> toString", "[toString][optional][approvals]" ) {
+ using type = std::vector<std::optional<int> >;
+ REQUIRE( "{ 0, { }, 2 }" == ::Catch::Detail::stringify( type{ 0, {}, 2 } ) );
+}
+
+TEST_CASE( "std::nullopt -> toString", "[toString][optional][approvals]" ) {
+ REQUIRE( "{ }" == ::Catch::Detail::stringify( std::nullopt ) );
+}
+
+#endif // CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
diff --git a/tests/SelfTest/UsageTests/ToStringPair.tests.cpp b/tests/SelfTest/UsageTests/ToStringPair.tests.cpp
new file mode 100644
index 0000000..f5cb239
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringPair.tests.cpp
@@ -0,0 +1,38 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#include <catch2/catch_test_macros.hpp>
+
+TEST_CASE( "std::pair<int,std::string> -> toString", "[toString][pair]" ) {
+ std::pair<int,std::string> value( 34, "xyzzy" );
+ REQUIRE( ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }" );
+}
+
+TEST_CASE( "std::pair<int,const std::string> -> toString", "[toString][pair]" ) {
+ std::pair<int,const std::string> value( 34, "xyzzy" );
+ REQUIRE( ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }" );
+}
+
+TEST_CASE( "std::vector<std::pair<std::string,int> > -> toString", "[toString][pair]" ) {
+ std::vector<std::pair<std::string,int> > pr;
+ pr.push_back( std::make_pair("green", 55 ) );
+ REQUIRE( ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }" );
+}
+
+// This is pretty contrived - I figure if this works, anything will...
+TEST_CASE( "pair<pair<int,const char *,pair<std::string,int> > -> toString", "[toString][pair]" ) {
+ typedef std::pair<int,const char *> left_t;
+ typedef std::pair<std::string,int> right_t;
+
+ left_t left( 42, "Arthur" );
+ right_t right( "Ford", 24 );
+
+ std::pair<left_t,right_t> pair( left, right );
+ REQUIRE( ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" );
+}
diff --git a/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp b/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp
new file mode 100644
index 0000000..9d1d2c4
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp
@@ -0,0 +1,54 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+#include <catch2/catch_test_macros.hpp>
+
+#include <tuple>
+
+TEST_CASE( "tuple<>", "[toString][tuple]" )
+{
+ typedef std::tuple<> type;
+ CHECK( "{ }" == ::Catch::Detail::stringify(type{}) );
+ type value {};
+ CHECK( "{ }" == ::Catch::Detail::stringify(value) );
+}
+
+TEST_CASE( "tuple<int>", "[toString][tuple]" )
+{
+ typedef std::tuple<int> type;
+ CHECK( "{ 0 }" == ::Catch::Detail::stringify(type{0}) );
+}
+
+
+TEST_CASE( "tuple<float,int>", "[toString][tuple]" )
+{
+ typedef std::tuple<float,int> type;
+ CHECK( "1.5f" == ::Catch::Detail::stringify(float(1.5)) );
+ CHECK( "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) );
+}
+
+TEST_CASE( "tuple<string,string>", "[toString][tuple]" )
+{
+ typedef std::tuple<std::string,std::string> type;
+ CHECK( "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) );
+}
+
+TEST_CASE( "tuple<tuple<int>,tuple<>,float>", "[toString][tuple]" )
+{
+ typedef std::tuple<std::tuple<int>,std::tuple<>,float> type;
+ type value { std::tuple<int>{42}, {}, 1.5f };
+ CHECK( "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) );
+}
+
+TEST_CASE( "tuple<nullptr,int,const char *>", "[approvals][toString][tuple]" ) {
+ typedef std::tuple<std::nullptr_t,int,const char *> type;
+ type value { nullptr, 42, "Catch me" };
+ CHECK( "{ nullptr, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value) );
+}
+
diff --git a/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp b/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp
new file mode 100644
index 0000000..197ba55
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp
@@ -0,0 +1,99 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+#include <catch2/catch_test_macros.hpp>
+
+#if defined(CATCH_CONFIG_CPP17_VARIANT)
+
+#include <string>
+#include <variant>
+
+// We need 2 types with non-trivial copies/moves
+struct MyType1 {
+ MyType1() = default;
+ [[noreturn]] MyType1(MyType1 const&) { throw 1; }
+ MyType1& operator=(MyType1 const&) { throw 3; }
+};
+struct MyType2 {
+ MyType2() = default;
+ [[noreturn]] MyType2(MyType2 const&) { throw 2; }
+ MyType2& operator=(MyType2 const&) { throw 4; }
+};
+
+TEST_CASE( "variant<std::monostate>", "[toString][variant][approvals]")
+{
+ using type = std::variant<std::monostate>;
+ CHECK( "{ }" == ::Catch::Detail::stringify(type{}) );
+ type value {};
+ CHECK( "{ }" == ::Catch::Detail::stringify(value) );
+ CHECK( "{ }" == ::Catch::Detail::stringify(std::get<0>(value)) );
+}
+
+TEST_CASE( "variant<int>", "[toString][variant][approvals]")
+{
+ using type = std::variant<int>;
+ CHECK( "0" == ::Catch::Detail::stringify(type{0}) );
+}
+
+TEST_CASE( "variant<float, int>", "[toString][variant][approvals]")
+{
+ using type = std::variant<float, int>;
+ CHECK( "0.5f" == ::Catch::Detail::stringify(type{0.5f}) );
+ CHECK( "0" == ::Catch::Detail::stringify(type{0}) );
+}
+
+TEST_CASE( "variant -- valueless-by-exception", "[toString][variant][approvals]" ) {
+ using type = std::variant<MyType1, MyType2>;
+
+ type value;
+ REQUIRE_THROWS_AS(value.emplace<MyType2>(MyType2{}), int);
+ REQUIRE(value.valueless_by_exception());
+ CHECK("{valueless variant}" == ::Catch::Detail::stringify(value));
+}
+
+
+TEST_CASE( "variant<string, int>", "[toString][variant][approvals]")
+{
+ using type = std::variant<std::string, int>;
+ CHECK( "\"foo\"" == ::Catch::Detail::stringify(type{"foo"}) );
+ CHECK( "0" == ::Catch::Detail::stringify(type{0}) );
+}
+
+TEST_CASE( "variant<variant<float, int>, string>", "[toString][variant][approvals]")
+{
+ using inner = std::variant<MyType1, float, int>;
+ using type = std::variant<inner, std::string>;
+ CHECK( "0.5f" == ::Catch::Detail::stringify(type{0.5f}) );
+ CHECK( "0" == ::Catch::Detail::stringify(type{0}) );
+ CHECK( "\"foo\"" == ::Catch::Detail::stringify(type{"foo"}) );
+
+ SECTION("valueless nested variant") {
+ type value = inner{0.5f};
+ REQUIRE( std::holds_alternative<inner>(value) );
+ REQUIRE( std::holds_alternative<float>(std::get<inner>(value)) );
+
+ REQUIRE_THROWS_AS( std::get<0>(value).emplace<MyType1>(MyType1{}), int );
+
+ // outer variant is still valid and contains inner
+ REQUIRE( std::holds_alternative<inner>(value) );
+ // inner variant is valueless
+ REQUIRE( std::get<inner>(value).valueless_by_exception() );
+ CHECK( "{valueless variant}" == ::Catch::Detail::stringify(value) );
+ }
+}
+
+TEST_CASE( "variant<nullptr,int,const char *>", "[toString][variant][approvals]" )
+{
+ using type = std::variant<std::nullptr_t,int,const char *>;
+ CHECK( "nullptr" == ::Catch::Detail::stringify(type{nullptr}) );
+ CHECK( "42" == ::Catch::Detail::stringify(type{42}) );
+ CHECK( "\"Catch me\"" == ::Catch::Detail::stringify(type{"Catch me"}) );
+}
+
+#endif // CATCH_INTERNAL_CONFIG_CPP17_VARIANT
diff --git a/tests/SelfTest/UsageTests/ToStringVector.tests.cpp b/tests/SelfTest/UsageTests/ToStringVector.tests.cpp
new file mode 100644
index 0000000..c042744
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringVector.tests.cpp
@@ -0,0 +1,94 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+#include <vector>
+#include <array>
+
+// vector
+TEST_CASE( "vector<int> -> toString", "[toString][vector]" )
+{
+ std::vector<int> vv;
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" );
+ vv.push_back( 42 );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" );
+ vv.push_back( 250 );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" );
+}
+
+TEST_CASE( "vector<string> -> toString", "[toString][vector]" )
+{
+ std::vector<std::string> vv;
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" );
+ vv.emplace_back( "hello" );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\" }" );
+ vv.emplace_back( "world" );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }" );
+}
+
+namespace {
+ /* Minimal Allocator */
+ template<typename T>
+ struct minimal_allocator {
+ using value_type = T;
+ using size_type = std::size_t;
+
+ minimal_allocator() = default;
+ template <typename U>
+ minimal_allocator(const minimal_allocator<U>&) {}
+
+
+ T *allocate( size_type n ) {
+ return static_cast<T *>( ::operator new( n * sizeof(T) ) );
+ }
+ void deallocate( T *p, size_type /*n*/ ) {
+ ::operator delete( static_cast<void *>(p) );
+ }
+ template<typename U>
+ bool operator==( const minimal_allocator<U>& ) const { return true; }
+ template<typename U>
+ bool operator!=( const minimal_allocator<U>& ) const { return false; }
+ };
+}
+
+TEST_CASE( "vector<int,allocator> -> toString", "[toString][vector,allocator]" ) {
+ std::vector<int,minimal_allocator<int> > vv;
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ }" );
+ vv.push_back( 42 );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42 }" );
+ vv.push_back( 250 );
+ REQUIRE( ::Catch::Detail::stringify(vv) == "{ 42, 250 }" );
+}
+
+TEST_CASE( "vec<vec<string,alloc>> -> toString", "[toString][vector,allocator]" ) {
+ using inner = std::vector<std::string, minimal_allocator<std::string>>;
+ using vector = std::vector<inner>;
+ vector v;
+ REQUIRE( ::Catch::Detail::stringify(v) == "{ }" );
+ v.push_back( inner { "hello" } );
+ v.push_back( inner { "world" } );
+ REQUIRE( ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }" );
+}
+
+// Based on PR by mat-so: https://github.com/catchorg/Catch2/pull/606/files#diff-43562f40f8c6dcfe2c54557316e0f852
+TEST_CASE( "vector<bool> -> toString", "[toString][containers][vector]" ) {
+ std::vector<bool> bools;
+ REQUIRE( ::Catch::Detail::stringify(bools) == "{ }");
+ bools.push_back(true);
+ REQUIRE( ::Catch::Detail::stringify(bools) == "{ true }");
+ bools.push_back(false);
+ REQUIRE( ::Catch::Detail::stringify(bools) == "{ true, false }");
+}
+TEST_CASE( "array<int, N> -> toString", "[toString][containers][array]" ) {
+ std::array<int, 0> empty;
+ REQUIRE( Catch::Detail::stringify( empty ) == "{ }" );
+ std::array<int, 1> oneValue = {{ 42 }};
+ REQUIRE( Catch::Detail::stringify( oneValue ) == "{ 42 }" );
+ std::array<int, 2> twoValues = {{ 42, 250 }};
+ REQUIRE( Catch::Detail::stringify( twoValues ) == "{ 42, 250 }" );
+}
diff --git a/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp b/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp
new file mode 100644
index 0000000..ec7a49e
--- /dev/null
+++ b/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp
@@ -0,0 +1,186 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+
+
+
+#if defined(__GNUC__)
+// This has to be left enabled until end of the TU, because the GCC
+// frontend reports operator<<(std::ostream& os, const has_maker_and_operator&)
+// as unused anyway
+# pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+namespace {
+
+struct has_operator { };
+struct has_maker {};
+struct has_maker_and_operator {};
+struct has_neither {};
+struct has_template_operator {};
+
+std::ostream& operator<<(std::ostream& os, const has_operator&) {
+ os << "operator<<( has_operator )";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const has_maker_and_operator&) {
+ os << "operator<<( has_maker_and_operator )";
+ return os;
+}
+
+template <typename StreamT>
+StreamT& operator<<(StreamT& os, const has_template_operator&) {
+ os << "operator<<( has_template_operator )";
+ return os;
+}
+
+} // end anonymous namespace
+
+namespace Catch {
+ template<>
+ struct StringMaker<has_maker> {
+ static std::string convert( const has_maker& ) {
+ return "StringMaker<has_maker>";
+ }
+ };
+ template<>
+ struct StringMaker<has_maker_and_operator> {
+ static std::string convert( const has_maker_and_operator& ) {
+ return "StringMaker<has_maker_and_operator>";
+ }
+ };
+}
+
+// Call the operator
+TEST_CASE( "stringify( has_operator )", "[toString]" ) {
+ has_operator item;
+ REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_operator )" );
+}
+
+// Call the stringmaker
+TEST_CASE( "stringify( has_maker )", "[toString]" ) {
+ has_maker item;
+ REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker>" );
+}
+
+// Call the stringmaker
+TEST_CASE( "stringify( has_maker_and_operator )", "[toString]" ) {
+ has_maker_and_operator item;
+ REQUIRE( ::Catch::Detail::stringify( item ) == "StringMaker<has_maker_and_operator>" );
+}
+
+TEST_CASE("stringify( has_neither )", "[toString]") {
+ has_neither item;
+ REQUIRE( ::Catch::Detail::stringify(item) == "{?}" );
+}
+
+// Call the templated operator
+TEST_CASE( "stringify( has_template_operator )", "[toString]" ) {
+ has_template_operator item;
+ REQUIRE( ::Catch::Detail::stringify( item ) == "operator<<( has_template_operator )" );
+}
+
+
+// Vectors...
+
+TEST_CASE( "stringify( vectors<has_operator> )", "[toString]" ) {
+ std::vector<has_operator> v(1);
+ REQUIRE( ::Catch::Detail::stringify( v ) == "{ operator<<( has_operator ) }" );
+}
+
+TEST_CASE( "stringify( vectors<has_maker> )", "[toString]" ) {
+ std::vector<has_maker> v(1);
+ REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker> }" );
+}
+
+TEST_CASE( "stringify( vectors<has_maker_and_operator> )", "[toString]" ) {
+ std::vector<has_maker_and_operator> v(1);
+ REQUIRE( ::Catch::Detail::stringify( v ) == "{ StringMaker<has_maker_and_operator> }" );
+}
+
+namespace {
+
+// Range-based conversion should only be used if other possibilities fail
+struct int_iterator {
+ using iterator_category = std::input_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = int;
+ using reference = int&;
+ using pointer = int*;
+
+ int_iterator() = default;
+ int_iterator(int i) :val(i) {}
+
+ value_type operator*() const { return val; }
+ bool operator==(int_iterator rhs) const { return val == rhs.val; }
+ bool operator!=(int_iterator rhs) const { return val != rhs.val; }
+ int_iterator operator++() { ++val; return *this; }
+ int_iterator operator++(int) {
+ auto temp(*this);
+ ++val;
+ return temp;
+ }
+private:
+ int val = 5;
+};
+
+struct streamable_range {
+ int_iterator begin() const { return int_iterator{ 1 }; }
+ int_iterator end() const { return {}; }
+};
+
+std::ostream& operator<<(std::ostream& os, const streamable_range&) {
+ os << "op<<(streamable_range)";
+ return os;
+}
+
+struct stringmaker_range {
+ int_iterator begin() const { return int_iterator{ 1 }; }
+ int_iterator end() const { return {}; }
+};
+
+} // end anonymous namespace
+
+namespace Catch {
+template <>
+struct StringMaker<stringmaker_range> {
+ static std::string convert(stringmaker_range const&) {
+ return "stringmaker(streamable_range)";
+ }
+};
+}
+
+namespace {
+
+struct just_range {
+ int_iterator begin() const { return int_iterator{ 1 }; }
+ int_iterator end() const { return {}; }
+};
+
+struct disabled_range {
+ int_iterator begin() const { return int_iterator{ 1 }; }
+ int_iterator end() const { return {}; }
+};
+
+} // end anonymous namespace
+
+namespace Catch {
+template <>
+struct is_range<disabled_range> {
+ static const bool value = false;
+};
+}
+
+TEST_CASE("stringify ranges", "[toString]") {
+ REQUIRE(::Catch::Detail::stringify(streamable_range{}) == "op<<(streamable_range)");
+ REQUIRE(::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)");
+ REQUIRE(::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }");
+ REQUIRE(::Catch::Detail::stringify(disabled_range{}) == "{?}");
+}
diff --git a/tests/SelfTest/UsageTests/Tricky.tests.cpp b/tests/SelfTest/UsageTests/Tricky.tests.cpp
new file mode 100644
index 0000000..041d786
--- /dev/null
+++ b/tests/SelfTest/UsageTests/Tricky.tests.cpp
@@ -0,0 +1,380 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4702) // Disable unreachable code warning for the last test
+ // that is triggered when compiling as Win32|Release
+#endif
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
+#include <catch2/generators/catch_generators_range.hpp>
+
+#include <cstdio>
+#include <sstream>
+#include <iostream>
+
+struct Opaque
+{
+ int val;
+ bool operator ==( const Opaque& o ) const
+ {
+ return val == o.val;
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE
+(
+ "A failing expression with a non streamable type is still captured",
+ "[Tricky][failing][.]"
+)
+{
+
+ Opaque o1, o2;
+ o1.val = 7;
+ o2.val = 8;
+
+ CHECK( &o1 == &o2 );
+ CHECK( o1 == o2 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_CASE
+(
+ "An expression with side-effects should only be evaluated once",
+ "[Tricky]"
+)
+{
+ int i = 7;
+
+ REQUIRE( i++ == 7 );
+ REQUIRE( i++ == 8 );
+
+}
+
+namespace A {
+ struct X
+ {
+ X() : a(4), b(2), c(7) {}
+ X(int v) : a(v), b(2), c(7) {}
+ int a;
+ int b;
+ int c;
+ };
+}
+
+namespace B {
+ struct Y
+ {
+ Y() : a(4), b(2), c(7) {}
+ Y(int v) : a(v), b(2), c(7) {}
+ int a;
+ int b;
+ int c;
+ };
+}
+
+inline bool operator==(const A::X& lhs, const B::Y& rhs)
+{
+ return (lhs.a == rhs.a);
+}
+
+inline bool operator==(const B::Y& lhs, const A::X& rhs)
+{
+ return (lhs.a == rhs.a);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/* This, currently, does not compile with LLVM
+TEST_CASE
+(
+ "Operators at different namespace levels not hijacked by Koenig lookup"
+ "[Tricky]"
+)
+{
+ A::X x;
+ B::Y y;
+ REQUIRE( x == y );
+}
+*/
+
+namespace ObjectWithConversions
+{
+ struct Object
+ {
+ operator unsigned int() const {return 0xc0000000;}
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ TEST_CASE
+ (
+ "Implicit conversions are supported inside assertion macros",
+ "[Tricky][approvals]"
+ )
+ {
+ Object o;
+ REQUIRE(0xc0000000 == o );
+ }
+}
+
+namespace EnumBitFieldTests
+{
+ enum Bits : uint32_t {
+ bit0 = 0x0001,
+ bit1 = 0x0002,
+ bit2 = 0x0004,
+ bit3 = 0x0008,
+ bit1and2 = bit1 | bit2,
+ bit30 = 0x40000000,
+ bit31 = 0x80000000,
+ bit30and31 = bit30 | bit31
+ };
+
+ TEST_CASE( "Test enum bit values", "[Tricky]" )
+ {
+ REQUIRE( 0xc0000000 == bit30and31 );
+ }
+}
+
+struct Obj
+{
+ Obj():prop(&p){}
+
+ int p = 0;
+ int* prop;
+};
+
+TEST_CASE("boolean member", "[Tricky]")
+{
+ Obj obj;
+ REQUIRE( obj.prop != nullptr );
+}
+
+// Tests for a problem submitted by Ralph McArdell
+//
+// The static bool value should not need to be defined outside the
+// struct it is declared in - but when evaluating it in a deduced
+// context it appears to require the extra definition.
+// The issue was fixed by adding bool overloads to bypass the
+// templates that were there to deduce it.
+template <bool B>
+struct is_true
+{
+ static const bool value = B;
+};
+
+TEST_CASE( "(unimplemented) static bools can be evaluated", "[Tricky]" )
+{
+ SECTION("compare to true")
+ {
+ REQUIRE( is_true<true>::value == true );
+ REQUIRE( true == is_true<true>::value );
+ }
+ SECTION("compare to false")
+ {
+ REQUIRE( is_true<false>::value == false );
+ REQUIRE( false == is_true<false>::value );
+ }
+
+ SECTION("negation")
+ {
+ REQUIRE( !is_true<false>::value );
+ }
+
+ SECTION("double negation")
+ {
+ REQUIRE( !!is_true<true>::value );
+ }
+
+ SECTION("direct")
+ {
+ REQUIRE( is_true<true>::value );
+ REQUIRE_FALSE( is_true<false>::value );
+ }
+}
+
+struct Boolable
+{
+ explicit Boolable( bool value ) : m_value( value ) {}
+
+ explicit operator bool() const {
+ return m_value;
+ }
+
+ bool m_value;
+};
+
+TEST_CASE( "Objects that evaluated in boolean contexts can be checked", "[Tricky][SafeBool]" )
+{
+ Boolable True( true );
+ Boolable False( false );
+
+ CHECK( True );
+ CHECK( !False );
+ CHECK_FALSE( False );
+}
+
+TEST_CASE( "Assertions then sections", "[Tricky]" )
+{
+ // This was causing a failure due to the way the console reporter was handling
+ // the current section
+
+ REQUIRE( true );
+
+ SECTION( "A section" )
+ {
+ REQUIRE( true );
+
+ SECTION( "Another section" )
+ {
+ REQUIRE( true );
+ }
+ SECTION( "Another other section" )
+ {
+ REQUIRE( true );
+ }
+ }
+}
+
+struct Awkward
+{
+ operator int() const { return 7; }
+};
+
+TEST_CASE( "non streamable - with conv. op", "[Tricky]" )
+{
+ Awkward awkward;
+ std::string s = ::Catch::Detail::stringify( awkward );
+ REQUIRE( s == "7" );
+}
+
+inline void foo() {}
+
+using fooptr_t = void (*)();
+
+TEST_CASE( "Comparing function pointers", "[Tricky][function pointer]" )
+{
+ // This was giving a warning in VS2010
+ // #179
+ fooptr_t a = foo;
+
+ REQUIRE( a );
+ REQUIRE( a == &foo );
+}
+
+struct S
+{
+ void f() {}
+};
+
+
+TEST_CASE( "Comparing member function pointers", "[Tricky][member function pointer][approvals]" )
+{
+ using MF = void (S::*)();
+ MF m = &S::f;
+
+ CHECK( m == &S::f );
+}
+
+class ClassName {};
+
+TEST_CASE( "pointer to class", "[Tricky]" )
+{
+ ClassName *p = 0;
+ REQUIRE( p == 0 );
+}
+
+#include <memory>
+
+TEST_CASE( "null_ptr", "[Tricky]" )
+{
+ std::unique_ptr<int> ptr;
+ REQUIRE(ptr.get() == nullptr);
+}
+
+TEST_CASE( "X/level/0/a", "[Tricky]" ) { SUCCEED(""); }
+TEST_CASE( "X/level/0/b", "[Tricky][fizz]" ){ SUCCEED(""); }
+TEST_CASE( "X/level/1/a", "[Tricky]" ) { SUCCEED(""); }
+TEST_CASE( "X/level/1/b", "[Tricky]" ) { SUCCEED(""); }
+
+TEST_CASE( "has printf" ) {
+
+ // This can cause problems as, currently, stdout itself is not redirected - only the cout (and cerr) buffer
+ printf( "loose text artifact\n" );
+}
+
+namespace {
+ struct constructor_throws {
+ [[noreturn]] constructor_throws() {
+ throw 1;
+ }
+ };
+}
+
+TEST_CASE("Commas in various macros are allowed") {
+ REQUIRE_THROWS( std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} );
+ CHECK_THROWS( std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} );
+ REQUIRE_NOTHROW( std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} );
+ CHECK_NOTHROW( std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} );
+
+ REQUIRE(std::vector<int>{1, 2} == std::vector<int>{1, 2});
+ CHECK( std::vector<int>{1, 2} == std::vector<int>{1, 2} );
+ REQUIRE_FALSE(std::vector<int>{1, 2} == std::vector<int>{1, 2, 3});
+ CHECK_FALSE( std::vector<int>{1, 2} == std::vector<int>{1, 2, 3} );
+
+ CHECK_NOFAIL( std::vector<int>{1, 2} == std::vector<int>{1, 2} );
+ CHECKED_IF( std::vector<int>{1, 2} == std::vector<int>{1, 2} ) {
+ REQUIRE(true);
+ } CHECKED_ELSE( std::vector<int>{1, 2} == std::vector<int>{1, 2} ) {
+ CHECK(true);
+ }
+}
+
+TEST_CASE( "non-copyable objects", "[.][failing]" ) {
+ // Thanks to Agustin Bergé (@k-ballo on the cpplang Slack) for raising this
+ std::type_info const& ti = typeid(int);
+ CHECK( ti == typeid(int) );
+}
+
+TEST_CASE("#1514: stderr/stdout is not captured in tests aborted by an exception", "[output-capture][regression][.]") {
+ std::cout << "This would not be caught previously\n" << std::flush;
+ std::clog << "Nor would this\n" << std::flush;
+ // FAIL aborts the test by throwing a Catch exception
+ FAIL("1514");
+}
+
+
+TEST_CASE( "#2025: -c shouldn't cause infinite loop", "[sections][generators][regression][.approvals]" ) {
+ SECTION( "Check cursor from buffer offset" ) {
+ auto bufPos = GENERATE_REF( range( 0, 44 ) );
+ WHEN( "Buffer position is " << bufPos ) { REQUIRE( 1 == 1 ); }
+ }
+}
+
+TEST_CASE("#2025: original repro", "[sections][generators][regression][.approvals]") {
+ auto fov = GENERATE(true, false);
+ DYNAMIC_SECTION("fov_" << fov) {
+ std::cout << "inside with fov: " << fov << '\n';
+ }
+}
+
+TEST_CASE("#2025: same-level sections", "[sections][generators][regression][.approvals]") {
+ SECTION("A") {
+ SUCCEED();
+ }
+ auto i = GENERATE(1, 2, 3);
+ SECTION("B") {
+ REQUIRE(i < 4);
+ }
+}
diff --git a/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp b/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp
new file mode 100644
index 0000000..92048a0
--- /dev/null
+++ b/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp
@@ -0,0 +1,29 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_test_macros.hpp>
+
+
+TEST_CASE()
+{
+ SUCCEED( "anonymous test case" );
+}
+
+TEST_CASE( "Test case with one argument" )
+{
+ SUCCEED( "no assertions" );
+}
+
+TEST_CASE( "Variadic macros", "[variadic][sections]" )
+{
+ SECTION( "Section with one argument" )
+ {
+ SUCCEED( "no assertions" );
+ }
+}
+