123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- /*
- * Created by Martin on 25/07/2017
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
- #include "catch_test_case_registry_impl.h"
- #include "catch_context.h"
- #include "catch_enforce.h"
- #include "catch_interfaces_registry_hub.h"
- #include "catch_random_number_generator.h"
- #include "catch_run_context.h"
- #include "catch_string_manip.h"
- #include "catch_test_case_info.h"
- #include <algorithm>
- #include <sstream>
- namespace Catch {
- namespace {
- struct TestHasher {
- using hash_t = uint64_t;
- explicit TestHasher( hash_t hashSuffix ):
- m_hashSuffix{ hashSuffix } {}
- uint32_t operator()( TestCase const& t ) const {
- // FNV-1a hash with multiplication fold.
- const hash_t prime = 1099511628211u;
- hash_t hash = 14695981039346656037u;
- for ( const char c : t.name ) {
- hash ^= c;
- hash *= prime;
- }
- hash ^= m_hashSuffix;
- hash *= prime;
- const uint32_t low{ static_cast<uint32_t>( hash ) };
- const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
- return low * high;
- }
- private:
- hash_t m_hashSuffix;
- };
- } // end unnamed namespace
- std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
- switch( config.runOrder() ) {
- case RunTests::InDeclarationOrder:
- // already in declaration order
- break;
- case RunTests::InLexicographicalOrder: {
- std::vector<TestCase> sorted = unsortedTestCases;
- std::sort( sorted.begin(), sorted.end() );
- return sorted;
- }
- case RunTests::InRandomOrder: {
- seedRng( config );
- TestHasher h{ config.rngSeed() };
- using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
- std::vector<hashedTest> indexed_tests;
- indexed_tests.reserve( unsortedTestCases.size() );
- for (auto const& testCase : unsortedTestCases) {
- indexed_tests.emplace_back(h(testCase), &testCase);
- }
- std::sort(indexed_tests.begin(), indexed_tests.end(),
- [](hashedTest const& lhs, hashedTest const& rhs) {
- if (lhs.first == rhs.first) {
- return lhs.second->name < rhs.second->name;
- }
- return lhs.first < rhs.first;
- });
- std::vector<TestCase> sorted;
- sorted.reserve( indexed_tests.size() );
- for (auto const& hashed : indexed_tests) {
- sorted.emplace_back(*hashed.second);
- }
- return sorted;
- }
- }
- return unsortedTestCases;
- }
- bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
- return !testCase.throws() || config.allowThrows();
- }
- bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
- return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
- }
- void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
- std::set<TestCase> seenFunctions;
- for( auto const& function : functions ) {
- auto prev = seenFunctions.insert( function );
- CATCH_ENFORCE( prev.second,
- "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
- << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
- << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
- }
- }
- std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
- std::vector<TestCase> filtered;
- filtered.reserve( testCases.size() );
- for (auto const& testCase : testCases) {
- if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
- (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
- filtered.push_back(testCase);
- }
- }
- return filtered;
- }
- std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
- return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
- }
- void TestRegistry::registerTest( TestCase const& testCase ) {
- std::string name = testCase.getTestCaseInfo().name;
- if( name.empty() ) {
- ReusableStringStream rss;
- rss << "Anonymous test case " << ++m_unnamedCount;
- return registerTest( testCase.withName( rss.str() ) );
- }
- m_functions.push_back( testCase );
- }
- std::vector<TestCase> const& TestRegistry::getAllTests() const {
- return m_functions;
- }
- std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
- if( m_sortedFunctions.empty() )
- enforceNoDuplicateTestCases( m_functions );
- if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
- m_sortedFunctions = sortTests( config, m_functions );
- m_currentSortOrder = config.runOrder();
- }
- return m_sortedFunctions;
- }
- ///////////////////////////////////////////////////////////////////////////
- TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
- void TestInvokerAsFunction::invoke() const {
- m_testAsFunction();
- }
- std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
- std::string className(classOrQualifiedMethodName);
- if( startsWith( className, '&' ) )
- {
- std::size_t lastColons = className.rfind( "::" );
- std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
- if( penultimateColons == std::string::npos )
- penultimateColons = 1;
- className = className.substr( penultimateColons, lastColons-penultimateColons );
- }
- return className;
- }
- } // end namespace Catch
|