mocking_bit_gen.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // -----------------------------------------------------------------------------
  16. // mocking_bit_gen.h
  17. // -----------------------------------------------------------------------------
  18. //
  19. // This file includes an `y_absl::MockingBitGen` class to use as a mock within the
  20. // Googletest testing framework. Such a mock is useful to provide deterministic
  21. // values as return values within (otherwise random) Abseil distribution
  22. // functions. Such determinism within a mock is useful within testing frameworks
  23. // to test otherwise indeterminate APIs.
  24. //
  25. // More information about the Googletest testing framework is available at
  26. // https://github.com/google/googletest
  27. #ifndef Y_ABSL_RANDOM_MOCKING_BIT_GEN_H_
  28. #define Y_ABSL_RANDOM_MOCKING_BIT_GEN_H_
  29. #include <memory>
  30. #include <tuple>
  31. #include <type_traits>
  32. #include <utility>
  33. #include "gmock/gmock.h"
  34. #include "y_absl/base/attributes.h"
  35. #include "y_absl/base/config.h"
  36. #include "y_absl/base/internal/fast_type_id.h"
  37. #include "y_absl/container/flat_hash_map.h"
  38. #include "y_absl/meta/type_traits.h"
  39. #include "y_absl/random/internal/mock_helpers.h"
  40. #include "y_absl/random/random.h"
  41. #include "y_absl/utility/utility.h"
  42. namespace y_absl {
  43. Y_ABSL_NAMESPACE_BEGIN
  44. class BitGenRef;
  45. namespace random_internal {
  46. template <typename>
  47. struct DistributionCaller;
  48. class MockHelpers;
  49. // Implements MockingBitGen with an option to turn on extra validation.
  50. template <bool EnableValidation>
  51. class MockingBitGenImpl {
  52. public:
  53. MockingBitGenImpl() = default;
  54. ~MockingBitGenImpl() = default;
  55. // URBG interface
  56. using result_type = y_absl::BitGen::result_type;
  57. static constexpr result_type(min)() { return (y_absl::BitGen::min)(); }
  58. static constexpr result_type(max)() { return (y_absl::BitGen::max)(); }
  59. result_type operator()() { return gen_(); }
  60. private:
  61. // GetMockFnType returns the testing::MockFunction for a result and tuple.
  62. // This method only exists for type deduction and is otherwise unimplemented.
  63. template <typename ResultT, typename... Args>
  64. static auto GetMockFnType(ResultT, std::tuple<Args...>)
  65. -> ::testing::MockFunction<ResultT(Args...)>;
  66. // MockFnCaller is a helper method for use with y_absl::apply to
  67. // apply an ArgTupleT to a compatible MockFunction.
  68. // NOTE: MockFnCaller is essentially equivalent to the lambda:
  69. // [fn](auto... args) { return fn->Call(std::move(args)...)}
  70. // however that fails to build on some supported platforms.
  71. template <typename MockFnType, typename ValidatorT, typename ResultT,
  72. typename Tuple>
  73. struct MockFnCaller;
  74. // specialization for std::tuple.
  75. template <typename MockFnType, typename ValidatorT, typename ResultT,
  76. typename... Args>
  77. struct MockFnCaller<MockFnType, ValidatorT, ResultT, std::tuple<Args...>> {
  78. MockFnType* fn;
  79. inline ResultT operator()(Args... args) {
  80. ResultT result = fn->Call(args...);
  81. ValidatorT::Validate(result, args...);
  82. return result;
  83. }
  84. };
  85. // FunctionHolder owns a particular ::testing::MockFunction associated with
  86. // a mocked type signature, and implement the type-erased Apply call, which
  87. // applies type-erased arguments to the mock.
  88. class FunctionHolder {
  89. public:
  90. virtual ~FunctionHolder() = default;
  91. // Call is a dispatch function which converts the
  92. // generic type-erased parameters into a specific mock invocation call.
  93. virtual void Apply(/*ArgTupleT*/ void* args_tuple,
  94. /*ResultT*/ void* result) = 0;
  95. };
  96. template <typename MockFnType, typename ValidatorT, typename ResultT,
  97. typename ArgTupleT>
  98. class FunctionHolderImpl final : public FunctionHolder {
  99. public:
  100. void Apply(void* args_tuple, void* result) final {
  101. // Requires tuple_args to point to a ArgTupleT, which is a
  102. // std::tuple<Args...> used to invoke the mock function. Requires result
  103. // to point to a ResultT, which is the result of the call.
  104. *static_cast<ResultT*>(result) = y_absl::apply(
  105. MockFnCaller<MockFnType, ValidatorT, ResultT, ArgTupleT>{&mock_fn_},
  106. *static_cast<ArgTupleT*>(args_tuple));
  107. }
  108. MockFnType mock_fn_;
  109. };
  110. // MockingBitGen::RegisterMock
  111. //
  112. // RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension
  113. // point for extending the MockingBitGen framework. It provides a mechanism to
  114. // install a mock expectation for a function like ResultT(Args...) keyed by
  115. // type_idex onto the MockingBitGen context. The key is that the type_index
  116. // used to register must match the type index used to call the mock.
  117. //
  118. // The returned MockFunction<...> type can be used to setup additional
  119. // distribution parameters of the expectation.
  120. template <typename ResultT, typename ArgTupleT, typename SelfT,
  121. typename ValidatorT>
  122. auto RegisterMock(SelfT&, base_internal::FastTypeIdType type, ValidatorT)
  123. -> decltype(GetMockFnType(std::declval<ResultT>(),
  124. std::declval<ArgTupleT>()))& {
  125. using ActualValidatorT =
  126. std::conditional_t<EnableValidation, ValidatorT, NoOpValidator>;
  127. using MockFnType = decltype(GetMockFnType(std::declval<ResultT>(),
  128. std::declval<ArgTupleT>()));
  129. using WrappedFnType = y_absl::conditional_t<
  130. std::is_same<SelfT, ::testing::NiceMock<MockingBitGenImpl>>::value,
  131. ::testing::NiceMock<MockFnType>,
  132. y_absl::conditional_t<
  133. std::is_same<SelfT, ::testing::NaggyMock<MockingBitGenImpl>>::value,
  134. ::testing::NaggyMock<MockFnType>,
  135. y_absl::conditional_t<
  136. std::is_same<SelfT,
  137. ::testing::StrictMock<MockingBitGenImpl>>::value,
  138. ::testing::StrictMock<MockFnType>, MockFnType>>>;
  139. using ImplT =
  140. FunctionHolderImpl<WrappedFnType, ActualValidatorT, ResultT, ArgTupleT>;
  141. auto& mock = mocks_[type];
  142. if (!mock) {
  143. mock = y_absl::make_unique<ImplT>();
  144. }
  145. return static_cast<ImplT*>(mock.get())->mock_fn_;
  146. }
  147. // MockingBitGen::InvokeMock
  148. //
  149. // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking
  150. // mocks registered on MockingBitGen.
  151. //
  152. // When no mocks are registered on the provided FastTypeIdType, returns false.
  153. // Otherwise attempts to invoke the mock function ResultT(Args...) that
  154. // was previously registered via the type_index.
  155. // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
  156. // used to invoke the mock function.
  157. // Requires result to point to a ResultT, which is the result of the call.
  158. inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
  159. void* result) {
  160. // Trigger a mock, if there exists one that matches `param`.
  161. auto it = mocks_.find(type);
  162. if (it == mocks_.end()) return false;
  163. it->second->Apply(args_tuple, result);
  164. return true;
  165. }
  166. y_absl::flat_hash_map<base_internal::FastTypeIdType,
  167. std::unique_ptr<FunctionHolder>>
  168. mocks_;
  169. y_absl::BitGen gen_;
  170. template <typename>
  171. friend struct ::y_absl::random_internal::DistributionCaller; // for InvokeMock
  172. friend class ::y_absl::BitGenRef; // for InvokeMock
  173. friend class ::y_absl::random_internal::MockHelpers; // for RegisterMock,
  174. // InvokeMock
  175. };
  176. } // namespace random_internal
  177. // MockingBitGen
  178. //
  179. // `y_absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class
  180. // which can act in place of an `y_absl::BitGen` URBG within tests using the
  181. // Googletest testing framework.
  182. //
  183. // Usage:
  184. //
  185. // Use an `y_absl::MockingBitGen` along with a mock distribution object (within
  186. // mock_distributions.h) inside Googletest constructs such as ON_CALL(),
  187. // EXPECT_TRUE(), etc. to produce deterministic results conforming to the
  188. // distribution's API contract.
  189. //
  190. // Example:
  191. //
  192. // // Mock a call to an `y_absl::Bernoulli` distribution using Googletest
  193. // y_absl::MockingBitGen bitgen;
  194. //
  195. // ON_CALL(y_absl::MockBernoulli(), Call(bitgen, 0.5))
  196. // .WillByDefault(testing::Return(true));
  197. // EXPECT_TRUE(y_absl::Bernoulli(bitgen, 0.5));
  198. //
  199. // // Mock a call to an `y_absl::Uniform` distribution within Googletest
  200. // y_absl::MockingBitGen bitgen;
  201. //
  202. // ON_CALL(y_absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_))
  203. // .WillByDefault([] (int low, int high) {
  204. // return low + (high - low) / 2;
  205. // });
  206. //
  207. // EXPECT_EQ(y_absl::Uniform<int>(gen, 0, 10), 5);
  208. // EXPECT_EQ(y_absl::Uniform<int>(gen, 30, 40), 35);
  209. //
  210. // At this time, only mock distributions supplied within the Abseil random
  211. // library are officially supported.
  212. //
  213. // EXPECT_CALL and ON_CALL need to be made within the same DLL component as
  214. // the call to y_absl::Uniform and related methods, otherwise mocking will fail
  215. // since the underlying implementation creates a type-specific pointer which
  216. // will be distinct across different DLL boundaries.
  217. //
  218. using MockingBitGen = random_internal::MockingBitGenImpl<true>;
  219. // UnvalidatedMockingBitGen
  220. //
  221. // UnvalidatedMockingBitGen is a variant of MockingBitGen which does no extra
  222. // validation.
  223. using UnvalidatedMockingBitGen Y_ABSL_DEPRECATED("Use MockingBitGen instead") =
  224. random_internal::MockingBitGenImpl<false>;
  225. Y_ABSL_NAMESPACE_END
  226. } // namespace y_absl
  227. #endif // Y_ABSL_RANDOM_MOCKING_BIT_GEN_H_