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>
85 lines
3.3 KiB
C++
85 lines
3.3 KiB
C++
|
|
// Copyright Catch2 Authors
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE.txt or copy at
|
|
// https://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
#include <catch2/internal/catch_test_registry.hpp>
|
|
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
|
#include <catch2/catch_test_case_info.hpp>
|
|
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
|
#include <catch2/internal/catch_string_manip.hpp>
|
|
#include <catch2/internal/catch_move_and_forward.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
namespace Catch {
|
|
void ITestInvoker::prepareTestCase() {}
|
|
void ITestInvoker::tearDownTestCase() {}
|
|
ITestInvoker::~ITestInvoker() = default;
|
|
|
|
namespace {
|
|
static StringRef extractClassName( StringRef classOrMethodName ) {
|
|
if ( !startsWith( classOrMethodName, '&' ) ) {
|
|
return classOrMethodName;
|
|
}
|
|
|
|
// Remove the leading '&' to avoid having to special case it later
|
|
const auto methodName =
|
|
classOrMethodName.substr( 1, classOrMethodName.size() );
|
|
|
|
auto reverseStart = std::make_reverse_iterator( methodName.end() );
|
|
auto reverseEnd = std::make_reverse_iterator( methodName.begin() );
|
|
|
|
// We make a simplifying assumption that ":" is only present
|
|
// in the input as part of "::" from C++ typenames (this is
|
|
// relatively safe assumption because the input is generated
|
|
// as stringification of type through preprocessor).
|
|
auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1;
|
|
auto secondLastColons =
|
|
std::find( lastColons + 1, reverseEnd, ':' );
|
|
|
|
auto const startIdx = reverseEnd - secondLastColons;
|
|
auto const classNameSize = secondLastColons - lastColons - 1;
|
|
|
|
return methodName.substr(
|
|
static_cast<std::size_t>( startIdx ),
|
|
static_cast<std::size_t>( classNameSize ) );
|
|
}
|
|
|
|
class TestInvokerAsFunction final : public ITestInvoker {
|
|
using TestType = void ( * )();
|
|
TestType m_testAsFunction;
|
|
|
|
public:
|
|
TestInvokerAsFunction( TestType testAsFunction ) noexcept:
|
|
m_testAsFunction( testAsFunction ) {}
|
|
|
|
void invoke() const override { m_testAsFunction(); }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
|
|
return Detail::make_unique<TestInvokerAsFunction>( testAsFunction );
|
|
}
|
|
|
|
AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept {
|
|
CATCH_TRY {
|
|
getMutableRegistryHub()
|
|
.registerTest(
|
|
makeTestCaseInfo(
|
|
extractClassName( classOrMethod ),
|
|
nameAndTags,
|
|
lineInfo),
|
|
CATCH_MOVE(invoker)
|
|
);
|
|
} CATCH_CATCH_ALL {
|
|
// Do not throw when constructing global objects, instead register the exception to be processed later
|
|
getMutableRegistryHub().registerStartupException();
|
|
}
|
|
}
|
|
}
|