Unify ITestCaseRegistry and TestRegistry

I also made a bunch of refactorings around the headers and includes
to simplify the main include path.
This commit is contained in:
Martin Hořeňovský 2023-03-14 23:25:35 +01:00
parent cf4d84a349
commit 02ce0a2eec
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
9 changed files with 82 additions and 88 deletions

View File

@ -35,7 +35,7 @@ namespace Catch {
ReporterRegistry const& getReporterRegistry() const override { ReporterRegistry const& getReporterRegistry() const override {
return m_reporterRegistry; return m_reporterRegistry;
} }
ITestCaseRegistry const& getTestCaseRegistry() const override { TestCaseRegistry const& getTestCaseRegistry() const override {
return m_testCaseRegistry; return m_testCaseRegistry;
} }
ExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { ExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
@ -76,7 +76,7 @@ namespace Catch {
} }
private: private:
TestRegistry m_testCaseRegistry; TestCaseRegistry m_testCaseRegistry;
ReporterRegistry m_reporterRegistry; ReporterRegistry m_reporterRegistry;
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry; TagAliasRegistry m_tagAliasRegistry;

View File

@ -24,6 +24,8 @@
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp> #include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_istream.hpp> #include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>

View File

@ -8,6 +8,7 @@
#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_spec.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_case_info.hpp>
#include <algorithm> #include <algorithm>

View File

@ -16,7 +16,7 @@ namespace Catch {
class TestCaseHandle; class TestCaseHandle;
struct TestCaseInfo; struct TestCaseInfo;
class ITestCaseRegistry; class TestCaseRegistry;
class ExceptionTranslatorRegistry; class ExceptionTranslatorRegistry;
class IExceptionTranslator; class IExceptionTranslator;
class ReporterRegistry; class ReporterRegistry;
@ -36,7 +36,7 @@ namespace Catch {
virtual ~IRegistryHub(); // = default virtual ~IRegistryHub(); // = default
virtual ReporterRegistry const& getReporterRegistry() const = 0; virtual ReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual TestCaseRegistry const& getTestCaseRegistry() const = 0;
virtual TagAliasRegistry const& getTagAliasRegistry() const = 0; virtual TagAliasRegistry const& getTagAliasRegistry() const = 0;
virtual ExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; virtual ExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;

View File

@ -10,5 +10,4 @@
namespace Catch { namespace Catch {
ITestInvoker::~ITestInvoker() = default; ITestInvoker::~ITestInvoker() = default;
ITestCaseRegistry::~ITestCaseRegistry() = default;
} }

View File

@ -8,36 +8,14 @@
#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED #ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED #define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
#include <vector>
namespace Catch { namespace Catch {
class TestSpec;
struct TestCaseInfo;
class ITestInvoker { class ITestInvoker {
public: public:
virtual void invoke () const = 0; virtual void invoke () const = 0;
virtual ~ITestInvoker(); // = default virtual ~ITestInvoker(); // = default
}; };
class TestCaseHandle;
class IConfig;
class ITestCaseRegistry {
public:
virtual ~ITestCaseRegistry(); // = default
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
};
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
} }
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED #endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED

View File

@ -14,7 +14,7 @@
#include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/catch_config.hpp> #include <catch2/catch_config.hpp>
#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_spec.hpp>

View File

@ -24,6 +24,54 @@
namespace Catch { namespace Catch {
namespace {
static bool matchTest( TestCaseHandle const& testCase,
TestSpec const& testSpec,
IConfig const& config ) {
return testSpec.matches( testCase.getTestCaseInfo() ) &&
isThrowSafe( testCase, config );
}
static void enforceNoDuplicateTestCases(
std::vector<TestCaseHandle> const& tests ) {
auto testInfoCmp = []( TestCaseInfo const* lhs,
TestCaseInfo const* rhs ) {
return *lhs < *rhs;
};
std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests(
testInfoCmp );
for ( auto const& test : tests ) {
const auto infoPtr = &test.getTestCaseInfo();
const auto prev = seenTests.insert( infoPtr );
CATCH_ENFORCE( prev.second,
"error: test case \""
<< infoPtr->name << "\", with tags \""
<< infoPtr->tagsAsString()
<< "\" already defined.\n"
<< "\tFirst seen at "
<< ( *prev.first )->lineInfo << "\n"
<< "\tRedefined at " << infoPtr->lineInfo );
}
}
} // namespace
struct TestCaseRegistry::TestCaseRegistryImpl {
std::vector<Detail::unique_ptr<TestCaseInfo>> owned_test_infos;
// Keeps a materialized vector for `getAllInfos`.
// We should get rid of that eventually (see interface note)
std::vector<TestCaseInfo*> viewed_test_infos;
std::vector<Detail::unique_ptr<ITestInvoker>> invokers;
std::vector<TestCaseHandle> handles;
mutable TestRunOrder currentSortOrder = TestRunOrder::Declared;
mutable std::vector<TestCaseHandle> sortedFunctions;
};
TestCaseRegistry::TestCaseRegistry():
m_impl( Detail::make_unique<TestCaseRegistryImpl>() ) {}
TestCaseRegistry ::~TestCaseRegistry() = default;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
switch (config.runOrder()) { switch (config.runOrder()) {
case TestRunOrder::Declared: case TestRunOrder::Declared:
@ -80,29 +128,6 @@ namespace Catch {
return !testCase.getTestCaseInfo().throws() || config.allowThrows(); return !testCase.getTestCaseInfo().throws() || config.allowThrows();
} }
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) {
return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
}
void
enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
auto testInfoCmp = []( TestCaseInfo const* lhs,
TestCaseInfo const* rhs ) {
return *lhs < *rhs;
};
std::set<TestCaseInfo const*, decltype(testInfoCmp) &> seenTests(testInfoCmp);
for ( auto const& test : tests ) {
const auto infoPtr = &test.getTestCaseInfo();
const auto prev = seenTests.insert( infoPtr );
CATCH_ENFORCE(
prev.second,
"error: test case \"" << infoPtr->name << "\", with tags \""
<< infoPtr->tagsAsString() << "\" already defined.\n"
<< "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
<< "\tRedefined at " << infoPtr->lineInfo );
}
}
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
std::vector<TestCaseHandle> filtered; std::vector<TestCaseHandle> filtered;
filtered.reserve( testCases.size() ); filtered.reserve( testCases.size() );
@ -118,29 +143,29 @@ namespace Catch {
return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
} }
void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) { void TestCaseRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
m_handles.emplace_back(testInfo.get(), testInvoker.get()); m_impl->handles.emplace_back(testInfo.get(), testInvoker.get());
m_viewed_test_infos.push_back(testInfo.get()); m_impl->viewed_test_infos.push_back(testInfo.get());
m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); m_impl->owned_test_infos.push_back(CATCH_MOVE(testInfo));
m_invokers.push_back(CATCH_MOVE(testInvoker)); m_impl->invokers.push_back(CATCH_MOVE(testInvoker));
} }
std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const { std::vector<TestCaseInfo*> const& TestCaseRegistry::getAllInfos() const {
return m_viewed_test_infos; return m_impl->viewed_test_infos;
} }
std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const { std::vector<TestCaseHandle> const& TestCaseRegistry::getAllTests() const {
return m_handles; return m_impl->handles;
} }
std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { std::vector<TestCaseHandle> const& TestCaseRegistry::getAllTestsSorted( IConfig const& config ) const {
if( m_sortedFunctions.empty() ) if( m_impl->sortedFunctions.empty() )
enforceNoDuplicateTestCases( m_handles ); enforceNoDuplicateTestCases( m_impl->handles );
if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { if( m_impl->currentSortOrder != config.runOrder() || m_impl->sortedFunctions.empty() ) {
m_sortedFunctions = sortTests( config, m_handles ); m_impl->sortedFunctions = sortTests( config, m_impl->handles );
m_currentSortOrder = config.runOrder(); m_impl->currentSortOrder = config.runOrder();
} }
return m_sortedFunctions; return m_impl->sortedFunctions;
} }
} // end namespace Catch } // end namespace Catch

