mirror of https://github.com/catchorg/Catch2.git
278 lines
8.8 KiB
C++
278 lines
8.8 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.hpp"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <assert.h>
|
||
|
#include <stdexcept>
|
||
|
#include <memory>
|
||
|
|
||
|
CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
|
||
|
|
||
|
namespace Catch {
|
||
|
namespace TestCaseTracking {
|
||
|
|
||
|
NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
|
||
|
: name( _name ),
|
||
|
location( _location )
|
||
|
{}
|
||
|
|
||
|
|
||
|
|
||
|
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 ) {
|
||
|
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
|
||
|
|
||
|
CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
|