diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
| commit | bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch) | |
| tree | 1e629e7b46b1d9972a973bc93fd100bcebd395be /examples | |
| download | nihil-bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1.tar.gz nihil-bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1.tar.bz2 | |
import catch2 3.8.1vendor/catch2/3.8.1vendor/catch2
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/010-TestCase.cpp | 41 | ||||
| -rw-r--r-- | examples/020-TestCase-1.cpp | 37 | ||||
| -rw-r--r-- | examples/020-TestCase-2.cpp | 41 | ||||
| -rw-r--r-- | examples/030-Asn-Require-Check.cpp | 82 | ||||
| -rw-r--r-- | examples/100-Fix-Section.cpp | 78 | ||||
| -rw-r--r-- | examples/110-Fix-ClassFixture.cpp | 74 | ||||
| -rw-r--r-- | examples/111-Fix-PersistentFixture.cpp | 74 | ||||
| -rw-r--r-- | examples/120-Bdd-ScenarioGivenWhenThen.cpp | 81 | ||||
| -rw-r--r-- | examples/210-Evt-EventListeners.cpp | 436 | ||||
| -rw-r--r-- | examples/231-Cfg-OutputStreams.cpp | 63 | ||||
| -rw-r--r-- | examples/232-Cfg-CustomMain.cpp | 41 | ||||
| -rw-r--r-- | examples/300-Gen-OwnGenerator.cpp | 77 | ||||
| -rw-r--r-- | examples/301-Gen-MapTypeConversion.cpp | 69 | ||||
| -rw-r--r-- | examples/302-Gen-Table.cpp | 63 | ||||
| -rw-r--r-- | examples/310-Gen-VariablesInGenerators.cpp | 43 | ||||
| -rw-r--r-- | examples/311-Gen-CustomCapture.cpp | 51 | ||||
| -rw-r--r-- | examples/CMakeLists.txt | 62 |
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) |
