Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
6.8 KiB
196 lines
6.8 KiB
// 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 <typeindex> |
|
#include <typeinfo> |
|
#include <utility> |
|
|
|
#include "gmock/gmock.h" |
|
#include "gtest/gtest.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/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, typename> |
|
struct MockSingleOverload; |
|
|
|
} // namespace random_internal |
|
|
|
// 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. |
|
// |
|
class MockingBitGen : public absl::random_internal::MockingBitGenBase { |
|
public: |
|
MockingBitGen() {} |
|
|
|
~MockingBitGen() override; |
|
|
|
private: |
|
template <typename DistrT, typename... Args> |
|
using MockFnType = |
|
::testing::MockFunction<typename DistrT::result_type(Args...)>; |
|
|
|
// MockingBitGen::Register |
|
// |
|
// Register<DistrT, FormatT, ArgTupleT> 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. |
|
// |
|
// The returned MockFunction<...> type can be used to setup additional |
|
// distribution parameters of the expectation. |
|
template <typename DistrT, typename... Args, typename... Ms> |
|
decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call( |
|
std::declval<Ms>()...)) |
|
Register(Ms&&... matchers) { |
|
auto& mock = |
|
mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())]; |
|
|
|
if (!mock.mock_fn) { |
|
auto* mock_fn = new MockFnType<DistrT, Args...>; |
|
mock.mock_fn = mock_fn; |
|
mock.match_impl = &MatchImpl<DistrT, Args...>; |
|
deleters_.emplace_back([mock_fn] { delete mock_fn; }); |
|
} |
|
|
|
return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn) |
|
->gmock_Call(std::forward<Ms>(matchers)...); |
|
} |
|
|
|
mutable std::vector<std::function<void()>> 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<std::type_index, MockData> mocks_; |
|
|
|
template <typename DistrT, typename... Args> |
|
static void MatchImpl(void* mock_fn, void* dist_args, void* result) { |
|
using result_type = typename DistrT::result_type; |
|
*static_cast<result_type*>(result) = absl::apply( |
|
[mock_fn](Args... args) -> result_type { |
|
return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn)) |
|
.Call(std::move(args)...); |
|
}, |
|
*static_cast<std::tuple<Args...>*>(dist_args)); |
|
} |
|
|
|
// 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 { |
|
// Trigger a mock, if there exists one that matches `param`. |
|
auto it = mocks_.find(std::type_index(key_type)); |
|
if (it == mocks_.end()) return false; |
|
auto* mock_data = static_cast<MockData*>(&it->second); |
|
mock_data->match_impl(mock_data->mock_fn, dist_args, result); |
|
return true; |
|
} |
|
|
|
template <typename, typename> |
|
friend struct ::absl::random_internal::MockSingleOverload; |
|
friend struct ::absl::random_internal::DistributionCaller< |
|
absl::MockingBitGen>; |
|
}; |
|
|
|
// ----------------------------------------------------------------------------- |
|
// Implementation Details Only Below |
|
// ----------------------------------------------------------------------------- |
|
|
|
namespace random_internal { |
|
|
|
template <> |
|
struct DistributionCaller<absl::MockingBitGen> { |
|
template <typename DistrT, typename FormatT, typename... Args> |
|
static typename DistrT::result_type Call(absl::MockingBitGen* gen, |
|
Args&&... args) { |
|
return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); |
|
} |
|
}; |
|
|
|
} // namespace random_internal |
|
ABSL_NAMESPACE_END |
|
} // namespace absl |
|
|
|
#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_
|
|
|