1 // Copyright 2015 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/util/beacons.h"
6 
7 #include <memory>
8 #include <tuple>
9 
10 #include "base/test/test_reg_util_win.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/threading/platform_thread.h"
13 #include "base/win/registry.h"
14 #include "base/win/win_util.h"
15 #include "build/branding_buildflags.h"
16 #include "chrome/install_static/install_details.h"
17 #include "chrome/install_static/install_modes.h"
18 #include "chrome/install_static/test/scoped_install_details.h"
19 #include "chrome/installer/util/install_util.h"
20 #include "chrome/installer/util/util_constants.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 using ::testing::Bool;
24 using ::testing::Combine;
25 using ::testing::Values;
26 using BeaconType = installer_util::Beacon::BeaconType;
27 using BeaconScope = installer_util::Beacon::BeaconScope;
28 
29 namespace installer_util {
30 
31 // A test fixture that exercises a beacon.
32 class BeaconTest : public ::testing::TestWithParam<
33                        ::testing::tuple<BeaconType, BeaconScope, bool>> {
34  protected:
35   static const base::char16 kBeaconName[];
36 
BeaconTest()37   BeaconTest()
38       : beacon_type_(::testing::get<0>(GetParam())),
39         beacon_scope_(::testing::get<1>(GetParam())),
40         system_install_(::testing::get<2>(GetParam())),
41         scoped_install_details_(system_install_),
42         beacon_(kBeaconName, beacon_type_, beacon_scope_) {}
43 
SetUp()44   void SetUp() override {
45     // Override the registry so that tests can freely push state to it.
46     ASSERT_NO_FATAL_FAILURE(
47         registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
48     ASSERT_NO_FATAL_FAILURE(
49         registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE));
50   }
51 
beacon_type() const52   BeaconType beacon_type() const { return beacon_type_; }
beacon_scope() const53   BeaconScope beacon_scope() const { return beacon_scope_; }
system_install() const54   bool system_install() const { return system_install_; }
beacon()55   Beacon* beacon() { return &beacon_; }
56 
57  private:
58   BeaconType beacon_type_;
59   BeaconScope beacon_scope_;
60   bool system_install_;
61   install_static::ScopedInstallDetails scoped_install_details_;
62   Beacon beacon_;
63   registry_util::RegistryOverrideManager registry_override_manager_;
64 };
65 
66 // static
67 const base::char16 BeaconTest::kBeaconName[] = L"TestBeacon";
68 
69 // Nothing in the regsitry, so the beacon should not exist.
TEST_P(BeaconTest,GetNonExistent)70 TEST_P(BeaconTest, GetNonExistent) {
71   ASSERT_TRUE(beacon()->Get().is_null());
72 }
73 
74 // Updating and then getting the beacon should return a value, and that it is
75 // within range.
TEST_P(BeaconTest,UpdateAndGet)76 TEST_P(BeaconTest, UpdateAndGet) {
77   base::Time before(base::Time::Now());
78   beacon()->Update();
79   base::Time after(base::Time::Now());
80   base::Time beacon_time(beacon()->Get());
81   ASSERT_FALSE(beacon_time.is_null());
82   ASSERT_LE(before, beacon_time);
83   ASSERT_GE(after, beacon_time);
84 }
85 
86 // Tests that updating a first beacon only updates it the first time, but doing
87 // so for a last beacon always updates.
TEST_P(BeaconTest,UpdateTwice)88 TEST_P(BeaconTest, UpdateTwice) {
89   beacon()->Update();
90   base::Time beacon_time(beacon()->Get());
91 
92   base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
93 
94   beacon()->Update();
95   if (beacon_type() == BeaconType::FIRST) {
96     ASSERT_EQ(beacon_time, beacon()->Get());
97   } else {
98     ASSERT_NE(beacon_time, beacon()->Get());
99   }
100 }
101 
102 // Tests that the beacon is written into the proper location in the registry.
TEST_P(BeaconTest,Location)103 TEST_P(BeaconTest, Location) {
104   beacon()->Update();
105   const install_static::InstallDetails& install_details =
106       install_static::InstallDetails::Get();
107   HKEY right_root = system_install() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
108   HKEY wrong_root = system_install() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
109   base::string16 right_key;
110   base::string16 wrong_key;
111   base::string16 value_name;
112 
113   if (beacon_scope() == BeaconScope::PER_INSTALL || !system_install()) {
114     value_name = kBeaconName;
115     right_key = install_details.GetClientStateKeyPath();
116     wrong_key = install_details.GetClientStateMediumKeyPath();
117   } else {
118     ASSERT_TRUE(base::win::GetUserSidString(&value_name));
119     right_key =
120         install_details.GetClientStateMediumKeyPath() + L"\\" + kBeaconName;
121     wrong_key = install_details.GetClientStateKeyPath();
122   }
123 
124 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
125   // Keys should not exist in the wrong root or in the right root but wrong key.
126   ASSERT_FALSE(
127       base::win::RegKey(wrong_root, right_key.c_str(), KEY_READ).Valid())
128       << right_key;
129   ASSERT_FALSE(
130       base::win::RegKey(wrong_root, wrong_key.c_str(), KEY_READ).Valid())
131       << wrong_key;
132   ASSERT_FALSE(
133       base::win::RegKey(right_root, wrong_key.c_str(), KEY_READ).Valid())
134       << wrong_key;
135 #else
136   // The tests above are skipped for Chromium builds because they fail for two
137   // reasons:
138   // - ClientState and ClientStateMedium are both Software\Chromium.
139   // - the registry override manager does its virtualization into
140   //   Software\Chromium, so it always exists.
141 
142   // Silence unused variable warnings.
143   ignore_result(wrong_root);
144 #endif
145 
146   // The right key should exist.
147   base::win::RegKey key(right_root, right_key.c_str(), KEY_READ);
148   ASSERT_TRUE(key.Valid()) << right_key;
149   // And should have the value.
150   ASSERT_TRUE(key.HasValue(value_name.c_str())) << value_name;
151 }
152 
153 // Run the tests for all combinations of beacon type, scope, and install level.
154 INSTANTIATE_TEST_SUITE_P(BeaconTest,
155                          BeaconTest,
156                          Combine(Values(BeaconType::FIRST, BeaconType::LAST),
157                                  Values(BeaconScope::PER_USER,
158                                         BeaconScope::PER_INSTALL),
159                                  Bool()));
160 
161 class DefaultBrowserBeaconTest
162     : public ::testing::TestWithParam<
163           std::tuple<install_static::InstallConstantIndex, const char*>> {
164  protected:
165   using Super = ::testing::TestWithParam<
166       std::tuple<install_static::InstallConstantIndex, const char*>>;
167 
SetUp()168   void SetUp() override {
169     Super::SetUp();
170 
171     install_static::InstallConstantIndex mode_index;
172     const char* level;
173     std::tie(mode_index, level) = GetParam();
174 
175     system_install_ = (std::string(level) != "user");
176 
177     // Configure InstallDetails for the test.
178     scoped_install_details_ =
179         std::make_unique<install_static::ScopedInstallDetails>(system_install_,
180                                                                mode_index);
181     // Override the registry so that tests can freely push state to it.
182     ASSERT_NO_FATAL_FAILURE(
183         registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
184     ASSERT_NO_FATAL_FAILURE(
185         registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE));
186   }
187 
188   bool system_install_ = false;
189 
190  private:
191   std::unique_ptr<install_static::ScopedInstallDetails> scoped_install_details_;
192   registry_util::RegistryOverrideManager registry_override_manager_;
193 };
194 
195 // Tests that the default browser beacons work as expected.
TEST_P(DefaultBrowserBeaconTest,All)196 TEST_P(DefaultBrowserBeaconTest, All) {
197   std::unique_ptr<Beacon> last_was_default(MakeLastWasDefaultBeacon());
198   std::unique_ptr<Beacon> first_not_default(MakeFirstNotDefaultBeacon());
199 
200   ASSERT_TRUE(last_was_default->Get().is_null());
201   ASSERT_TRUE(first_not_default->Get().is_null());
202 
203   // Chrome is not default.
204   UpdateDefaultBrowserBeaconWithState(ShellUtil::NOT_DEFAULT);
205   ASSERT_TRUE(last_was_default->Get().is_null());
206   ASSERT_FALSE(first_not_default->Get().is_null());
207 
208   // Then it is.
209   UpdateDefaultBrowserBeaconWithState(ShellUtil::IS_DEFAULT);
210   ASSERT_FALSE(last_was_default->Get().is_null());
211   ASSERT_TRUE(first_not_default->Get().is_null());
212 
213   // It still is.
214   UpdateDefaultBrowserBeaconWithState(ShellUtil::IS_DEFAULT);
215   ASSERT_FALSE(last_was_default->Get().is_null());
216   ASSERT_TRUE(first_not_default->Get().is_null());
217 
218   // Now it's not again.
219   UpdateDefaultBrowserBeaconWithState(ShellUtil::NOT_DEFAULT);
220   ASSERT_FALSE(last_was_default->Get().is_null());
221   ASSERT_FALSE(first_not_default->Get().is_null());
222 
223   // And it still isn't.
224   UpdateDefaultBrowserBeaconWithState(ShellUtil::NOT_DEFAULT);
225   ASSERT_FALSE(last_was_default->Get().is_null());
226   ASSERT_FALSE(first_not_default->Get().is_null());
227 }
228 
229 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
230 // Stable supports user and system levels.
231 INSTANTIATE_TEST_SUITE_P(
232     Stable,
233     DefaultBrowserBeaconTest,
234     testing::Combine(testing::Values(install_static::STABLE_INDEX),
235                      testing::Values("user", "system")));
236 // Beta supports user and system levels.
237 INSTANTIATE_TEST_SUITE_P(
238     Beta,
239     DefaultBrowserBeaconTest,
240     testing::Combine(testing::Values(install_static::BETA_INDEX),
241                      testing::Values("user", "system")));
242 // Dev supports user and system levels.
243 INSTANTIATE_TEST_SUITE_P(
244     Dev,
245     DefaultBrowserBeaconTest,
246     testing::Combine(testing::Values(install_static::DEV_INDEX),
247                      testing::Values("user", "system")));
248 // Canary is only at user level.
249 INSTANTIATE_TEST_SUITE_P(
250     Canary,
251     DefaultBrowserBeaconTest,
252     testing::Combine(testing::Values(install_static::CANARY_INDEX),
253                      testing::Values("user")));
254 #else   // BUILDFLAG(GOOGLE_CHROME_BRANDING)
255 // Chromium supports user and system levels.
256 INSTANTIATE_TEST_SUITE_P(
257     Chromium,
258     DefaultBrowserBeaconTest,
259     testing::Combine(testing::Values(install_static::CHROMIUM_INDEX),
260                      testing::Values("user", "system")));
261 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
262 
263 }  // namespace installer_util
264