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 : m_nameAndLocation( nameAndLocation ), 70 m_ctx( ctx ), 71 m_parent( parent ) 72 {} 73 nameAndLocation() const74 NameAndLocation const& TrackerBase::nameAndLocation() const { 75 return m_nameAndLocation; 76 } isComplete() const77 bool TrackerBase::isComplete() const { 78 return m_runState == CompletedSuccessfully || m_runState == Failed; 79 } isSuccessfullyCompleted() const80 bool TrackerBase::isSuccessfullyCompleted() const { 81 return m_runState == CompletedSuccessfully; 82 } isOpen() const83 bool TrackerBase::isOpen() const { 84 return m_runState != NotStarted && !isComplete(); 85 } hasChildren() const86 bool TrackerBase::hasChildren() const { 87 return !m_children.empty(); 88 } 89 90 addChild(ITrackerPtr const & child)91 void TrackerBase::addChild( ITrackerPtr const& child ) { 92 m_children.push_back( child ); 93 } 94 findChild(NameAndLocation const & nameAndLocation)95 ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { 96 auto it = std::find_if( m_children.begin(), m_children.end(), 97 [&nameAndLocation]( ITrackerPtr const& tracker ){ 98 return 99 tracker->nameAndLocation().location == nameAndLocation.location && 100 tracker->nameAndLocation().name == nameAndLocation.name; 101 } ); 102 return( it != m_children.end() ) 103 ? *it 104 : nullptr; 105 } parent()106 ITracker& TrackerBase::parent() { 107 assert( m_parent ); // Should always be non-null except for root 108 return *m_parent; 109 } 110 openChild()111 void TrackerBase::openChild() { 112 if( m_runState != ExecutingChildren ) { 113 m_runState = ExecutingChildren; 114 if( m_parent ) 115 m_parent->openChild(); 116 } 117 } 118 isSectionTracker() const119 bool TrackerBase::isSectionTracker() const { return false; } isGeneratorTracker() const120 bool TrackerBase::isGeneratorTracker() const { return false; } 121 open()122 void TrackerBase::open() { 123 m_runState = Executing; 124 moveToThis(); 125 if( m_parent ) 126 m_parent->openChild(); 127 } 128 close()129 void TrackerBase::close() { 130 131 // Close any still open children (e.g. generators) 132 while( &m_ctx.currentTracker() != this ) 133 m_ctx.currentTracker().close(); 134 135 switch( m_runState ) { 136 case NeedsAnotherRun: 137 break; 138 139 case Executing: 140 m_runState = CompletedSuccessfully; 141 break; 142 case ExecutingChildren: 143 if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) 144 m_runState = CompletedSuccessfully; 145 break; 146 147 case NotStarted: 148 case CompletedSuccessfully: 149 case Failed: 150 CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); 151 152 default: 153 CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); 154 } 155 moveToParent(); 156 m_ctx.completeCycle(); 157 } fail()158 void TrackerBase::fail() { 159 m_runState = Failed; 160 if( m_parent ) 161 m_parent->markAsNeedingAnotherRun(); 162 moveToParent(); 163 m_ctx.completeCycle(); 164 } markAsNeedingAnotherRun()165 void TrackerBase::markAsNeedingAnotherRun() { 166 m_runState = NeedsAnotherRun; 167 } 168 moveToParent()169 void TrackerBase::moveToParent() { 170 assert( m_parent ); 171 m_ctx.setCurrentTracker( m_parent ); 172 } moveToThis()173 void TrackerBase::moveToThis() { 174 m_ctx.setCurrentTracker( this ); 175 } 176 SectionTracker(NameAndLocation const & nameAndLocation,TrackerContext & ctx,ITracker * parent)177 SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) 178 : TrackerBase( nameAndLocation, ctx, parent ), 179 m_trimmed_name(trim(nameAndLocation.name)) 180 { 181 if( parent ) { 182 while( !parent->isSectionTracker() ) 183 parent = &parent->parent(); 184 185 SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); 186 addNextFilters( parentSection.m_filters ); 187 } 188 } 189 isComplete() const190 bool SectionTracker::isComplete() const { 191 bool complete = true; 192 193 if ((m_filters.empty() || m_filters[0] == "") 194 || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { 195 complete = TrackerBase::isComplete(); 196 } 197 return complete; 198 } 199 isSectionTracker() const200 bool SectionTracker::isSectionTracker() const { return true; } 201 acquire(TrackerContext & ctx,NameAndLocation const & nameAndLocation)202 SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { 203 std::shared_ptr<SectionTracker> section; 204 205 ITracker& currentTracker = ctx.currentTracker(); 206 if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { 207 assert( childTracker ); 208 assert( childTracker->isSectionTracker() ); 209 section = std::static_pointer_cast<SectionTracker>( childTracker ); 210 } 211 else { 212 section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); 213 currentTracker.addChild( section ); 214 } 215 if( !ctx.completedCycle() ) 216 section->tryOpen(); 217 return *section; 218 } 219 tryOpen()220 void SectionTracker::tryOpen() { 221 if( !isComplete() ) 222 open(); 223 } 224 addInitialFilters(std::vector<std::string> const & filters)225 void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { 226 if( !filters.empty() ) { 227 m_filters.reserve( m_filters.size() + filters.size() + 2 ); 228 m_filters.emplace_back(""); // Root - should never be consulted 229 m_filters.emplace_back(""); // Test Case - not a section filter 230 m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); 231 } 232 } addNextFilters(std::vector<std::string> const & filters)233 void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { 234 if( filters.size() > 1 ) 235 m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); 236 } 237 238 } // namespace TestCaseTracking 239 240 using TestCaseTracking::ITracker; 241 using TestCaseTracking::TrackerContext; 242 using TestCaseTracking::SectionTracker; 243 244 } // namespace Catch 245 246 #if defined(__clang__) 247 # pragma clang diagnostic pop 248 #endif 249