mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			288 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  *  Created by Martin on 19/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_tracker.h"
 | |
| 
 | |
| #include "catch_enforce.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <assert.h>
 | |
| #include <stdexcept>
 | |
| #include <memory>
 | |
| #include <sstream>
 | |
| 
 | |
| #if defined(__clang__)
 | |
| #    pragma clang diagnostic push
 | |
| #    pragma clang diagnostic ignored "-Wexit-time-destructors"
 | |
| #endif
 | |
| 
 | |
| namespace Catch {
 | |
| namespace TestCaseTracking {
 | |
| 
 | |
|     NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
 | |
|     :   name( _name ),
 | |
|         location( _location )
 | |
|     {}
 | |
| 
 | |
| 
 | |
|     ITracker::~ITracker() = default;
 | |
| 
 | |
| 
 | |
|     TrackerContext& TrackerContext::instance() {
 | |
|         static TrackerContext s_instance;
 | |
|         return s_instance;
 | |
|     }
 | |
| 
 | |
|     ITracker& TrackerContext::startRun() {
 | |
|         m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
 | |
|         m_currentTracker = nullptr;
 | |
|         m_runState = Executing;
 | |
|         return *m_rootTracker;
 | |
|     }
 | |
| 
 | |
|     void TrackerContext::endRun() {
 | |
|         m_rootTracker.reset();
 | |
|         m_currentTracker = nullptr;
 | |
|         m_runState = NotStarted;
 | |
|     }
 | |
| 
 | |
|     void TrackerContext::startCycle() {
 | |
|         m_currentTracker = m_rootTracker.get();
 | |
|         m_runState = Executing;
 | |
|     }
 | |
|     void TrackerContext::completeCycle() {
 | |
|         m_runState = CompletedCycle;
 | |
|     }
 | |
| 
 | |
|     bool TrackerContext::completedCycle() const {
 | |
|         return m_runState == CompletedCycle;
 | |
|     }
 | |
|     ITracker& TrackerContext::currentTracker() {
 | |
|         return *m_currentTracker;
 | |
|     }
 | |
|     void TrackerContext::setCurrentTracker( ITracker* tracker ) {
 | |
|         m_currentTracker = tracker;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
 | |
|     bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const {
 | |
|         return
 | |
|             tracker->nameAndLocation().name == m_nameAndLocation.name &&
 | |
|             tracker->nameAndLocation().location == m_nameAndLocation.location;
 | |
|     }
 | |
| 
 | |
|     TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
 | |
|     :   m_nameAndLocation( nameAndLocation ),
 | |
|         m_ctx( ctx ),
 | |
|         m_parent( parent )
 | |
|     {}
 | |
| 
 | |
|     NameAndLocation const& TrackerBase::nameAndLocation() const {
 | |
|         return m_nameAndLocation;
 | |
|     }
 | |
|     bool TrackerBase::isComplete() const {
 | |
|         return m_runState == CompletedSuccessfully || m_runState == Failed;
 | |
|     }
 | |
|     bool TrackerBase::isSuccessfullyCompleted() const {
 | |
|         return m_runState == CompletedSuccessfully;
 | |
|     }
 | |
|     bool TrackerBase::isOpen() const {
 | |
|         return m_runState != NotStarted && !isComplete();
 | |
|     }
 | |
|     bool TrackerBase::hasChildren() const {
 | |
|         return !m_children.empty();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void TrackerBase::addChild( ITrackerPtr const& child ) {
 | |
|         m_children.push_back( child );
 | |
|     }
 | |
| 
 | |
|     ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
 | |
|         auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
 | |
|         return( it != m_children.end() )
 | |
|             ? *it
 | |
|             : nullptr;
 | |
|     }
 | |
|     ITracker& TrackerBase::parent() {
 | |
|         assert( m_parent ); // Should always be non-null except for root
 | |
|         return *m_parent;
 | |
|     }
 | |
| 
 | |
|     void TrackerBase::openChild() {
 | |
|         if( m_runState != ExecutingChildren ) {
 | |
|             m_runState = ExecutingChildren;
 | |
|             if( m_parent )
 | |
|                 m_parent->openChild();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool TrackerBase::isSectionTracker() const { return false; }
 | |
|     bool TrackerBase::isIndexTracker() const { return false; }
 | |
| 
 | |
|     void TrackerBase::open() {
 | |
|         m_runState = Executing;
 | |
|         moveToThis();
 | |
|         if( m_parent )
 | |
|             m_parent->openChild();
 | |
|     }
 | |
| 
 | |
|     void TrackerBase::close() {
 | |
| 
 | |
|         // Close any still open children (e.g. generators)
 | |
|         while( &m_ctx.currentTracker() != this )
 | |
|             m_ctx.currentTracker().close();
 | |
| 
 | |
|         switch( m_runState ) {
 | |
|             case NeedsAnotherRun:
 | |
|                 break;
 | |
| 
 | |
|             case Executing:
 | |
|                 m_runState = CompletedSuccessfully;
 | |
|                 break;
 | |
|             case ExecutingChildren:
 | |
|                 if( m_children.empty() || m_children.back()->isComplete() )
 | |
|                     m_runState = CompletedSuccessfully;
 | |
|                 break;
 | |
| 
 | |
|             case NotStarted:
 | |
|             case CompletedSuccessfully:
 | |
|             case Failed:
 | |
|                 CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
 | |
| 
 | |
|             default:
 | |
|                 CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
 | |
|         }
 | |
|         moveToParent();
 | |
|         m_ctx.completeCycle();
 | |
|     }
 | |
|     void TrackerBase::fail() {
 | |
|         m_runState = Failed;
 | |
|         if( m_parent )
 | |
|             m_parent->markAsNeedingAnotherRun();
 | |
|         moveToParent();
 | |
|         m_ctx.completeCycle();
 | |
|     }
 | |
|     void TrackerBase::markAsNeedingAnotherRun() {
 | |
|         m_runState = NeedsAnotherRun;
 | |
|     }
 | |
| 
 | |
|     void TrackerBase::moveToParent() {
 | |
|         assert( m_parent );
 | |
|         m_ctx.setCurrentTracker( m_parent );
 | |
|     }
 | |
|     void TrackerBase::moveToThis() {
 | |
|         m_ctx.setCurrentTracker( this );
 | |
|     }
 | |
| 
 | |
|     SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
 | |
|     :   TrackerBase( nameAndLocation, ctx, parent )
 | |
|     {
 | |
|         if( parent ) {
 | |
|             while( !parent->isSectionTracker() )
 | |
|                 parent = &parent->parent();
 | |
| 
 | |
|             SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
 | |
|             addNextFilters( parentSection.m_filters );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool SectionTracker::isSectionTracker() const { return true; }
 | |
| 
 | |
|     SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
 | |
|         std::shared_ptr<SectionTracker> section;
 | |
| 
 | |
|         ITracker& currentTracker = ctx.currentTracker();
 | |
|         if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
 | |
|             assert( childTracker );
 | |
|             assert( childTracker->isSectionTracker() );
 | |
|             section = std::static_pointer_cast<SectionTracker>( childTracker );
 | |
|         }
 | |
|         else {
 | |
|             section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker );
 | |
|             currentTracker.addChild( section );
 | |
|         }
 | |
|         if( !ctx.completedCycle() )
 | |
|             section->tryOpen();
 | |
|         return *section;
 | |
|     }
 | |
| 
 | |
|     void SectionTracker::tryOpen() {
 | |
|         if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
 | |
|             open();
 | |
|     }
 | |
| 
 | |
|     void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
 | |
|         if( !filters.empty() ) {
 | |
|             m_filters.push_back(""); // Root - should never be consulted
 | |
|             m_filters.push_back(""); // Test Case - not a section filter
 | |
|             m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
 | |
|         }
 | |
|     }
 | |
|     void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
 | |
|         if( filters.size() > 1 )
 | |
|             m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
 | |
|     }
 | |
| 
 | |
|     IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
 | |
|     :   TrackerBase( nameAndLocation, ctx, parent ),
 | |
|         m_size( size )
 | |
|     {}
 | |
| 
 | |
|     bool IndexTracker::isIndexTracker() const { return true; }
 | |
| 
 | |
|     IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
 | |
|         std::shared_ptr<IndexTracker> tracker;
 | |
| 
 | |
|         ITracker& currentTracker = ctx.currentTracker();
 | |
|         if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
 | |
|             assert( childTracker );
 | |
|             assert( childTracker->isIndexTracker() );
 | |
|             tracker = std::static_pointer_cast<IndexTracker>( childTracker );
 | |
|         }
 | |
|         else {
 | |
|             tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size );
 | |
|             currentTracker.addChild( tracker );
 | |
|         }
 | |
| 
 | |
|         if( !ctx.completedCycle() && !tracker->isComplete() ) {
 | |
|             if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
 | |
|                 tracker->moveNext();
 | |
|             tracker->open();
 | |
|         }
 | |
| 
 | |
|         return *tracker;
 | |
|     }
 | |
| 
 | |
|     int IndexTracker::index() const { return m_index; }
 | |
| 
 | |
|     void IndexTracker::moveNext() {
 | |
|         m_index++;
 | |
|         m_children.clear();
 | |
|     }
 | |
| 
 | |
|     void IndexTracker::close() {
 | |
|         TrackerBase::close();
 | |
|         if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
 | |
|             m_runState = Executing;
 | |
|     }
 | |
| 
 | |
| } // namespace TestCaseTracking
 | |
| 
 | |
| using TestCaseTracking::ITracker;
 | |
| using TestCaseTracking::TrackerContext;
 | |
| using TestCaseTracking::SectionTracker;
 | |
| using TestCaseTracking::IndexTracker;
 | |
| 
 | |
| } // namespace Catch
 | |
| 
 | |
| #if defined(__clang__)
 | |
| #    pragma clang diagnostic pop
 | |
| #endif
 |