summaryrefslogtreecommitdiff
path: root/absl/random
diff options
context:
space:
mode:
Diffstat (limited to 'absl/random')
-rw-r--r--absl/random/BUILD.bazel7
-rw-r--r--absl/random/CMakeLists.txt22
-rw-r--r--absl/random/bit_gen_ref.h105
-rw-r--r--absl/random/bit_gen_ref_test.cc25
-rw-r--r--absl/random/internal/BUILD.bazel17
-rw-r--r--absl/random/internal/distribution_caller.h51
-rw-r--r--absl/random/internal/mock_overload_set.h33
-rw-r--r--absl/random/internal/mocking_bit_gen_base.h85
-rw-r--r--absl/random/mock_distributions.h5
-rw-r--r--absl/random/mocking_bit_gen.h175
10 files changed, 273 insertions, 252 deletions
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index 4b804c86..694331c2 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -115,11 +115,12 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":random",
"//absl/base:core_headers",
+ "//absl/base:fast_type_id",
"//absl/meta:type_traits",
"//absl/random/internal:distribution_caller",
"//absl/random/internal:fast_uniform_bits",
- "//absl/random/internal:mocking_bit_gen_base",
],
)
@@ -145,10 +146,11 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":distributions",
+ ":random",
+ "//absl/base:fast_type_id",
"//absl/container:flat_hash_map",
"//absl/meta:type_traits",
"//absl/random/internal:distribution_caller",
- "//absl/random/internal:mocking_bit_gen_base",
"//absl/strings",
"//absl/types:span",
"//absl/types:variant",
@@ -410,6 +412,7 @@ cc_test(
deps = [
":bit_gen_ref",
":random",
+ "//absl/base:fast_type_id",
"//absl/random/internal:sequence_urbg",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index 85a2ea37..8875b309 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -45,7 +45,6 @@ absl_cc_library(
absl::core_headers
absl::random_internal_distribution_caller
absl::random_internal_fast_uniform_bits
- absl::random_internal_mocking_bit_gen_base
absl::type_traits
)
@@ -62,6 +61,7 @@ absl_cc_test(
absl::random_bit_gen_ref
absl::random_random
absl::random_internal_sequence_urbg
+ absl::fast_type_id
gmock
gtest_main
)
@@ -69,21 +69,6 @@ absl_cc_test(
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
- random_internal_mocking_bit_gen_base
- HDRS
- "internal/mocking_bit_gen_base.h"
- COPTS
- ${ABSL_DEFAULT_COPTS}
- LINKOPTS
- ${ABSL_DEFAULT_LINKOPTS}
- DEPS
- absl::random_random
- absl::strings
-)
-
-# Internal-only target, do not depend on directly.
-absl_cc_library(
- NAME
random_internal_mock_overload_set
HDRS
"internal/mock_overload_set.h"
@@ -93,6 +78,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_mocking_bit_gen
+ absl::fast_type_id
TESTONLY
)
@@ -111,8 +97,8 @@ absl_cc_library(
absl::raw_logging_internal
absl::random_distributions
absl::random_internal_distribution_caller
- absl::random_internal_mocking_bit_gen_base
absl::random_internal_mock_overload_set
+ absl::random_random
absl::strings
absl::span
absl::type_traits
@@ -533,6 +519,8 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
+ absl::utility
+ absl::fast_type_id
)
# Internal-only target, do not depend on directly.
diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h
index 59591a47..00e36248 100644
--- a/absl/random/bit_gen_ref.h
+++ b/absl/random/bit_gen_ref.h
@@ -24,11 +24,11 @@
#ifndef ABSL_RANDOM_BIT_GEN_REF_H_
#define ABSL_RANDOM_BIT_GEN_REF_H_
+#include "absl/base/internal/fast_type_id.h"
#include "absl/base/macros.h"
#include "absl/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/fast_uniform_bits.h"
-#include "absl/random/internal/mocking_bit_gen_base.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -51,6 +51,9 @@ struct is_urbg<
typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
: std::true_type {};
+template <typename>
+struct DistributionCaller;
+
} // namespace random_internal
// -----------------------------------------------------------------------------
@@ -77,23 +80,50 @@ struct is_urbg<
// }
//
class BitGenRef {
- public:
- using result_type = uint64_t;
+ // SFINAE to detect whether the URBG type includes a member matching
+ // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
+ //
+ // These live inside BitGenRef so that they have friend access
+ // to MockingBitGen. (see similar methods in DistributionCaller).
+ template <template <class...> class Trait, class AlwaysVoid, class... Args>
+ struct detector : std::false_type {};
+ template <template <class...> class Trait, class... Args>
+ struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
+ : std::true_type {};
+
+ template <class T>
+ using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
+ std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(),
+ std::declval<void*>()));
+
+ template <typename T>
+ using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type;
- BitGenRef(const absl::BitGenRef&) = default;
- BitGenRef(absl::BitGenRef&&) = default;
- BitGenRef& operator=(const absl::BitGenRef&) = default;
- BitGenRef& operator=(absl::BitGenRef&&) = default;
+ public:
+ BitGenRef(const BitGenRef&) = default;
+ BitGenRef(BitGenRef&&) = default;
+ BitGenRef& operator=(const BitGenRef&) = default;
+ BitGenRef& operator=(BitGenRef&&) = default;
+
+ template <typename URBG, typename absl::enable_if_t<
+ (!std::is_same<URBG, BitGenRef>::value &&
+ random_internal::is_urbg<URBG>::value &&
+ !HasInvokeMock<URBG>::value)>* = nullptr>
+ BitGenRef(URBG& gen) // NOLINT
+ : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
+ mock_call_(NotAMock),
+ generate_impl_fn_(ImplFn<URBG>) {}
template <typename URBG,
- typename absl::enable_if_t<
- (!std::is_same<URBG, BitGenRef>::value &&
- random_internal::is_urbg<URBG>::value)>* = nullptr>
+ typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
+ random_internal::is_urbg<URBG>::value &&
+ HasInvokeMock<URBG>::value)>* = nullptr>
BitGenRef(URBG& gen) // NOLINT
- : mocked_gen_ptr_(MakeMockPointer(&gen)),
- t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
- generate_impl_fn_(ImplFn<URBG>) {
- }
+ : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
+ mock_call_(&MockCall<URBG>),
+ generate_impl_fn_(ImplFn<URBG>) {}
+
+ using result_type = uint64_t;
static constexpr result_type(min)() {
return (std::numeric_limits<result_type>::min)();
@@ -106,14 +136,9 @@ class BitGenRef {
result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
private:
- friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>;
using impl_fn = result_type (*)(uintptr_t);
- using mocker_base_t = absl::random_internal::MockingBitGenBase;
-
- // Convert an arbitrary URBG pointer into either a valid mocker_base_t
- // pointer or a nullptr.
- static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; }
- static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; }
+ using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
+ void*);
template <typename URBG>
static result_type ImplFn(uintptr_t ptr) {
@@ -123,29 +148,31 @@ class BitGenRef {
return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
}
- mocker_base_t* mocked_gen_ptr_;
+ // Get a type-erased InvokeMock pointer.
+ template <typename URBG>
+ static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type,
+ void* result, void* arg_tuple) {
+ return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result,
+ arg_tuple);
+ }
+ static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) {
+ return false;
+ }
+
+ inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
+ void* result) {
+ if (mock_call_ == NotAMock) return false; // avoids an indirect call.
+ return mock_call_(t_erased_gen_ptr_, type, args_tuple, result);
+ }
+
uintptr_t t_erased_gen_ptr_;
+ mock_call_fn mock_call_;
impl_fn generate_impl_fn_;
-};
-
-namespace random_internal {
-template <>
-struct DistributionCaller<absl::BitGenRef> {
- template <typename DistrT, typename... Args>
- static typename DistrT::result_type Call(absl::BitGenRef* gen_ref,
- Args&&... args) {
- auto* mock_ptr = gen_ref->mocked_gen_ptr_;
- if (mock_ptr == nullptr) {
- DistrT dist(std::forward<Args>(args)...);
- return dist(*gen_ref);
- } else {
- return mock_ptr->template Call<DistrT>(std::forward<Args>(args)...);
- }
- }
+ template <typename>
+ friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
};
-} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc
index ca0e4d70..1135cf2d 100644
--- a/absl/random/bit_gen_ref_test.cc
+++ b/absl/random/bit_gen_ref_test.cc
@@ -17,30 +17,31 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/internal/fast_type_id.h"
#include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
-class ConstBitGen : public absl::random_internal::MockingBitGenBase {
- bool CallImpl(const std::type_info&, void*, void* result) override {
+class ConstBitGen {
+ public:
+ // 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 1; }
+
+ // InvokeMock method
+ bool InvokeMock(base_internal::FastTypeIdType index, void*, void* result) {
*static_cast<int*>(result) = 42;
return true;
}
};
-namespace random_internal {
-template <>
-struct DistributionCaller<ConstBitGen> {
- template <typename DistrT, typename FormatT, typename... Args>
- static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) {
- return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...);
- }
-};
-} // namespace random_internal
-
namespace {
+
int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
template <typename T>
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 813d926e..3ab1177d 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -45,7 +45,11 @@ cc_library(
hdrs = ["distribution_caller.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = ["//absl/base:config"],
+ deps = [
+ "//absl/base:config",
+ "//absl/base:fast_type_id",
+ "//absl/utility",
+ ],
)
cc_library(
@@ -481,20 +485,11 @@ cc_test(
)
cc_library(
- name = "mocking_bit_gen_base",
- hdrs = ["mocking_bit_gen_base.h"],
- linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = [
- "//absl/random",
- "//absl/strings",
- ],
-)
-
-cc_library(
name = "mock_overload_set",
testonly = 1,
hdrs = ["mock_overload_set.h"],
deps = [
+ "//absl/base:fast_type_id",
"//absl/random:mocking_bit_gen",
"@com_google_googletest//:gtest",
],
diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h
index 4e072444..87a76845 100644
--- a/absl/random/internal/distribution_caller.h
+++ b/absl/random/internal/distribution_caller.h
@@ -20,6 +20,8 @@
#include <utility>
#include "absl/base/config.h"
+#include "absl/base/internal/fast_type_id.h"
+#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -30,14 +32,55 @@ namespace random_internal {
// to intercept such calls.
template <typename URBG>
struct DistributionCaller {
- // Call the provided distribution type. The parameters are expected
- // to be explicitly specified.
- // DistrT is the distribution type.
+ // SFINAE to detect whether the URBG type includes a member matching
+ // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
+ //
+ // These live inside BitGenRef so that they have friend access
+ // to MockingBitGen. (see similar methods in DistributionCaller).
+ template <template <class...> class Trait, class AlwaysVoid, class... Args>
+ struct detector : std::false_type {};
+ template <template <class...> class Trait, class... Args>
+ struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
+ : std::true_type {};
+
+ template <class T>
+ using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
+ std::declval<::absl::base_internal::FastTypeIdType>(),
+ std::declval<void*>(), std::declval<void*>()));
+
+ using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type;
+
+ // Default implementation of distribution caller.
template <typename DistrT, typename... Args>
- static typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
+ static inline typename DistrT::result_type Impl(std::false_type, URBG* urbg,
+ Args&&... args) {
DistrT dist(std::forward<Args>(args)...);
return dist(*urbg);
}
+
+ // Mock implementation of distribution caller.
+ template <typename DistrT, typename... Args>
+ static inline typename DistrT::result_type Impl(std::true_type, URBG* urbg,
+ Args&&... args) {
+ using ResultT = typename DistrT::result_type;
+ using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
+ ArgTupleT arg_tuple(std::forward<Args>(args)...);
+ ResultT result;
+ if (!urbg->InvokeMock(
+ ::absl::base_internal::FastTypeId<ResultT(DistrT, ArgTupleT)>(),
+ &arg_tuple, &result)) {
+ auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
+ result = dist(*urbg);
+ }
+ return result;
+ }
+
+ // Default implementation of distribution caller.
+ template <typename DistrT, typename... Args>
+ static inline typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
+ return Impl<DistrT, Args...>(HasInvokeMock{}, urbg,
+ std::forward<Args>(args)...);
+ }
};
} // namespace random_internal
diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h
index c2a30d89..60561b51 100644
--- a/absl/random/internal/mock_overload_set.h
+++ b/absl/random/internal/mock_overload_set.h
@@ -20,6 +20,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/internal/fast_type_id.h"
#include "absl/random/mocking_bit_gen.h"
namespace absl {
@@ -35,17 +36,23 @@ struct MockSingleOverload;
// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
-// arguments to Mocking::Register.
+// arguments to MockingBitGen::Register.
template <typename DistrT, typename Ret, typename... Args>
struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the "
- "distributions result type.");
+ "distribution result_type.");
+ using ArgTupleT = std::tuple<Args...>;
auto gmock_Call(
absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
- const ::testing::Matcher<Args>&... args)
- -> decltype(gen.Register<DistrT, Args...>(args...)) {
- return gen.Register<DistrT, Args...>(args...);
+ const ::testing::Matcher<Args>&... matchers)
+ -> decltype(gen.RegisterMock<Ret, ArgTupleT>(
+ std::declval<::absl::base_internal::FastTypeIdType>())
+ .gmock_Call(matchers...)) {
+ return gen
+ .RegisterMock<Ret, ArgTupleT>(
+ ::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>())
+ .gmock_Call(matchers...);
}
};
@@ -53,13 +60,19 @@ template <typename DistrT, typename Ret, typename Arg, typename... Args>
struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> {
static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
"Overload signature must have return type matching the "
- "distributions result type.");
+ "distribution result_type.");
+ using ArgTupleT = std::tuple<Arg, Args...>;
auto gmock_Call(
- const ::testing::Matcher<Arg>& arg,
+ const ::testing::Matcher<Arg>& matcher,
absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
- const ::testing::Matcher<Args>&... args)
- -> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) {
- return gen.Register<DistrT, Arg, Args...>(arg, args...);
+ const ::testing::Matcher<Args>&... matchers)
+ -> decltype(gen.RegisterMock<Ret, ArgTupleT>(
+ std::declval<::absl::base_internal::FastTypeIdType>())
+ .gmock_Call(matcher, matchers...)) {
+ return gen
+ .RegisterMock<Ret, ArgTupleT>(
+ ::absl::base_internal::FastTypeId<Ret(DistrT, ArgTupleT)>())
+ .gmock_Call(matcher, matchers...);
}
};
diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h
deleted file mode 100644
index 23ecaf6c..00000000
--- a/absl/random/internal/mocking_bit_gen_base.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// 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.
-//
-#ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
-#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
-
-#include <string>
-#include <typeinfo>
-
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace random_internal {
-
-class MockingBitGenBase {
- template <typename>
- friend struct DistributionCaller;
- using generator_type = absl::BitGen;
-
- public:
- // URBG interface
- using result_type = generator_type::result_type;
- static constexpr result_type(min)() { return (generator_type::min)(); }
- static constexpr result_type(max)() { return (generator_type::max)(); }
- result_type operator()() { return gen_(); }
-
- virtual ~MockingBitGenBase() = default;
-
- protected:
- // CallImpl is the type-erased virtual dispatch.
- // The type of dist is always distribution<T>,
- // The type of result is always distribution<T>::result_type.
- virtual bool CallImpl(const std::type_info& distr_type, void* dist_args,
- void* result) = 0;
-
- template <typename DistrT, typename ArgTupleT>
- static const std::type_info& GetTypeId() {
- return typeid(std::pair<absl::decay_t<DistrT>, absl::decay_t<ArgTupleT>>);
- }
-
- // Call the generating distribution function.
- // Invoked by DistributionCaller<>::Call<DistT>.
- // DistT is the distribution type.
- template <typename DistrT, typename... Args>
- typename DistrT::result_type Call(Args&&... args) {
- using distr_result_type = typename DistrT::result_type;
- using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
-
- ArgTupleT arg_tuple(std::forward<Args>(args)...);
- auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
-
- distr_result_type result{};
- bool found_match =
- CallImpl(GetTypeId<DistrT, ArgTupleT>(), &arg_tuple, &result);
-
- if (!found_match) {
- result = dist(gen_);
- }
-
- return result;
- }
-
- private:
- generator_type gen_;
-}; // namespace random_internal
-
-} // namespace random_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
diff --git a/absl/random/mock_distributions.h b/absl/random/mock_distributions.h
index d36d5ba0..764ab370 100644
--- a/absl/random/mock_distributions.h
+++ b/absl/random/mock_distributions.h
@@ -27,6 +27,11 @@
// More information about the Googletest testing framework is available at
// https://github.com/google/googletest
//
+// 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.
+//
// Example:
//
// absl::MockingBitGen mock;
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h
index 3d8a979e..c3c7d44d 100644
--- a/absl/random/mocking_bit_gen.h
+++ b/absl/random/mocking_bit_gen.h
@@ -33,17 +33,16 @@
#include <memory>
#include <tuple>
#include <type_traits>
-#include <typeindex>
-#include <typeinfo>
#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/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,13 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-
template <typename, typename>
struct MockSingleOverload;
+template <typename>
+struct DistributionCaller;
} // namespace random_internal
+class BitGenRef;
// MockingBitGen
//
@@ -96,102 +97,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() override {
+ ~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:
- template <typename DistrT, typename... Args>
- using MockFnType =
- ::testing::MockFunction<typename DistrT::result_type(Args...)>;
+ 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 <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
//
- // 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.
+ // 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 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...>>())];
-
+ 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<DistrT, Args...>;
+ auto* mock_fn = new MockFnType;
mock.mock_fn = mock_fn;
- mock.match_impl = &MatchImpl<DistrT, Args...>;
+ mock.match_impl = &MatchImpl<ResultT, ArgTupleT>;
deleters_.emplace_back([mock_fn] { delete mock_fn; });
}
-
- return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn)
- ->gmock_Call(std::forward<Ms>(matchers)...);
+ return *static_cast<MockFnType*>(mock.mock_fn);
}
- 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));
+ // 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));
}
- // 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<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(std::type_index(key_type));
+ 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, dist_args, result);
+ mock_data->match_impl(mock_data->mock_fn, args_tuple, result);
return true;
}
- template <typename, typename>
- friend struct ::absl::random_internal::MockSingleOverload;
- friend struct ::absl::random_internal::DistributionCaller<
- absl::MockingBitGen>;
-};
+ absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
+ std::vector<std::function<void()>> deleters_;
+ absl::BitGen gen_;
-// -----------------------------------------------------------------------------
-// Implementation Details Only Below
-// -----------------------------------------------------------------------------
-
-namespace random_internal {
-
-template <>
-struct DistributionCaller<absl::MockingBitGen> {
- template <typename DistrT, typename... Args>
- static typename DistrT::result_type Call(absl::MockingBitGen* gen,
- Args&&... args) {
- return gen->template Call<DistrT>(std::forward<Args>(args)...);
- }
+ template <typename, typename>
+ friend struct ::absl::random_internal::MockSingleOverload; // for Register
+ template <typename>
+ friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock
+ friend class ::absl::BitGenRef; // for InvokeMock
};
-} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl