aboutsummaryrefslogtreecommitdiffstats
path: root/src/catch2/internal/catch_test_case_tracker.hpp
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
commitbc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch)
tree1e629e7b46b1d9972a973bc93fd100bcebd395be /src/catch2/internal/catch_test_case_tracker.hpp
downloadnihil-bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1.tar.gz
nihil-bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1.tar.bz2
Diffstat (limited to 'src/catch2/internal/catch_test_case_tracker.hpp')
-rw-r--r--src/catch2/internal/catch_test_case_tracker.hpp244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/catch2/internal/catch_test_case_tracker.hpp b/src/catch2/internal/catch_test_case_tracker.hpp
new file mode 100644
index 0000000..50278c9
--- /dev/null
+++ b/src/catch2/internal/catch_test_case_tracker.hpp
@@ -0,0 +1,244 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <catch2/internal/catch_source_line_info.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
+#include <catch2/internal/catch_stringref.hpp>
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string&& _name, SourceLineInfo const& _location );
+ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
+ // This is a very cheap check that should have a very high hit rate.
+ // If we get to SourceLineInfo::operator==, we will redo it, but the
+ // cost of repeating is trivial at that point (we will be paying
+ // multiple strcmp/memcmps at that point).
+ if ( lhs.location.line != rhs.location.line ) { return false; }
+ return lhs.name == rhs.name && lhs.location == rhs.location;
+ }
+ friend bool operator!=(NameAndLocation const& lhs,
+ NameAndLocation const& rhs) {
+ return !( lhs == rhs );
+ }
+ };
+
+ /**
+ * This is a variant of `NameAndLocation` that does not own the name string
+ *
+ * This avoids extra allocations when trying to locate a tracker by its
+ * name and location, as long as we make sure that trackers only keep
+ * around the owning variant.
+ */
+ struct NameAndLocationRef {
+ StringRef name;
+ SourceLineInfo location;
+
+ constexpr NameAndLocationRef( StringRef name_,
+ SourceLineInfo location_ ):
+ name( name_ ), location( location_ ) {}
+
+ friend bool operator==( NameAndLocation const& lhs,
+ NameAndLocationRef const& rhs ) {
+ // This is a very cheap check that should have a very high hit rate.
+ // If we get to SourceLineInfo::operator==, we will redo it, but the
+ // cost of repeating is trivial at that point (we will be paying
+ // multiple strcmp/memcmps at that point).
+ if ( lhs.location.line != rhs.location.line ) { return false; }
+ return StringRef( lhs.name ) == rhs.name &&
+ lhs.location == rhs.location;
+ }
+ friend bool operator==( NameAndLocationRef const& lhs,
+ NameAndLocation const& rhs ) {
+ return rhs == lhs;
+ }
+ };
+
+ class ITracker;
+
+ using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
+
+ class ITracker {
+ NameAndLocation m_nameAndLocation;
+
+ using Children = std::vector<ITrackerPtr>;
+
+ protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+
+ ITracker* m_parent = nullptr;
+ Children m_children;
+ CycleState m_runState = NotStarted;
+
+ public:
+ ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ):
+ m_nameAndLocation( CATCH_MOVE(nameAndLoc) ),
+ m_parent( parent )
+ {}
+
+
+ // static queries
+ NameAndLocation const& nameAndLocation() const {
+ return m_nameAndLocation;
+ }
+ ITracker* parent() const {
+ return m_parent;
+ }
+
+ virtual ~ITracker(); // = default
+
+
+ // dynamic queries
+
+ //! Returns true if tracker run to completion (successfully or not)
+ virtual bool isComplete() const = 0;
+ //! Returns true if tracker run to completion successfully
+ bool isSuccessfullyCompleted() const {
+ return m_runState == CompletedSuccessfully;
+ }
+ //! Returns true if tracker has started but hasn't been completed
+ bool isOpen() const;
+ //! Returns true iff tracker has started
+ bool hasStarted() const;
+
+ // actions
+ virtual void close() = 0; // Successfully complete
+ virtual void fail() = 0;
+ void markAsNeedingAnotherRun();
+
+ //! Register a nested ITracker
+ void addChild( ITrackerPtr&& child );
+ /**
+ * Returns ptr to specific child if register with this tracker.
+ *
+ * Returns nullptr if not found.
+ */
+ ITracker* findChild( NameAndLocationRef const& nameAndLocation );
+ //! Have any children been added?
+ bool hasChildren() const {
+ return !m_children.empty();
+ }
+
+
+ //! Marks tracker as executing a child, doing se recursively up the tree
+ void openChild();
+
+ /**
+ * Returns true if the instance is a section tracker
+ *
+ * Subclasses should override to true if they are, replaces RTTI
+ * for internal debug checks.
+ */
+ virtual bool isSectionTracker() const;
+ /**
+ * Returns true if the instance is a generator tracker
+ *
+ * Subclasses should override to true if they are, replaces RTTI
+ * for internal debug checks.
+ */
+ virtual bool isGeneratorTracker() const;
+ };
+
+ class TrackerContext {
+
+ enum RunState {
+ NotStarted,
+ Executing,
+ CompletedCycle
+ };
+
+ ITrackerPtr m_rootTracker;
+ ITracker* m_currentTracker = nullptr;
+ RunState m_runState = NotStarted;
+
+ public:
+
+ ITracker& startRun();
+
+ void startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
+ void completeCycle();
+
+ bool completedCycle() const;
+ ITracker& currentTracker() { return *m_currentTracker; }
+ void setCurrentTracker( ITracker* tracker );
+ };
+
+ class TrackerBase : public ITracker {
+ protected:
+
+ TrackerContext& m_ctx;
+
+ public:
+ TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+ bool isComplete() const override;
+
+ void open();
+
+ void close() override;
+ void fail() override;
+
+ private:
+ void moveToParent();
+ void moveToThis();
+ };
+
+ class SectionTracker : public TrackerBase {
+ std::vector<StringRef> m_filters;
+ // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`.
+ // Currently it allocates owns the name, so this is safe. If it is later refactored
+ // to not own the name, the name still has to outlive the `ITracker` parent, so
+ // this should still be safe.
+ StringRef m_trimmed_name;
+ public:
+ SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+ bool isSectionTracker() const override;
+
+ bool isComplete() const override;
+
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation );
+
+ void tryOpen();
+
+ void addInitialFilters( std::vector<std::string> const& filters );
+ void addNextFilters( std::vector<StringRef> const& filters );
+ //! Returns filters active in this tracker
+ std::vector<StringRef> const& getFilters() const { return m_filters; }
+ //! Returns whitespace-trimmed name of the tracked section
+ StringRef trimmedName() const;
+ };
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+#endif // CATCH_TEST_CASE_TRACKER_HPP_INCLUDED