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