catch_test_case_registry_impl.cpp
1 2 // Copyright Catch2 Authors 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 // SPDX-License-Identifier: BSL-1.0 8 #include <catch2/internal/catch_test_case_registry_impl.hpp> 9 10 #include <catch2/internal/catch_context.hpp> 11 #include <catch2/internal/catch_enforce.hpp> 12 #include <catch2/interfaces/catch_interfaces_config.hpp> 13 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp> 14 #include <catch2/internal/catch_random_number_generator.hpp> 15 #include <catch2/internal/catch_run_context.hpp> 16 #include <catch2/internal/catch_sharding.hpp> 17 #include <catch2/catch_test_case_info.hpp> 18 #include <catch2/catch_test_spec.hpp> 19 #include <catch2/internal/catch_move_and_forward.hpp> 20 #include <catch2/internal/catch_test_case_info_hasher.hpp> 21 22 #include <algorithm> 23 #include <set> 24 25 namespace Catch { 26 27 namespace { 28 static void enforceNoDuplicateTestCases( 29 std::vector<TestCaseHandle> const& tests ) { 30 auto testInfoCmp = []( TestCaseInfo const* lhs, 31 TestCaseInfo const* rhs ) { 32 return *lhs < *rhs; 33 }; 34 std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests( 35 testInfoCmp ); 36 for ( auto const& test : tests ) { 37 const auto infoPtr = &test.getTestCaseInfo(); 38 const auto prev = seenTests.insert( infoPtr ); 39 CATCH_ENFORCE( prev.second, 40 "error: test case \"" 41 << infoPtr->name << "\", with tags \"" 42 << infoPtr->tagsAsString() 43 << "\" already defined.\n" 44 << "\tFirst seen at " 45 << ( *prev.first )->lineInfo << "\n" 46 << "\tRedefined at " << infoPtr->lineInfo ); 47 } 48 } 49 50 static bool matchTest( TestCaseHandle const& testCase, 51 TestSpec const& testSpec, 52 IConfig const& config ) { 53 return testSpec.matches( testCase.getTestCaseInfo() ) && 54 isThrowSafe( testCase, config ); 55 } 56 57 } // end unnamed namespace 58 59 std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { 60 switch (config.runOrder()) { 61 case TestRunOrder::Declared: 62 return unsortedTestCases; 63 64 case TestRunOrder::LexicographicallySorted: { 65 std::vector<TestCaseHandle> sorted = unsortedTestCases; 66 std::sort( 67 sorted.begin(), 68 sorted.end(), 69 []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) { 70 return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); 71 } 72 ); 73 return sorted; 74 } 75 case TestRunOrder::Randomized: { 76 seedRng(config); 77 using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; 78 79 TestCaseInfoHasher h{ config.rngSeed() }; 80 std::vector<TestWithHash> indexed_tests; 81 indexed_tests.reserve(unsortedTestCases.size()); 82 83 for (auto const& handle : unsortedTestCases) { 84 indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); 85 } 86 87 std::sort( indexed_tests.begin(), 88 indexed_tests.end(), 89 []( TestWithHash const& lhs, TestWithHash const& rhs ) { 90 if ( lhs.first == rhs.first ) { 91 return lhs.second.getTestCaseInfo() < 92 rhs.second.getTestCaseInfo(); 93 } 94 return lhs.first < rhs.first; 95 } ); 96 97 std::vector<TestCaseHandle> randomized; 98 randomized.reserve(indexed_tests.size()); 99 100 for (auto const& indexed : indexed_tests) { 101 randomized.push_back(indexed.second); 102 } 103 104 return randomized; 105 } 106 } 107 108 CATCH_INTERNAL_ERROR("Unknown test order value!"); 109 } 110 111 bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) { 112 return !testCase.getTestCaseInfo().throws() || config.allowThrows(); 113 } 114 115 std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) { 116 std::vector<TestCaseHandle> filtered; 117 filtered.reserve( testCases.size() ); 118 for (auto const& testCase : testCases) { 119 if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || 120 (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { 121 filtered.push_back(testCase); 122 } 123 } 124 return createShard(filtered, config.shardCount(), config.shardIndex()); 125 } 126 std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ) { 127 return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); 128 } 129 130 void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) { 131 m_handles.emplace_back(testInfo.get(), testInvoker.get()); 132 m_viewed_test_infos.push_back(testInfo.get()); 133 m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); 134 m_invokers.push_back(CATCH_MOVE(testInvoker)); 135 } 136 137 std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const { 138 return m_viewed_test_infos; 139 } 140 141 std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const { 142 return m_handles; 143 } 144 std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { 145 if( m_sortedFunctions.empty() ) 146 enforceNoDuplicateTestCases( m_handles ); 147 148 if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { 149 m_sortedFunctions = sortTests( config, m_handles ); 150 m_currentSortOrder = config.runOrder(); 151 } 152 return m_sortedFunctions; 153 } 154 155 } // end namespace Catch