diff options
| author | Leitwolf | 2026-10-25 10:31:36 +0300 |
|---|---|---|
| committer | GitHub | 2026-10-25 10:31:36 +0300 |
| commit | d2e3e074bb2e1b8ffd9fc990d0b87667ea602f58 (patch) | |
| tree | 0bce4462b2e190c39b3e1c5f697e3e4abd1a5e2a /src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp | |
| parent | 13f81a480a93807c14dddb5abdb97355ced4d4c5 (diff) | |
| parent | 126dc333564cb72b5115d9a9242d66694694b6d0 (diff) | |
| download | catch3-d2e3e074bb2e1b8ffd9fc990d0b87667ea602f58.tar.gz | |
Merge pull request #18 from Progmasoft/build/catch3-identity
Give Catch3 its own build names and headers
Diffstat (limited to 'src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp')
| -rw-r--r-- | src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp b/src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp new file mode 100644 index 00000000..3adf00eb --- /dev/null +++ b/src/Progmasoft/Catch3/Internal/TestCaseRegistryImpl.cpp @@ -0,0 +1,180 @@ + +// 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 <Progmasoft/Catch3/Internal/TestCaseRegistryImpl.hpp> + +#include <Progmasoft/Catch3/Internal/Enforce.hpp> +#include <Progmasoft/Catch3/Interfaces/IConfig.hpp> +#include <Progmasoft/Catch3/Interfaces/IRegistryHub.hpp> +#include <Progmasoft/Catch3/TestCaseInfo.hpp> +#include <Progmasoft/Catch3/TestSpec.hpp> +#include <Progmasoft/Catch3/Internal/MoveAndForward.hpp> +#include <Progmasoft/Catch3/Internal/TestCaseInfoHasher.hpp> + +#include <algorithm> + +namespace Catch { + + namespace { + // Picked small-ish number at random + static size_t kInitialTestCount = 120; + + static void enforceNoDuplicateTestCases( + std::vector<TestCaseHandle> const& tests ) { + // Flat set instead of std::set for performance. + std::vector<TestCaseInfo const*> seenTests; + seenTests.reserve( tests.size() ); + for ( auto const& test : tests ) { + seenTests.push_back( &test.getTestCaseInfo() ); + } + // We want to keep order of ~equal~ tests + // stable_sort so that duplicates are reported with the + // registration order preserved ("first seen" really is first) + std::stable_sort( + seenTests.begin(), + seenTests.end(), + []( TestCaseInfo const* lhs, TestCaseInfo const* rhs ) { + return *lhs < *rhs; + } + ); + const auto duplicate = std::adjacent_find( + seenTests.begin(), + seenTests.end(), + []( TestCaseInfo const* lhs, TestCaseInfo const* rhs ) { + return *lhs == *rhs; + } + ); + + if ( duplicate != seenTests.end() ) { + const auto infoPtr = *std::next( duplicate ); + CATCH_ERROR( "error: test case \"" + << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() + << "\" already defined.\n" + << "\tFirst seen at " + << ( *duplicate )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + + std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { + switch (config.runOrder()) { + case TestRunOrder::Declared: + return unsortedTestCases; + + case TestRunOrder::LexicographicallySorted: { + std::vector<TestCaseHandle> sorted = unsortedTestCases; + std::sort( + sorted.begin(), + sorted.end(), + []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) { + return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); + } + ); + return sorted; + } + case TestRunOrder::Randomized: { + using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; + + TestCaseInfoHasher h{ config.rngSeed() }; + std::vector<TestWithHash> indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const& handle : unsortedTestCases) { + indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); + } + + std::sort( indexed_tests.begin(), + indexed_tests.end(), + []( TestWithHash const& lhs, TestWithHash const& rhs ) { + if ( lhs.first == rhs.first ) { + return lhs.second.getTestCaseInfo() < + rhs.second.getTestCaseInfo(); + } + return lhs.first < rhs.first; + } ); + + std::vector<TestCaseHandle> randomized; + randomized.reserve(indexed_tests.size()); + + for (auto const& indexed : indexed_tests) { + randomized.push_back(indexed.second); + } + + return randomized; + } + } + + CATCH_INTERNAL_ERROR("Unknown test order value!"); + } + + bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) { + return !testCase.getTestCaseInfo().throws() || config.allowThrows(); + } + + std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector<TestCaseHandle> filtered; + filtered.reserve( testCases.size() ); + for (auto const& testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + // The Catch3 runner shards the run itself. + return filtered; + } + std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + + TestRegistry::TestRegistry() { + // We pre-reserve some reasonable number of tests to avoid the + // initial geometric growth churning during test registration. + m_handles.reserve( kInitialTestCount ); + m_test_infos.reserve( kInitialTestCount ); + m_invokers.reserve( kInitialTestCount ); + } + TestRegistry::~TestRegistry() = default; + + void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) { + m_handles.emplace_back(testInfo.get(), testInvoker.get()); + m_test_infos.push_back(CATCH_MOVE(testInfo)); + m_invokers.push_back(CATCH_MOVE(testInvoker)); + } + + void TestRegistry::enableFilenameTags() { + for (auto& info : m_test_infos) { + info->addFilenameTag(); + } + } + + std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const { + return m_handles; + } + std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_handles ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_handles ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + +} // end namespace Catch |