diff options
| author | Keith Stockdale | 2024-08-05 16:01:41 +0100 |
|---|---|---|
| committer | GitHub | 2024-08-05 17:01:41 +0200 |
| commit | f7cd0ba0511b61592e70f07533d7ea30a8a5395d (patch) | |
| tree | f9b92e05f1870f5b1b4f4f65a95ab3b3e5266c10 /src/catch2 | |
| parent | 33e24b14fcf95c1c31c8d6b68f445ebb12e026cc (diff) | |
| download | catch3-f7cd0ba0511b61592e70f07533d7ea30a8a5395d.tar.gz | |
TEST_CASE_PERSISTENT_FIXTURE: A new fixture macro for allowing persistent fixtures throughout a TEST_CASE (#2885)
This PR introduces a new `TEST_CASE` macro called `TEST_CASE_PERSISTENT_FIXTURE`. `TEST_CASE_PERSISTENT_FIXTURE` offers the same functionality as `TEST_CASE_METHOD` except for one difference. The object on which the test method is invoked is only created once for all invocations of the test case. The object is created just after the `testCaseStarting` event is broadcast and the object is destroyed just before the `testCaseEnding` event is broadcast.
The main motivation for this new functionality is to allow `TEST_CASE`s to do expensive setup and teardown once per `TEST_CASE`, without having to resort to abusing event listeners or static function variables with manual initialization.
Implements #1602
---------
Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
Diffstat (limited to 'src/catch2')
| -rw-r--r-- | src/catch2/catch_test_case_info.hpp | 8 | ||||
| -rw-r--r-- | src/catch2/catch_test_macros.hpp | 4 | ||||
| -rw-r--r-- | src/catch2/interfaces/catch_interfaces_test_invoker.hpp | 2 | ||||
| -rw-r--r-- | src/catch2/internal/catch_run_context.cpp | 2 | ||||
| -rw-r--r-- | src/catch2/internal/catch_test_registry.cpp | 2 | ||||
| -rw-r--r-- | src/catch2/internal/catch_test_registry.hpp | 47 |
6 files changed, 65 insertions, 0 deletions
diff --git a/src/catch2/catch_test_case_info.hpp b/src/catch2/catch_test_case_info.hpp index da9927e7..00b393b2 100644 --- a/src/catch2/catch_test_case_info.hpp +++ b/src/catch2/catch_test_case_info.hpp @@ -112,6 +112,14 @@ namespace Catch { TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : m_info(info), m_invoker(invoker) {} + void prepareTestCase() const { + m_invoker->prepareTestCase(); + } + + void tearDownTestCase() const { + m_invoker->tearDownTestCase(); + } + void invoke() const { m_invoker->invoke(); } diff --git a/src/catch2/catch_test_macros.hpp b/src/catch2/catch_test_macros.hpp index 1088afbe..6ee2129f 100644 --- a/src/catch2/catch_test_macros.hpp +++ b/src/catch2/catch_test_macros.hpp @@ -43,6 +43,7 @@ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -97,6 +98,7 @@ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) #define CATCH_DYNAMIC_SECTION( ... ) @@ -142,6 +144,7 @@ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -195,6 +198,7 @@ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define METHOD_AS_TEST_CASE( method, ... ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) #define DYNAMIC_SECTION( ... ) diff --git a/src/catch2/interfaces/catch_interfaces_test_invoker.hpp b/src/catch2/interfaces/catch_interfaces_test_invoker.hpp index 3caeff9a..124a7f7d 100644 --- a/src/catch2/interfaces/catch_interfaces_test_invoker.hpp +++ b/src/catch2/interfaces/catch_interfaces_test_invoker.hpp @@ -12,6 +12,8 @@ namespace Catch { class ITestInvoker { public: + virtual void prepareTestCase(); + virtual void tearDownTestCase(); virtual void invoke() const = 0; virtual ~ITestInvoker(); // = default }; diff --git a/src/catch2/internal/catch_run_context.cpp b/src/catch2/internal/catch_run_context.cpp index 07691788..8711352c 100644 --- a/src/catch2/internal/catch_run_context.cpp +++ b/src/catch2/internal/catch_run_context.cpp @@ -185,6 +185,7 @@ namespace Catch { auto const& testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting(testInfo); + testCase.prepareTestCase(); m_activeTestCase = &testCase; @@ -254,6 +255,7 @@ namespace Catch { deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; + testCase.tearDownTestCase(); m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, CATCH_MOVE(redirectedCout), diff --git a/src/catch2/internal/catch_test_registry.cpp b/src/catch2/internal/catch_test_registry.cpp index e9c999fe..07e44617 100644 --- a/src/catch2/internal/catch_test_registry.cpp +++ b/src/catch2/internal/catch_test_registry.cpp @@ -16,6 +16,8 @@ #include <iterator> namespace Catch { + void ITestInvoker::prepareTestCase() {} + void ITestInvoker::tearDownTestCase() {} ITestInvoker::~ITestInvoker() = default; namespace { diff --git a/src/catch2/internal/catch_test_registry.hpp b/src/catch2/internal/catch_test_registry.hpp index c62fbdcc..e275f2b9 100644 --- a/src/catch2/internal/catch_test_registry.hpp +++ b/src/catch2/internal/catch_test_registry.hpp @@ -47,6 +47,33 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) { return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod ); } +template <typename C> +class TestInvokerFixture : public ITestInvoker { + void ( C::*m_testAsMethod )() const; + Detail::unique_ptr<C> m_fixture = nullptr; + +public: + TestInvokerFixture( void ( C::*testAsMethod )() const) noexcept : m_testAsMethod( testAsMethod ) {} + + void prepareTestCase() override { + m_fixture = Detail::make_unique<C>(); + } + + void tearDownTestCase() override { + m_fixture.reset(); + } + + void invoke() const override { + auto* f = m_fixture.get(); + ( f->*m_testAsMethod )(); + } +}; + +template<typename C> +Detail::unique_ptr<ITestInvoker> makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { + return Detail::make_unique<TestInvokerFixture<C>>( testAsMethod ); +} + struct NameAndTags { constexpr NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept: @@ -143,6 +170,26 @@ static int catchInternalSectionHint = 0; #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \ + void test() const; \ + }; \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvokerFixture( &TestName::test ), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() const + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ |