mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 15:26:11 +01:00
7c25dae9ea
The support is to be considered experimental, that is, the interfaces, the first party generators and helper functions can change or be removed at any point in time. Related to #850
285 lines
8.8 KiB
C++
285 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.h"
|
|
|
|
#include "catch_enforce.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#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::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(),
|
|
[&nameAndLocation]( ITrackerPtr const& tracker ){
|
|
return
|
|
tracker->nameAndLocation().location == nameAndLocation.location &&
|
|
tracker->nameAndLocation().name == nameAndLocation.name;
|
|
} );
|
|
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
|