blob: 70db5c5896c3e3905d19802055d5b3032e735e74 [file] [log] [blame]
[email protected]20f999b52012-08-24 22:32:591// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]50ae9f12013-08-29 18:03:225#include "components/variations/entropy_provider.h"
[email protected]c277e2b2013-08-02 15:41:086
avi5dd91f82015-12-25 22:30:467#include <stddef.h>
8#include <stdint.h>
9
[email protected]20f999b52012-08-24 22:32:5910#include <cmath>
11#include <limits>
Jinho Bangc3bcb5c2018-01-15 16:13:0012#include <memory>
[email protected]20f999b52012-08-24 22:32:5913#include <numeric>
14
[email protected]20f999b52012-08-24 22:32:5915#include "base/guid.h"
avi5dd91f82015-12-25 22:30:4616#include "base/macros.h"
[email protected]20f999b52012-08-24 22:32:5917#include "base/rand_util.h"
Byoungkown1bb50222018-09-11 01:14:4118#include "base/stl_util.h"
[email protected]3ea1b182013-02-08 22:38:4119#include "base/strings/string_number_conversions.h"
Alexei Svitkine9de32cb2018-02-06 20:21:2120#include "components/variations/hashing.h"
[email protected]20f999b52012-08-24 22:32:5921#include "testing/gtest/include/gtest/gtest.h"
22
Alexei Svitkine9de32cb2018-02-06 20:21:2123namespace variations {
[email protected]20f999b52012-08-24 22:32:5924
25namespace {
26
27// Size of the low entropy source to use for the permuted entropy provider
28// in tests.
[email protected]9556a892013-06-21 16:53:2029const size_t kMaxLowEntropySize = 8000;
[email protected]20f999b52012-08-24 22:32:5930
31// Field trial names used in unit tests.
[email protected]c277e2b2013-08-02 15:41:0832const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
[email protected]20f999b52012-08-24 22:32:5933 "NewTabButton" };
34
35// Computes the Chi-Square statistic for |values| assuming they follow a uniform
36// distribution, where each entry has expected value |expected_value|.
37//
38// The Chi-Square statistic is defined as Sum((O-E)^2/E) where O is the observed
39// value and E is the expected value.
40double ComputeChiSquare(const std::vector<int>& values,
41 double expected_value) {
42 double sum = 0;
43 for (size_t i = 0; i < values.size(); ++i) {
44 const double delta = values[i] - expected_value;
45 sum += (delta * delta) / expected_value;
46 }
47 return sum;
48}
49
50// Computes SHA1-based entropy for the given |trial_name| based on
51// |entropy_source|
52double GenerateSHA1Entropy(const std::string& entropy_source,
53 const std::string& trial_name) {
54 SHA1EntropyProvider sha1_provider(entropy_source);
[email protected]6fded222013-04-11 20:59:5055 return sha1_provider.GetEntropyForTrial(trial_name, 0);
[email protected]20f999b52012-08-24 22:32:5956}
57
58// Generates permutation-based entropy for the given |trial_name| based on
59// |entropy_source| which must be in the range [0, entropy_max).
avi5dd91f82015-12-25 22:30:4660double GeneratePermutedEntropy(uint16_t entropy_source,
[email protected]20f999b52012-08-24 22:32:5961 size_t entropy_max,
62 const std::string& trial_name) {
63 PermutedEntropyProvider permuted_provider(entropy_source, entropy_max);
[email protected]6fded222013-04-11 20:59:5064 return permuted_provider.GetEntropyForTrial(trial_name, 0);
[email protected]20f999b52012-08-24 22:32:5965}
66
Steven Holtec59ddd9f2018-09-05 00:02:2767// Make a vector of consecutive integers for shuffling.
68std::vector<uint16_t> MakeRange(size_t vector_size) {
69 std::vector<uint16_t> range(vector_size);
70 for (size_t i = 0; i < vector_size; ++i)
71 range[i] = i;
72 return range;
73}
74
[email protected]20f999b52012-08-24 22:32:5975// Helper interface for testing used to generate entropy values for a given
76// field trial. Unlike EntropyProvider, which keeps the low/high entropy source
77// value constant and generates entropy for different trial names, instances
78// of TrialEntropyGenerator keep the trial name constant and generate low/high
79// entropy source values internally to produce each output entropy value.
80class TrialEntropyGenerator {
81 public:
82 virtual ~TrialEntropyGenerator() {}
83 virtual double GenerateEntropyValue() const = 0;
84};
85
86// An TrialEntropyGenerator that uses the SHA1EntropyProvider with the high
87// entropy source (random GUID with 128 bits of entropy + 13 additional bits of
88// entropy corresponding to a low entropy source).
89class SHA1EntropyGenerator : public TrialEntropyGenerator {
90 public:
91 explicit SHA1EntropyGenerator(const std::string& trial_name)
92 : trial_name_(trial_name) {
93 }
94
dcheng00ea022b2014-10-21 11:24:5695 ~SHA1EntropyGenerator() override {}
[email protected]20f999b52012-08-24 22:32:5996
dcheng00ea022b2014-10-21 11:24:5697 double GenerateEntropyValue() const override {
[email protected]20f999b52012-08-24 22:32:5998 // Use a random GUID + 13 additional bits of entropy to match how the
99 // SHA1EntropyProvider is used in metrics_service.cc.
100 const int low_entropy_source =
avi5dd91f82015-12-25 22:30:46101 static_cast<uint16_t>(base::RandInt(0, kMaxLowEntropySize - 1));
[email protected]20f999b52012-08-24 22:32:59102 const std::string high_entropy_source =
103 base::GenerateGUID() + base::IntToString(low_entropy_source);
104 return GenerateSHA1Entropy(high_entropy_source, trial_name_);
105 }
106
107 private:
[email protected]c277e2b2013-08-02 15:41:08108 std::string trial_name_;
[email protected]20f999b52012-08-24 22:32:59109
110 DISALLOW_COPY_AND_ASSIGN(SHA1EntropyGenerator);
111};
112
113// An TrialEntropyGenerator that uses the permuted entropy provider algorithm,
114// using 13-bit low entropy source values.
115class PermutedEntropyGenerator : public TrialEntropyGenerator {
116 public:
117 explicit PermutedEntropyGenerator(const std::string& trial_name)
118 : mapping_(kMaxLowEntropySize) {
119 // Note: Given a trial name, the computed mapping will be the same.
120 // As a performance optimization, pre-compute the mapping once per trial
121 // name and index into it for each entropy value.
avi5dd91f82015-12-25 22:30:46122 const uint32_t randomization_seed = HashName(trial_name);
[email protected]6fded222013-04-11 20:59:50123 internal::PermuteMappingUsingRandomizationSeed(randomization_seed,
124 &mapping_);
[email protected]20f999b52012-08-24 22:32:59125 }
126
dcheng00ea022b2014-10-21 11:24:56127 ~PermutedEntropyGenerator() override {}
[email protected]20f999b52012-08-24 22:32:59128
dcheng00ea022b2014-10-21 11:24:56129 double GenerateEntropyValue() const override {
[email protected]20f999b52012-08-24 22:32:59130 const int low_entropy_source =
avi5dd91f82015-12-25 22:30:46131 static_cast<uint16_t>(base::RandInt(0, kMaxLowEntropySize - 1));
[email protected]20f999b52012-08-24 22:32:59132 return mapping_[low_entropy_source] /
133 static_cast<double>(kMaxLowEntropySize);
134 }
135
136 private:
avi5dd91f82015-12-25 22:30:46137 std::vector<uint16_t> mapping_;
[email protected]20f999b52012-08-24 22:32:59138
139 DISALLOW_COPY_AND_ASSIGN(PermutedEntropyGenerator);
140};
141
142// Tests uniformity of a given |entropy_generator| using the Chi-Square Goodness
143// of Fit Test.
144void PerformEntropyUniformityTest(
145 const std::string& trial_name,
146 const TrialEntropyGenerator& entropy_generator) {
147 // Number of buckets in the simulated field trials.
148 const size_t kBucketCount = 20;
149 // Max number of iterations to perform before giving up and failing.
150 const size_t kMaxIterationCount = 100000;
151 // The number of iterations to perform before each time the statistical
152 // significance of the results is checked.
153 const size_t kCheckIterationCount = 10000;
154 // This is the Chi-Square threshold from the Chi-Square statistic table for
155 // 19 degrees of freedom (based on |kBucketCount|) with a 99.9% confidence
156 // level. See: http://www.medcalc.org/manual/chi-square-table.php
157 const double kChiSquareThreshold = 43.82;
158
159 std::vector<int> distribution(kBucketCount);
160
161 for (size_t i = 1; i <= kMaxIterationCount; ++i) {
162 const double entropy_value = entropy_generator.GenerateEntropyValue();
163 const size_t bucket = static_cast<size_t>(kBucketCount * entropy_value);
164 ASSERT_LT(bucket, kBucketCount);
165 distribution[bucket] += 1;
166
167 // After |kCheckIterationCount| iterations, compute the Chi-Square
168 // statistic of the distribution. If the resulting statistic is greater
169 // than |kChiSquareThreshold|, we can conclude with 99.9% confidence
170 // that the observed samples do not follow a uniform distribution.
171 //
172 // However, since 99.9% would still result in a false negative every
173 // 1000 runs of the test, do not treat it as a failure (else the test
174 // will be flaky). Instead, perform additional iterations to determine
175 // if the distribution will converge, up to |kMaxIterationCount|.
176 if ((i % kCheckIterationCount) == 0) {
177 const double expected_value_per_bucket =
178 static_cast<double>(i) / kBucketCount;
179 const double chi_square =
180 ComputeChiSquare(distribution, expected_value_per_bucket);
181 if (chi_square < kChiSquareThreshold)
182 break;
183
184 // If |i == kMaxIterationCount|, the Chi-Square statistic did not
185 // converge after |kMaxIterationCount|.
186 EXPECT_NE(i, kMaxIterationCount) << "Failed for trial " <<
187 trial_name << " with chi_square = " << chi_square <<
188 " after " << kMaxIterationCount << " iterations.";
189 }
190 }
191}
192
193} // namespace
194
[email protected]c277e2b2013-08-02 15:41:08195TEST(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
[email protected]20f999b52012-08-24 22:32:59196 // Simply asserts that two trials using one-time randomization
197 // that have different names, normally generate different results.
198 //
199 // Note that depending on the one-time random initialization, they
200 // _might_ actually give the same result, but we know that given
201 // the particular client_id we use for unit tests they won't.
robliao79393ffb2016-09-21 18:45:29202 base::FieldTrialList field_trial_list(
Jinho Bangc3bcb5c2018-01-15 16:13:00203 std::make_unique<SHA1EntropyProvider>("client_id"));
[email protected]ebcf69f02013-07-30 15:11:29204 const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
[email protected]20f999b52012-08-24 22:32:59205 scoped_refptr<base::FieldTrial> trials[] = {
[email protected]ebcf69f02013-07-30 15:11:29206 base::FieldTrialList::FactoryGetFieldTrial(
207 "one", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24208 base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29209 base::FieldTrialList::FactoryGetFieldTrial(
210 "two", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24211 base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29212 };
[email protected]20f999b52012-08-24 22:32:59213
Byoungkown1bb50222018-09-11 01:14:41214 for (size_t i = 0; i < base::size(trials); ++i) {
[email protected]20f999b52012-08-24 22:32:59215 for (int j = 0; j < 100; ++j)
[email protected]007b3f82013-04-09 08:46:45216 trials[i]->AppendGroup(std::string(), 1);
[email protected]20f999b52012-08-24 22:32:59217 }
218
219 // The trials are most likely to give different results since they have
220 // different names.
221 EXPECT_NE(trials[0]->group(), trials[1]->group());
222 EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
223}
224
[email protected]c277e2b2013-08-02 15:41:08225TEST(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
[email protected]20f999b52012-08-24 22:32:59226 // Simply asserts that two trials using one-time randomization
227 // that have different names, normally generate different results.
228 //
229 // Note that depending on the one-time random initialization, they
230 // _might_ actually give the same result, but we know that given
231 // the particular client_id we use for unit tests they won't.
232 base::FieldTrialList field_trial_list(
Jinho Bangc3bcb5c2018-01-15 16:13:00233 std::make_unique<PermutedEntropyProvider>(1234, kMaxLowEntropySize));
[email protected]ebcf69f02013-07-30 15:11:29234 const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
[email protected]20f999b52012-08-24 22:32:59235 scoped_refptr<base::FieldTrial> trials[] = {
[email protected]ebcf69f02013-07-30 15:11:29236 base::FieldTrialList::FactoryGetFieldTrial(
237 "one", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24238 base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29239 base::FieldTrialList::FactoryGetFieldTrial(
240 "two", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24241 base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29242 };
[email protected]20f999b52012-08-24 22:32:59243
Byoungkown1bb50222018-09-11 01:14:41244 for (size_t i = 0; i < base::size(trials); ++i) {
[email protected]20f999b52012-08-24 22:32:59245 for (int j = 0; j < 100; ++j)
[email protected]007b3f82013-04-09 08:46:45246 trials[i]->AppendGroup(std::string(), 1);
[email protected]20f999b52012-08-24 22:32:59247 }
248
249 // The trials are most likely to give different results since they have
250 // different names.
251 EXPECT_NE(trials[0]->group(), trials[1]->group());
252 EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
253}
254
[email protected]c277e2b2013-08-02 15:41:08255TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
[email protected]6fded222013-04-11 20:59:50256 // Ensures that two trials with different names but the same custom seed used
257 // for one time randomization produce the same group assignments.
258 base::FieldTrialList field_trial_list(
Jinho Bangc3bcb5c2018-01-15 16:13:00259 std::make_unique<PermutedEntropyProvider>(1234, kMaxLowEntropySize));
[email protected]ebcf69f02013-07-30 15:11:29260 const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
avi5dd91f82015-12-25 22:30:46261 const uint32_t kCustomSeed = 9001;
[email protected]ebcf69f02013-07-30 15:11:29262 scoped_refptr<base::FieldTrial> trials[] = {
263 base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
264 "one", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24265 base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, nullptr, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29266 base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
267 "two", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24268 base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, nullptr, nullptr),
[email protected]ebcf69f02013-07-30 15:11:29269 };
[email protected]6fded222013-04-11 20:59:50270
Byoungkown1bb50222018-09-11 01:14:41271 for (size_t i = 0; i < base::size(trials); ++i) {
[email protected]6fded222013-04-11 20:59:50272 for (int j = 0; j < 100; ++j)
273 trials[i]->AppendGroup(std::string(), 1);
274 }
275
276 // Normally, these trials should produce different groups, but if the same
277 // custom seed is used, they should produce the same group assignment.
278 EXPECT_EQ(trials[0]->group(), trials[1]->group());
279 EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
280}
281
jwdc6e07e22016-11-21 16:36:54282TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedSHA1) {
283 // Ensures that two trials with different names but the same custom seed used
284 // for one time randomization produce the same group assignments.
285 base::FieldTrialList field_trial_list(
Jinho Bangc3bcb5c2018-01-15 16:13:00286 std::make_unique<SHA1EntropyProvider>("client_id"));
jwdc6e07e22016-11-21 16:36:54287 const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
288 const uint32_t kCustomSeed = 9001;
289 scoped_refptr<base::FieldTrial> trials[] = {
290 base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
291 "one", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24292 base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, nullptr, nullptr),
jwdc6e07e22016-11-21 16:36:54293 base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
294 "two", 100, "default", kNoExpirationYear, 1, 1,
Ivan Kotenkov75b1c3a2017-10-24 14:47:24295 base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, nullptr, nullptr),
jwdc6e07e22016-11-21 16:36:54296 };
297
Byoungkown1bb50222018-09-11 01:14:41298 for (size_t i = 0; i < base::size(trials); ++i) {
jwdc6e07e22016-11-21 16:36:54299 for (int j = 0; j < 100; ++j)
300 trials[i]->AppendGroup(std::string(), 1);
301 }
302
303 // Normally, these trials should produce different groups, but if the same
304 // custom seed is used, they should produce the same group assignment.
305 EXPECT_EQ(trials[0]->group(), trials[1]->group());
306 EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
307}
308
[email protected]c277e2b2013-08-02 15:41:08309TEST(EntropyProviderTest, SHA1Entropy) {
[email protected]20f999b52012-08-24 22:32:59310 const double results[] = { GenerateSHA1Entropy("hi", "1"),
311 GenerateSHA1Entropy("there", "1") };
312
313 EXPECT_NE(results[0], results[1]);
Byoungkown1bb50222018-09-11 01:14:41314 for (size_t i = 0; i < base::size(results); ++i) {
[email protected]20f999b52012-08-24 22:32:59315 EXPECT_LE(0.0, results[i]);
316 EXPECT_GT(1.0, results[i]);
317 }
318
319 EXPECT_EQ(GenerateSHA1Entropy("yo", "1"),
320 GenerateSHA1Entropy("yo", "1"));
321 EXPECT_NE(GenerateSHA1Entropy("yo", "something"),
322 GenerateSHA1Entropy("yo", "else"));
323}
324
[email protected]c277e2b2013-08-02 15:41:08325TEST(EntropyProviderTest, PermutedEntropy) {
[email protected]20f999b52012-08-24 22:32:59326 const double results[] = {
327 GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
328 GeneratePermutedEntropy(4321, kMaxLowEntropySize, "1") };
329
330 EXPECT_NE(results[0], results[1]);
Byoungkown1bb50222018-09-11 01:14:41331 for (size_t i = 0; i < base::size(results); ++i) {
[email protected]20f999b52012-08-24 22:32:59332 EXPECT_LE(0.0, results[i]);
333 EXPECT_GT(1.0, results[i]);
334 }
335
336 EXPECT_EQ(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
337 GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"));
338 EXPECT_NE(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "something"),
339 GeneratePermutedEntropy(1234, kMaxLowEntropySize, "else"));
340}
341
[email protected]c277e2b2013-08-02 15:41:08342TEST(EntropyProviderTest, PermutedEntropyProviderResults) {
[email protected]20f999b52012-08-24 22:32:59343 // Verifies that PermutedEntropyProvider produces expected results. This
344 // ensures that the results are the same between platforms and ensures that
345 // changes to the implementation do not regress this accidentally.
346
347 EXPECT_DOUBLE_EQ(2194 / static_cast<double>(kMaxLowEntropySize),
348 GeneratePermutedEntropy(1234, kMaxLowEntropySize, "XYZ"));
349 EXPECT_DOUBLE_EQ(5676 / static_cast<double>(kMaxLowEntropySize),
350 GeneratePermutedEntropy(1, kMaxLowEntropySize, "Test"));
351 EXPECT_DOUBLE_EQ(1151 / static_cast<double>(kMaxLowEntropySize),
352 GeneratePermutedEntropy(5000, kMaxLowEntropySize, "Foo"));
353}
354
[email protected]c277e2b2013-08-02 15:41:08355TEST(EntropyProviderTest, SHA1EntropyIsUniform) {
Byoungkown1bb50222018-09-11 01:14:41356 for (size_t i = 0; i < base::size(kTestTrialNames); ++i) {
[email protected]20f999b52012-08-24 22:32:59357 SHA1EntropyGenerator entropy_generator(kTestTrialNames[i]);
358 PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
359 }
360}
361
[email protected]c277e2b2013-08-02 15:41:08362TEST(EntropyProviderTest, PermutedEntropyIsUniform) {
Byoungkown1bb50222018-09-11 01:14:41363 for (size_t i = 0; i < base::size(kTestTrialNames); ++i) {
[email protected]20f999b52012-08-24 22:32:59364 PermutedEntropyGenerator entropy_generator(kTestTrialNames[i]);
365 PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
366 }
367}
368
Steven Holtec59ddd9f2018-09-05 00:02:27369TEST(EntropyProviderTest, PermutedEntropyConsistency) {
370 std::vector<uint16_t> to_shuffle = MakeRange(10);
371 std::vector<uint16_t> expected = {7, 6, 8, 3, 2, 0, 1, 4, 9, 5};
372 internal::PermuteMappingUsingRandomizationSeed(5, &to_shuffle);
373 EXPECT_EQ(expected, to_shuffle);
374}
375
376TEST(EntropyProviderTest, PermutedEntropyConsistencyWithReroll) {
377 // Test that the permuted entropy provider is consistent with previously
378 // shipped versions, with a case that requires rerolls.
379 std::vector<uint16_t> to_shuffle = MakeRange(1000);
380 std::vector<uint16_t> expected = {776, 561, 72, 966, 500};
381 internal::PermuteMappingUsingRandomizationSeed(10694, &to_shuffle);
382 std::vector<uint16_t> slice(to_shuffle.begin(), to_shuffle.begin() + 5);
383 EXPECT_EQ(expected, slice);
384}
385
[email protected]c277e2b2013-08-02 15:41:08386TEST(EntropyProviderTest, SeededRandGeneratorIsUniform) {
[email protected]20f999b52012-08-24 22:32:59387 // Verifies that SeededRandGenerator has a uniform distribution.
388 //
389 // Mirrors RandUtilTest.RandGeneratorIsUniform in base/rand_util_unittest.cc.
390
avi5dd91f82015-12-25 22:30:46391 const uint32_t kTopOfRange =
392 (std::numeric_limits<uint32_t>::max() / 4ULL) * 3ULL;
393 const uint32_t kExpectedAverage = kTopOfRange / 2ULL;
394 const uint32_t kAllowedVariance = kExpectedAverage / 50ULL; // +/- 2%
[email protected]20f999b52012-08-24 22:32:59395 const int kMinAttempts = 1000;
396 const int kMaxAttempts = 1000000;
397
Byoungkown1bb50222018-09-11 01:14:41398 for (size_t i = 0; i < base::size(kTestTrialNames); ++i) {
avi5dd91f82015-12-25 22:30:46399 const uint32_t seed = HashName(kTestTrialNames[i]);
[email protected]20f999b52012-08-24 22:32:59400 internal::SeededRandGenerator rand_generator(seed);
401
402 double cumulative_average = 0.0;
403 int count = 0;
404 while (count < kMaxAttempts) {
avi5dd91f82015-12-25 22:30:46405 uint32_t value = rand_generator(kTopOfRange);
[email protected]20f999b52012-08-24 22:32:59406 cumulative_average = (count * cumulative_average + value) / (count + 1);
407
408 // Don't quit too quickly for things to start converging, or we may have
409 // a false positive.
410 if (count > kMinAttempts &&
411 kExpectedAverage - kAllowedVariance < cumulative_average &&
412 cumulative_average < kExpectedAverage + kAllowedVariance) {
413 break;
414 }
415
416 ++count;
417 }
418
419 ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
420 kExpectedAverage << ", average ended at " << cumulative_average <<
421 ", for trial " << kTestTrialNames[i];
422 }
423}
424
Alexei Svitkine9de32cb2018-02-06 20:21:21425} // namespace variations