From b56cbdd23834a65682c0b46f367f8679e83bc894 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 18 Sep 2020 15:55:15 -0700 Subject: Abseil LTS 20200923 What's New: * `absl::StatusOr` has been released. See our [blog post](https://abseil.io/blog/2020-091021-status) for more information. * Abseil Flags reflection interfaces have been released. * Abseil Flags memory usage has been significantly optimized. * Abseil now supports a "hardened" build mode. This build mode enables runtime checks that guard against programming errors that may lead to security vulnerabilities. Notable Fixes: * Sanitizer dynamic annotations like `AnnotateRWLockCreate` that are also defined by the compiler sanitizer implementation are no longer also defined by Abseil. * Sanitizer macros are now prefixed with `ABSL_` to avoid naming collisions. * Sanitizer usage is now automatically detected and no longer requires macros like `ADDRESS_SANITIZER` to be defined on the command line. Breaking Changes: * Abseil no longer contains a `dynamic_annotations` library. Users using a supported build system (Bazel or CMake) are unaffected by this, but users manually specifying link libraries may get an error about a missing linker input. Baseline: 7680a5f8efe32de4753baadbd63e74e59d95bac1 Cherry picks: None --- absl/random/mocking_bit_gen.h | 180 +++++++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 74 deletions(-) (limited to 'absl/random/mocking_bit_gen.h') diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h index 36cef911..6d2f2c83 100644 --- a/absl/random/mocking_bit_gen.h +++ b/absl/random/mocking_bit_gen.h @@ -33,17 +33,16 @@ #include #include #include -#include -#include #include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/fast_type_id.h" #include "absl/container/flat_hash_map.h" #include "absl/meta/type_traits.h" #include "absl/random/distributions.h" #include "absl/random/internal/distribution_caller.h" -#include "absl/random/internal/mocking_bit_gen_base.h" +#include "absl/random/random.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/types/span.h" @@ -54,11 +53,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { - -template -struct MockSingleOverload; +template +struct DistributionCaller; +class MockHelpers; } // namespace random_internal +class BitGenRef; // MockingBitGen // @@ -96,100 +96,132 @@ struct MockSingleOverload; // At this time, only mock distributions supplied within the Abseil random // library are officially supported. // -class MockingBitGen : public absl::random_internal::MockingBitGenBase { +// EXPECT_CALL and ON_CALL need to be made within the same DLL component as +// the call to absl::Uniform and related methods, otherwise mocking will fail +// since the underlying implementation creates a type-specific pointer which +// will be distinct across different DLL boundaries. +// +class MockingBitGen { public: - MockingBitGen() {} + MockingBitGen() = default; + + ~MockingBitGen() { + for (const auto& del : deleters_) del(); + } + + // URBG interface + using result_type = absl::BitGen::result_type; - ~MockingBitGen() override; + static constexpr result_type(min)() { return (absl::BitGen::min)(); } + static constexpr result_type(max)() { return (absl::BitGen::max)(); } + result_type operator()() { return gen_(); } private: - template - using MockFnType = - ::testing::MockFunction; + using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple, + void* t_erased_result); - // MockingBitGen::Register + struct MockData { + void* mock_fn = nullptr; + match_impl_fn match_impl = nullptr; + }; + + // GetMockFnType returns the testing::MockFunction for a result and tuple. + // This method only exists for type deduction and is otherwise unimplemented. + template + static auto GetMockFnType(ResultT, std::tuple) + -> ::testing::MockFunction; + + // MockFnCaller is a helper method for use with absl::apply to + // apply an ArgTupleT to a compatible MockFunction. + // NOTE: MockFnCaller is essentially equivalent to the lambda: + // [fn](auto... args) { return fn->Call(std::move(args)...)} + // however that fails to build on some supported platforms. + template + struct MockFnCaller; + // specialization for std::tuple. + template + struct MockFnCaller> { + MockFnType* fn; + inline ResultT operator()(Args... args) { + return fn->Call(std::move(args)...); + } + }; + + // MockingBitGen::RegisterMock // - // Register is the main extension point for - // extending the MockingBitGen framework. It provides a mechanism to install a - // mock expectation for the distribution `distr_t` onto the MockingBitGen - // context. + // RegisterMock(FastTypeIdType) is the main extension + // point for extending the MockingBitGen framework. It provides a mechanism to + // install a mock expectation for a function like ResultT(Args...) keyed by + // type_idex onto the MockingBitGen context. The key is that the type_index + // used to register must match the type index used to call the mock. // // The returned MockFunction<...> type can be used to setup additional // distribution parameters of the expectation. - template - decltype(std::declval>().gmock_Call( - std::declval()...)) - Register(Ms&&... matchers) { - auto& mock = - mocks_[std::type_index(GetTypeId>())]; - + template + auto RegisterMock(base_internal::FastTypeIdType type) + -> decltype(GetMockFnType(std::declval(), + std::declval()))& { + using MockFnType = decltype( + GetMockFnType(std::declval(), std::declval())); + auto& mock = mocks_[type]; if (!mock.mock_fn) { - auto* mock_fn = new MockFnType; + auto* mock_fn = new MockFnType; mock.mock_fn = mock_fn; - mock.match_impl = &MatchImpl; + mock.match_impl = &MatchImpl; deleters_.emplace_back([mock_fn] { delete mock_fn; }); } - - return static_cast*>(mock.mock_fn) - ->gmock_Call(std::forward(matchers)...); + return *static_cast(mock.mock_fn); } - mutable std::vector> deleters_; - - using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, - void* t_erased_result); - struct MockData { - void* mock_fn = nullptr; - match_impl_fn match_impl = nullptr; - }; - - mutable absl::flat_hash_map mocks_; - - template - static void MatchImpl(void* mock_fn, void* dist_args, void* result) { - using result_type = typename DistrT::result_type; - *static_cast(result) = absl::apply( - [mock_fn](Args... args) -> result_type { - return (*static_cast*>(mock_fn)) - .Call(std::move(args)...); - }, - *static_cast*>(dist_args)); + // MockingBitGen::MatchImpl<> is a dispatch function which converts the + // generic type-erased parameters into a specific mock invocation call. + // Requires tuple_args to point to a ArgTupleT, which is a std::tuple + // used to invoke the mock function. + // Requires result to point to a ResultT, which is the result of the call. + template + static void MatchImpl(/*MockFnType*/ void* mock_fn, + /*ArgTupleT*/ void* args_tuple, + /*ResultT*/ void* result) { + using MockFnType = decltype( + GetMockFnType(std::declval(), std::declval())); + *static_cast(result) = absl::apply( + MockFnCaller{ + static_cast(mock_fn)}, + *static_cast(args_tuple)); } - // Looks for an appropriate mock - Returns the mocked result if one is found. - // Otherwise, returns a random value generated by the underlying URBG. - bool CallImpl(const std::type_info& key_type, void* dist_args, - void* result) override { + // MockingBitGen::InvokeMock + // + // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking + // mocks registered on MockingBitGen. + // + // When no mocks are registered on the provided FastTypeIdType, returns false. + // Otherwise attempts to invoke the mock function ResultT(Args...) that + // was previously registered via the type_index. + // Requires tuple_args to point to a ArgTupleT, which is a std::tuple + // used to invoke the mock function. + // Requires result to point to a ResultT, which is the result of the call. + inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, + void* result) { // Trigger a mock, if there exists one that matches `param`. - auto it = mocks_.find(std::type_index(key_type)); + auto it = mocks_.find(type); if (it == mocks_.end()) return false; auto* mock_data = static_cast(&it->second); - mock_data->match_impl(mock_data->mock_fn, dist_args, result); + mock_data->match_impl(mock_data->mock_fn, args_tuple, result); return true; } - template - friend struct ::absl::random_internal::MockSingleOverload; - friend struct ::absl::random_internal::DistributionCaller< - absl::MockingBitGen>; -}; - -// ----------------------------------------------------------------------------- -// Implementation Details Only Below -// ----------------------------------------------------------------------------- + absl::flat_hash_map mocks_; + std::vector> deleters_; + absl::BitGen gen_; -namespace random_internal { - -template <> -struct DistributionCaller { - template - static typename DistrT::result_type Call(absl::MockingBitGen* gen, - Args&&... args) { - return gen->template Call(std::forward(args)...); - } + template + friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock + friend class ::absl::BitGenRef; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for RegisterMock, + // InvokeMock }; -} // namespace random_internal ABSL_NAMESPACE_END } // namespace absl -- cgit v1.2.3