1 /*
2  *  Created by Phil Nash on 15/6/2018.
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 #ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
8 #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
9 
10 #include "catch_interfaces_generatortracker.h"
11 #include "catch_common.h"
12 #include "catch_enforce.h"
13 
14 #include <memory>
15 #include <vector>
16 #include <cassert>
17 
18 #include <utility>
19 #include <exception>
20 
21 namespace Catch {
22 
23 class GeneratorException : public std::exception {
24     const char* const m_msg = "";
25 
26 public:
GeneratorException(const char * msg)27     GeneratorException(const char* msg):
28         m_msg(msg)
29     {}
30 
31     const char* what() const noexcept override final;
32 };
33 
34 namespace Generators {
35 
36     // !TBD move this into its own location?
37     namespace pf{
38         template<typename T, typename... Args>
make_unique(Args &&...args)39         std::unique_ptr<T> make_unique( Args&&... args ) {
40             return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
41         }
42     }
43 
44     template<typename T>
45     struct IGenerator : GeneratorUntypedBase {
46         virtual ~IGenerator() = default;
47 
48         // Returns the current element of the generator
49         //
50         // \Precondition The generator is either freshly constructed,
51         // or the last call to `next()` returned true
52         virtual T const& get() const = 0;
53         using type = T;
54     };
55 
56     template<typename T>
57     class SingleValueGenerator final : public IGenerator<T> {
58         T m_value;
59     public:
SingleValueGenerator(T && value)60         SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
61 
get() const62         T const& get() const override {
63             return m_value;
64         }
next()65         bool next() override {
66             return false;
67         }
68     };
69 
70     template<typename T>
71     class FixedValuesGenerator final : public IGenerator<T> {
72         static_assert(!std::is_same<T, bool>::value,
73             "FixedValuesGenerator does not support bools because of std::vector<bool>"
74             "specialization, use SingleValue Generator instead.");
75         std::vector<T> m_values;
76         size_t m_idx = 0;
77     public:
FixedValuesGenerator(std::initializer_list<T> values)78         FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
79 
get() const80         T const& get() const override {
81             return m_values[m_idx];
82         }
next()83         bool next() override {
84             ++m_idx;
85             return m_idx < m_values.size();
86         }
87     };
88 
89     template <typename T>
90     class GeneratorWrapper final {
91         std::unique_ptr<IGenerator<T>> m_generator;
92     public:
GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator)93         GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
94             m_generator(std::move(generator))
95         {}
get() const96         T const& get() const {
97             return m_generator->get();
98         }
next()99         bool next() {
100             return m_generator->next();
101         }
102     };
103 
104     template <typename T>
value(T && value)105     GeneratorWrapper<T> value(T&& value) {
106         return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
107     }
108     template <typename T>
values(std::initializer_list<T> values)109     GeneratorWrapper<T> values(std::initializer_list<T> values) {
110         return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
111     }
112 
113     template<typename T>
114     class Generators : public IGenerator<T> {
115         std::vector<GeneratorWrapper<T>> m_generators;
116         size_t m_current = 0;
117 
populate(GeneratorWrapper<T> && generator)118         void populate(GeneratorWrapper<T>&& generator) {
119             m_generators.emplace_back(std::move(generator));
120         }
populate(T && val)121         void populate(T&& val) {
122             m_generators.emplace_back(value(std::forward<T>(val)));
123         }
124         template<typename U>
populate(U && val)125         void populate(U&& val) {
126             populate(T(std::forward<U>(val)));
127         }
128         template<typename U, typename... Gs>
populate(U && valueOrGenerator,Gs &&...moreGenerators)129         void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
130             populate(std::forward<U>(valueOrGenerator));
131             populate(std::forward<Gs>(moreGenerators)...);
132         }
133 
134     public:
135         template <typename... Gs>
Generators(Gs &&...moreGenerators)136         Generators(Gs &&... moreGenerators) {
137             m_generators.reserve(sizeof...(Gs));
138             populate(std::forward<Gs>(moreGenerators)...);
139         }
140 
get() const141         T const& get() const override {
142             return m_generators[m_current].get();
143         }
144 
next()145         bool next() override {
146             if (m_current >= m_generators.size()) {
147                 return false;
148             }
149             const bool current_status = m_generators[m_current].next();
150             if (!current_status) {
151                 ++m_current;
152             }
153             return m_current < m_generators.size();
154         }
155     };
156 
157 
158     template<typename... Ts>
table(std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples)159     GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
160         return values<std::tuple<Ts...>>( tuples );
161     }
162 
163     // Tag type to signal that a generator sequence should convert arguments to a specific type
164     template <typename T>
165     struct as {};
166 
167     template<typename T, typename... Gs>
makeGenerators(GeneratorWrapper<T> && generator,Gs &&...moreGenerators)168     auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
169         return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
170     }
171     template<typename T>
makeGenerators(GeneratorWrapper<T> && generator)172     auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
173         return Generators<T>(std::move(generator));
174     }
175     template<typename T, typename... Gs>
makeGenerators(T && val,Gs &&...moreGenerators)176     auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
177         return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
178     }
179     template<typename T, typename U, typename... Gs>
makeGenerators(as<T>,U && val,Gs &&...moreGenerators)180     auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
181         return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
182     }
183 
184     auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
185 
186     template<typename L>
187     // Note: The type after -> is weird, because VS2015 cannot parse
188     //       the expression used in the typedef inside, when it is in
189     //       return type. Yeah.
generate(SourceLineInfo const & lineInfo,L const & generatorExpression)190     auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
191         using UnderlyingType = typename decltype(generatorExpression())::type;
192 
193         IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
194         if (!tracker.hasGenerator()) {
195             tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
196         }
197 
198         auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
199         return generator.get();
200     }
201 
202 } // namespace Generators
203 } // namespace Catch
204 
205 #define GENERATE( ... ) \
206     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
207 #define GENERATE_COPY( ... ) \
208     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
209 #define GENERATE_REF( ... ) \
210     Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
211 
212 #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
213