aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
commitbc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch)
tree1e629e7b46b1d9972a973bc93fd100bcebd395be /examples
downloadnihil-vendor/catch2.tar.gz
nihil-vendor/catch2.tar.bz2
Diffstat (limited to 'examples')
-rw-r--r--examples/010-TestCase.cpp41
-rw-r--r--examples/020-TestCase-1.cpp37
-rw-r--r--examples/020-TestCase-2.cpp41
-rw-r--r--examples/030-Asn-Require-Check.cpp82
-rw-r--r--examples/100-Fix-Section.cpp78
-rw-r--r--examples/110-Fix-ClassFixture.cpp74
-rw-r--r--examples/111-Fix-PersistentFixture.cpp74
-rw-r--r--examples/120-Bdd-ScenarioGivenWhenThen.cpp81
-rw-r--r--examples/210-Evt-EventListeners.cpp436
-rw-r--r--examples/231-Cfg-OutputStreams.cpp63
-rw-r--r--examples/232-Cfg-CustomMain.cpp41
-rw-r--r--examples/300-Gen-OwnGenerator.cpp77
-rw-r--r--examples/301-Gen-MapTypeConversion.cpp69
-rw-r--r--examples/302-Gen-Table.cpp63
-rw-r--r--examples/310-Gen-VariablesInGenerators.cpp43
-rw-r--r--examples/311-Gen-CustomCapture.cpp51
-rw-r--r--examples/CMakeLists.txt62
17 files changed, 1413 insertions, 0 deletions
diff --git a/examples/010-TestCase.cpp b/examples/010-TestCase.cpp
new file mode 100644
index 0000000..9e5cd8c
--- /dev/null
+++ b/examples/010-TestCase.cpp
@@ -0,0 +1,41 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+// 010-TestCase.cpp
+// And write tests in the same file:
+#include <catch2/catch_test_macros.hpp>
+
+static int Factorial( int number ) {
+ return number <= 1 ? number : Factorial( number - 1 ) * number; // fail
+// return number <= 1 ? 1 : Factorial( number - 1 ) * number; // pass
+}
+
+TEST_CASE( "Factorial of 0 is 1 (fail)", "[single-file]" ) {
+ REQUIRE( Factorial(0) == 1 );
+}
+
+TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
+ REQUIRE( Factorial(1) == 1 );
+ REQUIRE( Factorial(2) == 2 );
+ REQUIRE( Factorial(3) == 6 );
+ REQUIRE( Factorial(10) == 3628800 );
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 010-TestCase.cpp && 010-TestCase --success
+
+// Expected compact output (all assertions):
+//
+// prompt> 010-TestCase --reporter compact --success
+// 010-TestCase.cpp:14: failed: Factorial(0) == 1 for: 0 == 1
+// 010-TestCase.cpp:18: passed: Factorial(1) == 1 for: 1 == 1
+// 010-TestCase.cpp:19: passed: Factorial(2) == 2 for: 2 == 2
+// 010-TestCase.cpp:20: passed: Factorial(3) == 6 for: 6 == 6
+// 010-TestCase.cpp:21: passed: Factorial(10) == 3628800 for: 3628800 (0x375f00) == 3628800 (0x375f00)
+// Failed 1 test case, failed 1 assertion.
diff --git a/examples/020-TestCase-1.cpp b/examples/020-TestCase-1.cpp
new file mode 100644
index 0000000..a9d87db
--- /dev/null
+++ b/examples/020-TestCase-1.cpp
@@ -0,0 +1,37 @@
+
+// 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
+
+// 020-TestCase-1.cpp
+
+#include <catch2/catch_test_macros.hpp>
+
+TEST_CASE( "1: All test cases reside in other .cpp files (empty)", "[multi-file:1]" ) {
+}
+
+// ^^^
+// Normally no TEST_CASEs in this file.
+// Here just to show there are two source files via option --list-tests.
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
+//
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 020-TestCase-1.cpp
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -Fe020-TestCase.exe 020-TestCase-1.obj 020-TestCase-2.cpp && 020-TestCase --success
+
+// Expected test case listing:
+//
+// prompt> 020-TestCase --list-tests *
+// Matching test cases:
+// 1: All test cases reside in other .cpp files (empty)
+// [multi-file:1]
+// 2: Factorial of 0 is computed (fail)
+// [multi-file:2]
+// 2: Factorials of 1 and higher are computed (pass)
+// [multi-file:2]
+// 3 matching test cases
diff --git a/examples/020-TestCase-2.cpp b/examples/020-TestCase-2.cpp
new file mode 100644
index 0000000..72dd0ff
--- /dev/null
+++ b/examples/020-TestCase-2.cpp
@@ -0,0 +1,41 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+// 020-TestCase-2.cpp
+
+// main() provided by Catch in file 020-TestCase-1.cpp.
+
+#include <catch2/catch_test_macros.hpp>
+
+static int Factorial( int number ) {
+ return number <= 1 ? number : Factorial( number - 1 ) * number; // fail
+// return number <= 1 ? 1 : Factorial( number - 1 ) * number; // pass
+}
+
+TEST_CASE( "2: Factorial of 0 is 1 (fail)", "[multi-file:2]" ) {
+ REQUIRE( Factorial(0) == 1 );
+}
+
+TEST_CASE( "2: Factorials of 1 and higher are computed (pass)", "[multi-file:2]" ) {
+ REQUIRE( Factorial(1) == 1 );
+ REQUIRE( Factorial(2) == 2 );
+ REQUIRE( Factorial(3) == 6 );
+ REQUIRE( Factorial(10) == 3628800 );
+}
+
+// Compile: see 020-TestCase-1.cpp
+
+// Expected compact output (all assertions):
+//
+// prompt> 020-TestCase --reporter compact --success
+// 020-TestCase-2.cpp:13: failed: Factorial(0) == 1 for: 0 == 1
+// 020-TestCase-2.cpp:17: passed: Factorial(1) == 1 for: 1 == 1
+// 020-TestCase-2.cpp:18: passed: Factorial(2) == 2 for: 2 == 2
+// 020-TestCase-2.cpp:19: passed: Factorial(3) == 6 for: 6 == 6
+// 020-TestCase-2.cpp:20: passed: Factorial(10) == 3628800 for: 3628800 (0x375f00) == 3628800 (0x375f00)
+// Failed 1 test case, failed 1 assertion.
diff --git a/examples/030-Asn-Require-Check.cpp b/examples/030-Asn-Require-Check.cpp
new file mode 100644
index 0000000..62cd3cf
--- /dev/null
+++ b/examples/030-Asn-Require-Check.cpp
@@ -0,0 +1,82 @@
+
+// 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
+
+// 030-Asn-Require-Check.cpp
+
+// Catch has two natural expression assertion macro's:
+// - REQUIRE() stops at first failure.
+// - CHECK() continues after failure.
+
+// There are two variants to support decomposing negated expressions:
+// - REQUIRE_FALSE() stops at first failure.
+// - CHECK_FALSE() continues after failure.
+
+// main() provided by linkage to Catch2WithMain
+
+#include <catch2/catch_test_macros.hpp>
+
+static std::string one() {
+ return "1";
+}
+
+TEST_CASE( "Assert that something is true (pass)", "[require]" ) {
+ REQUIRE( one() == "1" );
+}
+
+TEST_CASE( "Assert that something is true (fail)", "[require]" ) {
+ REQUIRE( one() == "x" );
+}
+
+TEST_CASE( "Assert that something is true (stop at first failure)", "[require]" ) {
+ WARN( "REQUIRE stops at first failure:" );
+
+ REQUIRE( one() == "x" );
+ REQUIRE( one() == "1" );
+}
+
+TEST_CASE( "Assert that something is true (continue after failure)", "[check]" ) {
+ WARN( "CHECK continues after failure:" );
+
+ CHECK( one() == "x" );
+ REQUIRE( one() == "1" );
+}
+
+TEST_CASE( "Assert that something is false (stops at first failure)", "[require-false]" ) {
+ WARN( "REQUIRE_FALSE stops at first failure:" );
+
+ REQUIRE_FALSE( one() == "1" );
+ REQUIRE_FALSE( one() != "1" );
+}
+
+TEST_CASE( "Assert that something is false (continue after failure)", "[check-false]" ) {
+ WARN( "CHECK_FALSE continues after failure:" );
+
+ CHECK_FALSE( one() == "1" );
+ REQUIRE_FALSE( one() != "1" );
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
+
+// Expected compact output (all assertions):
+//
+// prompt> 030-Asn-Require-Check.exe --reporter compact --success
+// 030-Asn-Require-Check.cpp:20: passed: one() == "1" for: "1" == "1"
+// 030-Asn-Require-Check.cpp:24: failed: one() == "x" for: "1" == "x"
+// 030-Asn-Require-Check.cpp:28: warning: 'REQUIRE stops at first failure:'
+// 030-Asn-Require-Check.cpp:30: failed: one() == "x" for: "1" == "x"
+// 030-Asn-Require-Check.cpp:35: warning: 'CHECK continues after failure:'
+// 030-Asn-Require-Check.cpp:37: failed: one() == "x" for: "1" == "x"
+// 030-Asn-Require-Check.cpp:38: passed: one() == "1" for: "1" == "1"
+// 030-Asn-Require-Check.cpp:42: warning: 'REQUIRE_FALSE stops at first failure:'
+// 030-Asn-Require-Check.cpp:44: failed: !(one() == "1") for: !("1" == "1")
+// 030-Asn-Require-Check.cpp:49: warning: 'CHECK_FALSE continues after failure:'
+// 030-Asn-Require-Check.cpp:51: failed: !(one() == "1") for: !("1" == "1")
+// 030-Asn-Require-Check.cpp:52: passed: !(one() != "1") for: !("1" != "1")
+// Failed 5 test cases, failed 5 assertions.
diff --git a/examples/100-Fix-Section.cpp b/examples/100-Fix-Section.cpp
new file mode 100644
index 0000000..7c8d8aa
--- /dev/null
+++ b/examples/100-Fix-Section.cpp
@@ -0,0 +1,78 @@
+
+// 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
+
+// 100-Fix-Section.cpp
+
+// Catch has two ways to express fixtures:
+// - Sections (this file)
+// - Traditional class-based fixtures
+
+// main() provided by linkage to Catch2WithMain
+
+#include <catch2/catch_test_macros.hpp>
+#include <vector>
+
+TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
+
+ // For each section, vector v is anew:
+
+ 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( "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 );
+ }
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp && 100-Fix-Section --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 100-Fix-Section.cpp && 100-Fix-Section --success
+
+// Expected compact output (all assertions):
+//
+// prompt> 100-Fix-Section.exe --reporter compact --success
+// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
+// 100-Fix-Section.cpp:23: passed: v.size() == 10 for: 10 == 10
+// 100-Fix-Section.cpp:24: passed: v.capacity() >= 10 for: 10 >= 10
+// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
+// 100-Fix-Section.cpp:29: passed: v.size() == 0 for: 0 == 0
+// 100-Fix-Section.cpp:30: passed: v.capacity() >= 5 for: 5 >= 5
+// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
+// 100-Fix-Section.cpp:35: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:36: passed: v.capacity() >= 10 for: 10 >= 10
+// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
+// 100-Fix-Section.cpp:41: passed: v.size() == 5 for: 5 == 5
+// 100-Fix-Section.cpp:42: passed: v.capacity() >= 5 for: 5 >= 5
+// Passed 1 test case with 16 assertions.
diff --git a/examples/110-Fix-ClassFixture.cpp b/examples/110-Fix-ClassFixture.cpp
new file mode 100644
index 0000000..614c379
--- /dev/null
+++ b/examples/110-Fix-ClassFixture.cpp
@@ -0,0 +1,74 @@
+
+// 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
+
+// 110-Fix-ClassFixture.cpp
+
+// Catch has two ways to express fixtures:
+// - Sections
+// - Traditional class-based fixtures (this file)
+
+// main() provided by linkage to Catch2WithMain
+
+#include <catch2/catch_test_macros.hpp>
+
+class DBConnection
+{
+public:
+ static DBConnection createConnection( std::string const & /*dbName*/ ) {
+ return DBConnection();
+ }
+
+ bool executeSQL( std::string const & /*query*/, int const /*id*/, std::string const & arg ) {
+ if ( arg.length() == 0 ) {
+ throw std::logic_error("empty SQL query argument");
+ }
+ return true; // ok
+ }
+};
+
+class UniqueTestsFixture
+{
+protected:
+ UniqueTestsFixture()
+ : conn( DBConnection::createConnection( "myDB" ) )
+ {}
+
+ int getID() {
+ return ++uniqueID;
+ }
+
+protected:
+ DBConnection conn;
+
+private:
+ static int uniqueID;
+};
+
+int UniqueTestsFixture::uniqueID = 0;
+
+TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/No Name", "[create]" ) {
+ REQUIRE_THROWS( conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "") );
+}
+
+TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/Normal", "[create]" ) {
+ REQUIRE( conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs" ) );
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
+//
+// Compile with pkg-config:
+// - g++ -std=c++14 -Wall $(pkg-config catch2-with-main --cflags) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp $(pkg-config catch2-with-main --libs)
+
+// Expected compact output (all assertions):
+//
+// prompt> 110-Fix-ClassFixture.exe --reporter compact --success
+// 110-Fix-ClassFixture.cpp:47: passed: conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "")
+// 110-Fix-ClassFixture.cpp:51: passed: conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs" ) for: true
+// Passed both 2 test cases with 2 assertions.
diff --git a/examples/111-Fix-PersistentFixture.cpp b/examples/111-Fix-PersistentFixture.cpp
new file mode 100644
index 0000000..2bef90f
--- /dev/null
+++ b/examples/111-Fix-PersistentFixture.cpp
@@ -0,0 +1,74 @@
+
+// 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
+
+// Fixture.cpp
+
+// Catch2 has three ways to express fixtures:
+// - Sections
+// - Traditional class-based fixtures that are created and destroyed on every
+// partial run
+// - Traditional class-based fixtures that are created at the start of a test
+// case and destroyed at the end of a test case (this file)
+
+// main() provided by linkage to Catch2WithMain
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <thread>
+
+class ClassWithExpensiveSetup {
+public:
+ ClassWithExpensiveSetup() {
+ // Imagine some really expensive set up here.
+ // e.g.
+ // setting up a D3D12/Vulkan Device,
+ // connecting to a database,
+ // loading a file
+ // etc etc etc
+ std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
+ }
+
+ ~ClassWithExpensiveSetup() noexcept {
+ // We can do any clean up of the expensive class in the destructor
+ // e.g.
+ // destroy D3D12/Vulkan Device,
+ // disconnecting from a database,
+ // release file handle
+ // etc etc etc
+ std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
+ }
+
+ int getInt() const { return 42; }
+};
+
+struct MyFixture {
+
+ // The test case member function is const.
+ // Therefore we need to mark any member of the fixture
+ // that needs to mutate as mutable.
+ mutable int myInt = 0;
+ ClassWithExpensiveSetup expensive;
+};
+
+// Only one object of type MyFixture will be instantiated for the run
+// of this test case even though there are two leaf sections.
+// This is useful if your test case requires an object that is
+// expensive to create and could be reused for each partial run of the
+// test case.
+TEST_CASE_PERSISTENT_FIXTURE( MyFixture, "Tests with MyFixture" ) {
+
+ const int val = myInt++;
+
+ SECTION( "First partial run" ) {
+ const auto otherValue = expensive.getInt();
+ REQUIRE( val == 0 );
+ REQUIRE( otherValue == 42 );
+ }
+
+ SECTION( "Second partial run" ) { REQUIRE( val == 1 ); }
+} \ No newline at end of file
diff --git a/examples/120-Bdd-ScenarioGivenWhenThen.cpp b/examples/120-Bdd-ScenarioGivenWhenThen.cpp
new file mode 100644
index 0000000..345d53c
--- /dev/null
+++ b/examples/120-Bdd-ScenarioGivenWhenThen.cpp
@@ -0,0 +1,81 @@
+
+// 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
+
+// 120-Bdd-ScenarioGivenWhenThen.cpp
+
+// main() provided by linkage with Catch2WithMain
+
+#include <catch2/catch_test_macros.hpp>
+
+SCENARIO( "vectors can be sized and resized", "[vector]" ) {
+
+ GIVEN( "A vector with some items" ) {
+ std::vector<int> v( 5 );
+
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+
+ WHEN( "the size is increased" ) {
+ v.resize( 10 );
+
+ THEN( "the size and capacity change" ) {
+ REQUIRE( v.size() == 10 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ }
+ WHEN( "the size is reduced" ) {
+ v.resize( 0 );
+
+ THEN( "the size changes but not capacity" ) {
+ REQUIRE( v.size() == 0 );
+ REQUIRE( v.capacity() >= 5 );
+ }
+ }
+ WHEN( "more capacity is reserved" ) {
+ v.reserve( 10 );
+
+ THEN( "the capacity changes but not the size" ) {
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 10 );
+ }
+ }
+ WHEN( "less capacity is reserved" ) {
+ v.reserve( 0 );
+
+ THEN( "neither size nor capacity are changed" ) {
+ REQUIRE( v.size() == 5 );
+ REQUIRE( v.capacity() >= 5 );
+ }
+ }
+ }
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
+
+// Expected compact output (all assertions):
+//
+// prompt> 120-Bdd-ScenarioGivenWhenThen.exe --reporter compact --success
+// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:19: passed: v.size() == 10 for: 10 == 10
+// 120-Bdd-ScenarioGivenWhenThen.cpp:20: passed: v.capacity() >= 10 for: 10 >= 10
+// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:27: passed: v.size() == 0 for: 0 == 0
+// 120-Bdd-ScenarioGivenWhenThen.cpp:28: passed: v.capacity() >= 5 for: 5 >= 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:35: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:36: passed: v.capacity() >= 10 for: 10 >= 10
+// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:43: passed: v.size() == 5 for: 5 == 5
+// 120-Bdd-ScenarioGivenWhenThen.cpp:44: passed: v.capacity() >= 5 for: 5 >= 5
+// Passed 1 test case with 16 assertions.
diff --git a/examples/210-Evt-EventListeners.cpp b/examples/210-Evt-EventListeners.cpp
new file mode 100644
index 0000000..d05dfaa
--- /dev/null
+++ b/examples/210-Evt-EventListeners.cpp
@@ -0,0 +1,436 @@
+
+// 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
+
+// 210-Evt-EventListeners.cpp
+
+// Contents:
+// 1. Printing of listener data
+// 2. My listener and registration
+// 3. Test cases
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/reporters/catch_reporter_event_listener.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
+#include <catch2/catch_test_case_info.hpp>
+#include <iostream>
+
+// -----------------------------------------------------------------------
+// 1. Printing of listener data:
+//
+
+
+namespace {
+std::string ws(int const level) {
+ return std::string( 2 * level, ' ' );
+}
+
+std::ostream& operator<<(std::ostream& out, Catch::Tag t) {
+ return out << "original: " << t.original;
+}
+
+template< typename T >
+std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
+ os << "{ ";
+ for ( const auto& x : v )
+ os << x << ", ";
+ return os << "}";
+}
+// struct SourceLineInfo {
+// char const* file;
+// std::size_t line;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::SourceLineInfo const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- file: " << info.file << "\n"
+ << ws(level+1) << "- line: " << info.line << "\n";
+}
+
+//struct MessageInfo {
+// std::string macroName;
+// std::string message;
+// SourceLineInfo lineInfo;
+// ResultWas::OfType type;
+// unsigned int sequence;
+//};
+
+void print( std::ostream& os, int const level, Catch::MessageInfo const& info ) {
+ os << ws(level+1) << "- macroName: '" << info.macroName << "'\n"
+ << ws(level+1) << "- message '" << info.message << "'\n";
+ print( os,level+1 , "- lineInfo", info.lineInfo );
+ os << ws(level+1) << "- sequence " << info.sequence << "\n";
+}
+
+void print( std::ostream& os, int const level, std::string const& title, std::vector<Catch::MessageInfo> const& v ) {
+ os << ws(level ) << title << ":\n";
+ for ( const auto& x : v )
+ {
+ os << ws(level+1) << "{\n";
+ print( os, level+2, x );
+ os << ws(level+1) << "}\n";
+ }
+// os << ws(level+1) << "\n";
+}
+
+// struct TestRunInfo {
+// std::string name;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunInfo const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- name: " << info.name << "\n";
+}
+
+// struct Counts {
+// std::size_t total() const;
+// bool allPassed() const;
+// bool allOk() const;
+//
+// std::size_t passed = 0;
+// std::size_t failed = 0;
+// std::size_t failedButOk = 0;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::Counts const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- total(): " << info.total() << "\n"
+ << ws(level+1) << "- allPassed(): " << info.allPassed() << "\n"
+ << ws(level+1) << "- allOk(): " << info.allOk() << "\n"
+ << ws(level+1) << "- passed: " << info.passed << "\n"
+ << ws(level+1) << "- failed: " << info.failed << "\n"
+ << ws(level+1) << "- failedButOk: " << info.failedButOk << "\n";
+}
+
+// struct Totals {
+// Counts assertions;
+// Counts testCases;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::Totals const& info ) {
+ os << ws(level) << title << ":\n";
+ print( os, level+1, "- assertions", info.assertions );
+ print( os, level+1, "- testCases" , info.testCases );
+}
+
+// struct TestRunStats {
+// TestRunInfo runInfo;
+// Totals totals;
+// bool aborting;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunStats const& info ) {
+ os << ws(level) << title << ":\n";
+ print( os, level+1 , "- runInfo", info.runInfo );
+ print( os, level+1 , "- totals" , info.totals );
+ os << ws(level+1) << "- aborting: " << info.aborting << "\n";
+}
+
+// struct Tag {
+// StringRef original, lowerCased;
+// };
+//
+//
+// enum class TestCaseProperties : uint8_t {
+// None = 0,
+// IsHidden = 1 << 1,
+// ShouldFail = 1 << 2,
+// MayFail = 1 << 3,
+// Throws = 1 << 4,
+// NonPortable = 1 << 5,
+// Benchmark = 1 << 6
+// };
+//
+//
+// struct TestCaseInfo : NonCopyable {
+//
+// bool isHidden() const;
+// bool throws() const;
+// bool okToFail() const;
+// bool expectedToFail() const;
+//
+//
+// std::string name;
+// std::string className;
+// std::vector<Tag> tags;
+// SourceLineInfo lineInfo;
+// TestCaseProperties properties = TestCaseProperties::None;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseInfo const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- isHidden(): " << info.isHidden() << "\n"
+ << ws(level+1) << "- throws(): " << info.throws() << "\n"
+ << ws(level+1) << "- okToFail(): " << info.okToFail() << "\n"
+ << ws(level+1) << "- expectedToFail(): " << info.expectedToFail() << "\n"
+ << ws(level+1) << "- tagsAsString(): '" << info.tagsAsString() << "'\n"
+ << ws(level+1) << "- name: '" << info.name << "'\n"
+ << ws(level+1) << "- className: '" << info.className << "'\n"
+ << ws(level+1) << "- tags: " << info.tags << "\n";
+ print( os, level+1 , "- lineInfo", info.lineInfo );
+ os << ws(level+1) << "- properties (flags): 0x" << std::hex << static_cast<uint32_t>(info.properties) << std::dec << "\n";
+}
+
+// struct TestCaseStats {
+// TestCaseInfo testInfo;
+// Totals totals;
+// std::string stdOut;
+// std::string stdErr;
+// bool aborting;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseStats const& info ) {
+ os << ws(level ) << title << ":\n";
+ print( os, level+1 , "- testInfo", *info.testInfo );
+ print( os, level+1 , "- totals" , info.totals );
+ os << ws(level+1) << "- stdOut: " << info.stdOut << "\n"
+ << ws(level+1) << "- stdErr: " << info.stdErr << "\n"
+ << ws(level+1) << "- aborting: " << info.aborting << "\n";
+}
+
+// struct SectionInfo {
+// std::string name;
+// std::string description;
+// SourceLineInfo lineInfo;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::SectionInfo const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- name: " << info.name << "\n";
+ print( os, level+1 , "- lineInfo", info.lineInfo );
+}
+
+// struct SectionStats {
+// SectionInfo sectionInfo;
+// Counts assertions;
+// double durationInSeconds;
+// bool missingAssertions;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::SectionStats const& info ) {
+ os << ws(level ) << title << ":\n";
+ print( os, level+1 , "- sectionInfo", info.sectionInfo );
+ print( os, level+1 , "- assertions" , info.assertions );
+ os << ws(level+1) << "- durationInSeconds: " << info.durationInSeconds << "\n"
+ << ws(level+1) << "- missingAssertions: " << info.missingAssertions << "\n";
+}
+
+// struct AssertionInfo
+// {
+// StringRef macroName;
+// SourceLineInfo lineInfo;
+// StringRef capturedExpression;
+// ResultDisposition::Flags resultDisposition;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionInfo const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- macroName: '" << info.macroName << "'\n";
+ print( os, level+1 , "- lineInfo" , info.lineInfo );
+ os << ws(level+1) << "- capturedExpression: '" << info.capturedExpression << "'\n"
+ << ws(level+1) << "- resultDisposition (flags): 0x" << std::hex << info.resultDisposition << std::dec << "\n";
+}
+
+//struct AssertionResultData
+//{
+// std::string reconstructExpression() const;
+//
+// std::string message;
+// mutable std::string reconstructedExpression;
+// LazyExpression lazyExpression;
+// ResultWas::OfType resultType;
+//};
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResultData const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- reconstructExpression(): '" << info.reconstructExpression() << "'\n"
+ << ws(level+1) << "- message: '" << info.message << "'\n"
+ << ws(level+1) << "- lazyExpression: '" << "(info.lazyExpression)" << "'\n"
+ << ws(level+1) << "- resultType: '" << info.resultType << "'\n";
+}
+
+//class AssertionResult {
+// bool isOk() const;
+// bool succeeded() const;
+// ResultWas::OfType getResultType() const;
+// bool hasExpression() const;
+// bool hasMessage() const;
+// std::string getExpression() const;
+// std::string getExpressionInMacro() const;
+// bool hasExpandedExpression() const;
+// std::string getExpandedExpression() const;
+// std::string getMessage() const;
+// SourceLineInfo getSourceInfo() const;
+// std::string getTestMacroName() const;
+//
+// AssertionInfo m_info;
+// AssertionResultData m_resultData;
+//};
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResult const& info ) {
+ os << ws(level ) << title << ":\n"
+ << ws(level+1) << "- isOk(): " << info.isOk() << "\n"
+ << ws(level+1) << "- succeeded(): " << info.succeeded() << "\n"
+ << ws(level+1) << "- getResultType(): " << info.getResultType() << "\n"
+ << ws(level+1) << "- hasExpression(): " << info.hasExpression() << "\n"
+ << ws(level+1) << "- hasMessage(): " << info.hasMessage() << "\n"
+ << ws(level+1) << "- getExpression(): '" << info.getExpression() << "'\n"
+ << ws(level+1) << "- getExpressionInMacro(): '" << info.getExpressionInMacro() << "'\n"
+ << ws(level+1) << "- hasExpandedExpression(): " << info.hasExpandedExpression() << "\n"
+ << ws(level+1) << "- getExpandedExpression(): " << info.getExpandedExpression() << "'\n"
+ << ws(level+1) << "- getMessage(): '" << info.getMessage() << "'\n";
+ print( os, level+1 , "- getSourceInfo(): ", info.getSourceInfo() );
+ os << ws(level+1) << "- getTestMacroName(): '" << info.getTestMacroName() << "'\n";
+
+ print( os, level+1 , "- *** m_info (AssertionInfo)", info.m_info );
+ print( os, level+1 , "- *** m_resultData (AssertionResultData)", info.m_resultData );
+}
+
+// struct AssertionStats {
+// AssertionResult assertionResult;
+// std::vector<MessageInfo> infoMessages;
+// Totals totals;
+// };
+
+void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionStats const& info ) {
+ os << ws(level ) << title << ":\n";
+ print( os, level+1 , "- assertionResult", info.assertionResult );
+ print( os, level+1 , "- infoMessages", info.infoMessages );
+ print( os, level+1 , "- totals", info.totals );
+}
+
+// -----------------------------------------------------------------------
+// 2. My listener and registration:
+//
+
+char const * dashed_line =
+ "--------------------------------------------------------------------------";
+
+
+struct MyListener : Catch::EventListenerBase {
+
+ using EventListenerBase::EventListenerBase; // inherit constructor
+
+ // Get rid of Wweak-tables
+ ~MyListener() override;
+
+ // The whole test run starting
+ void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
+ std::cout
+ << std::boolalpha
+ << "\nEvent: testRunStarting:\n";
+ print( std::cout, 1, "- testRunInfo", testRunInfo );
+ }
+
+ // The whole test run ending
+ void testRunEnded( Catch::TestRunStats const& testRunStats ) override {
+ std::cout
+ << dashed_line
+ << "\nEvent: testRunEnded:\n";
+ print( std::cout, 1, "- testRunStats", testRunStats );
+ }
+
+ // A test is being skipped (because it is "hidden")
+ void skipTest( Catch::TestCaseInfo const& testInfo ) override {
+ std::cout
+ << dashed_line
+ << "\nEvent: skipTest:\n";
+ print( std::cout, 1, "- testInfo", testInfo );
+ }
+
+ // Test cases starting
+ void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
+ std::cout
+ << dashed_line
+ << "\nEvent: testCaseStarting:\n";
+ print( std::cout, 1, "- testInfo", testInfo );
+ }
+
+ // Test cases ending
+ void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
+ std::cout << "\nEvent: testCaseEnded:\n";
+ print( std::cout, 1, "testCaseStats", testCaseStats );
+ }
+
+ // Sections starting
+ void sectionStarting( Catch::SectionInfo const& sectionInfo ) override {
+ std::cout << "\nEvent: sectionStarting:\n";
+ print( std::cout, 1, "- sectionInfo", sectionInfo );
+ }
+
+ // Sections ending
+ void sectionEnded( Catch::SectionStats const& sectionStats ) override {
+ std::cout << "\nEvent: sectionEnded:\n";
+ print( std::cout, 1, "- sectionStats", sectionStats );
+ }
+
+ // Assertions before/ after
+ void assertionStarting( Catch::AssertionInfo const& assertionInfo ) override {
+ std::cout << "\nEvent: assertionStarting:\n";
+ print( std::cout, 1, "- assertionInfo", assertionInfo );
+ }
+
+ void assertionEnded( Catch::AssertionStats const& assertionStats ) override {
+ std::cout << "\nEvent: assertionEnded:\n";
+ print( std::cout, 1, "- assertionStats", assertionStats );
+ }
+};
+
+} // end anonymous namespace
+
+CATCH_REGISTER_LISTENER( MyListener )
+
+// Get rid of Wweak-tables
+MyListener::~MyListener() = default;
+
+// -----------------------------------------------------------------------
+// 3. Test cases:
+//
+
+TEST_CASE( "1: Hidden testcase", "[.hidden]" ) {
+}
+
+TEST_CASE( "2: Testcase with sections", "[tag-A][tag-B]" ) {
+
+ int i = 42;
+
+ REQUIRE( i == 42 );
+
+ SECTION("Section 1") {
+ INFO("Section 1");
+ i = 7;
+ SECTION("Section 1.1") {
+ INFO("Section 1.1");
+ REQUIRE( i == 42 );
+ }
+ }
+
+ SECTION("Section 2") {
+ INFO("Section 2");
+ REQUIRE( i == 42 );
+ }
+ WARN("At end of test case");
+}
+
+struct Fixture {
+ int fortytwo() const {
+ return 42;
+ }
+};
+
+TEST_CASE_METHOD( Fixture, "3: Testcase with class-based fixture", "[tag-C][tag-D]" ) {
+ REQUIRE( fortytwo() == 42 );
+}
+
+// Compile & run:
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
+
+// Expected compact output (all assertions):
+//
+// prompt> 210-Evt-EventListeners --reporter compact --success
+// result omitted for brevity.
diff --git a/examples/231-Cfg-OutputStreams.cpp b/examples/231-Cfg-OutputStreams.cpp
new file mode 100644
index 0000000..5aee38b
--- /dev/null
+++ b/examples/231-Cfg-OutputStreams.cpp
@@ -0,0 +1,63 @@
+
+// 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
+
+// 231-Cfg-OutputStreams.cpp
+// Show how to replace the streams with a simple custom made streambuf.
+
+// Note that this reimplementation _does not_ follow `std::cerr`
+// semantic, because it buffers the output. For most uses however,
+// there is no important difference between having `std::cerr` buffered
+// or unbuffered.
+#include <catch2/catch_test_macros.hpp>
+
+#include <sstream>
+#include <cstdio>
+
+class out_buff : public std::stringbuf {
+ std::FILE* m_stream;
+public:
+ out_buff(std::FILE* stream):m_stream(stream) {}
+ ~out_buff() override;
+ int sync() override {
+ int ret = 0;
+ for (unsigned char c : str()) {
+ if (putc(c, m_stream) == EOF) {
+ ret = -1;
+ break;
+ }
+ }
+ // Reset the buffer to avoid printing it multiple times
+ str("");
+ return ret;
+ }
+};
+
+out_buff::~out_buff() { pubsync(); }
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wexit-time-destructors" // static variables in cout/cerr/clog
+#endif
+
+namespace Catch {
+ std::ostream& cout() {
+ static std::ostream ret(new out_buff(stdout));
+ return ret;
+ }
+ std::ostream& clog() {
+ static std::ostream ret(new out_buff(stderr));
+ return ret;
+ }
+ std::ostream& cerr() {
+ return clog();
+ }
+}
+
+
+TEST_CASE("This binary uses putc to write out output", "[compilation-only]") {
+ SUCCEED("Nothing to test.");
+}
diff --git a/examples/232-Cfg-CustomMain.cpp b/examples/232-Cfg-CustomMain.cpp
new file mode 100644
index 0000000..2704099
--- /dev/null
+++ b/examples/232-Cfg-CustomMain.cpp
@@ -0,0 +1,41 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+// 232-Cfg-CustomMain.cpp
+// Show how to use custom main and add a custom option to the CLI parser
+
+#include <catch2/catch_session.hpp>
+
+#include <iostream>
+
+int main(int argc, char** argv) {
+ Catch::Session session; // There must be exactly one instance
+
+ int height = 0; // Some user variable you want to be able to set
+
+ // Build a new parser on top of Catch2's
+ using namespace Catch::Clara;
+ auto cli
+ = session.cli() // Get Catch2's command line parser
+ | Opt( height, "height" ) // bind variable to a new option, with a hint string
+ ["--height"] // the option names it will respond to
+ ("how high?"); // description string for the help output
+
+ // Now pass the new composite back to Catch2 so it uses that
+ session.cli( cli );
+
+ // Let Catch2 (using Clara) parse the command line
+ int returnCode = session.applyCommandLine( argc, argv );
+ if( returnCode != 0 ) // Indicates a command line error
+ return returnCode;
+
+ // if set on the command line then 'height' is now set at this point
+ std::cout << "height: " << height << '\n';
+
+ return session.run();
+}
diff --git a/examples/300-Gen-OwnGenerator.cpp b/examples/300-Gen-OwnGenerator.cpp
new file mode 100644
index 0000000..9cb02e3
--- /dev/null
+++ b/examples/300-Gen-OwnGenerator.cpp
@@ -0,0 +1,77 @@
+
+// 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
+
+// 300-Gen-OwnGenerator.cpp
+// Shows how to define a custom generator.
+
+// Specifically we will implement a random number generator for integers
+// It will have infinite capacity and settable lower/upper bound
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+
+#include <random>
+
+namespace {
+
+// This class shows how to implement a simple generator for Catch tests
+class RandomIntGenerator final : public Catch::Generators::IGenerator<int> {
+ std::minstd_rand m_rand;
+ std::uniform_int_distribution<> m_dist;
+ int current_number;
+public:
+
+ RandomIntGenerator(int low, int high):
+ m_rand(std::random_device{}()),
+ m_dist(low, high)
+ {
+ static_cast<void>(next());
+ }
+
+ int const& get() const override;
+ bool next() override {
+ current_number = m_dist(m_rand);
+ return true;
+ }
+};
+
+// Avoids -Wweak-vtables
+int const& RandomIntGenerator::get() const {
+ return current_number;
+}
+
+// This helper function provides a nicer UX when instantiating the generator
+// Notice that it returns an instance of GeneratorWrapper<int>, which
+// is a value-wrapper around std::unique_ptr<IGenerator<int>>.
+Catch::Generators::GeneratorWrapper<int> random(int low, int high) {
+ return Catch::Generators::GeneratorWrapper<int>(
+ new RandomIntGenerator(low, high)
+ // Another possibility:
+ // Catch::Detail::make_unique<RandomIntGenerator>(low, high)
+ );
+}
+
+} // end anonymous namespaces
+
+// The two sections in this test case are equivalent, but the first one
+// is much more readable/nicer to use
+TEST_CASE("Generating random ints", "[example][generator]") {
+ SECTION("Nice UX") {
+ auto i = GENERATE(take(100, random(-100, 100)));
+ REQUIRE(i >= -100);
+ REQUIRE(i <= 100);
+ }
+ SECTION("Creating the random generator directly") {
+ auto i = GENERATE(take(100, GeneratorWrapper<int>(Catch::Detail::make_unique<RandomIntGenerator>(-100, 100))));
+ REQUIRE(i >= -100);
+ REQUIRE(i <= 100);
+ }
+}
+
+// Compiling and running this file will result in 400 successful assertions
diff --git a/examples/301-Gen-MapTypeConversion.cpp b/examples/301-Gen-MapTypeConversion.cpp
new file mode 100644
index 0000000..0a28448
--- /dev/null
+++ b/examples/301-Gen-MapTypeConversion.cpp
@@ -0,0 +1,69 @@
+
+// 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
+
+// 301-Gen-MapTypeConversion.cpp
+// Shows how to use map to modify generator's return type.
+
+// Specifically we wrap a std::string returning generator with a generator
+// that converts the strings using stoi, so the returned type is actually
+// an int.
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+
+#include <string>
+#include <sstream>
+
+namespace {
+
+// Returns a line from a stream. You could have it e.g. read lines from
+// a file, but to avoid problems with paths in examples, we will use
+// a fixed stringstream.
+class LineGenerator final : public Catch::Generators::IGenerator<std::string> {
+ std::string m_line;
+ std::stringstream m_stream;
+public:
+ explicit LineGenerator( std::string const& lines ) {
+ m_stream.str( lines );
+ if (!next()) {
+ Catch::Generators::Detail::throw_generator_exception("Couldn't read a single line");
+ }
+ }
+
+ std::string const& get() const override;
+
+ bool next() override {
+ return !!std::getline(m_stream, m_line);
+ }
+};
+
+std::string const& LineGenerator::get() const {
+ return m_line;
+}
+
+// This helper function provides a nicer UX when instantiating the generator
+// Notice that it returns an instance of GeneratorWrapper<std::string>, which
+// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
+Catch::Generators::GeneratorWrapper<std::string>
+lines( std::string const& lines ) {
+ return Catch::Generators::GeneratorWrapper<std::string>(
+ new LineGenerator( lines ) );
+}
+
+} // end anonymous namespace
+
+
+TEST_CASE("filter can convert types inside the generator expression", "[example][generator]") {
+ auto num = GENERATE(
+ map<int>( []( std::string const& line ) { return std::stoi( line ); },
+ lines( "1\n2\n3\n4\n" ) ) );
+
+ REQUIRE(num > 0);
+}
+
+// Compiling and running this file will result in 4 successful assertions
diff --git a/examples/302-Gen-Table.cpp b/examples/302-Gen-Table.cpp
new file mode 100644
index 0000000..3cdb143
--- /dev/null
+++ b/examples/302-Gen-Table.cpp
@@ -0,0 +1,63 @@
+
+// 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
+
+// 302-Gen-Table.cpp
+// Shows how to use table to run a test many times with different inputs. Lifted from examples on
+// issue #850.
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
+#include <string>
+
+struct TestSubject {
+ // this is the method we are going to test. It returns the length of the
+ // input string.
+ size_t GetLength( const std::string& input ) const { return input.size(); }
+};
+
+
+TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][generator]") {
+ using std::make_tuple;
+ // do setup here as normal
+ TestSubject subj;
+
+ SECTION("This section is run for each row in the table") {
+ std::string test_input;
+ size_t expected_output;
+ std::tie( test_input, expected_output ) =
+ GENERATE( table<std::string, size_t>(
+ { /* In this case one of the parameters to our test case is the
+ * expected output, but this is not required. There could be
+ * multiple expected values in the table, which can have any
+ * (fixed) number of columns.
+ */
+ make_tuple( "one", 3 ),
+ make_tuple( "two", 3 ),
+ make_tuple( "three", 5 ),
+ make_tuple( "four", 4 ) } ) );
+
+ // run the test
+ auto result = subj.GetLength(test_input);
+ // capture the input data to go with the outputs.
+ CAPTURE(test_input);
+ // check it matches the pre-calculated data
+ REQUIRE(result == expected_output);
+ } // end section
+}
+
+/* Possible simplifications where less legacy toolchain support is needed:
+ *
+ * - With libstdc++6 or newer, the make_tuple() calls can be omitted
+ * (technically C++17 but does not require -std in GCC/Clang). See
+ * https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
+ *
+ * - In C++17 mode std::tie() and the preceding variable declarations can be
+ * replaced by structured bindings: auto [test_input, expected] = GENERATE(
+ * table<std::string, size_t>({ ...
+ */
+// Compiling and running this file will result in 4 successful assertions
diff --git a/examples/310-Gen-VariablesInGenerators.cpp b/examples/310-Gen-VariablesInGenerators.cpp
new file mode 100644
index 0000000..5d24d45
--- /dev/null
+++ b/examples/310-Gen-VariablesInGenerators.cpp
@@ -0,0 +1,43 @@
+
+// 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
+
+// 310-Gen-VariablesInGenerator.cpp
+// Shows how to use variables when creating generators.
+
+// Note that using variables inside generators is dangerous and should
+// be done only if you know what you are doing, because the generators
+// _WILL_ outlive the variables -- thus they should be either captured
+// by value directly, or copied by the generators during construction.
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+
+TEST_CASE("Generate random doubles across different ranges",
+ "[generator][example][advanced]") {
+ // Workaround for old libstdc++
+ using record = std::tuple<double, double>;
+ // Set up 3 ranges to generate numbers from
+ auto r = GENERATE(table<double, double>({
+ record{3, 4},
+ record{-4, -3},
+ record{10, 1000}
+ }));
+
+ // This will not compile (intentionally), because it accesses a variable
+ // auto number = GENERATE(take(50, random(std::get<0>(r), std::get<1>(r))));
+
+ // GENERATE_COPY copies all variables mentioned inside the expression
+ // thus this will work.
+ auto number = GENERATE_COPY(take(50, random(std::get<0>(r), std::get<1>(r))));
+
+ REQUIRE(std::abs(number) > 0);
+}
+
+// Compiling and running this file will result in 150 successful assertions
+
diff --git a/examples/311-Gen-CustomCapture.cpp b/examples/311-Gen-CustomCapture.cpp
new file mode 100644
index 0000000..ee31038
--- /dev/null
+++ b/examples/311-Gen-CustomCapture.cpp
@@ -0,0 +1,51 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+// 311-Gen-CustomCapture.cpp
+// Shows how to provide custom capture list to the generator expression
+
+// Note that using variables inside generators is dangerous and should
+// be done only if you know what you are doing, because the generators
+// _WILL_ outlive the variables. Also, even if you know what you are
+// doing, you should probably use GENERATE_COPY or GENERATE_REF macros
+// instead. However, if your use case requires having a
+// per-variable custom capture list, this example shows how to achieve
+// that.
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
+
+TEST_CASE("Generate random doubles across different ranges",
+ "[generator][example][advanced]") {
+ // Workaround for old libstdc++
+ using record = std::tuple<double, double>;
+ // Set up 3 ranges to generate numbers from
+ auto r1 = GENERATE(table<double, double>({
+ record{3, 4},
+ record{-4, -3},
+ record{10, 1000}
+ }));
+
+ auto r2(r1);
+
+ // This will take r1 by reference and r2 by value.
+ // Note that there are no advantages for doing so in this example,
+ // it is done only for expository purposes.
+ auto number = Catch::Generators::generate( "custom capture generator", CATCH_INTERNAL_LINEINFO,
+ [&r1, r2]{
+ using namespace Catch::Generators;
+ return makeGenerators(take(50, random(std::get<0>(r1), std::get<1>(r2))));
+ }
+ );
+
+ REQUIRE(std::abs(number) > 0);
+}
+
+// Compiling and running this file will result in 150 successful assertions
+
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..d45ddfc
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,62 @@
+cmake_minimum_required( VERSION 3.16 )
+
+project( Catch2Examples LANGUAGES CXX )
+
+message( STATUS "Examples included" )
+
+
+# Some one-offs first:
+# 1) Tests and main in one file
+add_executable( 010-TestCase
+ 010-TestCase.cpp
+)
+
+# 2) Tests and main across two files
+add_executable( 020-MultiFile
+ 020-TestCase-1.cpp
+ 020-TestCase-2.cpp
+)
+
+add_executable(231-Cfg_OutputStreams
+ 231-Cfg-OutputStreams.cpp
+)
+target_link_libraries(231-Cfg_OutputStreams Catch2_buildall_interface)
+target_compile_definitions(231-Cfg_OutputStreams PUBLIC CATCH_CONFIG_NOSTDOUT)
+
+# These examples use the standard separate compilation
+set( SOURCES_IDIOMATIC_EXAMPLES
+ 030-Asn-Require-Check.cpp
+ 100-Fix-Section.cpp
+ 110-Fix-ClassFixture.cpp
+ 111-Fix-PersistentFixture.cpp
+ 120-Bdd-ScenarioGivenWhenThen.cpp
+ 210-Evt-EventListeners.cpp
+ 232-Cfg-CustomMain.cpp
+ 300-Gen-OwnGenerator.cpp
+ 301-Gen-MapTypeConversion.cpp
+ 302-Gen-Table.cpp
+ 310-Gen-VariablesInGenerators.cpp
+ 311-Gen-CustomCapture.cpp
+)
+
+string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_EXAMPLES "${SOURCES_IDIOMATIC_EXAMPLES}" )
+set( TARGETS_IDIOMATIC_EXAMPLES ${BASENAMES_IDIOMATIC_EXAMPLES} )
+
+
+foreach( name ${TARGETS_IDIOMATIC_EXAMPLES} )
+ add_executable( ${name} ${name}.cpp )
+endforeach()
+
+set(ALL_EXAMPLE_TARGETS
+ ${TARGETS_IDIOMATIC_EXAMPLES}
+ 010-TestCase
+ 020-MultiFile
+)
+
+foreach( name ${ALL_EXAMPLE_TARGETS} )
+ target_link_libraries( ${name} Catch2WithMain )
+endforeach()
+
+
+list(APPEND CATCH_WARNING_TARGETS ${ALL_EXAMPLE_TARGETS})
+set(CATCH_WARNING_TARGETS ${CATCH_WARNING_TARGETS} PARENT_SCOPE)