blob: 880d7dad92cabb71b5d937ff38e1f00c47fcd86d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "base/callback.h"
#include "media/learning/impl/test_random_number_generator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
class RandomNumberGeneratorTest : public testing::Test {
public:
RandomNumberGeneratorTest() : rng_(0) {}
void GenerateAndVerify(base::RepeatingCallback<int64_t()> generate,
int64_t lower_inclusive,
int64_t upper_inclusive) {
const size_t n = 10000;
std::map<int64_t, size_t> counts;
for (size_t i = 0; i < n; i++)
counts[generate.Run()]++;
// Verify that it's uniform over |lower_inclusive| and
// |upper_inclusive|, at least approximately.
size_t min_counts = counts[lower_inclusive];
size_t max_counts = min_counts;
size_t total_counts = min_counts;
for (int64_t i = lower_inclusive + 1; i <= upper_inclusive; i++) {
size_t c = counts[i];
if (c < min_counts)
min_counts = c;
if (c > max_counts)
max_counts = c;
total_counts += c;
}
// See if the min and the max are too far from the expected counts.
// Note that this will catch only egregious problems. Also note that
// random variation might actually exceed these limits fairly often.
// It's only because the test rng has no variation that we know it
// won't happen. However, there might be reasonable implementation
// changes that trip these tests (deterministically!); manual
// verification of the result is needed in those cases.
//
// These just catch things like "rng range is off by one", etc.
size_t expected_counts = n / (upper_inclusive - lower_inclusive + 1);
EXPECT_LT(max_counts, expected_counts * 1.05);
EXPECT_GT(max_counts, expected_counts * 0.95);
EXPECT_LT(min_counts, expected_counts * 1.05);
EXPECT_GT(min_counts, expected_counts * 0.95);
// Verify that the counts between the limits accounts for all of them.
// Otherwise, some rng values were out of range.
EXPECT_EQ(total_counts, n);
}
// We use TestRandomNumberGenerator, since we really want to test the base
// class method implementations with a predictable random source.
TestRandomNumberGenerator rng_;
};
TEST_F(RandomNumberGeneratorTest, ExclusiveUpTo) {
// Try Generate with something that's not a divisor of max_int, to try to
// catch any bias. I.e., an implementation like "rng % range" should fail
// this test.
//
// Unfortunately, it won't.
//
// With uint64_t random values, it's unlikely that we would ever notice such a
// problem. For example, a range of size three would just remove ~three from
// the upper range of the rng, and it's unlikely that we'd ever pick the three
// highest values anyway. If, instead, we make |range| really big, then we're
// not going to sample enough points to notice the deviation from uniform.
//
// However, we still look for issues like "off by one".
const uint64_t range = 5;
GenerateAndVerify(base::BindRepeating(
[](RandomNumberGenerator* rng, uint64_t range) {
return static_cast<int64_t>(rng->Generate(range));
},
&rng_, range),
0, range - 1);
}
TEST_F(RandomNumberGeneratorTest, DoublesStayInRange) {
const double limit = 1000.5;
int num_non_integer = 0;
for (int i = 0; i < 1000; i++) {
double v = rng_.GenerateDouble(limit);
EXPECT_GE(v, 0.);
EXPECT_LT(v, limit);
// Also count how many non-integers we get.
num_non_integer += (v != static_cast<int>(v));
}
// Expect a lot of non-integers.
EXPECT_GE(num_non_integer, 900);
}
} // namespace media