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, &currentTracker );
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.push_back(""); // Root - should never be consulted
229             m_filters.push_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