tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mocking_bit_gen.h (9364B)


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