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