/ externals / catch / src / catch2 / internal / catch_test_case_tracker.cpp
catch_test_case_tracker.cpp
  1  
  2  //              Copyright Catch2 Authors
  3  // Distributed under the Boost Software License, Version 1.0.
  4  //   (See accompanying file LICENSE.txt or copy at
  5  //        https://www.boost.org/LICENSE_1_0.txt)
  6  
  7  // SPDX-License-Identifier: BSL-1.0
  8  #include <catch2/internal/catch_test_case_tracker.hpp>
  9  
 10  #include <catch2/internal/catch_enforce.hpp>
 11  #include <catch2/internal/catch_string_manip.hpp>
 12  #include <catch2/internal/catch_move_and_forward.hpp>
 13  
 14  #include <algorithm>
 15  #include <cassert>
 16  
 17  #if defined(__clang__)
 18  #    pragma clang diagnostic push
 19  #    pragma clang diagnostic ignored "-Wexit-time-destructors"
 20  #endif
 21  
 22  namespace Catch {
 23  namespace TestCaseTracking {
 24  
 25      NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location )
 26      :   name( CATCH_MOVE(_name) ),
 27          location( _location )
 28      {}
 29  
 30  
 31      ITracker::~ITracker() = default;
 32  
 33      void ITracker::markAsNeedingAnotherRun() {
 34          m_runState = NeedsAnotherRun;
 35      }
 36  
 37      void ITracker::addChild( ITrackerPtr&& child ) {
 38          m_children.push_back( CATCH_MOVE(child) );
 39      }
 40  
 41      ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) {
 42          auto it = std::find_if(
 43              m_children.begin(),
 44              m_children.end(),
 45              [&nameAndLocation]( ITrackerPtr const& tracker ) {
 46                  auto const& tnameAndLoc = tracker->nameAndLocation();
 47                  if ( tnameAndLoc.location.line !=
 48                       nameAndLocation.location.line ) {
 49                      return false;
 50                  }
 51                  return tnameAndLoc == nameAndLocation;
 52              } );
 53          return ( it != m_children.end() ) ? it->get() : nullptr;
 54      }
 55  
 56      bool ITracker::isSectionTracker() const { return false; }
 57      bool ITracker::isGeneratorTracker() const { return false; }
 58  
 59      bool ITracker::isOpen() const {
 60          return m_runState != NotStarted && !isComplete();
 61      }
 62  
 63      bool ITracker::hasStarted() const { return m_runState != NotStarted; }
 64  
 65      void ITracker::openChild() {
 66          if (m_runState != ExecutingChildren) {
 67              m_runState = ExecutingChildren;
 68              if (m_parent) {
 69                  m_parent->openChild();
 70              }
 71          }
 72      }
 73  
 74      ITracker& TrackerContext::startRun() {
 75          using namespace std::string_literals;
 76          m_rootTracker = Catch::Detail::make_unique<SectionTracker>(
 77              NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ),
 78              *this,
 79              nullptr );
 80          m_currentTracker = nullptr;
 81          m_runState = Executing;
 82          return *m_rootTracker;
 83      }
 84  
 85      void TrackerContext::completeCycle() {
 86          m_runState = CompletedCycle;
 87      }
 88  
 89      bool TrackerContext::completedCycle() const {
 90          return m_runState == CompletedCycle;
 91      }
 92      void TrackerContext::setCurrentTracker( ITracker* tracker ) {
 93          m_currentTracker = tracker;
 94      }
 95  
 96  
 97      TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
 98          ITracker(CATCH_MOVE(nameAndLocation), parent),
 99          m_ctx( ctx )
100      {}
101  
102      bool TrackerBase::isComplete() const {
103          return m_runState == CompletedSuccessfully || m_runState == Failed;
104      }
105  
106      void TrackerBase::open() {
107          m_runState = Executing;
108          moveToThis();
109          if( m_parent )
110              m_parent->openChild();
111      }
112  
113      void TrackerBase::close() {
114  
115          // Close any still open children (e.g. generators)
116          while( &m_ctx.currentTracker() != this )
117              m_ctx.currentTracker().close();
118  
119          switch( m_runState ) {
120              case NeedsAnotherRun:
121                  break;
122  
123              case Executing:
124                  m_runState = CompletedSuccessfully;
125                  break;
126              case ExecutingChildren:
127                  if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
128                      m_runState = CompletedSuccessfully;
129                  break;
130  
131              case NotStarted:
132              case CompletedSuccessfully:
133              case Failed:
134                  CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
135  
136              default:
137                  CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
138          }
139          moveToParent();
140          m_ctx.completeCycle();
141      }
142      void TrackerBase::fail() {
143          m_runState = Failed;
144          if( m_parent )
145              m_parent->markAsNeedingAnotherRun();
146          moveToParent();
147          m_ctx.completeCycle();
148      }
149  
150      void TrackerBase::moveToParent() {
151          assert( m_parent );
152          m_ctx.setCurrentTracker( m_parent );
153      }
154      void TrackerBase::moveToThis() {
155          m_ctx.setCurrentTracker( this );
156      }
157  
158      SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
159      :   TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
160          m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
161      {
162          if( parent ) {
163              while ( !parent->isSectionTracker() ) {
164                  parent = parent->parent();
165              }
166  
167              SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
168              addNextFilters( parentSection.m_filters );
169          }
170      }
171  
172      bool SectionTracker::isComplete() const {
173          bool complete = true;
174  
175          if (m_filters.empty()
176              || m_filters[0].empty()
177              || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
178              complete = TrackerBase::isComplete();
179          }
180          return complete;
181      }
182  
183      bool SectionTracker::isSectionTracker() const { return true; }
184  
185      SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) {
186          SectionTracker* tracker;
187  
188          ITracker& currentTracker = ctx.currentTracker();
189          if ( ITracker* childTracker =
190                   currentTracker.findChild( nameAndLocation ) ) {
191              assert( childTracker );
192              assert( childTracker->isSectionTracker() );
193              tracker = static_cast<SectionTracker*>( childTracker );
194          } else {
195              auto newTracker = Catch::Detail::make_unique<SectionTracker>(
196                  NameAndLocation{ static_cast<std::string>(nameAndLocation.name),
197                                   nameAndLocation.location },
198                  ctx,
199                  &currentTracker );
200              tracker = newTracker.get();
201              currentTracker.addChild( CATCH_MOVE( newTracker ) );
202          }
203  
204          if ( !ctx.completedCycle() ) {
205              tracker->tryOpen();
206          }
207  
208          return *tracker;
209      }
210  
211      void SectionTracker::tryOpen() {
212          if( !isComplete() )
213              open();
214      }
215  
216      void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
217          if( !filters.empty() ) {
218              m_filters.reserve( m_filters.size() + filters.size() + 2 );
219              m_filters.emplace_back(StringRef{}); // Root - should never be consulted
220              m_filters.emplace_back(StringRef{}); // Test Case - not a section filter
221              m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
222          }
223      }
224      void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) {
225          if( filters.size() > 1 )
226              m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
227      }
228  
229      StringRef SectionTracker::trimmedName() const {
230          return m_trimmed_name;
231      }
232  
233  } // namespace TestCaseTracking
234  
235  } // namespace Catch
236  
237  #if defined(__clang__)
238  #    pragma clang diagnostic pop
239  #endif