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 <assert.h> 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 72 TrackerHasName(NameAndLocation const & nameAndLocation)73 TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} operator ()(ITrackerPtr const & tracker) const74 bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { 75 return 76 tracker->nameAndLocation().name == m_nameAndLocation.name && 77 tracker->nameAndLocation().location == m_nameAndLocation.location; 78 } 79 TrackerBase(NameAndLocation const & nameAndLocation,TrackerContext & ctx,ITracker * parent)80 TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) 81 : m_nameAndLocation( nameAndLocation ), 82 m_ctx( ctx ), 83 m_parent( parent ) 84 {} 85 nameAndLocation() const86 NameAndLocation const& TrackerBase::nameAndLocation() const { 87 return m_nameAndLocation; 88 } isComplete() const89 bool TrackerBase::isComplete() const { 90 return m_runState == CompletedSuccessfully || m_runState == Failed; 91 } isSuccessfullyCompleted() const92 bool TrackerBase::isSuccessfullyCompleted() const { 93 return m_runState == CompletedSuccessfully; 94 } isOpen() const95 bool TrackerBase::isOpen() const { 96 return m_runState != NotStarted && !isComplete(); 97 } hasChildren() const98 bool TrackerBase::hasChildren() const { 99 return !m_children.empty(); 100 } 101 102 addChild(ITrackerPtr const & child)103 void TrackerBase::addChild( ITrackerPtr const& child ) { 104 m_children.push_back( child ); 105 } 106 findChild(NameAndLocation const & nameAndLocation)107 ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { 108 auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); 109 return( it != m_children.end() ) 110 ? *it 111 : nullptr; 112 } parent()113 ITracker& TrackerBase::parent() { 114 assert( m_parent ); // Should always be non-null except for root 115 return *m_parent; 116 } 117 openChild()118 void TrackerBase::openChild() { 119 if( m_runState != ExecutingChildren ) { 120 m_runState = ExecutingChildren; 121 if( m_parent ) 122 m_parent->openChild(); 123 } 124 } 125 isSectionTracker() const126 bool TrackerBase::isSectionTracker() const { return false; } isIndexTracker() const127 bool TrackerBase::isIndexTracker() const { return false; } 128 open()129 void TrackerBase::open() { 130 m_runState = Executing; 131 moveToThis(); 132 if( m_parent ) 133 m_parent->openChild(); 134 } 135 close()136 void TrackerBase::close() { 137 138 // Close any still open children (e.g. generators) 139 while( &m_ctx.currentTracker() != this ) 140 m_ctx.currentTracker().close(); 141 142 switch( m_runState ) { 143 case NeedsAnotherRun: 144 break; 145 146 case Executing: 147 m_runState = CompletedSuccessfully; 148 break; 149 case ExecutingChildren: 150 if( m_children.empty() || m_children.back()->isComplete() ) 151 m_runState = CompletedSuccessfully; 152 break; 153 154 case NotStarted: 155 case CompletedSuccessfully: 156 case Failed: 157 CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); 158 159 default: 160 CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); 161 } 162 moveToParent(); 163 m_ctx.completeCycle(); 164 } fail()165 void TrackerBase::fail() { 166 m_runState = Failed; 167 if( m_parent ) 168 m_parent->markAsNeedingAnotherRun(); 169 moveToParent(); 170 m_ctx.completeCycle(); 171 } markAsNeedingAnotherRun()172 void TrackerBase::markAsNeedingAnotherRun() { 173 m_runState = NeedsAnotherRun; 174 } 175 moveToParent()176 void TrackerBase::moveToParent() { 177 assert( m_parent ); 178 m_ctx.setCurrentTracker( m_parent ); 179 } moveToThis()180 void TrackerBase::moveToThis() { 181 m_ctx.setCurrentTracker( this ); 182 } 183 SectionTracker(NameAndLocation const & nameAndLocation,TrackerContext & ctx,ITracker * parent)184 SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) 185 : TrackerBase( nameAndLocation, ctx, parent ) 186 { 187 if( parent ) { 188 while( !parent->isSectionTracker() ) 189 parent = &parent->parent(); 190 191 SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); 192 addNextFilters( parentSection.m_filters ); 193 } 194 } 195 isSectionTracker() const196 bool SectionTracker::isSectionTracker() const { return true; } 197 acquire(TrackerContext & ctx,NameAndLocation const & nameAndLocation)198 SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { 199 std::shared_ptr<SectionTracker> section; 200 201 ITracker& currentTracker = ctx.currentTracker(); 202 if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { 203 assert( childTracker ); 204 assert( childTracker->isSectionTracker() ); 205 section = std::static_pointer_cast<SectionTracker>( childTracker ); 206 } 207 else { 208 section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); 209 currentTracker.addChild( section ); 210 } 211 if( !ctx.completedCycle() ) 212 section->tryOpen(); 213 return *section; 214 } 215 tryOpen()216 void SectionTracker::tryOpen() { 217 if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) 218 open(); 219 } 220 addInitialFilters(std::vector<std::string> const & filters)221 void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { 222 if( !filters.empty() ) { 223 m_filters.push_back(""); // Root - should never be consulted 224 m_filters.push_back(""); // Test Case - not a section filter 225 m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); 226 } 227 } addNextFilters(std::vector<std::string> const & filters)228 void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { 229 if( filters.size() > 1 ) 230 m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); 231 } 232 IndexTracker(NameAndLocation const & nameAndLocation,TrackerContext & ctx,ITracker * parent,int size)233 IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) 234 : TrackerBase( nameAndLocation, ctx, parent ), 235 m_size( size ) 236 {} 237 isIndexTracker() const238 bool IndexTracker::isIndexTracker() const { return true; } 239 acquire(TrackerContext & ctx,NameAndLocation const & nameAndLocation,int size)240 IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { 241 std::shared_ptr<IndexTracker> tracker; 242 243 ITracker& currentTracker = ctx.currentTracker(); 244 if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { 245 assert( childTracker ); 246 assert( childTracker->isIndexTracker() ); 247 tracker = std::static_pointer_cast<IndexTracker>( childTracker ); 248 } 249 else { 250 tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size ); 251 currentTracker.addChild( tracker ); 252 } 253 254 if( !ctx.completedCycle() && !tracker->isComplete() ) { 255 if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) 256 tracker->moveNext(); 257 tracker->open(); 258 } 259 260 return *tracker; 261 } 262 index() const263 int IndexTracker::index() const { return m_index; } 264 moveNext()265 void IndexTracker::moveNext() { 266 m_index++; 267 m_children.clear(); 268 } 269 close()270 void IndexTracker::close() { 271 TrackerBase::close(); 272 if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) 273 m_runState = Executing; 274 } 275 276 } // namespace TestCaseTracking 277 278 using TestCaseTracking::ITracker; 279 using TestCaseTracking::TrackerContext; 280 using TestCaseTracking::SectionTracker; 281 using TestCaseTracking::IndexTracker; 282 283 } // namespace Catch 284 285 #if defined(__clang__) 286 # pragma clang diagnostic pop 287 #endif 288