blob: abc2f7b89a67276c59709ddb4ccbfc473675438b [file] [log] [blame]
// Copyright 2019 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 <cstring>
#include "base/memory/scoped_refptr.h"
#include "base/metrics/field_trial.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/variations_seed_simulator.h"
#include "testing/gtest/include/gtest/gtest.h"
// These tests mirror testSimulateTrialAssignments and
// testSimulateTrialAssignmentsWithForcedSalt in the internal crosstalk_test.py.
// Both places hard-code the same expected results, ensuring the chromium
// implementation matches the Python implementation.
namespace variations {
namespace {
// These studies must match the ones in crosstalk_test.py.
const char* const kDnsStudyName = "DnsProbe-Attempts";
const char* const kInstantStudyName = "InstantExtended";
constexpr int kDnsGroups = 2;
constexpr int kInstantGroups = 7;
constexpr size_t kMaxLowEntropySize = 8000;
scoped_refptr<base::FieldTrial> CreateDnsStudy(double value) {
scoped_refptr<base::FieldTrial> trial(
base::FieldTrial::CreateSimulatedFieldTrial(kDnsStudyName, 100, "default",
value));
trial->AppendGroup("1", 10);
return trial;
}
scoped_refptr<base::FieldTrial> CreateInstantStudy(double value) {
scoped_refptr<base::FieldTrial> trial(
base::FieldTrial::CreateSimulatedFieldTrial(kInstantStudyName, 100,
"DefaultGroup", value));
trial->AppendGroup("Group1", 5);
trial->AppendGroup("Control1", 5);
trial->AppendGroup("Group2", 5);
trial->AppendGroup("Control2", 5);
trial->AppendGroup("Group3", 5);
trial->AppendGroup("Control3", 5);
return trial;
}
// Simulate assigning users with every possible low entropy source to each
// study. Populate |counts| such that count[x][y] is the number of users in both
// DnsProbe-Attempts' group x and InstantExtended's group y. If |salt|, add salt
// to InstantExtended's name.
void countAssignments(int (&counts)[kDnsGroups][kInstantGroups], bool salt) {
// Must pass |counts| by reference, to prevent decay, for sizeof() to work.
memset(counts, 0, sizeof(counts));
for (uint16_t source = 0; source < kMaxLowEntropySize; source++) {
NormalizedMurmurHashEntropyProvider provider(source, kMaxLowEntropySize);
double dns_value = provider.GetEntropyForTrial(kDnsStudyName, 0);
int dns_group = CreateDnsStudy(dns_value)->group();
ASSERT_GE(dns_group, 0);
ASSERT_LT(dns_group, kDnsGroups);
std::string instant_study_name = kInstantStudyName;
if (salt)
instant_study_name += "abcdefghijklmnop";
double instant_value = provider.GetEntropyForTrial(instant_study_name, 0);
int instant_group = CreateInstantStudy(instant_value)->group();
ASSERT_GE(instant_group, 0);
ASSERT_LT(instant_group, kInstantGroups);
counts[dns_group][instant_group]++;
}
}
} // namespace
TEST(SimulateForCrosstalkTest, WithoutSalt) {
// These must match crosstalk_test.py's testSimulateTrialAssignments.
int expected[kDnsGroups][kInstantGroups] = {
{5053, 360, 365, 355, 347, 366, 354},
{547, 40, 35, 45, 53, 34, 46}};
int actual[kDnsGroups][kInstantGroups];
countAssignments(actual, false);
for (int i = 0; i < kDnsGroups; i++) {
for (int j = 0; j < kInstantGroups; j++) {
EXPECT_EQ(expected[i][j], actual[i][j])
<< " at groups " << i << " and " << j;
}
}
}
TEST(SimulateForCrosstalkTest, WithSalt) {
// These must match crosstalk_test.py's
// testSimulateTrialAssignmentsWithForcedSalt.
int expected[kDnsGroups][kInstantGroups] = {
{5029, 362, 372, 365, 360, 357, 355},
{571, 38, 28, 35, 40, 43, 45}};
int actual[kDnsGroups][kInstantGroups];
countAssignments(actual, true);
for (int i = 0; i < kDnsGroups; i++) {
for (int j = 0; j < kInstantGroups; j++) {
EXPECT_EQ(expected[i][j], actual[i][j])
<< " at groups " << i << " and " << j;
}
}
}
} // namespace variations