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, &currentTracker );
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