View File

@ -8,8 +8,6 @@
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_unique_ptr.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <vector> #include <vector>
@ -19,37 +17,28 @@ namespace Catch {
class TestCaseHandle; class TestCaseHandle;
class IConfig; class IConfig;
class TestSpec; class TestSpec;
class ITestInvoker;
struct TestCaseInfo;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ); std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions );
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ); std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
class TestRegistry : public ITestCaseRegistry { class TestCaseRegistry {
struct TestCaseRegistryImpl;
Detail::unique_ptr<TestCaseRegistryImpl> m_impl;
public: public:
~TestRegistry() override = default; TestCaseRegistry();
~TestCaseRegistry(); // = default;
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker ); void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
std::vector<TestCaseInfo*> const& getAllInfos() const override; std::vector<TestCaseInfo*> const& getAllInfos() const;
std::vector<TestCaseHandle> const& getAllTests() const override; std::vector<TestCaseHandle> const& getAllTests() const;
std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override; std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const;
private:
std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
// Keeps a materialized vector for `getAllInfos`.
// We should get rid of that eventually (see interface note)
std::vector<TestCaseInfo*> m_viewed_test_infos;
std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
std::vector<TestCaseHandle> m_handles;
mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
mutable std::vector<TestCaseHandle> m_sortedFunctions;
}; };
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////