// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #include #include #include #include #include #include #include #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif namespace Catch { namespace TestCaseTracking { NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location ) : name( CATCH_MOVE(_name) ), location( _location ) {} ITracker::ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): m_nameAndLocation( CATCH_MOVE( nameAndLoc ) ), m_parent( parent ) { if ( m_parent ) { m_allTrackerDepth = m_parent->m_allTrackerDepth + 1; // We leave section trackers to bump themselves up, as // we cannot use `isSectionTracker` in constructor m_sectionOnlyDepth = m_parent->m_sectionOnlyDepth; m_filterRef = m_parent->m_filterRef; m_newStyleFilters = m_parent->m_newStyleFilters; } } ITracker::~ITracker() = default; void ITracker::markAsNeedingAnotherRun() { m_runState = NeedsAnotherRun; } void ITracker::addChild( ITrackerPtr&& child ) { m_children.push_back( CATCH_MOVE(child) ); } ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { auto it = std::find_if( m_children.begin(), m_children.end(), [&nameAndLocation]( ITrackerPtr const& tracker ) { auto const& tnameAndLoc = tracker->nameAndLocation(); if ( tnameAndLoc.location.line != nameAndLocation.location.line ) { return false; } return tnameAndLoc == nameAndLocation; } ); return ( it != m_children.end() ) ? it->get() : nullptr; } bool ITracker::isSectionTracker() const { return false; } bool ITracker::isGeneratorTracker() const { return false; } bool ITracker::isOpen() const { return m_runState != NotStarted && !isComplete(); } bool ITracker::hasStarted() const { return m_runState != NotStarted; } void ITracker::openChild() { if (m_runState != ExecutingChildren) { m_runState = ExecutingChildren; if (m_parent) { m_parent->openChild(); } } } ITracker& TrackerContext::startRun() { using namespace std::string_literals; m_rootTracker = Catch::Detail::make_unique( NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), *this, nullptr ); m_currentTracker = nullptr; m_runState = Executing; return *m_rootTracker; } void TrackerContext::completeCycle() { m_runState = CompletedCycle; } bool TrackerContext::completedCycle() const { return m_runState == CompletedCycle; } void TrackerContext::setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ): ITracker(CATCH_MOVE(nameAndLocation), parent), m_ctx( ctx ) {} bool TrackerBase::isComplete() const { return m_runState == CompletedSuccessfully || m_runState == Failed; } 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( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->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::moveToParent() { assert( m_parent ); m_ctx.setCurrentTracker( m_parent ); } void TrackerBase::moveToThis() { m_ctx.setCurrentTracker( this ); } bool SectionTracker::isFilteredImpl() const { // TBD: This is currently _very_ similar to the block in `isComplete`. // Is this necessarily that way, or just accident of current semantics? const size_t filterIndex = m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; if ( filterIndex < m_filterRef->size() ) { // 1) New style filter must explicitly target section if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type != PathFilter::For::Section ) { return true; } // 2) Both style filters must match the trimmed name exactly if ( m_trimmed_name != StringRef( ( *m_filterRef )[filterIndex].filter ) ) { return true; } } return false; } SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { if( m_parent ) { ++m_sectionOnlyDepth; } } bool SectionTracker::isComplete() const { // If there are active filters AND we do not pass them, // the section is always "completed" const size_t filterIndex = m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; if ( filterIndex < m_filterRef->size() ) { // There is active filter, check it // 1) New style filter must explicitly target section if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type != PathFilter::For::Section ) { return true; } // 2) Both style filters must match the trimmed name exactly if ( m_trimmed_name != StringRef( ( *m_filterRef )[filterIndex].filter ) ) { return true; } } // Otherwise we delegate to the generic processing return TrackerBase::isComplete(); } bool SectionTracker::isSectionTracker() const { return true; } SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { SectionTracker* tracker; ITracker& currentTracker = ctx.currentTracker(); if ( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); tracker = static_cast( childTracker ); } else { auto newTracker = Catch::Detail::make_unique( NameAndLocation{ static_cast(nameAndLocation.name), nameAndLocation.location }, ctx, ¤tTracker ); tracker = newTracker.get(); currentTracker.addChild( CATCH_MOVE( newTracker ) ); } if ( !ctx.completedCycle() ) { tracker->tryOpen(); } return *tracker; } void SectionTracker::tryOpen() { if( !isComplete() ) open(); } StringRef SectionTracker::trimmedName() const { return m_trimmed_name; } } // namespace TestCaseTracking } // namespace Catch #if defined(__clang__) # pragma clang diagnostic pop #endif