mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 04:07:10 +01:00 
			
		
		
		
	Redo generator interface
This commit is contained in:
		| @@ -18,33 +18,11 @@ IGeneratorTracker::~IGeneratorTracker() {} | ||||
|  | ||||
| namespace Generators { | ||||
|  | ||||
|     GeneratorBase::~GeneratorBase() {} | ||||
|  | ||||
|     std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) { | ||||
|  | ||||
|         assert( selectionSize <= sourceSize ); | ||||
|         std::vector<size_t> indices; | ||||
|         indices.reserve( selectionSize ); | ||||
|         std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 ); | ||||
|  | ||||
|         std::set<size_t> seen; | ||||
|         // !TBD: improve this algorithm | ||||
|         while( indices.size() < selectionSize ) { | ||||
|             auto index = uid( rng() ); | ||||
|             if( seen.insert( index ).second ) | ||||
|                 indices.push_back( index ); | ||||
|         } | ||||
|         return indices; | ||||
|     } | ||||
|     GeneratorUntypedBase::~GeneratorUntypedBase() {} | ||||
|  | ||||
|     auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { | ||||
|         return getResultCapture().acquireGeneratorTracker( lineInfo ); | ||||
|     } | ||||
|  | ||||
|     template<> | ||||
|     auto all<int>() -> Generator<int> { | ||||
|         return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() ); | ||||
|     } | ||||
|  | ||||
| } // namespace Generators | ||||
| } // namespace Catch | ||||
|   | ||||
| @@ -29,199 +29,140 @@ namespace Generators { | ||||
|     } | ||||
|  | ||||
|     template<typename T> | ||||
|     struct IGenerator { | ||||
|         virtual ~IGenerator() {} | ||||
|         virtual auto get( size_t index ) const -> T = 0; | ||||
|     struct IGenerator : GeneratorUntypedBase { | ||||
|         virtual ~IGenerator() = default; | ||||
|  | ||||
|         // Returns the current element of the generator | ||||
|         // | ||||
|         // \Precondition The generator is either freshly constructed, | ||||
|         // or the last call to `next()` returned true | ||||
|         virtual T const& get() const = 0; | ||||
|         using type = T; | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     class SingleValueGenerator : public IGenerator<T> { | ||||
|     class SingleValueGenerator final : public IGenerator<T> { | ||||
|         T m_value; | ||||
|     public: | ||||
|         SingleValueGenerator( T const& value ) : m_value( value ) {} | ||||
|         SingleValueGenerator(T const& value) : m_value( value ) {} | ||||
|         SingleValueGenerator(T&& value) : m_value(std::move(value)) {} | ||||
|  | ||||
|         auto get( size_t ) const -> T override { | ||||
|         T const& get() const override { | ||||
|             return m_value; | ||||
|         } | ||||
|         bool next() override { | ||||
|             return false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     class FixedValuesGenerator : public IGenerator<T> { | ||||
|     class FixedValuesGenerator final : public IGenerator<T> { | ||||
|         std::vector<T> m_values; | ||||
|  | ||||
|         size_t m_idx = 0; | ||||
|     public: | ||||
|         FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} | ||||
|  | ||||
|         auto get( size_t index ) const -> T override { | ||||
|             return m_values[index]; | ||||
|         T const& get() const override { | ||||
|             return m_values[m_idx]; | ||||
|         } | ||||
|         bool next() override { | ||||
|             ++m_idx; | ||||
|             return m_idx < m_values.size(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     class RangeGenerator : public IGenerator<T> { | ||||
|         T const m_first; | ||||
|         T const m_last; | ||||
|  | ||||
|     public: | ||||
|         RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) { | ||||
|             assert( m_last > m_first ); | ||||
|         } | ||||
|  | ||||
|         auto get( size_t index ) const -> T override { | ||||
|             // ToDo:: introduce a safe cast to catch potential overflows | ||||
|             return static_cast<T>(m_first+index); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     template<typename T> | ||||
|     struct NullGenerator : IGenerator<T> { | ||||
|         auto get( size_t ) const -> T override { | ||||
|             CATCH_INTERNAL_ERROR("A Null Generator is always empty"); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     class Generator { | ||||
|     template <typename T> | ||||
|     class GeneratorWrapper final { | ||||
|         std::unique_ptr<IGenerator<T>> m_generator; | ||||
|         size_t m_size; | ||||
|  | ||||
|     public: | ||||
|         Generator( size_t size, std::unique_ptr<IGenerator<T>> generator ) | ||||
|         :   m_generator( std::move( generator ) ), | ||||
|             m_size( size ) | ||||
|         GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): | ||||
|             m_generator(std::move(generator)) | ||||
|         {} | ||||
|  | ||||
|         auto size() const -> size_t { return m_size; } | ||||
|         auto operator[]( size_t index ) const -> T { | ||||
|             assert( index < m_size ); | ||||
|             return m_generator->get( index ); | ||||
|         T const& get() const { | ||||
|             return m_generator->get(); | ||||
|         } | ||||
|         bool next() { | ||||
|             return m_generator->next(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ); | ||||
|     template <typename T> | ||||
|     GeneratorWrapper<T> value(T&& value) { | ||||
|         return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); | ||||
|     } | ||||
|     template <typename T> | ||||
|     GeneratorWrapper<T> values(std::initializer_list<T> values) { | ||||
|         return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); | ||||
|     } | ||||
|  | ||||
|     template<typename T> | ||||
|     class GeneratorRandomiser : public IGenerator<T> { | ||||
|         Generator<T> m_baseGenerator; | ||||
|     class Generators : public IGenerator<T> { | ||||
|         std::vector<GeneratorWrapper<T>> m_generators; | ||||
|         size_t m_current = 0; | ||||
|  | ||||
|         std::vector<size_t> m_indices; | ||||
|     public: | ||||
|         GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems ) | ||||
|         :   m_baseGenerator( std::move( baseGenerator ) ), | ||||
|             m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) ) | ||||
|         {} | ||||
|  | ||||
|         auto get( size_t index ) const -> T override { | ||||
|             return m_baseGenerator[m_indices[index]]; | ||||
|         void populate(GeneratorWrapper<T>&& generator) { | ||||
|             m_generators.emplace_back(std::move(generator)); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     struct RequiresASpecialisationFor; | ||||
|  | ||||
|     template<typename T> | ||||
|     auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); } | ||||
|  | ||||
|     template<> | ||||
|     auto all<int>() -> Generator<int>; | ||||
|  | ||||
|  | ||||
|     template<typename T> | ||||
|     auto range( T const& first, T const& last ) -> Generator<T> { | ||||
|         return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) ); | ||||
|     } | ||||
|  | ||||
|     template<typename T> | ||||
|     auto random( T const& first, T const& last ) -> Generator<T> { | ||||
|         auto gen = range( first, last ); | ||||
|         auto size = gen.size(); | ||||
|  | ||||
|         return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) ); | ||||
|     } | ||||
|     template<typename T> | ||||
|     auto random( size_t size ) -> Generator<T> { | ||||
|         return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) ); | ||||
|     } | ||||
|  | ||||
|     template<typename T> | ||||
|     auto values( std::initializer_list<T> values ) -> Generator<T> { | ||||
|         return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) ); | ||||
|     } | ||||
|     template<typename T> | ||||
|     auto value( T const& val ) -> Generator<T> { | ||||
|         return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) ); | ||||
|     } | ||||
|  | ||||
|     template<typename T> | ||||
|     auto as() -> Generator<T> { | ||||
|         return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() ); | ||||
|     } | ||||
|  | ||||
|     template<typename... Ts> | ||||
|     auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> { | ||||
|         return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     template<typename T> | ||||
|     struct Generators : GeneratorBase { | ||||
|         std::vector<Generator<T>> m_generators; | ||||
|  | ||||
|         using type = T; | ||||
|  | ||||
|         Generators() : GeneratorBase( 0 ) {} | ||||
|  | ||||
|         void populate( T&& val ) { | ||||
|             m_size += 1; | ||||
|             m_generators.emplace_back( value( std::move( val ) ) ); | ||||
|         void populate(T&& val) { | ||||
|             m_generators.emplace_back(value(std::move(val))); | ||||
|         } | ||||
|         template<typename U> | ||||
|         void populate( U&& val ) { | ||||
|             populate( T( std::move( val ) ) ); | ||||
|         void populate(U&& val) { | ||||
|             populate(T(std::move(val))); | ||||
|         } | ||||
|         void populate( Generator<T>&& generator ) { | ||||
|             m_size += generator.size(); | ||||
|             m_generators.emplace_back( std::move( generator ) ); | ||||
|         } | ||||
|  | ||||
|         template<typename U, typename... Gs> | ||||
|         void populate( U&& valueOrGenerator, Gs... moreGenerators ) { | ||||
|             populate( std::forward<U>( valueOrGenerator ) ); | ||||
|             populate( std::forward<Gs>( moreGenerators )... ); | ||||
|         void populate(U&& valueOrGenerator, Gs... moreGenerators) { | ||||
|             populate(std::forward<U>(valueOrGenerator)); | ||||
|             populate(std::forward<Gs>(moreGenerators)...); | ||||
|         } | ||||
|  | ||||
|         auto operator[]( size_t index ) const -> T { | ||||
|             size_t sizes = 0; | ||||
|             for( auto const& gen : m_generators ) { | ||||
|                 auto localIndex = index-sizes; | ||||
|                 sizes += gen.size(); | ||||
|                 if( index < sizes ) | ||||
|                     return gen[localIndex]; | ||||
|     public: | ||||
|         template <typename... Gs> | ||||
|         Generators(Gs... moreGenerators) { | ||||
|             m_generators.reserve(sizeof...(Gs)); | ||||
|             populate(std::forward<Gs>(moreGenerators)...); | ||||
|         } | ||||
|  | ||||
|         T const& get() const override { | ||||
|             return m_generators[m_current].get(); | ||||
|         } | ||||
|  | ||||
|         bool next() override { | ||||
|             if (m_current >= m_generators.size()) { | ||||
|                 return false; | ||||
|             } | ||||
|             CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')'); | ||||
|             const bool current_status = m_generators[m_current].next(); | ||||
|             if (!current_status) { | ||||
|                 ++m_current; | ||||
|             } | ||||
|             return m_current < m_generators.size(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     template<typename... Ts> | ||||
|     GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { | ||||
|         return values<std::tuple<Ts...>>( tuples ); | ||||
|     } | ||||
|  | ||||
|     // Tag type to signal that a generator sequence should convert arguments to a specific type | ||||
|     template <typename T> | ||||
|     struct as {}; | ||||
|  | ||||
|     template<typename T, typename... Gs> | ||||
|     auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> { | ||||
|         Generators<T> generators; | ||||
|         generators.m_generators.reserve( 1+sizeof...(Gs) ); | ||||
|         generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... ); | ||||
|         return generators; | ||||
|     auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> { | ||||
|         return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); | ||||
|     } | ||||
|     template<typename T> | ||||
|     auto makeGenerators( Generator<T>&& generator ) -> Generators<T> { | ||||
|         Generators<T> generators; | ||||
|         generators.populate( std::move( generator ) ); | ||||
|         return generators; | ||||
|     auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { | ||||
|         return Generators<T>(std::move(generator)); | ||||
|     } | ||||
|     template<typename T, typename... Gs> | ||||
|     auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { | ||||
|         return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); | ||||
|     } | ||||
|     template<typename T, typename U, typename... Gs> | ||||
|     auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> { | ||||
|     auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> { | ||||
|         return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); | ||||
|     } | ||||
|  | ||||
| @@ -232,15 +173,16 @@ namespace Generators { | ||||
|     // Note: The type after -> is weird, because VS2015 cannot parse | ||||
|     //       the expression used in the typedef inside, when it is in | ||||
|     //       return type. Yeah, ¯\_(ツ)_/¯ | ||||
|     auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) { | ||||
|     auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { | ||||
|         using UnderlyingType = typename decltype(generatorExpression())::type; | ||||
|  | ||||
|         IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); | ||||
|         if( !tracker.hasGenerator() ) | ||||
|             tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) ); | ||||
|         if (!tracker.hasGenerator()) { | ||||
|             tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); | ||||
|         } | ||||
|  | ||||
|         auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() ); | ||||
|         return generator[tracker.getIndex()]; | ||||
|         auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); | ||||
|         return generator.get(); | ||||
|     } | ||||
|  | ||||
| } // namespace Generators | ||||
|   | ||||
| @@ -13,16 +13,17 @@ | ||||
| namespace Catch { | ||||
|  | ||||
|     namespace Generators { | ||||
|         class GeneratorBase { | ||||
|         protected: | ||||
|             size_t m_size = 0; | ||||
|  | ||||
|         class GeneratorUntypedBase { | ||||
|         public: | ||||
|             GeneratorBase( size_t size ) : m_size( size ) {} | ||||
|             virtual ~GeneratorBase(); | ||||
|             auto size() const -> size_t { return m_size; } | ||||
|             GeneratorUntypedBase() = default; | ||||
|             virtual ~GeneratorUntypedBase(); | ||||
|             // Attempts to move the generator to the next element | ||||
|              // | ||||
|              // Returns true iff the move succeeded (and a valid element | ||||
|              // can be retrieved). | ||||
|             virtual bool next() = 0; | ||||
|         }; | ||||
|         using GeneratorBasePtr = std::unique_ptr<GeneratorBase>; | ||||
|         using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>; | ||||
|  | ||||
|     } // namespace Generators | ||||
|  | ||||
| @@ -31,7 +32,6 @@ namespace Catch { | ||||
|         virtual auto hasGenerator() const -> bool = 0; | ||||
|         virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; | ||||
|         virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; | ||||
|         virtual auto getIndex() const -> std::size_t = 0; | ||||
|     }; | ||||
|  | ||||
| } // namespace Catch | ||||
|   | ||||
| @@ -14,7 +14,6 @@ namespace Catch { | ||||
|  | ||||
|     namespace Generators { | ||||
|         struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { | ||||
|             size_t m_index = static_cast<size_t>( -1 ); | ||||
|             GeneratorBasePtr m_generator; | ||||
|  | ||||
|             GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) | ||||
| @@ -28,7 +27,7 @@ namespace Catch { | ||||
|                 ITracker& currentTracker = ctx.currentTracker(); | ||||
|                 if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { | ||||
|                     assert( childTracker ); | ||||
|                     assert( childTracker->isIndexTracker() ); | ||||
|                     assert( childTracker->isGeneratorTracker() ); | ||||
|                     tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); | ||||
|                 } | ||||
|                 else { | ||||
| @@ -37,28 +36,24 @@ namespace Catch { | ||||
|                 } | ||||
|  | ||||
|                 if( !ctx.completedCycle() && !tracker->isComplete() ) { | ||||
|                     if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) | ||||
|                         tracker->moveNext(); | ||||
|                     tracker->open(); | ||||
|                 } | ||||
|  | ||||
|                 return *tracker; | ||||
|             } | ||||
|  | ||||
|             void moveNext() { | ||||
|                 m_index++; | ||||
|                 m_children.clear(); | ||||
|             } | ||||
|  | ||||
|             // TrackerBase interface | ||||
|             bool isIndexTracker() const override { return true; } | ||||
|             bool isGeneratorTracker() const override { return true; } | ||||
|             auto hasGenerator() const -> bool override { | ||||
|                 return !!m_generator; | ||||
|             } | ||||
|             void close() override { | ||||
|                 TrackerBase::close(); | ||||
|                 if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) | ||||
|                 // Generator interface only finds out if it has another item on atual move | ||||
|                 if (m_runState == CompletedSuccessfully && m_generator->next()) { | ||||
|                     m_children.clear(); | ||||
|                     m_runState = Executing; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // IGeneratorTracker interface | ||||
| @@ -68,9 +63,6 @@ namespace Catch { | ||||
|             void setGenerator( GeneratorBasePtr&& generator ) override { | ||||
|                 m_generator = std::move( generator ); | ||||
|             } | ||||
|             auto getIndex() const -> size_t override { | ||||
|                 return m_index; | ||||
|             } | ||||
|         }; | ||||
|         GeneratorTracker::~GeneratorTracker() {} | ||||
|     } | ||||
|   | ||||
| @@ -121,7 +121,7 @@ namespace TestCaseTracking { | ||||
|     } | ||||
|  | ||||
|     bool TrackerBase::isSectionTracker() const { return false; } | ||||
|     bool TrackerBase::isIndexTracker() const { return false; } | ||||
|     bool TrackerBase::isGeneratorTracker() const { return false; } | ||||
|  | ||||
|     void TrackerBase::open() { | ||||
|         m_runState = Executing; | ||||
|   | ||||
| @@ -54,7 +54,7 @@ namespace TestCaseTracking { | ||||
|  | ||||
|         // Debug/ checking | ||||
|         virtual bool isSectionTracker() const = 0; | ||||
|         virtual bool isIndexTracker() const = 0; | ||||
|         virtual bool isGeneratorTracker() const = 0; | ||||
|     }; | ||||
|  | ||||
|     class TrackerContext { | ||||
| @@ -120,7 +120,7 @@ namespace TestCaseTracking { | ||||
|         void openChild() override; | ||||
|  | ||||
|         bool isSectionTracker() const override; | ||||
|         bool isIndexTracker() const override; | ||||
|         bool isGeneratorTracker() const override; | ||||
|  | ||||
|         void open(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský