• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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