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