diff options
author | Justin Bassett <jbassett@google.com> | 2024-05-20 10:44:01 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-05-20 10:45:19 -0700 |
commit | 254b3a5326932026fd23923fd367619d2837f0ad (patch) | |
tree | ae7b15f8c6462ed407c89ef5144fdbf5a5a91e2b /absl/random/mock_distributions_test.cc | |
parent | 93ac3a4f9ee7792af399cebd873ee99ce15aed08 (diff) | |
download | abseil-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/mock_distributions_test.cc')
-rw-r--r-- | absl/random/mock_distributions_test.cc | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/absl/random/mock_distributions_test.cc b/absl/random/mock_distributions_test.cc index de23bafe..917799f0 100644 --- a/absl/random/mock_distributions_test.cc +++ b/absl/random/mock_distributions_test.cc @@ -14,7 +14,12 @@ #include "absl/random/mock_distributions.h" +#include <cmath> +#include <limits> + +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/random/distributions.h" #include "absl/random/mocking_bit_gen.h" #include "absl/random/random.h" @@ -69,4 +74,205 @@ TEST(MockDistributions, Examples) { EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040); } +TEST(MockUniform, OutOfBoundsIsAllowed) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 100)).WillOnce(Return(0)); + EXPECT_EQ(absl::Uniform<int>(gen, 1, 100), 0); +} + +TEST(ValidatedMockDistributions, UniformDoubleBoundaryCases) { + absl::random_internal::MockingBitGenImpl<true> gen; + + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 1.0, 10.0)) + .WillOnce(Return( + std::nextafter(10.0, -std::numeric_limits<double>::infinity()))); + EXPECT_EQ(absl::Uniform<double>(gen, 1.0, 10.0), + std::nextafter(10.0, -std::numeric_limits<double>::infinity())); + + EXPECT_CALL(absl::MockUniform<double>(), + Call(absl::IntervalOpen, gen, 1.0, 10.0)) + .WillOnce(Return( + std::nextafter(10.0, -std::numeric_limits<double>::infinity()))); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpen, gen, 1.0, 10.0), + std::nextafter(10.0, -std::numeric_limits<double>::infinity())); + + EXPECT_CALL(absl::MockUniform<double>(), + Call(absl::IntervalOpen, gen, 1.0, 10.0)) + .WillOnce( + Return(std::nextafter(1.0, std::numeric_limits<double>::infinity()))); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpen, gen, 1.0, 10.0), + std::nextafter(1.0, std::numeric_limits<double>::infinity())); +} + +TEST(ValidatedMockDistributions, UniformDoubleEmptyRangeCases) { + absl::random_internal::MockingBitGenImpl<true> gen; + + ON_CALL(absl::MockUniform<double>(), Call(absl::IntervalOpen, gen, 1.0, 1.0)) + .WillByDefault(Return(1.0)); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpen, gen, 1.0, 1.0), 1.0); + + ON_CALL(absl::MockUniform<double>(), + Call(absl::IntervalOpenClosed, gen, 1.0, 1.0)) + .WillByDefault(Return(1.0)); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpenClosed, gen, 1.0, 1.0), + 1.0); + + ON_CALL(absl::MockUniform<double>(), + Call(absl::IntervalClosedOpen, gen, 1.0, 1.0)) + .WillByDefault(Return(1.0)); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalClosedOpen, gen, 1.0, 1.0), + 1.0); +} + +TEST(ValidatedMockDistributions, UniformIntEmptyRangeCases) { + absl::random_internal::MockingBitGenImpl<true> gen; + + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalOpen, gen, 1, 1)) + .WillByDefault(Return(1)); + EXPECT_EQ(absl::Uniform<int>(absl::IntervalOpen, gen, 1, 1), 1); + + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalOpenClosed, gen, 1, 1)) + .WillByDefault(Return(1)); + EXPECT_EQ(absl::Uniform<int>(absl::IntervalOpenClosed, gen, 1, 1), 1); + + ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosedOpen, gen, 1, 1)) + .WillByDefault(Return(1)); + EXPECT_EQ(absl::Uniform<int>(absl::IntervalClosedOpen, gen, 1, 1), 1); +} + +TEST(ValidatedMockUniformDeathTest, Examples) { + absl::random_internal::MockingBitGenImpl<true> gen; + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 100)) + .WillOnce(Return(0)); + absl::Uniform<int>(gen, 1, 100); + }, + " 0 is not in \\[1, 100\\)"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 100)) + .WillOnce(Return(101)); + absl::Uniform<int>(gen, 1, 100); + }, + " 101 is not in \\[1, 100\\)"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 100)) + .WillOnce(Return(100)); + absl::Uniform<int>(gen, 1, 100); + }, + " 100 is not in \\[1, 100\\)"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpen, gen, 1, 100)) + .WillOnce(Return(1)); + absl::Uniform<int>(absl::IntervalOpen, gen, 1, 100); + }, + " 1 is not in \\(1, 100\\)"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpen, gen, 1, 100)) + .WillOnce(Return(101)); + absl::Uniform<int>(absl::IntervalOpen, gen, 1, 100); + }, + " 101 is not in \\(1, 100\\)"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpen, gen, 1, 100)) + .WillOnce(Return(100)); + absl::Uniform<int>(absl::IntervalOpen, gen, 1, 100); + }, + " 100 is not in \\(1, 100\\)"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpenClosed, gen, 1, 100)) + .WillOnce(Return(1)); + absl::Uniform<int>(absl::IntervalOpenClosed, gen, 1, 100); + }, + " 1 is not in \\(1, 100\\]"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpenClosed, gen, 1, 100)) + .WillOnce(Return(101)); + absl::Uniform<int>(absl::IntervalOpenClosed, gen, 1, 100); + }, + " 101 is not in \\(1, 100\\]"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpenClosed, gen, 1, 100)) + .WillOnce(Return(0)); + absl::Uniform<int>(absl::IntervalOpenClosed, gen, 1, 100); + }, + " 0 is not in \\(1, 100\\]"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalOpenClosed, gen, 1, 100)) + .WillOnce(Return(101)); + absl::Uniform<int>(absl::IntervalOpenClosed, gen, 1, 100); + }, + " 101 is not in \\(1, 100\\]"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, 1, 100)) + .WillOnce(Return(0)); + absl::Uniform<int>(absl::IntervalClosed, gen, 1, 100); + }, + " 0 is not in \\[1, 100\\]"); + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<int>(), + Call(absl::IntervalClosed, gen, 1, 100)) + .WillOnce(Return(101)); + absl::Uniform<int>(absl::IntervalClosed, gen, 1, 100); + }, + " 101 is not in \\[1, 100\\]"); +} + +TEST(ValidatedMockUniformDeathTest, DoubleBoundaryCases) { + absl::random_internal::MockingBitGenImpl<true> gen; + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 1.0, 10.0)) + .WillOnce(Return(10.0)); + EXPECT_EQ(absl::Uniform<double>(gen, 1.0, 10.0), 10.0); + }, + " 10 is not in \\[1, 10\\)"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<double>(), + Call(absl::IntervalOpen, gen, 1.0, 10.0)) + .WillOnce(Return(10.0)); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpen, gen, 1.0, 10.0), + 10.0); + }, + " 10 is not in \\(1, 10\\)"); + + EXPECT_DEATH_IF_SUPPORTED( + { + EXPECT_CALL(absl::MockUniform<double>(), + Call(absl::IntervalOpen, gen, 1.0, 10.0)) + .WillOnce(Return(1.0)); + EXPECT_EQ(absl::Uniform<double>(absl::IntervalOpen, gen, 1.0, 10.0), + 1.0); + }, + " 1 is not in \\(1, 10\\)"); +} + } // namespace |