OLD | NEW |
(Empty) | |
| 1 // 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 "chrome/installer/setup/user_experiment.h" |
| 6 |
| 7 #include "base/macros.h" |
| 8 #include "base/test/test_reg_util_win.h" |
| 9 #include "base/win/registry.h" |
| 10 #include "chrome/common/chrome_version.h" |
| 11 #include "chrome/install_static/install_details.h" |
| 12 #include "chrome/install_static/install_modes.h" |
| 13 #include "chrome/install_static/install_util.h" |
| 14 #include "chrome/install_static/test/scoped_install_details.h" |
| 15 #include "chrome/installer/util/experiment_metrics.h" |
| 16 #include "chrome/installer/util/experiment_storage.h" |
| 17 #include "chrome/installer/util/google_update_constants.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 |
| 20 namespace installer { |
| 21 |
| 22 class UserExperimentTest : public ::testing::TestWithParam<bool> { |
| 23 protected: |
| 24 UserExperimentTest() |
| 25 : system_level_(GetParam()), |
| 26 root_(system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER), |
| 27 install_details_(system_level_) {} |
| 28 |
| 29 void SetUp() override { |
| 30 ASSERT_NO_FATAL_FAILURE(registry_override_manager_.OverrideRegistry(root_)); |
| 31 |
| 32 // Create the ClientState key. |
| 33 base::win::RegKey key; |
| 34 ASSERT_EQ(key.Create(root_, |
| 35 install_static::InstallDetails::Get() |
| 36 .GetClientStateKeyPath() |
| 37 .c_str(), |
| 38 KEY_WOW64_64KEY | KEY_SET_VALUE), |
| 39 ERROR_SUCCESS); |
| 40 } |
| 41 |
| 42 void SetBrand(const base::char16* brand) { |
| 43 SetClientStateValue(google_update::kRegBrandField, brand); |
| 44 } |
| 45 |
| 46 void SetProductVersion(const base::char16* version) { |
| 47 SetClientsValue(google_update::kRegVersionField, version); |
| 48 } |
| 49 |
| 50 void SetOldProductVersion(const base::char16* version) { |
| 51 SetClientsValue(google_update::kRegOldVersionField, version); |
| 52 } |
| 53 |
| 54 private: |
| 55 void SetClientStateValue(const base::char16* value_name, |
| 56 const base::char16* value_data) { |
| 57 base::win::RegKey key( |
| 58 root_, |
| 59 install_static::InstallDetails::Get().GetClientStateKeyPath().c_str(), |
| 60 KEY_WOW64_64KEY | KEY_SET_VALUE); |
| 61 ASSERT_TRUE(key.Valid()); |
| 62 ASSERT_EQ(key.WriteValue(value_name, value_data), ERROR_SUCCESS); |
| 63 } |
| 64 |
| 65 void SetClientsValue(const base::char16* value_name, |
| 66 const base::char16* value_data) { |
| 67 base::win::RegKey key( |
| 68 root_, |
| 69 install_static::GetClientsKeyPath(install_static::GetAppGuid()).c_str(), |
| 70 KEY_WOW64_64KEY | KEY_SET_VALUE); |
| 71 ASSERT_TRUE(key.Valid()); |
| 72 ASSERT_EQ(key.WriteValue(value_name, value_data), ERROR_SUCCESS); |
| 73 } |
| 74 |
| 75 const bool system_level_; |
| 76 const HKEY root_; |
| 77 registry_util::RegistryOverrideManager registry_override_manager_; |
| 78 install_static::ScopedInstallDetails install_details_; |
| 79 DISALLOW_COPY_AND_ASSIGN(UserExperimentTest); |
| 80 }; |
| 81 |
| 82 TEST_P(UserExperimentTest, WriteInitialStateNoData) { |
| 83 ExperimentStorage storage; |
| 84 |
| 85 // A first call should write the desired state. |
| 86 WriteInitialState(&storage, ExperimentMetrics::kWaitingForUserLogon); |
| 87 |
| 88 ExperimentMetrics metrics = ExperimentMetrics(); |
| 89 EXPECT_TRUE(storage.AcquireLock()->LoadMetrics(&metrics)); |
| 90 EXPECT_EQ(metrics.state, ExperimentMetrics::kWaitingForUserLogon); |
| 91 |
| 92 // A subsequent should update it state. |
| 93 WriteInitialState(&storage, ExperimentMetrics::kSingletonWaitTimeout); |
| 94 |
| 95 metrics = ExperimentMetrics(); |
| 96 EXPECT_TRUE(storage.AcquireLock()->LoadMetrics(&metrics)); |
| 97 EXPECT_EQ(metrics.state, ExperimentMetrics::kSingletonWaitTimeout); |
| 98 } |
| 99 |
| 100 // Nothing should be written if the experiment is underway. |
| 101 TEST_P(UserExperimentTest, WriteInitialStateInExperiment) { |
| 102 ExperimentStorage storage; |
| 103 |
| 104 { |
| 105 ExperimentMetrics metrics = ExperimentMetrics(); |
| 106 metrics.state = ExperimentMetrics::kGroupAssigned; |
| 107 storage.AcquireLock()->StoreMetrics(metrics); |
| 108 } |
| 109 |
| 110 WriteInitialState(&storage, ExperimentMetrics::kSingletonWaitTimeout); |
| 111 |
| 112 ExperimentMetrics metrics = ExperimentMetrics(); |
| 113 EXPECT_TRUE(storage.AcquireLock()->LoadMetrics(&metrics)); |
| 114 EXPECT_EQ(metrics.state, ExperimentMetrics::kGroupAssigned); |
| 115 } |
| 116 |
| 117 TEST_P(UserExperimentTest, IsEnterpriseBrand) { |
| 118 static constexpr const base::char16* kEnterpriseBrands[] = { |
| 119 L"GGRV", L"GCEA", L"GCEB", L"GCEC", L"GCED", L"GCEE", L"GCEF", |
| 120 L"GCEG", L"GCEH", L"GCEI", L"GCEJ", L"GCEK", L"GCEM", L"GCEN", |
| 121 L"GCEO", L"GCEP", L"GCEQ", L"GCER", L"GCES", L"GCET", L"GCEU", |
| 122 }; |
| 123 |
| 124 for (const base::char16* brand : kEnterpriseBrands) { |
| 125 SCOPED_TRACE(::testing::Message() << "brand = " << brand); |
| 126 ASSERT_NO_FATAL_FAILURE(SetBrand(brand)); |
| 127 EXPECT_TRUE(IsEnterpriseBrand()); |
| 128 } |
| 129 |
| 130 static constexpr const base::char16* kNonEnterpriseBrands[] = {L"GGEL", |
| 131 L"GGLS"}; |
| 132 |
| 133 for (const base::char16* brand : kNonEnterpriseBrands) { |
| 134 SCOPED_TRACE(::testing::Message() << "brand = " << brand); |
| 135 ASSERT_NO_FATAL_FAILURE(SetBrand(brand)); |
| 136 EXPECT_FALSE(IsEnterpriseBrand()); |
| 137 } |
| 138 } |
| 139 |
| 140 TEST_P(UserExperimentTest, IsDomainJoined) { |
| 141 // Just make sure it doesn't crash or leak. |
| 142 IsDomainJoined(); |
| 143 } |
| 144 |
| 145 TEST_P(UserExperimentTest, IsSelectedForStudyFirstCall) { |
| 146 ExperimentStorage storage; |
| 147 auto lock = storage.AcquireLock(); |
| 148 |
| 149 // The first call will pick a study. |
| 150 bool is_selected = |
| 151 IsSelectedForStudy(lock.get(), ExperimentStorage::kStudyOne); |
| 152 |
| 153 // A value must have been written. |
| 154 ExperimentStorage::Study participation = ExperimentStorage::kNoStudySelected; |
| 155 ASSERT_TRUE(lock->ReadParticipation(&participation)); |
| 156 EXPECT_GE(participation, ExperimentStorage::kStudyOne); |
| 157 EXPECT_LE(participation, ExperimentStorage::kStudyTwo); |
| 158 |
| 159 // is_selected should be set based on the value that was written. |
| 160 if (participation == ExperimentStorage::kStudyOne) |
| 161 EXPECT_TRUE(is_selected); |
| 162 else |
| 163 EXPECT_FALSE(is_selected); |
| 164 } |
| 165 |
| 166 // A user selected into study one participates in both studies. |
| 167 TEST_P(UserExperimentTest, IsSelectedForStudyOne) { |
| 168 ExperimentStorage storage; |
| 169 auto lock = storage.AcquireLock(); |
| 170 |
| 171 ASSERT_TRUE(lock->WriteParticipation(ExperimentStorage::kStudyOne)); |
| 172 |
| 173 EXPECT_TRUE(IsSelectedForStudy(lock.get(), ExperimentStorage::kStudyOne)); |
| 174 EXPECT_TRUE(IsSelectedForStudy(lock.get(), ExperimentStorage::kStudyTwo)); |
| 175 } |
| 176 |
| 177 // A user selected into study two only participates in that study. |
| 178 TEST_P(UserExperimentTest, IsSelectedForStudyTwo) { |
| 179 ExperimentStorage storage; |
| 180 auto lock = storage.AcquireLock(); |
| 181 |
| 182 ASSERT_TRUE(lock->WriteParticipation(ExperimentStorage::kStudyTwo)); |
| 183 |
| 184 EXPECT_FALSE(IsSelectedForStudy(lock.get(), ExperimentStorage::kStudyOne)); |
| 185 EXPECT_TRUE(IsSelectedForStudy(lock.get(), ExperimentStorage::kStudyTwo)); |
| 186 } |
| 187 |
| 188 // Ensure that group selection is within bounds. |
| 189 TEST_P(UserExperimentTest, PickGroupStudyOne) { |
| 190 int group = PickGroup(ExperimentStorage::kStudyOne); |
| 191 EXPECT_GE(group, 0); |
| 192 EXPECT_LT(group, ExperimentMetrics::kNumGroups); |
| 193 } |
| 194 |
| 195 // Ensure that group selection is within bounds. |
| 196 TEST_P(UserExperimentTest, PickGroupStudyTwo) { |
| 197 int group = PickGroup(ExperimentStorage::kStudyTwo); |
| 198 EXPECT_GE(group, 0); |
| 199 EXPECT_LT(group, ExperimentMetrics::kNumGroups); |
| 200 } |
| 201 |
| 202 // When there's nothing in the registry, default to false. |
| 203 TEST_P(UserExperimentTest, IsUpdateRenamePendingNoRegistration) { |
| 204 EXPECT_FALSE(IsUpdateRenamePending()); |
| 205 } |
| 206 |
| 207 // No update is pending if "pv" matches the current version. |
| 208 TEST_P(UserExperimentTest, IsUpdateRenamePendingNo) { |
| 209 ASSERT_NO_FATAL_FAILURE(SetProductVersion(TEXT(CHROME_VERSION_STRING))); |
| 210 EXPECT_FALSE(IsUpdateRenamePending()); |
| 211 } |
| 212 |
| 213 // An update is pending if an old version needs to be restarted to be the |
| 214 // current. |
| 215 TEST_P(UserExperimentTest, IsUpdateRenamePendingYes) { |
| 216 static constexpr base::char16 kSillyOldVersion[] = L"47.0.1.0"; |
| 217 ASSERT_STRNE(kSillyOldVersion, TEXT(CHROME_VERSION_STRING)); |
| 218 |
| 219 ASSERT_NO_FATAL_FAILURE(SetProductVersion(TEXT(CHROME_VERSION_STRING))); |
| 220 ASSERT_NO_FATAL_FAILURE(SetOldProductVersion(kSillyOldVersion)); |
| 221 EXPECT_TRUE(IsUpdateRenamePending()); |
| 222 } |
| 223 |
| 224 INSTANTIATE_TEST_CASE_P(UserLevel, |
| 225 UserExperimentTest, |
| 226 ::testing::Values(false)); |
| 227 INSTANTIATE_TEST_CASE_P(SystemLevel, |
| 228 UserExperimentTest, |
| 229 ::testing::Values(true)); |
| 230 |
| 231 } // namespace installer |
OLD | NEW |