diff --git a/CMakeLists.txt b/CMakeLists.txt index 6489e3a0..9c56cca3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,7 +225,9 @@ set(IMPL_SOURCES ${HEADER_DIR}/internal/catch_tag_alias.cpp ${HEADER_DIR}/internal/catch_tag_alias_registry.cpp ${HEADER_DIR}/internal/catch_test_case_info.cpp + ${HEADER_DIR}/internal/catch_test_case_registry_impl.cpp ${HEADER_DIR}/internal/catch_test_case_tracker.cpp + ${HEADER_DIR}/internal/catch_test_registry.cpp ${HEADER_DIR}/internal/catch_test_spec.cpp ${HEADER_DIR}/internal/catch_test_spec_parser.cpp ${HEADER_DIR}/internal/catch_timer.cpp diff --git a/include/internal/catch_test_case_registry_impl.cpp b/include/internal/catch_test_case_registry_impl.cpp new file mode 100644 index 00000000..d7cab1d8 --- /dev/null +++ b/include/internal/catch_test_case_registry_impl.cpp @@ -0,0 +1,113 @@ +/* + * 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.hpp" + +#include "catch_context.h" +#include "catch_interfaces_registry_hub.h" +#include "catch_string_manip.h" +#include "catch_test_case_info.h" + +#include + +namespace Catch { + + RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { return std::rand() % n; } + RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { return std::rand() % max(); } + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector 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() ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector 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( std::string 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 diff --git a/include/internal/catch_test_case_registry_impl.hpp b/include/internal/catch_test_case_registry_impl.hpp index 7008ae9f..27b64eb8 100644 --- a/include/internal/catch_test_case_registry_impl.hpp +++ b/include/internal/catch_test_case_registry_impl.hpp @@ -9,28 +9,27 @@ #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #include "catch_test_registry.hpp" -#include "catch_test_case_info.h" #include "catch_test_spec.hpp" -#include "catch_context.h" #include "catch_interfaces_config.h" -#include "catch_string_manip.h" #include #include -#include #include namespace Catch { - struct RandomNumberGenerator { - typedef std::ptrdiff_t result_type; + class TestCase; + class IConfig; - result_type operator()( result_type n ) const { return std::rand() % n; } + struct RandomNumberGenerator { + using result_type = std::ptrdiff_t; + + result_type operator()( result_type n ) const; static constexpr result_type min() { return 0; } static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } + result_type operator()() const; template static void shuffle( V& vector ) { RandomNumberGenerator rng; @@ -38,78 +37,22 @@ namespace Catch { } }; - inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector sorted = unsortedTestCases; + void enforceNoDuplicateTestCases( std::vector const& functions ); - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } - - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( auto const& testCase : testCases ) - if( matchTest( testCase, testSpec, config ) ) - filtered.push_back( testCase ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); class TestRegistry : public ITestCaseRegistry { public: - virtual ~TestRegistry(); + virtual ~TestRegistry() = default; - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name.empty() ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); - } + virtual void registerTest( TestCase const& testCase ); - std::vector const& getAllTests() const override { - return m_functions; - } - std::vector const& getAllTestsSorted( IConfig const& config ) const override { - 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; - } + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; private: std::vector m_functions; @@ -119,57 +62,21 @@ namespace Catch { std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; - TestRegistry::~TestRegistry() {} - - /////////////////////////////////////////////////////////////////////////// class TestInvokerAsFunction : public ITestInvoker { void(*m_testAsFunction)(); public: - TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; - void invoke() const override { - m_testAsFunction(); - } + void invoke() const override; }; - auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { - return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); - } - inline std::string extractClassName( std::string 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; - } + std::string extractClassName( std::string const& classOrQualifiedMethodName ); /////////////////////////////////////////////////////////////////////////// - AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept - { - try { - getMutableRegistryHub() - .registerTest( - makeTestCase( - invoker, - extractClassName( classOrMethod.c_str() ), - nameAndTags.name.c_str(), - nameAndTags.tags.c_str(), - lineInfo)); - } catch (...) { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException( std::current_exception() ); - } - } - AutoReg::~AutoReg() {} } // end namespace Catch diff --git a/include/internal/catch_test_registry.cpp b/include/internal/catch_test_registry.cpp new file mode 100644 index 00000000..28517628 --- /dev/null +++ b/include/internal/catch_test_registry.cpp @@ -0,0 +1,35 @@ +/* + * 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_registry.hpp" +#include "catch_test_case_registry_impl.hpp" +#include "catch_interfaces_registry_hub.h" + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + try { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod.c_str() ), + nameAndTags.name.c_str(), + nameAndTags.tags.c_str(), + lineInfo)); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException( std::current_exception() ); + } + } +} diff --git a/include/internal/catch_test_registry.hpp b/include/internal/catch_test_registry.hpp index c8538a2c..1cd5fa40 100644 --- a/include/internal/catch_test_registry.hpp +++ b/include/internal/catch_test_registry.hpp @@ -35,16 +35,14 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { } struct NameAndTags { - - NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept : name( name_ ), tags( tags_ ) {} - + NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; StringRef name; StringRef tags; }; struct AutoReg : NonCopyable { AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; - ~AutoReg(); + ~AutoReg() = default; }; } // end namespace Catch