catch_generators.hpp 7.8 KB

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