1 /*
2  *  Created by Martin on 25/07/2017
3  *
4  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
5  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  */
7 
8 #include "catch_test_case_registry_impl.h"
9 
10 #include "catch_context.h"
11 #include "catch_enforce.h"
12 #include "catch_interfaces_registry_hub.h"
13 #include "catch_random_number_generator.h"
14 #include "catch_run_context.h"
15 #include "catch_string_manip.h"
16 #include "catch_test_case_info.h"
17 
18 #include <algorithm>
19 #include <sstream>
20 
21 namespace Catch {
22 
23     namespace {
24         struct TestHasher {
TestHasherCatch::__anonee2b127c0111::TestHasher25             explicit TestHasher(Catch::SimplePcg32& rng_instance) {
26                 basis = rng_instance();
27                 basis <<= 32;
28                 basis |= rng_instance();
29             }
30 
31             uint64_t basis;
32 
operator ()Catch::__anonee2b127c0111::TestHasher33             uint64_t operator()(TestCase const& t) const {
34                 // Modified FNV-1a hash
35                 static constexpr uint64_t prime = 1099511628211;
36                 uint64_t hash = basis;
37                 for (const char c : t.name) {
38                     hash ^= c;
39                     hash *= prime;
40                 }
41                 return hash;
42             }
43         };
44     } // end unnamed namespace
45 
46 
sortTests(IConfig const & config,std::vector<TestCase> const & unsortedTestCases)47     std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
48         switch( config.runOrder() ) {
49             case RunTests::InDeclarationOrder:
50                 // already in declaration order
51                 break;
52 
53             case RunTests::InLexicographicalOrder: {
54                 std::vector<TestCase> sorted = unsortedTestCases;
55                 std::sort( sorted.begin(), sorted.end() );
56                 return sorted;
57             }
58 
59             case RunTests::InRandomOrder: {
60                 seedRng( config );
61                 TestHasher h( rng() );
62 
63                 using hashedTest = std::pair<uint64_t, TestCase const*>;
64                 std::vector<hashedTest> indexed_tests;
65                 indexed_tests.reserve( unsortedTestCases.size() );
66 
67                 for (auto const& testCase : unsortedTestCases) {
68                     indexed_tests.emplace_back(h(testCase), &testCase);
69                 }
70 
71                 std::sort(indexed_tests.begin(), indexed_tests.end(),
72                           [](hashedTest const& lhs, hashedTest const& rhs) {
73                           if (lhs.first == rhs.first) {
74                               return lhs.second->name < rhs.second->name;
75                           }
76                           return lhs.first < rhs.first;
77                 });
78 
79                 std::vector<TestCase> sorted;
80                 sorted.reserve( indexed_tests.size() );
81 
82                 for (auto const& hashed : indexed_tests) {
83                     sorted.emplace_back(*hashed.second);
84                 }
85 
86                 return sorted;
87             }
88         }
89         return unsortedTestCases;
90     }
91 
isThrowSafe(TestCase const & testCase,IConfig const & config)92     bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
93         return !testCase.throws() || config.allowThrows();
94     }
95 
matchTest(TestCase const & testCase,TestSpec const & testSpec,IConfig const & config)96     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
97         return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
98     }
99 
enforceNoDuplicateTestCases(std::vector<TestCase> const & functions)100     void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
101         std::set<TestCase> seenFunctions;
102         for( auto const& function : functions ) {
103             auto prev = seenFunctions.insert( function );
104             CATCH_ENFORCE( prev.second,
105                     "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
106                     << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
107                     << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
108         }
109     }
110 
filterTests(std::vector<TestCase> const & testCases,TestSpec const & testSpec,IConfig const & config)111     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
112         std::vector<TestCase> filtered;
113         filtered.reserve( testCases.size() );
114         for (auto const& testCase : testCases) {
115             if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
116                 (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
117                 filtered.push_back(testCase);
118             }
119         }
120         return filtered;
121     }
getAllTestCasesSorted(IConfig const & config)122     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
123         return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
124     }
125 
registerTest(TestCase const & testCase)126     void TestRegistry::registerTest( TestCase const& testCase ) {
127         std::string name = testCase.getTestCaseInfo().name;
128         if( name.empty() ) {
129             ReusableStringStream rss;
130             rss << "Anonymous test case " << ++m_unnamedCount;
131             return registerTest( testCase.withName( rss.str() ) );
132         }
133         m_functions.push_back( testCase );
134     }
135 
getAllTests() const136     std::vector<TestCase> const& TestRegistry::getAllTests() const {
137         return m_functions;
138     }
getAllTestsSorted(IConfig const & config) const139     std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
140         if( m_sortedFunctions.empty() )
141             enforceNoDuplicateTestCases( m_functions );
142 
143         if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
144             m_sortedFunctions = sortTests( config, m_functions );
145             m_currentSortOrder = config.runOrder();
146         }
147         return m_sortedFunctions;
148     }
149 
150 
151 
152     ///////////////////////////////////////////////////////////////////////////
TestInvokerAsFunction(void (* testAsFunction)())153     TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
154 
invoke() const155     void TestInvokerAsFunction::invoke() const {
156         m_testAsFunction();
157     }
158 
extractClassName(StringRef const & classOrQualifiedMethodName)159     std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
160         std::string className(classOrQualifiedMethodName);
161         if( startsWith( className, '&' ) )
162         {
163             std::size_t lastColons = className.rfind( "::" );
164             std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
165             if( penultimateColons == std::string::npos )
166                 penultimateColons = 1;
167             className = className.substr( penultimateColons, lastColons-penultimateColons );
168         }
169         return className;
170     }
171 
172 } // end namespace Catch
173