diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
| commit | bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch) | |
| tree | 1e629e7b46b1d9972a973bc93fd100bcebd395be /src/catch2/internal/catch_test_case_tracker.hpp | |
| download | nihil-vendor/catch2.tar.gz nihil-vendor/catch2.tar.bz2 | |
import catch2 3.8.1vendor/catch2/3.8.1vendor/catch2
Diffstat (limited to 'src/catch2/internal/catch_test_case_tracker.hpp')
| -rw-r--r-- | src/catch2/internal/catch_test_case_tracker.hpp | 244 |
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 |
