blob: 5e8f0f391e77596f92d8a702982895185d1f2e3f [file] [log] [blame]
Steven Holtef58f2b22017-07-17 18:43:451// Copyright 2017 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
5#include "components/variations/synthetic_trial_registry.h"
6
Alexei Svitkinee726dd522018-03-09 04:41:037#include <string>
8
9#include "base/metrics/field_trial.h"
Steven Holtef58f2b22017-07-17 18:43:4510#include "base/strings/stringprintf.h"
Carlos Caballero954ca162018-11-12 10:47:0911#include "base/test/scoped_task_environment.h"
Steven Holtef58f2b22017-07-17 18:43:4512#include "components/variations/active_field_trials.h"
Alexei Svitkine9de32cb2018-02-06 20:21:2113#include "components/variations/hashing.h"
Steven Holtef58f2b22017-07-17 18:43:4514#include "components/variations/synthetic_trials_active_group_id_provider.h"
Alexei Svitkinee726dd522018-03-09 04:41:0315#include "components/variations/variations_crash_keys.h"
Steven Holtef58f2b22017-07-17 18:43:4516#include "testing/gtest/include/gtest/gtest.h"
17
18namespace variations {
19
20namespace {
21
22class SyntheticTrialRegistryTest : public ::testing::Test {
23 public:
Alexei Svitkinee726dd522018-03-09 04:41:0324 SyntheticTrialRegistryTest() : field_trial_list_(nullptr) { InitCrashKeys(); }
25 ~SyntheticTrialRegistryTest() override { ClearCrashKeysInstanceForTesting(); }
26
Steven Holtef58f2b22017-07-17 18:43:4527 // Returns true if there is a synthetic trial in the given vector that matches
28 // the given trial name and trial group; returns false otherwise.
29 bool HasSyntheticTrial(const std::vector<ActiveGroupId>& synthetic_trials,
30 const std::string& trial_name,
31 const std::string& trial_group) {
Alexei Svitkine9de32cb2018-02-06 20:21:2132 uint32_t trial_name_hash = HashName(trial_name);
33 uint32_t trial_group_hash = HashName(trial_group);
Steven Holtef58f2b22017-07-17 18:43:4534 for (const ActiveGroupId& trial : synthetic_trials) {
35 if (trial.name == trial_name_hash && trial.group == trial_group_hash)
36 return true;
37 }
38 return false;
39 }
40
41 // Waits until base::TimeTicks::Now() no longer equals |value|. This should
42 // take between 1-15ms per the documented resolution of base::TimeTicks.
43 void WaitUntilTimeChanges(const base::TimeTicks& value) {
44 while (base::TimeTicks::Now() == value) {
45 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
46 }
47 }
Alexei Svitkinee726dd522018-03-09 04:41:0348
49 private:
Carlos Caballero954ca162018-11-12 10:47:0950 base::test::ScopedTaskEnvironment task_environment_;
Alexei Svitkinefc2facc2018-03-27 21:00:4751
Alexei Svitkinee726dd522018-03-09 04:41:0352 base::FieldTrialList field_trial_list_;
53
54 DISALLOW_COPY_AND_ASSIGN(SyntheticTrialRegistryTest);
Steven Holtef58f2b22017-07-17 18:43:4555};
56
57} // namespace
58
59TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticTrial) {
60 SyntheticTrialRegistry registry;
61
62 // Add two synthetic trials and confirm that they show up in the list.
Alexei Svitkine9de32cb2018-02-06 20:21:2163 SyntheticTrialGroup trial1(HashName("TestTrial1"), HashName("Group1"));
Steven Holtef58f2b22017-07-17 18:43:4564 registry.RegisterSyntheticFieldTrial(trial1);
65
Alexei Svitkine9de32cb2018-02-06 20:21:2166 SyntheticTrialGroup trial2(HashName("TestTrial2"), HashName("Group2"));
Steven Holtef58f2b22017-07-17 18:43:4567 registry.RegisterSyntheticFieldTrial(trial2);
68 // Ensure that time has advanced by at least a tick before proceeding.
69 WaitUntilTimeChanges(base::TimeTicks::Now());
70
71 // Save the time when the log was started (it's okay for this to be greater
72 // than the time recorded by the above call since it's used to ensure the
73 // value changes).
74 const base::TimeTicks begin_log_time = base::TimeTicks::Now();
75
76 std::vector<ActiveGroupId> synthetic_trials;
77 registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
78 &synthetic_trials);
79 EXPECT_EQ(2U, synthetic_trials.size());
80 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group1"));
81 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
82
83 // Ensure that time has advanced by at least a tick before proceeding.
84 WaitUntilTimeChanges(begin_log_time);
85
86 // Change the group for the first trial after the log started.
Alexei Svitkine9de32cb2018-02-06 20:21:2187 SyntheticTrialGroup trial3(HashName("TestTrial1"), HashName("Group2"));
Steven Holtef58f2b22017-07-17 18:43:4588 registry.RegisterSyntheticFieldTrial(trial3);
89 registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
90 EXPECT_EQ(1U, synthetic_trials.size());
91 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
92
93 // Add a new trial after the log started and confirm that it doesn't show up.
Alexei Svitkine9de32cb2018-02-06 20:21:2194 SyntheticTrialGroup trial4(HashName("TestTrial3"), HashName("Group3"));
Steven Holtef58f2b22017-07-17 18:43:4595 registry.RegisterSyntheticFieldTrial(trial4);
96 registry.GetSyntheticFieldTrialsOlderThan(begin_log_time, &synthetic_trials);
97 EXPECT_EQ(1U, synthetic_trials.size());
98 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
99
100 // Ensure that time has advanced by at least a tick before proceeding.
101 WaitUntilTimeChanges(base::TimeTicks::Now());
102
103 // Start a new log and ensure all three trials appear in it.
104 registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
105 &synthetic_trials);
106 EXPECT_EQ(3U, synthetic_trials.size());
107 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group2"));
108 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2"));
109 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial3", "Group3"));
110}
111
112TEST_F(SyntheticTrialRegistryTest, RegisterSyntheticMultiGroupFieldTrial) {
113 SyntheticTrialRegistry registry;
114
115 // Register a synthetic trial TestTrial1 with groups A and B.
Alexei Svitkine9de32cb2018-02-06 20:21:21116 uint32_t trial_name_hash = HashName("TestTrial1");
117 std::vector<uint32_t> group_name_hashes = {HashName("A"), HashName("B")};
Steven Holtef58f2b22017-07-17 18:43:45118 registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
119 group_name_hashes);
120 // Ensure that time has advanced by at least a tick before proceeding.
121 WaitUntilTimeChanges(base::TimeTicks::Now());
122
123 std::vector<ActiveGroupId> synthetic_trials;
124 registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
125 &synthetic_trials);
126 EXPECT_EQ(2U, synthetic_trials.size());
127 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "A"));
128 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "B"));
129
130 // Change the group for the trial to a single group.
Alexei Svitkine9de32cb2018-02-06 20:21:21131 group_name_hashes = {HashName("X")};
Steven Holtef58f2b22017-07-17 18:43:45132 registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
133 group_name_hashes);
134 // Ensure that time has advanced by at least a tick before proceeding.
135 WaitUntilTimeChanges(base::TimeTicks::Now());
136
137 registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
138 &synthetic_trials);
139 EXPECT_EQ(1U, synthetic_trials.size());
140 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "X"));
141
142 // Register a trial with no groups, which should effectively remove the trial.
143 group_name_hashes.clear();
144 registry.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash,
145 group_name_hashes);
146 // Ensure that time has advanced by at least a tick before proceeding.
147 WaitUntilTimeChanges(base::TimeTicks::Now());
148
149 registry.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(),
150 &synthetic_trials);
151}
152
153TEST_F(SyntheticTrialRegistryTest, GetSyntheticFieldTrialActiveGroups) {
154 SyntheticTrialRegistry registry;
155
156 // Instantiate and setup the corresponding singleton observer which tracks the
157 // creation of all SyntheticTrialGroups.
158 registry.AddSyntheticTrialObserver(
159 SyntheticTrialsActiveGroupIdProvider::GetInstance());
160
161 // Add two synthetic trials and confirm that they show up in the list.
Alexei Svitkine9de32cb2018-02-06 20:21:21162 SyntheticTrialGroup trial1(HashName("TestTrial1"), HashName("Group1"));
Steven Holtef58f2b22017-07-17 18:43:45163 registry.RegisterSyntheticFieldTrial(trial1);
164
Alexei Svitkine9de32cb2018-02-06 20:21:21165 SyntheticTrialGroup trial2(HashName("TestTrial2"), HashName("Group2"));
Steven Holtef58f2b22017-07-17 18:43:45166 registry.RegisterSyntheticFieldTrial(trial2);
167
168 // Ensure that time has advanced by at least a tick before proceeding.
169 WaitUntilTimeChanges(base::TimeTicks::Now());
170
171 // Now get the list of currently active groups.
172 std::vector<std::string> output;
173 GetSyntheticTrialGroupIdsAsString(&output);
174 EXPECT_EQ(2U, output.size());
175
176 std::string trial1_hash =
177 base::StringPrintf("%x-%x", trial1.id.name, trial1.id.group);
178 EXPECT_TRUE(base::ContainsValue(output, trial1_hash));
179
180 std::string trial2_hash =
181 base::StringPrintf("%x-%x", trial2.id.name, trial2.id.group);
182 EXPECT_TRUE(base::ContainsValue(output, trial2_hash));
183}
184
185} // namespace variations