// 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 #include #include #include #include #include #include #include #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 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(), Call(bitgen, testing::_, testing::_)) // .WillByDefault([] (int low, int high) { // return (low + high) / 2; // }); // // EXPECT_EQ(absl::Uniform(gen, 0, 10), 5); // EXPECT_EQ(absl::Uniform(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 using MockFnType = ::testing::MockFunction; // MockingBitGen::Register // // Register is the main extension point for // extending the MockingBitGen framework. It provides a mechanism to install a // mock expectation for the distribution `distr_t` onto the MockingBitGen // context. // // The returned MockFunction<...> type can be used to setup additional // distribution parameters of the expectation. template decltype(std::declval>().gmock_Call( std::declval()...)) Register(Ms&&... matchers) { auto& mock = mocks_[std::type_index(GetTypeId>())]; if (!mock.mock_fn) { auto* mock_fn = new MockFnType; mock.mock_fn = mock_fn; mock.match_impl = &MatchImpl; deleters_.emplace_back([mock_fn] { delete mock_fn; }); } return static_cast*>(mock.mock_fn) ->gmock_Call(std::forward(matchers)...); } mutable std::vector> deleters_; using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, void* t_erased_result); struct MockData { void* mock_fn = nullptr; match_impl_fn match_impl = nullptr; }; mutable absl::flat_hash_map mocks_; template static void MatchImpl(void* mock_fn, void* dist_args, void* result) { using result_type = typename DistrT::result_type; *static_cast(result) = absl::apply( [mock_fn](Args... args) -> result_type { return (*static_cast*>(mock_fn)) .Call(std::move(args)...); }, *static_cast*>(dist_args)); } // 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(&it->second); mock_data->match_impl(mock_data->mock_fn, dist_args, result); return true; } template friend struct ::absl::random_internal::MockSingleOverload; friend struct ::absl::random_internal::DistributionCaller< absl::MockingBitGen>; }; // ----------------------------------------------------------------------------- // Implementation Details Only Below // ----------------------------------------------------------------------------- namespace random_internal { template <> struct DistributionCaller { template static typename DistrT::result_type Call(absl::MockingBitGen* gen, Args&&... args) { return gen->template Call(std::forward(args)...); } }; } // namespace random_internal } // namespace absl #endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_