| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Created by Phil Nash on 15/6/2018. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | 
					
						
							|  |  |  |  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 | 
					
						
							|  |  |  | #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "catch_interfaces_generatortracker.h"
 | 
					
						
							|  |  |  | #include "catch_common.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-03 10:03:47 +02:00
										 |  |  | #include "catch_enforce.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <memory>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | #include <cassert>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  | #include <exception>
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class GeneratorException : public std::exception { | 
					
						
							|  |  |  |     const char* const m_msg = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     GeneratorException(const char* msg): | 
					
						
							|  |  |  |         m_msg(msg) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const char* what() const noexcept override final; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | namespace Generators { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // !TBD move this into its own location?
 | 
					
						
							|  |  |  |     namespace pf{ | 
					
						
							|  |  |  |         template<typename T, typename... Args> | 
					
						
							|  |  |  |         std::unique_ptr<T> make_unique( Args&&... args ) { | 
					
						
							|  |  |  |             return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename T> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     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; | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename T> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     class SingleValueGenerator final : public IGenerator<T> { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         T m_value; | 
					
						
							|  |  |  |     public: | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         SingleValueGenerator(T const& value) : m_value( value ) {} | 
					
						
							|  |  |  |         SingleValueGenerator(T&& value) : m_value(std::move(value)) {} | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         T const& get() const override { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |             return m_value; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         bool next() override { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename T> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     class FixedValuesGenerator final : public IGenerator<T> { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         std::vector<T> m_values; | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         size_t m_idx = 0; | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     public: | 
					
						
							|  |  |  |         FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         T const& get() const override { | 
					
						
							|  |  |  |             return m_values[m_idx]; | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         bool next() override { | 
					
						
							|  |  |  |             ++m_idx; | 
					
						
							|  |  |  |             return m_idx < m_values.size(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     template <typename T> | 
					
						
							|  |  |  |     class GeneratorWrapper final { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         std::unique_ptr<IGenerator<T>> m_generator; | 
					
						
							|  |  |  |     public: | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): | 
					
						
							|  |  |  |             m_generator(std::move(generator)) | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         {} | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         T const& get() const { | 
					
						
							|  |  |  |             return m_generator->get(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         bool next() { | 
					
						
							|  |  |  |             return m_generator->next(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     template <typename T> | 
					
						
							|  |  |  |     GeneratorWrapper<T> value(T&& value) { | 
					
						
							|  |  |  |         return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     template <typename T> | 
					
						
							|  |  |  |     GeneratorWrapper<T> values(std::initializer_list<T> values) { | 
					
						
							|  |  |  |         return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename T> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     class Generators : public IGenerator<T> { | 
					
						
							|  |  |  |         std::vector<GeneratorWrapper<T>> m_generators; | 
					
						
							|  |  |  |         size_t m_current = 0; | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         void populate(GeneratorWrapper<T>&& generator) { | 
					
						
							|  |  |  |             m_generators.emplace_back(std::move(generator)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         void populate(T&& val) { | 
					
						
							|  |  |  |             m_generators.emplace_back(value(std::move(val))); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         template<typename U> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         void populate(U&& val) { | 
					
						
							|  |  |  |             populate(T(std::move(val))); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         template<typename U, typename... Gs> | 
					
						
							|  |  |  |         void populate(U&& valueOrGenerator, Gs... moreGenerators) { | 
					
						
							|  |  |  |             populate(std::forward<U>(valueOrGenerator)); | 
					
						
							|  |  |  |             populate(std::forward<Gs>(moreGenerators)...); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     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(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         bool next() override { | 
					
						
							|  |  |  |             if (m_current >= m_generators.size()) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const bool current_status = m_generators[m_current].next(); | 
					
						
							|  |  |  |             if (!current_status) { | 
					
						
							|  |  |  |                 ++m_current; | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |             return m_current < m_generators.size(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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 {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     template<typename T, typename... Gs> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> { | 
					
						
							|  |  |  |         return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     template<typename T> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { | 
					
						
							|  |  |  |         return Generators<T>(std::move(generator)); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     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> | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename L> | 
					
						
							|  |  |  |     // Note: The type after -> is weird, because VS2015 cannot parse
 | 
					
						
							|  |  |  |     //       the expression used in the typedef inside, when it is in
 | 
					
						
							| 
									
										
										
										
											2019-02-18 10:33:41 +01:00
										 |  |  |     //       return type. Yeah.
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |     auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |         using UnderlyingType = typename decltype(generatorExpression())::type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         if (!tracker.hasGenerator()) { | 
					
						
							|  |  |  |             tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  |         auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); | 
					
						
							|  |  |  |         return generator.get(); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Generators
 | 
					
						
							|  |  |  | } // namespace Catch
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define GENERATE( ... ) \
 | 
					
						
							| 
									
										
										
										
											2019-03-31 14:11:10 +02:00
										 |  |  |     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) | 
					
						
							|  |  |  | #define GENERATE_COPY( ... ) \
 | 
					
						
							|  |  |  |     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) | 
					
						
							|  |  |  | #define GENERATE_REF( ... ) \
 | 
					
						
							|  |  |  |     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 |