aboutsummaryrefslogtreecommitdiff
path: root/absl/random/internal/mock_helpers.h
diff options
context:
space:
mode:
authorJustin Bassett <jbassett@google.com>2024-05-20 10:44:01 -0700
committerCopybara-Service <copybara-worker@google.com>2024-05-20 10:45:19 -0700
commit254b3a5326932026fd23923fd367619d2837f0ad (patch)
treeae7b15f8c6462ed407c89ef5144fdbf5a5a91e2b /absl/random/internal/mock_helpers.h
parent93ac3a4f9ee7792af399cebd873ee99ce15aed08 (diff)
downloadabseil-254b3a5326932026fd23923fd367619d2837f0ad.tar.gz
abseil-254b3a5326932026fd23923fd367619d2837f0ad.tar.bz2
abseil-254b3a5326932026fd23923fd367619d2837f0ad.zip
Add (unused) validation to absl::MockingBitGen
`absl::Uniform(tag, rng, a, b)` has some restrictions on the values it can produce in that it will always be in the range specified by `a` and `b`, but these restrictions can be violated by `absl::MockingBitGen`. This makes it easier than necessary to introduce a bug in tests using a mock RNG. We can fix this by making `MockingBitGen` emit a runtime error if the value produced is out of bounds. Immediately fixing all the internal buggy uses of `MockingBitGen` is currently infeasible, so the plan is this: 1. Add turned-off validation to `MockingBitGen` to avoid the costs of maintaining unsubmitted code. 2. Temporarily migrate the internal buggy use cases to keep the current behavior, to be fixed later. 3. Turn on validation for `MockingBitGen`. 4. Fix the internal buggy use cases over time. --- A few of the different categories of errors I found: - `Call(tag, rng, a, b) -> a or b`, for open/half-open intervals (i.e. incorrect boundary condition). This case happens quite a lot, e.g. by specifying `absl::Uniform<double>(rng, 0, 1)` to return `1.0`. - `Call(tag, rng, 0, 1) -> 42` (i.e. return an arbitrary value). These may be straightforward to fix by just returning an in-range value, or sometimes they are difficult to fix because other data structures depend on those values. PiperOrigin-RevId: 635503223 Change-Id: I9293ab78e79450e2b7b682dcb05149f238ecc550
Diffstat (limited to 'absl/random/internal/mock_helpers.h')
-rw-r--r--absl/random/internal/mock_helpers.h40
1 files changed, 33 insertions, 7 deletions
diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h
index a7a97bfc..19d05612 100644
--- a/absl/random/internal/mock_helpers.h
+++ b/absl/random/internal/mock_helpers.h
@@ -16,10 +16,9 @@
#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_
-#include <tuple>
-#include <type_traits>
#include <utility>
+#include "absl/base/config.h"
#include "absl/base/internal/fast_type_id.h"
#include "absl/types/optional.h"
@@ -27,6 +26,16 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
+// A no-op validator meeting the ValidatorT requirements for MockHelpers.
+//
+// Custom validators should follow a similar structure, passing the type to
+// MockHelpers::MockFor<KeyT>(m, CustomValidatorT()).
+struct NoOpValidator {
+ // Default validation: do nothing.
+ template <typename ResultT, typename... Args>
+ static void Validate(ResultT, Args&&...) {}
+};
+
// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and
// BitGenRef to enable the mocking capability for absl distribution functions.
//
@@ -109,22 +118,39 @@ class MockHelpers {
0, urbg, std::forward<Args>(args)...);
}
- // Acquire a mock for the KeyT (may or may not be a signature).
+ // Acquire a mock for the KeyT (may or may not be a signature), set up to use
+ // the ValidatorT to verify that the result is in the range of the RNG
+ // function.
//
// KeyT is used to generate a typeid-based lookup for the mock.
// KeyT is a signature of the form:
// result_type(discriminator_type, std::tuple<args...>)
// The mocked function signature will be composed from KeyT as:
// result_type(args...)
- template <typename KeyT, typename MockURBG>
- static auto MockFor(MockURBG& m)
+ // ValidatorT::Validate will be called after the result of the RNG. The
+ // signature is expected to be of the form:
+ // ValidatorT::Validate(result, args...)
+ template <typename KeyT, typename ValidatorT, typename MockURBG>
+ static auto MockFor(MockURBG& m, ValidatorT)
-> decltype(m.template RegisterMock<
typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type>(
- m, std::declval<IdType>())) {
+ m, std::declval<IdType>(), ValidatorT())) {
return m.template RegisterMock<typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type>(
- m, ::absl::base_internal::FastTypeId<KeyT>());
+ m, ::absl::base_internal::FastTypeId<KeyT>(), ValidatorT());
+ }
+
+ // Acquire a mock for the KeyT (may or may not be a signature).
+ //
+ // KeyT is used to generate a typeid-based lookup for the mock.
+ // KeyT is a signature of the form:
+ // result_type(discriminator_type, std::tuple<args...>)
+ // The mocked function signature will be composed from KeyT as:
+ // result_type(args...)
+ template <typename KeyT, typename MockURBG>
+ static decltype(auto) MockFor(MockURBG& m) {
+ return MockFor<KeyT>(m, NoOpValidator());
}
};