catch_test_case_tracker.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. #include "catch_test_case_tracker.h"
  8. #include "catch_enforce.h"
  9. #include "catch_string_manip.h"
  10. #include <algorithm>
  11. #include <cassert>
  12. #include <stdexcept>
  13. #include <memory>
  14. #include <sstream>
  15. #if defined(__clang__)
  16. # pragma clang diagnostic push
  17. # pragma clang diagnostic ignored "-Wexit-time-destructors"
  18. #endif
  19. namespace Catch {
  20. namespace TestCaseTracking {
  21. NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
  22. : name( _name ),
  23. location( _location )
  24. {}
  25. ITracker::~ITracker() = default;
  26. ITracker& TrackerContext::startRun() {
  27. m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
  28. m_currentTracker = nullptr;
  29. m_runState = Executing;
  30. return *m_rootTracker;
  31. }
  32. void TrackerContext::endRun() {
  33. m_rootTracker.reset();
  34. m_currentTracker = nullptr;
  35. m_runState = NotStarted;
  36. }
  37. void TrackerContext::startCycle() {
  38. m_currentTracker = m_rootTracker.get();
  39. m_runState = Executing;
  40. }
  41. void TrackerContext::completeCycle() {
  42. m_runState = CompletedCycle;
  43. }
  44. bool TrackerContext::completedCycle() const {
  45. return m_runState == CompletedCycle;
  46. }
  47. ITracker& TrackerContext::currentTracker() {
  48. return *m_currentTracker;
  49. }
  50. void TrackerContext::setCurrentTracker( ITracker* tracker ) {
  51. m_currentTracker = tracker;
  52. }
  53. TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
  54. ITracker(nameAndLocation),
  55. m_ctx( ctx ),
  56. m_parent( parent )
  57. {}
  58. bool TrackerBase::isComplete() const {
  59. return m_runState == CompletedSuccessfully || m_runState == Failed;
  60. }
  61. bool TrackerBase::isSuccessfullyCompleted() const {
  62. return m_runState == CompletedSuccessfully;
  63. }
  64. bool TrackerBase::isOpen() const {
  65. return m_runState != NotStarted && !isComplete();
  66. }
  67. bool TrackerBase::hasChildren() const {
  68. return !m_children.empty();
  69. }
  70. void TrackerBase::addChild( ITrackerPtr const& child ) {
  71. m_children.push_back( child );
  72. }
  73. ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
  74. auto it = std::find_if( m_children.begin(), m_children.end(),
  75. [&nameAndLocation]( ITrackerPtr const& tracker ){
  76. return
  77. tracker->nameAndLocation().location == nameAndLocation.location &&
  78. tracker->nameAndLocation().name == nameAndLocation.name;
  79. } );
  80. return( it != m_children.end() )
  81. ? *it
  82. : nullptr;
  83. }
  84. ITracker& TrackerBase::parent() {
  85. assert( m_parent ); // Should always be non-null except for root
  86. return *m_parent;
  87. }
  88. void TrackerBase::openChild() {
  89. if( m_runState != ExecutingChildren ) {
  90. m_runState = ExecutingChildren;
  91. if( m_parent )
  92. m_parent->openChild();
  93. }
  94. }
  95. bool TrackerBase::isSectionTracker() const { return false; }
  96. bool TrackerBase::isGeneratorTracker() const { return false; }
  97. void TrackerBase::open() {
  98. m_runState = Executing;
  99. moveToThis();
  100. if( m_parent )
  101. m_parent->openChild();
  102. }
  103. void TrackerBase::close() {
  104. // Close any still open children (e.g. generators)
  105. while( &m_ctx.currentTracker() != this )
  106. m_ctx.currentTracker().close();
  107. switch( m_runState ) {
  108. case NeedsAnotherRun:
  109. break;
  110. case Executing:
  111. m_runState = CompletedSuccessfully;
  112. break;
  113. case ExecutingChildren:
  114. if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
  115. m_runState = CompletedSuccessfully;
  116. break;
  117. case NotStarted:
  118. case CompletedSuccessfully:
  119. case Failed:
  120. CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
  121. default:
  122. CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
  123. }
  124. moveToParent();
  125. m_ctx.completeCycle();
  126. }
  127. void TrackerBase::fail() {
  128. m_runState = Failed;
  129. if( m_parent )
  130. m_parent->markAsNeedingAnotherRun();
  131. moveToParent();
  132. m_ctx.completeCycle();
  133. }
  134. void TrackerBase::markAsNeedingAnotherRun() {
  135. m_runState = NeedsAnotherRun;
  136. }
  137. void TrackerBase::moveToParent() {
  138. assert( m_parent );
  139. m_ctx.setCurrentTracker( m_parent );
  140. }
  141. void TrackerBase::moveToThis() {
  142. m_ctx.setCurrentTracker( this );
  143. }
  144. SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
  145. : TrackerBase( nameAndLocation, ctx, parent ),
  146. m_trimmed_name(trim(nameAndLocation.name))
  147. {
  148. if( parent ) {
  149. while( !parent->isSectionTracker() )
  150. parent = &parent->parent();
  151. SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
  152. addNextFilters( parentSection.m_filters );
  153. }
  154. }
  155. bool SectionTracker::isComplete() const {
  156. bool complete = true;
  157. if (m_filters.empty()
  158. || m_filters[0] == ""
  159. || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
  160. complete = TrackerBase::isComplete();
  161. }
  162. return complete;
  163. }
  164. bool SectionTracker::isSectionTracker() const { return true; }
  165. SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
  166. std::shared_ptr<SectionTracker> section;
  167. ITracker& currentTracker = ctx.currentTracker();
  168. if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
  169. assert( childTracker );
  170. assert( childTracker->isSectionTracker() );
  171. section = std::static_pointer_cast<SectionTracker>( childTracker );
  172. }
  173. else {
  174. section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
  175. currentTracker.addChild( section );
  176. }
  177. if( !ctx.completedCycle() )
  178. section->tryOpen();
  179. return *section;
  180. }
  181. void SectionTracker::tryOpen() {
  182. if( !isComplete() )
  183. open();
  184. }
  185. void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
  186. if( !filters.empty() ) {
  187. m_filters.reserve( m_filters.size() + filters.size() + 2 );
  188. m_filters.emplace_back(""); // Root - should never be consulted
  189. m_filters.emplace_back(""); // Test Case - not a section filter
  190. m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
  191. }
  192. }
  193. void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
  194. if( filters.size() > 1 )
  195. m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
  196. }
  197. std::vector<std::string> const& SectionTracker::getFilters() const {
  198. return m_filters;
  199. }
  200. std::string const& SectionTracker::trimmedName() const {
  201. return m_trimmed_name;
  202. }
  203. } // namespace TestCaseTracking
  204. using TestCaseTracking::ITracker;
  205. using TestCaseTracking::TrackerContext;
  206. using TestCaseTracking::SectionTracker;
  207. } // namespace Catch
  208. #if defined(__clang__)
  209. # pragma clang diagnostic pop
  210. #endif