catch_test_case_tracker.cpp
1 2 // Copyright Catch2 Authors 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 // SPDX-License-Identifier: BSL-1.0 8 #include <catch2/internal/catch_test_case_tracker.hpp> 9 10 #include <catch2/internal/catch_enforce.hpp> 11 #include <catch2/internal/catch_string_manip.hpp> 12 #include <catch2/internal/catch_move_and_forward.hpp> 13 14 #include <algorithm> 15 #include <cassert> 16 17 #if defined(__clang__) 18 # pragma clang diagnostic push 19 # pragma clang diagnostic ignored "-Wexit-time-destructors" 20 #endif 21 22 namespace Catch { 23 namespace TestCaseTracking { 24 25 NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location ) 26 : name( CATCH_MOVE(_name) ), 27 location( _location ) 28 {} 29 30 31 ITracker::~ITracker() = default; 32 33 void ITracker::markAsNeedingAnotherRun() { 34 m_runState = NeedsAnotherRun; 35 } 36 37 void ITracker::addChild( ITrackerPtr&& child ) { 38 m_children.push_back( CATCH_MOVE(child) ); 39 } 40 41 ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { 42 auto it = std::find_if( 43 m_children.begin(), 44 m_children.end(), 45 [&nameAndLocation]( ITrackerPtr const& tracker ) { 46 auto const& tnameAndLoc = tracker->nameAndLocation(); 47 if ( tnameAndLoc.location.line != 48 nameAndLocation.location.line ) { 49 return false; 50 } 51 return tnameAndLoc == nameAndLocation; 52 } ); 53 return ( it != m_children.end() ) ? it->get() : nullptr; 54 } 55 56 bool ITracker::isSectionTracker() const { return false; } 57 bool ITracker::isGeneratorTracker() const { return false; } 58 59 bool ITracker::isOpen() const { 60 return m_runState != NotStarted && !isComplete(); 61 } 62 63 bool ITracker::hasStarted() const { return m_runState != NotStarted; } 64 65 void ITracker::openChild() { 66 if (m_runState != ExecutingChildren) { 67 m_runState = ExecutingChildren; 68 if (m_parent) { 69 m_parent->openChild(); 70 } 71 } 72 } 73 74 ITracker& TrackerContext::startRun() { 75 using namespace std::string_literals; 76 m_rootTracker = Catch::Detail::make_unique<SectionTracker>( 77 NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), 78 *this, 79 nullptr ); 80 m_currentTracker = nullptr; 81 m_runState = Executing; 82 return *m_rootTracker; 83 } 84 85 void TrackerContext::completeCycle() { 86 m_runState = CompletedCycle; 87 } 88 89 bool TrackerContext::completedCycle() const { 90 return m_runState == CompletedCycle; 91 } 92 void TrackerContext::setCurrentTracker( ITracker* tracker ) { 93 m_currentTracker = tracker; 94 } 95 96 97 TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ): 98 ITracker(CATCH_MOVE(nameAndLocation), parent), 99 m_ctx( ctx ) 100 {} 101 102 bool TrackerBase::isComplete() const { 103 return m_runState == CompletedSuccessfully || m_runState == Failed; 104 } 105 106 void TrackerBase::open() { 107 m_runState = Executing; 108 moveToThis(); 109 if( m_parent ) 110 m_parent->openChild(); 111 } 112 113 void TrackerBase::close() { 114 115 // Close any still open children (e.g. generators) 116 while( &m_ctx.currentTracker() != this ) 117 m_ctx.currentTracker().close(); 118 119 switch( m_runState ) { 120 case NeedsAnotherRun: 121 break; 122 123 case Executing: 124 m_runState = CompletedSuccessfully; 125 break; 126 case ExecutingChildren: 127 if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) 128 m_runState = CompletedSuccessfully; 129 break; 130 131 case NotStarted: 132 case CompletedSuccessfully: 133 case Failed: 134 CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); 135 136 default: 137 CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); 138 } 139 moveToParent(); 140 m_ctx.completeCycle(); 141 } 142 void TrackerBase::fail() { 143 m_runState = Failed; 144 if( m_parent ) 145 m_parent->markAsNeedingAnotherRun(); 146 moveToParent(); 147 m_ctx.completeCycle(); 148 } 149 150 void TrackerBase::moveToParent() { 151 assert( m_parent ); 152 m_ctx.setCurrentTracker( m_parent ); 153 } 154 void TrackerBase::moveToThis() { 155 m_ctx.setCurrentTracker( this ); 156 } 157 158 SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) 159 : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), 160 m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) 161 { 162 if( parent ) { 163 while ( !parent->isSectionTracker() ) { 164 parent = parent->parent(); 165 } 166 167 SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); 168 addNextFilters( parentSection.m_filters ); 169 } 170 } 171 172 bool SectionTracker::isComplete() const { 173 bool complete = true; 174 175 if (m_filters.empty() 176 || m_filters[0].empty() 177 || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { 178 complete = TrackerBase::isComplete(); 179 } 180 return complete; 181 } 182 183 bool SectionTracker::isSectionTracker() const { return true; } 184 185 SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { 186 SectionTracker* tracker; 187 188 ITracker& currentTracker = ctx.currentTracker(); 189 if ( ITracker* childTracker = 190 currentTracker.findChild( nameAndLocation ) ) { 191 assert( childTracker ); 192 assert( childTracker->isSectionTracker() ); 193 tracker = static_cast<SectionTracker*>( childTracker ); 194 } else { 195 auto newTracker = Catch::Detail::make_unique<SectionTracker>( 196 NameAndLocation{ static_cast<std::string>(nameAndLocation.name), 197 nameAndLocation.location }, 198 ctx, 199 ¤tTracker ); 200 tracker = newTracker.get(); 201 currentTracker.addChild( CATCH_MOVE( newTracker ) ); 202 } 203 204 if ( !ctx.completedCycle() ) { 205 tracker->tryOpen(); 206 } 207 208 return *tracker; 209 } 210 211 void SectionTracker::tryOpen() { 212 if( !isComplete() ) 213 open(); 214 } 215 216 void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { 217 if( !filters.empty() ) { 218 m_filters.reserve( m_filters.size() + filters.size() + 2 ); 219 m_filters.emplace_back(StringRef{}); // Root - should never be consulted 220 m_filters.emplace_back(StringRef{}); // Test Case - not a section filter 221 m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); 222 } 223 } 224 void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) { 225 if( filters.size() > 1 ) 226 m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); 227 } 228 229 StringRef SectionTracker::trimmedName() const { 230 return m_trimmed_name; 231 } 232 233 } // namespace TestCaseTracking 234 235 } // namespace Catch 236 237 #if defined(__clang__) 238 # pragma clang diagnostic pop 239 #endif