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 `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 28 #ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_ 29 #define ABSL_RANDOM_MOCKING_BIT_GEN_H_ 30 31 #include <memory> 32 #include <tuple> 33 #include <type_traits> 34 #include <utility> 35 36 #include "gmock/gmock.h" 37 #include "absl/base/attributes.h" 38 #include "absl/base/config.h" 39 #include "absl/base/internal/fast_type_id.h" 40 #include "absl/container/flat_hash_map.h" 41 #include "absl/meta/type_traits.h" 42 #include "absl/random/internal/mock_helpers.h" 43 #include "absl/random/random.h" 44 #include "absl/utility/utility.h" 45 46 namespace absl { 47 ABSL_NAMESPACE_BEGIN 48 49 class BitGenRef; 50 51 namespace random_internal { 52 template <typename> 53 struct DistributionCaller; 54 class MockHelpers; 55 56 // Implements MockingBitGen with an option to turn on extra validation. 57 template <bool EnableValidation> 58 class MockingBitGenImpl { 59 public: 60 MockingBitGenImpl() = default; 61 ~MockingBitGenImpl() = default; 62 63 // URBG interface 64 using result_type = absl::BitGen::result_type; 65 result_type(min)66 static constexpr result_type(min)() { return (absl::BitGen::min)(); } result_type(max)67 static constexpr result_type(max)() { return (absl::BitGen::max)(); } operator()68 result_type operator()() { return gen_(); } 69 70 private: 71 // GetMockFnType returns the testing::MockFunction for a result and tuple. 72 // This method only exists for type deduction and is otherwise unimplemented. 73 template <typename ResultT, typename... Args> 74 static auto GetMockFnType(ResultT, std::tuple<Args...>) 75 -> ::testing::MockFunction<ResultT(Args...)>; 76 77 // MockFnCaller is a helper method for use with absl::apply to 78 // apply an ArgTupleT to a compatible MockFunction. 79 // NOTE: MockFnCaller is essentially equivalent to the lambda: 80 // [fn](auto... args) { return fn->Call(std::move(args)...)} 81 // however that fails to build on some supported platforms. 82 template <typename MockFnType, typename ValidatorT, typename ResultT, 83 typename Tuple> 84 struct MockFnCaller; 85 86 // specialization for std::tuple. 87 template <typename MockFnType, typename ValidatorT, typename ResultT, 88 typename... Args> 89 struct MockFnCaller<MockFnType, ValidatorT, ResultT, std::tuple<Args...>> { 90 MockFnType* fn; 91 inline ResultT operator()(Args... args) { 92 ResultT result = fn->Call(args...); 93 ValidatorT::Validate(result, args...); 94 return result; 95 } 96 }; 97 98 // FunctionHolder owns a particular ::testing::MockFunction associated with 99 // a mocked type signature, and implement the type-erased Apply call, which 100 // applies type-erased arguments to the mock. 101 class FunctionHolder { 102 public: 103 virtual ~FunctionHolder() = default; 104 105 // Call is a dispatch function which converts the 106 // generic type-erased parameters into a specific mock invocation call. 107 virtual void Apply(/*ArgTupleT*/ void* args_tuple, 108 /*ResultT*/ void* result) = 0; 109 }; 110 111 template <typename MockFnType, typename ValidatorT, typename ResultT, 112 typename ArgTupleT> 113 class FunctionHolderImpl final : public FunctionHolder { 114 public: 115 void Apply(void* args_tuple, void* result) final { 116 // Requires tuple_args to point to a ArgTupleT, which is a 117 // std::tuple<Args...> used to invoke the mock function. Requires result 118 // to point to a ResultT, which is the result of the call. 119 *static_cast<ResultT*>(result) = absl::apply( 120 MockFnCaller<MockFnType, ValidatorT, ResultT, ArgTupleT>{&mock_fn_}, 121 *static_cast<ArgTupleT*>(args_tuple)); 122 } 123 124 MockFnType mock_fn_; 125 }; 126 127 // MockingBitGen::RegisterMock 128 // 129 // RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension 130 // point for extending the MockingBitGen framework. It provides a mechanism to 131 // install a mock expectation for a function like ResultT(Args...) keyed by 132 // type_idex onto the MockingBitGen context. The key is that the type_index 133 // used to register must match the type index used to call the mock. 134 // 135 // The returned MockFunction<...> type can be used to setup additional 136 // distribution parameters of the expectation. 137 template <typename ResultT, typename ArgTupleT, typename SelfT, 138 typename ValidatorT> 139 auto RegisterMock(SelfT&, base_internal::FastTypeIdType type, ValidatorT) 140 -> decltype(GetMockFnType(std::declval<ResultT>(), 141 std::declval<ArgTupleT>()))& { 142 using ActualValidatorT = 143 std::conditional_t<EnableValidation, ValidatorT, NoOpValidator>; 144 using MockFnType = decltype(GetMockFnType(std::declval<ResultT>(), 145 std::declval<ArgTupleT>())); 146 147 using WrappedFnType = absl::conditional_t< 148 std::is_same<SelfT, ::testing::NiceMock<MockingBitGenImpl>>::value, 149 ::testing::NiceMock<MockFnType>, 150 absl::conditional_t< 151 std::is_same<SelfT, ::testing::NaggyMock<MockingBitGenImpl>>::value, 152 ::testing::NaggyMock<MockFnType>, 153 absl::conditional_t< 154 std::is_same<SelfT, 155 ::testing::StrictMock<MockingBitGenImpl>>::value, 156 ::testing::StrictMock<MockFnType>, MockFnType>>>; 157 158 using ImplT = 159 FunctionHolderImpl<WrappedFnType, ActualValidatorT, ResultT, ArgTupleT>; 160 auto& mock = mocks_[type]; 161 if (!mock) { 162 mock = absl::make_unique<ImplT>(); 163 } 164 return static_cast<ImplT*>(mock.get())->mock_fn_; 165 } 166 167 // MockingBitGen::InvokeMock 168 // 169 // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking 170 // mocks registered on MockingBitGen. 171 // 172 // When no mocks are registered on the provided FastTypeIdType, returns false. 173 // Otherwise attempts to invoke the mock function ResultT(Args...) that 174 // was previously registered via the type_index. 175 // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...> 176 // used to invoke the mock function. 177 // Requires result to point to a ResultT, which is the result of the call. 178 inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, 179 void* result) { 180 // Trigger a mock, if there exists one that matches `param`. 181 auto it = mocks_.find(type); 182 if (it == mocks_.end()) return false; 183 it->second->Apply(args_tuple, result); 184 return true; 185 } 186 187 absl::flat_hash_map<base_internal::FastTypeIdType, 188 std::unique_ptr<FunctionHolder>> 189 mocks_; 190 absl::BitGen gen_; 191 192 template <typename> 193 friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock 194 friend class ::absl::BitGenRef; // for InvokeMock 195 friend class ::absl::random_internal::MockHelpers; // for RegisterMock, 196 // InvokeMock 197 }; 198 199 } // namespace random_internal 200 201 // MockingBitGen 202 // 203 // `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class 204 // which can act in place of an `absl::BitGen` URBG within tests using the 205 // Googletest testing framework. 206 // 207 // Usage: 208 // 209 // Use an `absl::MockingBitGen` along with a mock distribution object (within 210 // mock_distributions.h) inside Googletest constructs such as ON_CALL(), 211 // EXPECT_TRUE(), etc. to produce deterministic results conforming to the 212 // distribution's API contract. 213 // 214 // Example: 215 // 216 // // Mock a call to an `absl::Bernoulli` distribution using Googletest 217 // absl::MockingBitGen bitgen; 218 // 219 // ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5)) 220 // .WillByDefault(testing::Return(true)); 221 // EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5)); 222 // 223 // // Mock a call to an `absl::Uniform` distribution within Googletest 224 // absl::MockingBitGen bitgen; 225 // 226 // ON_CALL(absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_)) 227 // .WillByDefault([] (int low, int high) { 228 // return low + (high - low) / 2; 229 // }); 230 // 231 // EXPECT_EQ(absl::Uniform<int>(gen, 0, 10), 5); 232 // EXPECT_EQ(absl::Uniform<int>(gen, 30, 40), 35); 233 // 234 // At this time, only mock distributions supplied within the Abseil random 235 // library are officially supported. 236 // 237 // EXPECT_CALL and ON_CALL need to be made within the same DLL component as 238 // the call to absl::Uniform and related methods, otherwise mocking will fail 239 // since the underlying implementation creates a type-specific pointer which 240 // will be distinct across different DLL boundaries. 241 // 242 using MockingBitGen = random_internal::MockingBitGenImpl<true>; 243 244 // UnvalidatedMockingBitGen 245 // 246 // UnvalidatedMockingBitGen is a variant of MockingBitGen which does no extra 247 // validation. 248 using UnvalidatedMockingBitGen ABSL_DEPRECATED("Use MockingBitGen instead") = 249 random_internal::MockingBitGenImpl<false>; 250 251 ABSL_NAMESPACE_END 252 } // namespace absl 253 254 #endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_ 255