1 /*
2  *  Created by Phil on 7/1/2011
3  *  Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 #ifndef TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
10 
11 #include "catch_test_registry.hpp"
12 #include "catch_test_case_info.h"
13 #include "catch_test_spec.hpp"
14 #include "catch_context.h"
15 
16 #include <vector>
17 #include <set>
18 #include <sstream>
19 #include <algorithm>
20 
21 
22 namespace Catch {
23 
24     struct RandomNumberGenerator {
25         typedef std::ptrdiff_t result_type;
26 
operator ()Catch::RandomNumberGenerator27         result_type operator()( result_type n ) const { return std::rand() % n; }
28 
29 #ifdef CATCH_CONFIG_CPP11_SHUFFLE
minCatch::RandomNumberGenerator30         static constexpr result_type min() { return 0; }
maxCatch::RandomNumberGenerator31         static constexpr result_type max() { return 1000000; }
operator ()Catch::RandomNumberGenerator32         result_type operator()() const { return std::rand() % max(); }
33 #endif
34         template<typename V>
shuffleCatch::RandomNumberGenerator35         static void shuffle( V& vector ) {
36             RandomNumberGenerator rng;
37 #ifdef CATCH_CONFIG_CPP11_SHUFFLE
38             std::shuffle( vector.begin(), vector.end(), rng );
39 #else
40             std::random_shuffle( vector.begin(), vector.end(), rng );
41 #endif
42         }
43     };
44 
sortTests(IConfig const & config,std::vector<TestCase> const & unsortedTestCases)45     inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
46 
47         std::vector<TestCase> sorted = unsortedTestCases;
48 
49         switch( config.runOrder() ) {
50             case RunTests::InLexicographicalOrder:
51                 std::sort( sorted.begin(), sorted.end() );
52                 break;
53             case RunTests::InRandomOrder:
54                 {
55                     seedRng( config );
56                     RandomNumberGenerator::shuffle( sorted );
57                 }
58                 break;
59             case RunTests::InDeclarationOrder:
60                 // already in declaration order
61                 break;
62         }
63         return sorted;
64     }
matchTest(TestCase const & testCase,TestSpec const & testSpec,IConfig const & config)65     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
66         return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
67     }
68 
enforceNoDuplicateTestCases(std::vector<TestCase> const & functions)69     void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
70         std::set<TestCase> seenFunctions;
71         for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
72             it != itEnd;
73             ++it ) {
74             std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
75             if( !prev.second ) {
76                 std::ostringstream ss;
77 
78                 ss  << Colour( Colour::Red )
79                     << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
80                     << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
81                     << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
82 
83                 throw std::runtime_error(ss.str());
84             }
85         }
86     }
87 
filterTests(std::vector<TestCase> const & testCases,TestSpec const & testSpec,IConfig const & config)88     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
89         std::vector<TestCase> filtered;
90         filtered.reserve( testCases.size() );
91         for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
92                 it != itEnd;
93                 ++it )
94             if( matchTest( *it, testSpec, config ) )
95                 filtered.push_back( *it );
96         return filtered;
97     }
getAllTestCasesSorted(IConfig const & config)98     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
99         return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
100     }
101 
102     class TestRegistry : public ITestCaseRegistry {
103     public:
TestRegistry()104         TestRegistry()
105         :   m_currentSortOrder( RunTests::InDeclarationOrder ),
106             m_unnamedCount( 0 )
107         {}
108         virtual ~TestRegistry();
109 
registerTest(TestCase const & testCase)110         virtual void registerTest( TestCase const& testCase ) {
111             std::string name = testCase.getTestCaseInfo().name;
112             if( name.empty() ) {
113                 std::ostringstream oss;
114                 oss << "Anonymous test case " << ++m_unnamedCount;
115                 return registerTest( testCase.withName( oss.str() ) );
116             }
117             m_functions.push_back( testCase );
118         }
119 
getAllTests() const120         virtual std::vector<TestCase> const& getAllTests() const {
121             return m_functions;
122         }
getAllTestsSorted(IConfig const & config) const123         virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
124             if( m_sortedFunctions.empty() )
125                 enforceNoDuplicateTestCases( m_functions );
126 
127             if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
128                 m_sortedFunctions = sortTests( config, m_functions );
129                 m_currentSortOrder = config.runOrder();
130             }
131             return m_sortedFunctions;
132         }
133 
134     private:
135         std::vector<TestCase> m_functions;
136         mutable RunTests::InWhatOrder m_currentSortOrder;
137         mutable std::vector<TestCase> m_sortedFunctions;
138         size_t m_unnamedCount;
139         std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
140     };
141 
142     ///////////////////////////////////////////////////////////////////////////
143 
144     class FreeFunctionTestCase : public SharedImpl<ITestCase> {
145     public:
146 
FreeFunctionTestCase(TestFunction fun)147         FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
148 
invoke() const149         virtual void invoke() const {
150             m_fun();
151         }
152 
153     private:
154         virtual ~FreeFunctionTestCase();
155 
156         TestFunction m_fun;
157     };
158 
extractClassName(std::string const & classOrQualifiedMethodName)159     inline std::string extractClassName( std::string 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 
registerTestCase(ITestCase * testCase,char const * classOrQualifiedMethodName,NameAndDesc const & nameAndDesc,SourceLineInfo const & lineInfo)172     void registerTestCase
173         (   ITestCase* testCase,
174             char const* classOrQualifiedMethodName,
175             NameAndDesc const& nameAndDesc,
176             SourceLineInfo const& lineInfo ) {
177 
178         getMutableRegistryHub().registerTest
179             ( makeTestCase
180                 (   testCase,
181                     extractClassName( classOrQualifiedMethodName ),
182                     nameAndDesc.name,
183                     nameAndDesc.description,
184                     lineInfo ) );
185     }
registerTestCaseFunction(TestFunction function,SourceLineInfo const & lineInfo,NameAndDesc const & nameAndDesc)186     void registerTestCaseFunction
187         (   TestFunction function,
188             SourceLineInfo const& lineInfo,
189             NameAndDesc const& nameAndDesc ) {
190         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
191     }
192 
193     ///////////////////////////////////////////////////////////////////////////
194 
AutoReg(TestFunction function,SourceLineInfo const & lineInfo,NameAndDesc const & nameAndDesc)195     AutoReg::AutoReg
196         (   TestFunction function,
197             SourceLineInfo const& lineInfo,
198             NameAndDesc const& nameAndDesc ) {
199         registerTestCaseFunction( function, lineInfo, nameAndDesc );
200     }
201 
~AutoReg()202     AutoReg::~AutoReg() {}
203 
204 } // end namespace Catch
205 
206 
207 #endif // TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
208