diff options
Diffstat (limited to 'third_party/abseil_cpp/absl/random/mocking_bit_gen.h')
-rw-r--r-- | third_party/abseil_cpp/absl/random/mocking_bit_gen.h | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/third_party/abseil_cpp/absl/random/mocking_bit_gen.h b/third_party/abseil_cpp/absl/random/mocking_bit_gen.h new file mode 100644 index 000000000000..6d2f2c836245 --- /dev/null +++ b/third_party/abseil_cpp/absl/random/mocking_bit_gen.h @@ -0,0 +1,228 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// mocking_bit_gen.h +// ----------------------------------------------------------------------------- +// +// This file includes an `absl::MockingBitGen` class to use as a mock within the +// Googletest testing framework. Such a mock is useful to provide deterministic +// values as return values within (otherwise random) Abseil distribution +// functions. Such determinism within a mock is useful within testing frameworks +// to test otherwise indeterminate APIs. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest + +#ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_ +#define ABSL_RANDOM_MOCKING_BIT_GEN_H_ + +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> + +#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/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/span.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace random_internal { +template <typename> +struct DistributionCaller; +class MockHelpers; + +} // namespace random_internal +class BitGenRef; + +// MockingBitGen +// +// `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class +// which can act in place of an `absl::BitGen` URBG within tests using the +// Googletest testing framework. +// +// Usage: +// +// Use an `absl::MockingBitGen` along with a mock distribution object (within +// mock_distributions.h) inside Googletest constructs such as ON_CALL(), +// EXPECT_TRUE(), etc. to produce deterministic results conforming to the +// distribution's API contract. +// +// Example: +// +// // Mock a call to an `absl::Bernoulli` distribution using Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5)) +// .WillByDefault(testing::Return(true)); +// EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5)); +// +// // Mock a call to an `absl::Uniform` distribution within Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_)) +// .WillByDefault([] (int low, int high) { +// return (low + high) / 2; +// }); +// +// EXPECT_EQ(absl::Uniform<int>(gen, 0, 10), 5); +// EXPECT_EQ(absl::Uniform<int>(gen, 30, 40), 35); +// +// At this time, only mock distributions supplied within the Abseil random +// library are officially supported. +// +// 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() = default; + + ~MockingBitGen() { + for (const auto& del : deleters_) del(); + } + + // URBG interface + using result_type = absl::BitGen::result_type; + + static constexpr result_type(min)() { return (absl::BitGen::min)(); } + static constexpr result_type(max)() { return (absl::BitGen::max)(); } + result_type operator()() { return gen_(); } + + private: + using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple, + void* t_erased_result); + + 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 <typename ResultT, typename... Args> + static auto GetMockFnType(ResultT, std::tuple<Args...>) + -> ::testing::MockFunction<ResultT(Args...)>; + + // 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 <typename ResultT, typename MockFnType, typename Tuple> + struct MockFnCaller; + // specialization for std::tuple. + template <typename ResultT, typename MockFnType, typename... Args> + struct MockFnCaller<ResultT, MockFnType, std::tuple<Args...>> { + MockFnType* fn; + inline ResultT operator()(Args... args) { + return fn->Call(std::move(args)...); + } + }; + + // MockingBitGen::RegisterMock + // + // RegisterMock<ResultT, ArgTupleT>(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 <typename ResultT, typename ArgTupleT> + auto RegisterMock(base_internal::FastTypeIdType type) + -> decltype(GetMockFnType(std::declval<ResultT>(), + std::declval<ArgTupleT>()))& { + using MockFnType = decltype( + GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>())); + auto& mock = mocks_[type]; + if (!mock.mock_fn) { + auto* mock_fn = new MockFnType; + mock.mock_fn = mock_fn; + mock.match_impl = &MatchImpl<ResultT, ArgTupleT>; + deleters_.emplace_back([mock_fn] { delete mock_fn; }); + } + return *static_cast<MockFnType*>(mock.mock_fn); + } + + // 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<Args...> + // used to invoke the mock function. + // Requires result to point to a ResultT, which is the result of the call. + template <typename ResultT, typename ArgTupleT> + static void MatchImpl(/*MockFnType<ResultT, Args...>*/ void* mock_fn, + /*ArgTupleT*/ void* args_tuple, + /*ResultT*/ void* result) { + using MockFnType = decltype( + GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>())); + *static_cast<ResultT*>(result) = absl::apply( + MockFnCaller<ResultT, MockFnType, ArgTupleT>{ + static_cast<MockFnType*>(mock_fn)}, + *static_cast<ArgTupleT*>(args_tuple)); + } + + // 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<Args...> + // 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(type); + if (it == mocks_.end()) return false; + auto* mock_data = static_cast<MockData*>(&it->second); + mock_data->match_impl(mock_data->mock_fn, args_tuple, result); + return true; + } + + absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_; + std::vector<std::function<void()>> deleters_; + absl::BitGen gen_; + + template <typename> + friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock + friend class ::absl::BitGenRef; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for RegisterMock, + // InvokeMock +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_ |