diff options
author | Abseil Team <absl-team@google.com> | 2019-11-20 13:55:25 -0800 |
---|---|---|
committer | Andy Soffer <asoffer@google.com> | 2019-11-20 17:01:15 -0500 |
commit | bcaae6009c0833b73c6fa7bdd972921d8081a724 (patch) | |
tree | 129b38c7e160276c10af9efa482536b05dd514fc /absl | |
parent | 8ba96a8244bbe334d09542e92d566673a65c1f78 (diff) |
Export of internal Abseil changes
--
4503a1945d61540d3f7dd9ee2399297fbd423b82 by Andy Soffer <asoffer@google.com>:
Releasing absl::MockingBitGen and absl::BitGenRef
PiperOrigin-RevId: 281594380
--
2cb926fd9e39b5fa24c9541bfafcf18916cf7df2 by Abseil Team <absl-team@google.com>:
Explicitly export files needed by other packages
PiperOrigin-RevId: 281482304
GitOrigin-RevId: 4503a1945d61540d3f7dd9ee2399297fbd423b82
Change-Id: Id9f694eaaa23f42de817c8e8a28e6f86444f5637
Diffstat (limited to 'absl')
-rw-r--r-- | absl/random/BUILD.bazel | 92 | ||||
-rw-r--r-- | absl/random/CMakeLists.txt | 126 | ||||
-rw-r--r-- | absl/random/bit_gen_ref.h | 151 | ||||
-rw-r--r-- | absl/random/bit_gen_ref_test.cc | 99 | ||||
-rw-r--r-- | absl/random/internal/BUILD.bazel | 23 | ||||
-rw-r--r-- | absl/random/internal/mock_overload_set.h | 89 | ||||
-rw-r--r-- | absl/random/internal/mocking_bit_gen_base.h | 118 | ||||
-rw-r--r-- | absl/random/mock_distributions.h | 259 | ||||
-rw-r--r-- | absl/random/mock_distributions_test.cc | 72 | ||||
-rw-r--r-- | absl/random/mocking_bit_gen.cc | 28 | ||||
-rw-r--r-- | absl/random/mocking_bit_gen.h | 194 | ||||
-rw-r--r-- | absl/random/mocking_bit_gen_test.cc | 347 |
12 files changed, 1598 insertions, 0 deletions
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index c3520df8..2585b397 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -110,6 +110,58 @@ cc_library( ], ) +cc_library( + name = "bit_gen_ref", + hdrs = ["bit_gen_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_caller", + "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:mocking_bit_gen_base", + ], +) + +cc_library( + name = "mock_distributions", + testonly = 1, + hdrs = ["mock_distributions.h"], + deps = [ + ":distributions", + ":mocking_bit_gen", + "//absl/meta:type_traits", + "//absl/random/internal:mock_overload_set", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "mocking_bit_gen", + testonly = 1, + srcs = [ + "mocking_bit_gen.cc", + ], + hdrs = [ + "mocking_bit_gen.h", + ], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + "//absl/base:raw_logging_internal", + "//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", + "//absl/utility", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "bernoulli_distribution_test", size = "small", @@ -346,6 +398,46 @@ cc_test( ) cc_test( + name = "bit_gen_ref_test", + size = "small", + srcs = ["bit_gen_ref_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":random", + "//absl/random/internal:sequence_urbg", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mocking_bit_gen_test", + size = "small", + srcs = ["mocking_bit_gen_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mock_distributions_test", + size = "small", + srcs = ["mock_distributions_test.cc"], + deps = [ + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( name = "examples_test", size = "small", srcs = ["examples_test.cc"], diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 264a6f3c..13e96357 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -34,6 +34,132 @@ absl_cc_library( absl_cc_library( NAME + random_bit_gen_ref + HDRS + "bit_gen_ref.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::random_internal_distribution_caller + absl::random_internal_fast_uniform_bits + absl::random_internal_mocking_bit_gen_base + absl::type_traits +) + +absl_cc_test( + NAME + random_bit_gen_ref_test + SRCS + "bit_gen_ref_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_random + absl::random_internal_sequence_urbg + gmock + gtest_main +) + +# 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" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + TESTONLY +) + +absl_cc_library( + NAME + random_mocking_bit_gen + HDRS + "mock_distributions.h" + "mocking_bit_gen.h" + SRCS + "mocking_bit_gen.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flat_hash_map + 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::strings + absl::span + absl::type_traits + absl::utility + absl::variant + gmock + gtest + TESTONLY +) + +absl_cc_test( + NAME + random_mock_distributions_test + SRCS + "mock_distributions_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + +absl_cc_test( + NAME + random_mocking_bit_gen_test + SRCS + "mocking_bit_gen_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + +absl_cc_library( + NAME random_distributions SRCS "discrete_distribution.cc" diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h new file mode 100644 index 00000000..00e904f8 --- /dev/null +++ b/absl/random/bit_gen_ref.h @@ -0,0 +1,151 @@ +// +// 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. +// +// ----------------------------------------------------------------------------- +// File: bit_gen_ref.h +// ----------------------------------------------------------------------------- +// +// This header defines a bit generator "reference" class, for use in interfaces +// that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g. +// `std::mt19937`) bit generators. + +#ifndef ABSL_RANDOM_BIT_GEN_REF_H_ +#define ABSL_RANDOM_BIT_GEN_REF_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 { +namespace random_internal { + +template <typename URBG, typename = void, typename = void, typename = void> +struct is_urbg : std::false_type {}; + +template <typename URBG> +struct is_urbg< + URBG, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::min)())>::type>::value>, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype((URBG::max)())>::type>::value>, + absl::enable_if_t<std::is_same< + typename URBG::result_type, + typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> + : std::true_type {}; + +} // namespace random_internal + +// ----------------------------------------------------------------------------- +// absl::BitGenRef +// ----------------------------------------------------------------------------- +// +// `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic +// non-owning "reference" interface for use in place of any specific uniform +// random bit generator (URBG). This class may be used for both Abseil +// (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g +// `std::mt19937`, `std::minstd_rand`) bit generators. +// +// Like other reference classes, `absl::BitGenRef` does not own the +// underlying bit generator, and the underlying instance must outlive the +// `absl::BitGenRef`. +// +// `absl::BitGenRef` is particularly useful when used with an +// `absl::MockingBitGen` to test specific paths in functions which use random +// values. +// +// Example: +// void TakesBitGenRef(absl::BitGenRef gen) { +// int x = absl::Uniform<int>(gen, 0, 1000); +// } +// +class BitGenRef { + public: + using result_type = uint64_t; + + BitGenRef(const absl::BitGenRef&) = default; + BitGenRef(absl::BitGenRef&&) = default; + BitGenRef& operator=(const absl::BitGenRef&) = default; + BitGenRef& operator=(absl::BitGenRef&&) = default; + + template <typename URBG, + typename absl::enable_if_t< + (!std::is_same<URBG, BitGenRef>::value && + random_internal::is_urbg<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>) { + } + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + 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; } + + template <typename URBG> + static result_type ImplFn(uintptr_t ptr) { + // Ensure that the return values from operator() fill the entire + // range promised by result_type, min() and max(). + absl::random_internal::FastUniformBits<result_type> fast_uniform_bits; + return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); + } + + mocker_base_t* mocked_gen_ptr_; + uintptr_t t_erased_gen_ptr_; + impl_fn generate_impl_fn_; +}; + +namespace random_internal { + +template <> +struct DistributionCaller<absl::BitGenRef> { + template <typename DistrT, typename FormatT, 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, FormatT>( + std::forward<Args>(args)...); + } + } +}; + +} // namespace random_internal +} // namespace absl + +#endif // ABSL_RANDOM_BIT_GEN_REF_H_ diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc new file mode 100644 index 00000000..bc02ca5c --- /dev/null +++ b/absl/random/bit_gen_ref_test.cc @@ -0,0 +1,99 @@ +// +// 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. +// +#include "absl/random/bit_gen_ref.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" + +namespace absl { + +class ConstBitGen : public absl::random_internal::MockingBitGenBase { + bool CallImpl(const std::type_info&, void*, void* result) override { + *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> +class BitGenRefTest : public testing::Test {}; + +using BitGenTypes = + ::testing::Types<absl::BitGen, absl::InsecureBitGen, std::mt19937, + std::mt19937_64, std::minstd_rand>; +TYPED_TEST_SUITE(BitGenRefTest, BitGenTypes); + +TYPED_TEST(BitGenRefTest, BasicTest) { + TypeParam gen; + auto x = FnTest(gen); + EXPECT_NEAR(x, 4, 3); +} + +TYPED_TEST(BitGenRefTest, Copyable) { + TypeParam gen; + absl::BitGenRef gen_ref(gen); + FnTest(gen_ref); // Copy +} + +TEST(BitGenRefTest, PassThroughEquivalence) { + // sequence_urbg returns 64-bit results. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<uint64_t> output(12); + + { + absl::BitGenRef view(urbg); + for (auto& v : output) { + v = view(); + } + } + + std::vector<uint64_t> expected( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + EXPECT_THAT(output, testing::Eq(expected)); +} + +TEST(BitGenRefTest, MockingBitGenBaseOverrides) { + ConstBitGen const_gen; + EXPECT_EQ(FnTest(const_gen), 42); + + absl::BitGenRef gen_ref(const_gen); + EXPECT_EQ(FnTest(gen_ref), 42); // Copy +} +} // namespace +} // namespace absl diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index cc9bc013..5026e2b2 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -493,6 +493,29 @@ 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"], + visibility = [ + "//absl/random:__pkg__", + ], + deps = [ + "//absl/random:mocking_bit_gen", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "nonsecure_base_test", size = "small", diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h new file mode 100644 index 00000000..539313d7 --- /dev/null +++ b/absl/random/internal/mock_overload_set.h @@ -0,0 +1,89 @@ +// +// Copyright 2019 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_MOCK_OVERLOAD_SET_H_ +#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ + +#include <type_traits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { +namespace random_internal { + +template <typename DistrT, typename Fn> +struct MockSingleOverload; + +// MockSingleOverload +// +// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros. +// 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. +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."); + 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...); + } +}; + +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."); + auto gmock_Call( + const ::testing::Matcher<Arg>& arg, + 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...); + } +}; + +// MockOverloadSet +// +// MockOverloadSet takes a distribution and a collection of signatures and +// performs overload resolution amongst all the overloads. This makes +// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution +// correctly. +template <typename DistrT, typename... Signatures> +struct MockOverloadSet; + +template <typename DistrT, typename Sig> +struct MockOverloadSet<DistrT, Sig> : public MockSingleOverload<DistrT, Sig> { + using MockSingleOverload<DistrT, Sig>::gmock_Call; +}; + +template <typename DistrT, typename FirstSig, typename... Rest> +struct MockOverloadSet<DistrT, FirstSig, Rest...> + : public MockSingleOverload<DistrT, FirstSig>, + public MockOverloadSet<DistrT, Rest...> { + using MockSingleOverload<DistrT, FirstSig>::gmock_Call; + using MockOverloadSet<DistrT, Rest...>::gmock_Call; +}; + +} // namespace random_internal +} // namespace absl +#endif // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h new file mode 100644 index 00000000..aff2ba6d --- /dev/null +++ b/absl/random/internal/mocking_bit_gen_base.h @@ -0,0 +1,118 @@ +// +// 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 <atomic> +#include <deque> +#include <string> +#include <typeinfo> + +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +namespace absl { +namespace random_internal { + +// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks +// and remaining results into a description string. +template <typename DistrT, typename FormatT> +struct MockingBitGenExpectationFormatter { + std::string operator()(absl::string_view args) { + return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); + } +}; + +// MockingBitGenCallFormatter is invoked to format each distribution call +// into a description string for the mock log. +template <typename DistrT, typename FormatT> +struct MockingBitGenCallFormatter { + std::string operator()(const DistrT& dist, + const typename DistrT::result_type& result) { + return absl::StrCat( + FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", + FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); + } +}; + +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_(); } + + MockingBitGenBase() : gen_(), observed_call_log_() {} + virtual ~MockingBitGenBase() = default; + + protected: + const std::deque<std::string>& observed_call_log() { + return observed_call_log_; + } + + // 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, FormatT>. + // DistT is the distribution type. + // FormatT is the distribution formatter traits type. + template <typename DistrT, typename FormatT, 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_); + } + + // TODO(asoffer): Forwarding the args through means we no longer need to + // extract them from the from the distribution in formatter traits. We can + // just StrJoin them. + observed_call_log_.push_back( + MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result)); + return result; + } + + private: + generator_type gen_; + std::deque<std::string> observed_call_log_; +}; // namespace random_internal + +} // namespace random_internal +} // 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 new file mode 100644 index 00000000..1af98a24 --- /dev/null +++ b/absl/random/mock_distributions.h @@ -0,0 +1,259 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: mock_distributions.h +// ----------------------------------------------------------------------------- +// +// This file contains mock distribution functions for use alongside an +// `absl::MockingBitGen` object within the Googletest testing framework. Such +// mocks are useful to provide deterministic values as return values within +// (otherwise random) Abseil distribution functions. +// +// The return type of each function is a mock expectation object which +// is used to set the match result. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform<int>(), Call(mock, 1, 1000)) +// .WillRepeatedly(testing::ReturnRoundRobin({20, 40})); +// +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40); + +#ifndef ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ +#define ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ + +#include <limits> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/random/distributions.h" +#include "absl/random/internal/mock_overload_set.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { + +// ----------------------------------------------------------------------------- +// absl::MockUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Uniform. +// +// `absl::MockUniform` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(mock)) +// .WillOnce(Return(123456)); +// auto x = absl::Uniform<uint32_t>(mock); +// assert(x == 123456) +// +template <typename R> +using MockUniform = random_internal::MockOverloadSet< + random_internal::UniformDistributionWrapper<R>, + R(IntervalClosedOpenTag, MockingBitGen&, R, R), + R(IntervalClosedClosedTag, MockingBitGen&, R, R), + R(IntervalOpenOpenTag, MockingBitGen&, R, R), + R(IntervalOpenClosedTag, MockingBitGen&, R, R), R(MockingBitGen&, R, R), + R(MockingBitGen&)>; + +// ----------------------------------------------------------------------------- +// absl::MockBernoulli +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Bernoulli. +// +// `absl::MockBernoulli` is a class used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBernoulli(), Call(mock, testing::_)) +// .WillOnce(Return(false)); +// assert(absl::Bernoulli(mock, 0.5) == false); +// +using MockBernoulli = + random_internal::MockOverloadSet<absl::bernoulli_distribution, + bool(MockingBitGen&, double)>; + +// ----------------------------------------------------------------------------- +// absl::MockBeta +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Beta. +// +// `absl::MockBeta` is a class used in conjunction with Googletest's `ON_CALL()` +// and `EXPECT_CALL()` macros. To use it, default-construct an instance of it +// inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the same way one +// would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBeta(), Call(mock, 3.0, 2.0)) +// .WillOnce(Return(0.567)); +// auto x = absl::Beta<double>(mock, 3.0, 2.0); +// assert(x == 0.567); +// +template <typename RealType> +using MockBeta = + random_internal::MockOverloadSet<absl::beta_distribution<RealType>, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockExponential +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Exponential. +// +// `absl::MockExponential` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockExponential<double>(), Call(mock, 0.5)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Exponential<double>(mock, 0.5); +// assert(x == 12.3456789) +// +template <typename RealType> +using MockExponential = + random_internal::MockOverloadSet<absl::exponential_distribution<RealType>, + RealType(MockingBitGen&, RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockGaussian +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Gaussian. +// +// `absl::MockGaussian` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockGaussian<double>(), Call(mock, 16.3, 3.3)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Gaussian<double>(mock, 16.3, 3.3); +// assert(x == 12.3456789) +// +template <typename RealType> +using MockGaussian = + random_internal::MockOverloadSet<absl::gaussian_distribution<RealType>, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockLogUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::LogUniform. +// +// `absl::MockLogUniform` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockLogUniform<int>(), Call(mock, 10, 10000, 10)) +// .WillOnce(Return(1221)); +// auto x = absl::LogUniform<int>(mock, 10, 10000, 10); +// assert(x == 1221) +// +template <typename IntType> +using MockLogUniform = random_internal::MockOverloadSet< + absl::log_uniform_int_distribution<IntType>, + IntType(MockingBitGen&, IntType, IntType, IntType)>; + +// ----------------------------------------------------------------------------- +// absl::MockPoisson +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Poisson. +// +// `absl::MockPoisson` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockPoisson<int>(), Call(mock, 2.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Poisson<int>(mock, 2.0); +// assert(x == 1221) +// +template <typename IntType> +using MockPoisson = + random_internal::MockOverloadSet<absl::poisson_distribution<IntType>, + IntType(MockingBitGen&, double)>; + +// ----------------------------------------------------------------------------- +// absl::MockZipf +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Zipf. +// +// `absl::MockZipf` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockZipf<int>(), Call(mock, 1000000, 2.0, 1.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Zipf<int>(mock, 1000000, 2.0, 1.0); +// assert(x == 1221) +// +template <typename IntType> +using MockZipf = + random_internal::MockOverloadSet<absl::zipf_distribution<IntType>, + IntType(MockingBitGen&, IntType, double, + double)>; + +} // namespace absl + +#endif // ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ diff --git a/absl/random/mock_distributions_test.cc b/absl/random/mock_distributions_test.cc new file mode 100644 index 00000000..de23bafe --- /dev/null +++ b/absl/random/mock_distributions_test.cc @@ -0,0 +1,72 @@ +// 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. + +#include "absl/random/mock_distributions.h" + +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Return; + +TEST(MockDistributions, Examples) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Beta<double>(gen, 3.0, 2.0), 0.567); + EXPECT_CALL(absl::MockBeta<double>(), Call(gen, 3.0, 2.0)) + .WillOnce(Return(0.567)); + EXPECT_EQ(absl::Beta<double>(gen, 3.0, 2.0), 0.567); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); + EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(2040)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); +} + +} // namespace diff --git a/absl/random/mocking_bit_gen.cc b/absl/random/mocking_bit_gen.cc new file mode 100644 index 00000000..73144528 --- /dev/null +++ b/absl/random/mocking_bit_gen.cc @@ -0,0 +1,28 @@ +// +// 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. +// +#include "absl/random/mocking_bit_gen.h" + +#include <string> + +namespace absl { +MockingBitGen::~MockingBitGen() { + + for (const auto& del : deleters_) { + del(); + } +} + +} // namespace absl diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h new file mode 100644 index 00000000..d1b524a9 --- /dev/null +++ b/absl/random/mocking_bit_gen.h @@ -0,0 +1,194 @@ +// 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 { + +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 +} // namespace absl + +#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_ diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc new file mode 100644 index 00000000..dcf74fd6 --- /dev/null +++ b/absl/random/mocking_bit_gen_test.cc @@ -0,0 +1,347 @@ +// +// 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. +// +#include "absl/random/mocking_bit_gen.h" + +#include <numeric> +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest-spi.h" +#include "gtest/gtest.h" +#include "absl/random/bit_gen_ref.h" +#include "absl/random/mock_distributions.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Ne; +using ::testing::Return; + +TEST(BasicMocking, AllDistributionsAreOverridable) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); + EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(2040)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); +} + +TEST(BasicMocking, OnDistribution) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20); + ON_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .WillByDefault(Return(20)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + ON_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0)) + .WillByDefault(Return(5.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42); + ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillByDefault(Return(42)); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500); + ON_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillByDefault(Return(500)); + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + ON_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillByDefault(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + ON_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0)) + .WillByDefault(Return(0.001)); + EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); + ON_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2)) + .WillByDefault(Return(2040)); + EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); +} + +TEST(BasicMocking, GMockMatchers) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221); +} + +TEST(BasicMocking, OverridesWithMultipleGMockExpectations) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 10000)) + .WillOnce(Return(20)) + .WillOnce(Return(40)) + .WillOnce(Return(60)); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 20); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 40); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 60); +} + +TEST(BasicMocking, DefaultArgument) { + absl::MockingBitGen gen; + + ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillByDefault(Return(200)); + + EXPECT_EQ(absl::Exponential<double>(gen), 200); + EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 200); +} + +TEST(BasicMocking, MultipleGenerators) { + auto get_value = [](absl::BitGenRef gen_ref) { + return absl::Uniform(gen_ref, 1, 1000000); + }; + absl::MockingBitGen unmocked_generator; + absl::MockingBitGen mocked_with_3; + absl::MockingBitGen mocked_with_11; + + EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_3, 1, 1000000)) + .WillOnce(Return(3)) + .WillRepeatedly(Return(17)); + EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_11, 1, 1000000)) + .WillOnce(Return(11)) + .WillRepeatedly(Return(17)); + + // Ensure that unmocked generator generates neither value. + int unmocked_value = get_value(unmocked_generator); + EXPECT_NE(unmocked_value, 3); + EXPECT_NE(unmocked_value, 11); + // Mocked generators should generate their mocked values. + EXPECT_EQ(get_value(mocked_with_3), 3); + EXPECT_EQ(get_value(mocked_with_11), 11); + // Ensure that the mocks have expired. + EXPECT_NE(get_value(mocked_with_3), 3); + EXPECT_NE(get_value(mocked_with_11), 11); +} + +TEST(BasicMocking, MocksNotTrigeredForIncorrectTypes) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42)); + + EXPECT_NE(absl::Uniform<uint16_t>(gen), 42); // Not mocked + EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42); // Mock triggered +} + +TEST(BasicMocking, FailsOnUnsatisfiedMocks) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0)) + .WillOnce(Return(3.0)); + // Does not call absl::Exponential(). + }(), + "unsatisfied and active"); +} + +TEST(OnUniform, RespectsUniformIntervalSemantics) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, 1, 1000000)) + .WillOnce(Return(301)); + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 301); // Not mocked + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 1, 1000000), 301); +} + +TEST(OnUniform, RespectsNoArgUnsignedShorthand) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42)); + EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42); +} + +TEST(RepeatedlyModifier, ForceSnakeEyesForManyDice) { + auto roll_some_dice = [](absl::BitGenRef gen_ref) { + std::vector<int> results(16); + for (auto& r : results) { + r = absl::Uniform(absl::IntervalClosed, gen_ref, 1, 6); + } + return results; + }; + std::vector<int> results; + absl::MockingBitGen gen; + + // Without any mocked calls, not all dice roll a "6". + results = roll_some_dice(gen); + EXPECT_LT(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); + + // Verify that we can force all "6"-rolls, with mocking. + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, 1, 6)) + .WillByDefault(Return(6)); + results = roll_some_dice(gen); + EXPECT_EQ(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); +} + +TEST(WillOnce, DistinctCounters) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(0)); + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1000001, 2000000)) + .Times(3) + .WillRepeatedly(Return(1)); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); +} + +TEST(TimesModifier, ModifierSaturatesAndExpires) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(15)) + .RetiresOnSaturation(); + + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + // Times(3) has expired - Should get a different value now. + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 15); + }(), + ""); +} + +TEST(TimesModifier, Times0) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.0)).Times(0); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).Times(0); +} + +TEST(AnythingMatcher, MatchesAnyArgument) { + using testing::_; + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, _, 1000)) + .WillByDefault(Return(11)); + ON_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, _, Ne(1000))) + .WillByDefault(Return(99)); + + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000000), 99); + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000), 11); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(gen, 1, _)) + .WillByDefault(Return(25)); + ON_CALL(absl::MockUniform<int>(), Call(gen, Ne(1), _)) + .WillByDefault(Return(99)); + EXPECT_EQ(absl::Uniform(gen, 3, 1000000), 99); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 25); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform<int>(), Call(gen, _, _)) + .WillByDefault(Return(145)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145); + } +} + +TEST(AnythingMatcher, WithWillByDefault) { + using testing::_; + absl::MockingBitGen gen; + std::vector<int> values = {11, 22, 33, 44, 55, 66, 77, 88, 99, 1010}; + + ON_CALL(absl::MockUniform<size_t>(), Call(gen, 0, _)) + .WillByDefault(Return(0)); + for (int i = 0; i < 100; i++) { + auto& elem = values[absl::Uniform(gen, 0u, values.size())]; + EXPECT_EQ(elem, 11); + } +} + +TEST(BasicMocking, WillByDefaultWithArgs) { + using testing::_; + + absl::MockingBitGen gen; + ON_CALL(absl::MockPoisson<int>(), Call(gen, _)) + .WillByDefault( + [](double lambda) { return static_cast<int>(lambda * 10); }); + EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17); + EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0); +} + +TEST(MockingBitGen, InSequenceSucceedsInOrder) { + absl::MockingBitGen gen; + + testing::InSequence seq; + + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(3)); + EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 2.0)).WillOnce(Return(4)); + + EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 3); + EXPECT_EQ(absl::Poisson<int>(gen, 2.0), 4); +} + +} // namespace |