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