Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / installer / util / installation_validator_unittest.cc
blob941dc5c23f64cf08571fc4869a11a5bf1aa20486
1 // 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.
5 #include <map>
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/version.h"
12 #include "chrome/common/chrome_constants.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/installer/util/channel_info.h"
15 #include "chrome/installer/util/helper.h"
16 #include "chrome/installer/util/installation_state.h"
17 #include "chrome/installer/util/installation_validator.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
21 using installer::ChannelInfo;
22 using installer::InstallationValidator;
23 using installer::InstallationState;
24 using installer::AppCommand;
25 using installer::ProductState;
26 using testing::_;
27 using testing::StrictMock;
28 using testing::Values;
30 namespace {
32 enum Channel {
33 STABLE_CHANNEL,
34 BETA_CHANNEL,
35 DEV_CHANNEL
38 enum PackageType {
39 SINGLE_INSTALL,
40 MULTI_INSTALL
43 enum Level {
44 USER_LEVEL,
45 SYSTEM_LEVEL
48 enum Vehicle {
49 GOOGLE_UPDATE,
50 MSI
53 enum ChannelModifier {
54 CM_MULTI = 0x01,
55 CM_CHROME = 0x02,
56 CM_CHROME_FRAME = 0x04,
57 CM_FULL = 0x08
60 const wchar_t* const kChromeChannels[] = {
61 L"",
62 L"1.1-beta",
63 L"2.0-dev"
66 const wchar_t* const kChromeFrameChannels[] = {
67 L"",
68 L"beta",
69 L"dev"
72 class FakeProductState : public ProductState {
73 public:
74 void SetChannel(const wchar_t* base, int channel_modifiers);
75 void SetVersion(const char* version);
76 void SetUninstallCommand(BrowserDistribution::Type dist_type,
77 Level install_level,
78 const char* version,
79 int channel_modifiers,
80 Vehicle vehicle);
81 void AddOsUpgradeCommand(BrowserDistribution::Type dist_type,
82 Level install_level,
83 const char* version,
84 int channel_modifiers);
85 void AddQueryEULAAcceptanceCommand(BrowserDistribution::Type dist_type,
86 Level install_level,
87 const char* version,
88 int channel_modifiers);
89 void AddQuickEnableApplicationHostCommand(BrowserDistribution::Type dist_type,
90 Level install_level,
91 const char* version,
92 int channel_modifiers);
93 void set_multi_install(bool is_multi_install) {
94 multi_install_ = is_multi_install;
96 installer::AppCommands& commands() { return commands_; }
98 protected:
99 struct ChannelMethodForModifier {
100 ChannelModifier modifier;
101 bool (ChannelInfo::*method)(bool value);
104 static base::FilePath GetSetupPath(
105 BrowserDistribution::Type dist_type,
106 Level install_level,
107 int channel_modifiers);
109 static base::FilePath GetSetupExePath(
110 BrowserDistribution::Type dist_type,
111 Level install_level,
112 const char* version,
113 int channel_modifiers);
115 static const ChannelMethodForModifier kChannelMethods[];
118 class FakeInstallationState : public InstallationState {
119 public:
120 void SetProductState(BrowserDistribution::Type type,
121 Level install_level,
122 const ProductState& product) {
123 GetProducts(install_level)[IndexFromDistType(type)].CopyFrom(product);
126 protected:
127 ProductState* GetProducts(Level install_level) {
128 return install_level == USER_LEVEL ? user_products_ : system_products_;
132 // static
133 const FakeProductState::ChannelMethodForModifier
134 FakeProductState::kChannelMethods[] = {
135 { CM_MULTI, &ChannelInfo::SetMultiInstall },
136 { CM_CHROME, &ChannelInfo::SetChrome },
137 { CM_CHROME_FRAME, &ChannelInfo::SetChromeFrame },
138 { CM_FULL, &ChannelInfo::SetFullSuffix }
141 // static
142 base::FilePath FakeProductState::GetSetupPath(
143 BrowserDistribution::Type dist_type,
144 Level install_level,
145 int channel_modifiers) {
146 const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0;
147 return installer::GetChromeInstallPath(
148 install_level == SYSTEM_LEVEL,
149 BrowserDistribution::GetSpecificDistribution(is_multi_install ?
150 BrowserDistribution::CHROME_BINARIES : dist_type));
153 // static
154 base::FilePath FakeProductState::GetSetupExePath(
155 BrowserDistribution::Type dist_type,
156 Level install_level,
157 const char* version,
158 int channel_modifiers) {
159 base::FilePath setup_path = GetSetupPath(dist_type, install_level,
160 channel_modifiers);
161 return setup_path
162 .AppendASCII(version)
163 .Append(installer::kInstallerDir)
164 .Append(installer::kSetupExe);
167 // Sets the channel_ member of this instance according to a base channel value
168 // and a set of modifiers.
169 void FakeProductState::SetChannel(const wchar_t* base, int channel_modifiers) {
170 channel_.set_value(base);
171 for (size_t i = 0; i < arraysize(kChannelMethods); ++i) {
172 if ((channel_modifiers & kChannelMethods[i].modifier) != 0)
173 (channel_.*kChannelMethods[i].method)(true);
177 void FakeProductState::SetVersion(const char* version) {
178 version_.reset(version == NULL ? NULL : new Version(version));
181 // Sets the uninstall command for this object.
182 void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type,
183 Level install_level,
184 const char* version,
185 int channel_modifiers,
186 Vehicle vehicle) {
187 DCHECK(version);
189 const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0;
190 uninstall_command_ = CommandLine(GetSetupExePath(dist_type, install_level,
191 version, channel_modifiers));
192 uninstall_command_.AppendSwitch(installer::switches::kUninstall);
193 if (install_level == SYSTEM_LEVEL)
194 uninstall_command_.AppendSwitch(installer::switches::kSystemLevel);
195 if (is_multi_install) {
196 uninstall_command_.AppendSwitch(installer::switches::kMultiInstall);
197 if (dist_type == BrowserDistribution::CHROME_BROWSER)
198 uninstall_command_.AppendSwitch(installer::switches::kChrome);
199 else if (dist_type == BrowserDistribution::CHROME_FRAME)
200 uninstall_command_.AppendSwitch(installer::switches::kChromeFrame);
201 } else if (dist_type == BrowserDistribution::CHROME_FRAME) {
202 uninstall_command_.AppendSwitch(installer::switches::kChromeFrame);
204 if (vehicle == MSI)
205 uninstall_command_.AppendSwitch(installer::switches::kMsi);
208 // Adds the "on-os-upgrade" Google Update product command.
209 void FakeProductState::AddOsUpgradeCommand(BrowserDistribution::Type dist_type,
210 Level install_level,
211 const char* version,
212 int channel_modifiers) {
213 // Right now only Chrome browser uses this.
214 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BROWSER);
216 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
217 channel_modifiers));
218 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
219 // Imitating ChromeBrowserOperations::AppendProductFlags().
220 if ((channel_modifiers & CM_MULTI) != 0) {
221 cmd_line.AppendSwitch(installer::switches::kMultiInstall);
222 cmd_line.AppendSwitch(installer::switches::kChrome);
224 if (install_level == SYSTEM_LEVEL)
225 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
226 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
227 AppCommand app_cmd(cmd_line.GetCommandLineString());
228 app_cmd.set_is_auto_run_on_os_upgrade(true);
229 commands_.Set(installer::kCmdOnOsUpgrade, app_cmd);
232 // Adds the "query-eula-acceptance" Google Update product command.
233 void FakeProductState::AddQueryEULAAcceptanceCommand(
234 BrowserDistribution::Type dist_type,
235 Level install_level,
236 const char* version,
237 int channel_modifiers) {
238 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES);
240 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
241 channel_modifiers));
242 cmd_line.AppendSwitch(installer::switches::kQueryEULAAcceptance);
243 if (install_level == SYSTEM_LEVEL)
244 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
245 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
246 AppCommand app_cmd(cmd_line.GetCommandLineString());
247 app_cmd.set_is_web_accessible(true);
248 app_cmd.set_is_run_as_user(true);
249 commands_.Set(installer::kCmdQueryEULAAcceptance, app_cmd);
252 // Adds the "quick-enable-application-host" Google Update product command.
253 void FakeProductState::AddQuickEnableApplicationHostCommand(
254 BrowserDistribution::Type dist_type,
255 Level install_level,
256 const char* version,
257 int channel_modifiers) {
258 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES);
259 DCHECK_NE(channel_modifiers & CM_MULTI, 0);
261 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
262 channel_modifiers));
263 cmd_line.AppendSwitch(installer::switches::kMultiInstall);
264 cmd_line.AppendSwitch(installer::switches::kChromeAppLauncher);
265 cmd_line.AppendSwitch(installer::switches::kEnsureGoogleUpdatePresent);
266 AppCommand app_cmd(cmd_line.GetCommandLineString());
267 app_cmd.set_sends_pings(true);
268 app_cmd.set_is_web_accessible(true);
269 app_cmd.set_is_run_as_user(true);
270 commands_.Set(installer::kCmdQuickEnableApplicationHost, app_cmd);
273 } // namespace
275 // Fixture for testing the InstallationValidator. Errors logged by the
276 // validator are sent to an optional mock recipient (see
277 // set_validation_error_recipient) upon which expectations can be placed.
278 class InstallationValidatorTest
279 : public testing::TestWithParam<InstallationValidator::InstallationType> {
280 public:
282 // These shouldn't need to be public, but there seems to be some interaction
283 // with parameterized tests that requires it.
284 static void SetUpTestCase();
285 static void TearDownTestCase();
287 // Returns the multi channel modifiers for a given installation type.
288 static int GetChannelModifiers(InstallationValidator::InstallationType type);
290 protected:
291 typedef std::map<InstallationValidator::InstallationType, int>
292 InstallationTypeToModifiers;
294 class ValidationErrorRecipient {
295 public:
296 virtual ~ValidationErrorRecipient() { }
297 virtual void ReceiveValidationError(const char* file,
298 int line,
299 const char* message) = 0;
301 class MockValidationErrorRecipient : public ValidationErrorRecipient {
302 public:
303 MOCK_METHOD3(ReceiveValidationError, void(const char* file,
304 int line,
305 const char* message));
308 protected:
309 static bool HandleLogMessage(int severity,
310 const char* file,
311 int line,
312 size_t message_start,
313 const std::string& str);
314 static void set_validation_error_recipient(
315 ValidationErrorRecipient* recipient);
316 static void MakeProductState(
317 BrowserDistribution::Type prod_type,
318 InstallationValidator::InstallationType inst_type,
319 Level install_level,
320 Channel channel,
321 Vehicle vehicle,
322 FakeProductState* state);
323 static void MakeMachineState(
324 InstallationValidator::InstallationType inst_type,
325 Level install_level,
326 Channel channel,
327 Vehicle vehicle,
328 FakeInstallationState* state);
329 virtual void TearDown();
331 static logging::LogMessageHandlerFunction old_log_message_handler_;
332 static ValidationErrorRecipient* validation_error_recipient_;
333 static InstallationTypeToModifiers* type_to_modifiers_;
336 // static
337 logging::LogMessageHandlerFunction
338 InstallationValidatorTest::old_log_message_handler_ = NULL;
340 // static
341 InstallationValidatorTest::ValidationErrorRecipient*
342 InstallationValidatorTest::validation_error_recipient_ = NULL;
344 // static
345 InstallationValidatorTest::InstallationTypeToModifiers*
346 InstallationValidatorTest::type_to_modifiers_ = NULL;
348 // static
349 int InstallationValidatorTest::GetChannelModifiers(
350 InstallationValidator::InstallationType type) {
351 DCHECK(type_to_modifiers_);
352 DCHECK(type_to_modifiers_->find(type) != type_to_modifiers_->end());
354 return (*type_to_modifiers_)[type];
357 // static
358 void InstallationValidatorTest::SetUpTestCase() {
359 DCHECK(type_to_modifiers_ == NULL);
360 old_log_message_handler_ = logging::GetLogMessageHandler();
361 logging::SetLogMessageHandler(&HandleLogMessage);
363 type_to_modifiers_ = new InstallationTypeToModifiers();
364 InstallationTypeToModifiers& ttm = *type_to_modifiers_;
365 ttm[InstallationValidator::NO_PRODUCTS] = 0;
366 ttm[InstallationValidator::CHROME_SINGLE] = 0;
367 ttm[InstallationValidator::CHROME_MULTI] = CM_MULTI | CM_CHROME;
368 ttm[InstallationValidator::CHROME_FRAME_SINGLE] = 0;
369 ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE] = 0;
370 ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI] =
371 CM_MULTI | CM_CHROME;
372 ttm[InstallationValidator::CHROME_FRAME_MULTI] = CM_MULTI | CM_CHROME_FRAME;
373 ttm[InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI] =
374 CM_MULTI | CM_CHROME_FRAME | CM_CHROME;
377 // static
378 void InstallationValidatorTest::TearDownTestCase() {
379 logging::SetLogMessageHandler(old_log_message_handler_);
380 old_log_message_handler_ = NULL;
382 delete type_to_modifiers_;
383 type_to_modifiers_ = NULL;
386 // static
387 bool InstallationValidatorTest::HandleLogMessage(int severity,
388 const char* file,
389 int line,
390 size_t message_start,
391 const std::string& str) {
392 // All validation failures result in LOG(ERROR)
393 if (severity == logging::LOG_ERROR && !str.empty()) {
394 // Remove the trailing newline, if present.
395 size_t message_length = str.size() - message_start;
396 if (*str.rbegin() == '\n')
397 --message_length;
398 if (validation_error_recipient_ != NULL) {
399 validation_error_recipient_->ReceiveValidationError(
400 file, line, str.substr(message_start, message_length).c_str());
401 } else {
402 // Fail the test if an error wasn't handled.
403 ADD_FAILURE_AT(file, line)
404 << base::StringPiece(str.c_str() + message_start, message_length);
406 return true;
409 if (old_log_message_handler_ != NULL)
410 return (old_log_message_handler_)(severity, file, line, message_start, str);
412 return false;
415 // static
416 void InstallationValidatorTest::set_validation_error_recipient(
417 ValidationErrorRecipient* recipient) {
418 validation_error_recipient_ = recipient;
421 // static
422 // Populates |state| with the state of a valid installation of product
423 // |prod_type|. |inst_type| dictates properties of the installation
424 // (multi-install, etc).
425 void InstallationValidatorTest::MakeProductState(
426 BrowserDistribution::Type prod_type,
427 InstallationValidator::InstallationType inst_type,
428 Level install_level,
429 Channel channel,
430 Vehicle vehicle,
431 FakeProductState* state) {
432 DCHECK(state);
434 const bool is_multi_install =
435 prod_type == BrowserDistribution::CHROME_BINARIES ||
436 (prod_type == BrowserDistribution::CHROME_BROWSER &&
437 (inst_type & InstallationValidator::ProductBits::CHROME_MULTI) != 0) ||
438 (prod_type == BrowserDistribution::CHROME_FRAME &&
439 (inst_type &
440 InstallationValidator::ProductBits::CHROME_FRAME_MULTI) != 0);
442 const wchar_t* const* channels = &kChromeChannels[0];
443 if (prod_type == BrowserDistribution::CHROME_FRAME && !is_multi_install)
444 channels = &kChromeFrameChannels[0]; // SxS GCF has its own channel names.
445 const int channel_modifiers =
446 is_multi_install ? GetChannelModifiers(inst_type) : 0;
448 state->Clear();
449 state->SetChannel(channels[channel], channel_modifiers);
450 state->SetVersion(chrome::kChromeVersion);
451 state->SetUninstallCommand(prod_type, install_level, chrome::kChromeVersion,
452 channel_modifiers, vehicle);
453 state->set_multi_install(is_multi_install);
454 if (prod_type == BrowserDistribution::CHROME_BINARIES) {
455 state->AddQueryEULAAcceptanceCommand(prod_type,
456 install_level,
457 chrome::kChromeVersion,
458 channel_modifiers);
460 if (prod_type == BrowserDistribution::CHROME_BINARIES) {
461 state->AddQuickEnableApplicationHostCommand(prod_type,
462 install_level,
463 chrome::kChromeVersion,
464 channel_modifiers);
466 if (prod_type == BrowserDistribution::CHROME_BROWSER) {
467 state->AddOsUpgradeCommand(prod_type,
468 install_level,
469 chrome::kChromeVersion,
470 channel_modifiers);
474 // static
475 // Populates |state| with the state of a valid installation of |inst_type|.
476 void InstallationValidatorTest::MakeMachineState(
477 InstallationValidator::InstallationType inst_type,
478 Level install_level,
479 Channel channel,
480 Vehicle vehicle,
481 FakeInstallationState* state) {
482 DCHECK(state);
484 static const int kChromeMask =
485 (InstallationValidator::ProductBits::CHROME_SINGLE |
486 InstallationValidator::ProductBits::CHROME_MULTI);
487 static const int kChromeFrameMask =
488 (InstallationValidator::ProductBits::CHROME_FRAME_SINGLE |
489 InstallationValidator::ProductBits::CHROME_FRAME_MULTI);
490 static const int kBinariesMask =
491 (InstallationValidator::ProductBits::CHROME_MULTI |
492 InstallationValidator::ProductBits::CHROME_FRAME_MULTI);
494 FakeProductState prod_state;
496 if ((inst_type & kChromeMask) != 0) {
497 MakeProductState(BrowserDistribution::CHROME_BROWSER, inst_type,
498 install_level, channel, vehicle, &prod_state);
499 state->SetProductState(BrowserDistribution::CHROME_BROWSER, install_level,
500 prod_state);
503 if ((inst_type & kChromeFrameMask) != 0) {
504 MakeProductState(BrowserDistribution::CHROME_FRAME, inst_type,
505 install_level, channel, vehicle, &prod_state);
506 state->SetProductState(BrowserDistribution::CHROME_FRAME, install_level,
507 prod_state);
510 if ((inst_type & kBinariesMask) != 0) {
511 MakeProductState(BrowserDistribution::CHROME_BINARIES, inst_type,
512 install_level, channel, vehicle, &prod_state);
513 state->SetProductState(BrowserDistribution::CHROME_BINARIES, install_level,
514 prod_state);
518 void InstallationValidatorTest::TearDown() {
519 validation_error_recipient_ = NULL;
522 // Builds a proper machine state for a given InstallationType, then validates
523 // it.
524 TEST_P(InstallationValidatorTest, TestValidInstallation) {
525 const InstallationValidator::InstallationType inst_type = GetParam();
526 FakeInstallationState machine_state;
527 InstallationValidator::InstallationType type;
528 StrictMock<MockValidationErrorRecipient> recipient;
529 set_validation_error_recipient(&recipient);
531 MakeMachineState(inst_type, SYSTEM_LEVEL, STABLE_CHANNEL, GOOGLE_UPDATE,
532 &machine_state);
533 EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState(
534 machine_state, true, &type));
535 EXPECT_EQ(inst_type, type);
538 // Run the test for all installation types.
539 INSTANTIATE_TEST_CASE_P(
540 AllValidInstallations,
541 InstallationValidatorTest,
542 Values(InstallationValidator::NO_PRODUCTS,
543 InstallationValidator::CHROME_SINGLE,
544 InstallationValidator::CHROME_MULTI,
545 InstallationValidator::CHROME_FRAME_SINGLE,
546 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE,
547 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI,
548 InstallationValidator::CHROME_FRAME_MULTI,
549 InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI));