vendor Catch2 and ETL
This commit is contained in:
@ -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));
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -0,0 +1,469 @@
|
||||
|
||||
// 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 <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/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
|
@ -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 );
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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 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 REGISTER_ENUM" ) {
|
||||
using Catch::Detail::stringify;
|
||||
REQUIRE( stringify( Bikeshed::Colours::Red ) == "Red" );
|
||||
REQUIRE( stringify( Bikeshed::Colours::Blue ) == "Blue" );
|
||||
}
|
@ -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
|
@ -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 );
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,917 @@
|
||||
|
||||
// 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("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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ) );
|
||||
}
|
||||
}
|
||||
}
|
@ -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!" );
|
||||
}
|
@ -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
|
@ -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 );
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
@ -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");
|
||||
}
|
@ -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
|
@ -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 } }" );
|
||||
}
|
@ -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) );
|
||||
}
|
||||
|
@ -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
|
@ -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 }" );
|
||||
}
|
@ -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{}) == "{?}");
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user