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 unsigned int result_type; 26 operator ()Catch::RandomNumberGenerator27 result_type operator()( result_type n ) const { return std::rand() % n; } 28 29 #ifdef CATCH_CONFIG_CPP11_SHUFFLE result_typeCatch::RandomNumberGenerator30 static constexpr result_type (min)() { return 0; } result_typeCatch::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