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.
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;
27 using testing::StrictMock;
28 using testing::Values;
53 enum ChannelModifier {
56 CM_CHROME_FRAME = 0x04,
61 const wchar_t* const kChromeChannels[] = {
67 const wchar_t* const kChromeFrameChannels[] = {
73 class FakeProductState : public ProductState {
75 void SetChannel(const wchar_t* base, int channel_modifiers);
76 void SetVersion(const char* version);
77 void SetUninstallCommand(BrowserDistribution::Type dist_type,
80 int channel_modifiers,
82 void AddInstallExtensionCommand(BrowserDistribution::Type dist_type,
85 int channel_modifiers);
86 void AddOsUpgradeCommand(BrowserDistribution::Type dist_type,
89 int channel_modifiers);
90 void AddQueryEULAAcceptanceCommand(BrowserDistribution::Type dist_type,
93 int channel_modifiers);
94 void AddQuickEnableApplicationHostCommand(BrowserDistribution::Type dist_type,
97 int channel_modifiers);
98 void AddQuickEnableCfCommand(BrowserDistribution::Type dist_type,
101 int channel_modifiers);
102 void set_multi_install(bool is_multi_install) {
103 multi_install_ = is_multi_install;
105 installer::AppCommands& commands() { return commands_; }
108 struct ChannelMethodForModifier {
109 ChannelModifier modifier;
110 bool (ChannelInfo::*method)(bool value);
113 static base::FilePath GetSetupPath(
114 BrowserDistribution::Type dist_type,
116 int channel_modifiers);
118 static base::FilePath GetSetupExePath(
119 BrowserDistribution::Type dist_type,
122 int channel_modifiers);
124 static const ChannelMethodForModifier kChannelMethods[];
127 class FakeInstallationState : public InstallationState {
129 void SetProductState(BrowserDistribution::Type type,
131 const ProductState& product) {
132 GetProducts(install_level)[IndexFromDistType(type)].CopyFrom(product);
136 ProductState* GetProducts(Level install_level) {
137 return install_level == USER_LEVEL ? user_products_ : system_products_;
142 const FakeProductState::ChannelMethodForModifier
143 FakeProductState::kChannelMethods[] = {
144 { CM_MULTI, &ChannelInfo::SetMultiInstall },
145 { CM_CHROME, &ChannelInfo::SetChrome },
146 { CM_CHROME_FRAME, &ChannelInfo::SetChromeFrame },
147 { CM_READY_MODE, &ChannelInfo::SetReadyMode },
148 { CM_FULL, &ChannelInfo::SetFullSuffix }
152 base::FilePath FakeProductState::GetSetupPath(
153 BrowserDistribution::Type dist_type,
155 int channel_modifiers) {
156 const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0;
157 return installer::GetChromeInstallPath(
158 install_level == SYSTEM_LEVEL,
159 BrowserDistribution::GetSpecificDistribution(is_multi_install ?
160 BrowserDistribution::CHROME_BINARIES : dist_type));
164 base::FilePath FakeProductState::GetSetupExePath(
165 BrowserDistribution::Type dist_type,
168 int channel_modifiers) {
169 base::FilePath setup_path = GetSetupPath(dist_type, install_level,
172 .AppendASCII(version)
173 .Append(installer::kInstallerDir)
174 .Append(installer::kSetupExe);
177 // Sets the channel_ member of this instance according to a base channel value
178 // and a set of modifiers.
179 void FakeProductState::SetChannel(const wchar_t* base, int channel_modifiers) {
180 channel_.set_value(base);
181 for (size_t i = 0; i < arraysize(kChannelMethods); ++i) {
182 if ((channel_modifiers & kChannelMethods[i].modifier) != 0)
183 (channel_.*kChannelMethods[i].method)(true);
187 void FakeProductState::SetVersion(const char* version) {
188 version_.reset(version == NULL ? NULL : new Version(version));
191 // Sets the uninstall command for this object.
192 void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type,
195 int channel_modifiers,
199 const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0;
200 uninstall_command_ = CommandLine(GetSetupExePath(dist_type, install_level,
201 version, channel_modifiers));
202 uninstall_command_.AppendSwitch(installer::switches::kUninstall);
203 if (install_level == SYSTEM_LEVEL)
204 uninstall_command_.AppendSwitch(installer::switches::kSystemLevel);
205 if (is_multi_install) {
206 uninstall_command_.AppendSwitch(installer::switches::kMultiInstall);
207 if (dist_type == BrowserDistribution::CHROME_BROWSER) {
208 uninstall_command_.AppendSwitch(installer::switches::kChrome);
209 if ((channel_modifiers & CM_READY_MODE) != 0) {
210 uninstall_command_.AppendSwitch(installer::switches::kChromeFrame);
211 uninstall_command_.AppendSwitch(
212 installer::switches::kChromeFrameReadyMode);
214 } else if (dist_type == BrowserDistribution::CHROME_FRAME) {
215 uninstall_command_.AppendSwitch(installer::switches::kChromeFrame);
216 if ((channel_modifiers & CM_READY_MODE) != 0) {
217 uninstall_command_.AppendSwitch(
218 installer::switches::kChromeFrameReadyMode);
221 } else if (dist_type == BrowserDistribution::CHROME_FRAME) {
222 uninstall_command_.AppendSwitch(installer::switches::kChromeFrame);
225 uninstall_command_.AppendSwitch(installer::switches::kMsi);
228 // Adds the "install-extension" Google Update product command.
229 void FakeProductState::AddInstallExtensionCommand(
230 BrowserDistribution::Type dist_type,
233 int channel_modifiers) {
234 // Right now only Chrome browser uses this.
235 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BROWSER);
237 CommandLine cmd_line(GetSetupPath(dist_type, install_level,
239 Append(installer::kChromeExe));
240 cmd_line.AppendSwitchASCII(::switches::kLimitedInstallFromWebstore, "%1");
241 AppCommand app_cmd(cmd_line.GetCommandLineString());
242 app_cmd.set_sends_pings(true);
243 app_cmd.set_is_web_accessible(true);
244 app_cmd.set_is_run_as_user(true);
245 commands_.Set(installer::kCmdInstallExtension, app_cmd);
248 // Adds the "on-os-upgrade" Google Update product command.
249 void FakeProductState::AddOsUpgradeCommand(BrowserDistribution::Type dist_type,
252 int channel_modifiers) {
253 // Right now only Chrome browser uses this.
254 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BROWSER);
256 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
258 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
259 // Imitating ChromeBrowserOperations::AppendProductFlags().
260 if ((channel_modifiers & CM_MULTI) != 0) {
261 cmd_line.AppendSwitch(installer::switches::kMultiInstall);
262 cmd_line.AppendSwitch(installer::switches::kChrome);
264 if (install_level == SYSTEM_LEVEL)
265 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
266 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
267 AppCommand app_cmd(cmd_line.GetCommandLineString());
268 app_cmd.set_is_auto_run_on_os_upgrade(true);
269 commands_.Set(installer::kCmdOnOsUpgrade, app_cmd);
272 // Adds the "query-eula-acceptance" Google Update product command.
273 void FakeProductState::AddQueryEULAAcceptanceCommand(
274 BrowserDistribution::Type dist_type,
277 int channel_modifiers) {
278 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES);
280 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
282 cmd_line.AppendSwitch(installer::switches::kQueryEULAAcceptance);
283 if (install_level == SYSTEM_LEVEL)
284 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
285 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
286 AppCommand app_cmd(cmd_line.GetCommandLineString());
287 app_cmd.set_is_web_accessible(true);
288 app_cmd.set_is_run_as_user(true);
289 commands_.Set(installer::kCmdQueryEULAAcceptance, app_cmd);
292 // Adds the "quick-enable-application-host" Google Update product command.
293 void FakeProductState::AddQuickEnableApplicationHostCommand(
294 BrowserDistribution::Type dist_type,
297 int channel_modifiers) {
298 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES);
299 DCHECK_NE(channel_modifiers & CM_MULTI, 0);
301 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
303 cmd_line.AppendSwitch(installer::switches::kMultiInstall);
304 cmd_line.AppendSwitch(installer::switches::kChromeAppLauncher);
305 cmd_line.AppendSwitch(installer::switches::kEnsureGoogleUpdatePresent);
306 AppCommand app_cmd(cmd_line.GetCommandLineString());
307 app_cmd.set_sends_pings(true);
308 app_cmd.set_is_web_accessible(true);
309 app_cmd.set_is_run_as_user(true);
310 commands_.Set(installer::kCmdQuickEnableApplicationHost, app_cmd);
313 // Adds the "quick-enable-cf" Google Update product command.
314 void FakeProductState::AddQuickEnableCfCommand(
315 BrowserDistribution::Type dist_type,
318 int channel_modifiers) {
319 DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES);
320 DCHECK_NE(channel_modifiers & CM_MULTI, 0);
322 CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version,
324 cmd_line.AppendSwitch(installer::switches::kMultiInstall);
325 if (install_level == SYSTEM_LEVEL)
326 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
327 cmd_line.AppendSwitch(installer::switches::kChromeFrameQuickEnable);
328 AppCommand app_cmd(cmd_line.GetCommandLineString());
329 app_cmd.set_sends_pings(true);
330 app_cmd.set_is_web_accessible(true);
331 commands_.Set(installer::kCmdQuickEnableCf, app_cmd);
336 // Fixture for testing the InstallationValidator. Errors logged by the
337 // validator are sent to an optional mock recipient (see
338 // set_validation_error_recipient) upon which expectations can be placed.
339 class InstallationValidatorTest
340 : public testing::TestWithParam<InstallationValidator::InstallationType> {
343 // These shouldn't need to be public, but there seems to be some interaction
344 // with parameterized tests that requires it.
345 static void SetUpTestCase();
346 static void TearDownTestCase();
348 // Returns the multi channel modifiers for a given installation type.
349 static int GetChannelModifiers(InstallationValidator::InstallationType type);
352 typedef std::map<InstallationValidator::InstallationType, int>
353 InstallationTypeToModifiers;
355 class ValidationErrorRecipient {
357 virtual ~ValidationErrorRecipient() { }
358 virtual void ReceiveValidationError(const char* file,
360 const char* message) = 0;
362 class MockValidationErrorRecipient : public ValidationErrorRecipient {
364 MOCK_METHOD3(ReceiveValidationError, void(const char* file,
366 const char* message));
370 static bool HandleLogMessage(int severity,
373 size_t message_start,
374 const std::string& str);
375 static void set_validation_error_recipient(
376 ValidationErrorRecipient* recipient);
377 static void MakeProductState(
378 BrowserDistribution::Type prod_type,
379 InstallationValidator::InstallationType inst_type,
383 FakeProductState* state);
384 static void MakeMachineState(
385 InstallationValidator::InstallationType inst_type,
389 FakeInstallationState* state);
390 virtual void TearDown();
392 static logging::LogMessageHandlerFunction old_log_message_handler_;
393 static ValidationErrorRecipient* validation_error_recipient_;
394 static InstallationTypeToModifiers* type_to_modifiers_;
398 logging::LogMessageHandlerFunction
399 InstallationValidatorTest::old_log_message_handler_ = NULL;
402 InstallationValidatorTest::ValidationErrorRecipient*
403 InstallationValidatorTest::validation_error_recipient_ = NULL;
406 InstallationValidatorTest::InstallationTypeToModifiers*
407 InstallationValidatorTest::type_to_modifiers_ = NULL;
410 int InstallationValidatorTest::GetChannelModifiers(
411 InstallationValidator::InstallationType type) {
412 DCHECK(type_to_modifiers_);
413 DCHECK(type_to_modifiers_->find(type) != type_to_modifiers_->end());
415 return (*type_to_modifiers_)[type];
419 void InstallationValidatorTest::SetUpTestCase() {
420 DCHECK(type_to_modifiers_ == NULL);
421 old_log_message_handler_ = logging::GetLogMessageHandler();
422 logging::SetLogMessageHandler(&HandleLogMessage);
424 type_to_modifiers_ = new InstallationTypeToModifiers();
425 InstallationTypeToModifiers& ttm = *type_to_modifiers_;
426 ttm[InstallationValidator::NO_PRODUCTS] = 0;
427 ttm[InstallationValidator::CHROME_SINGLE] = 0;
428 ttm[InstallationValidator::CHROME_MULTI] = CM_MULTI | CM_CHROME;
429 ttm[InstallationValidator::CHROME_FRAME_SINGLE] = 0;
430 ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE] = 0;
431 ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI] =
432 CM_MULTI | CM_CHROME;
433 ttm[InstallationValidator::CHROME_FRAME_MULTI] = CM_MULTI | CM_CHROME_FRAME;
434 ttm[InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI] =
435 CM_MULTI | CM_CHROME_FRAME | CM_CHROME;
436 ttm[InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI] =
437 CM_MULTI | CM_CHROME_FRAME | CM_CHROME | CM_READY_MODE;
441 void InstallationValidatorTest::TearDownTestCase() {
442 logging::SetLogMessageHandler(old_log_message_handler_);
443 old_log_message_handler_ = NULL;
445 delete type_to_modifiers_;
446 type_to_modifiers_ = NULL;
450 bool InstallationValidatorTest::HandleLogMessage(int severity,
453 size_t message_start,
454 const std::string& str) {
455 // All validation failures result in LOG(ERROR)
456 if (severity == logging::LOG_ERROR && !str.empty()) {
457 // Remove the trailing newline, if present.
458 size_t message_length = str.size() - message_start;
459 if (*str.rbegin() == '\n')
461 if (validation_error_recipient_ != NULL) {
462 validation_error_recipient_->ReceiveValidationError(
463 file, line, str.substr(message_start, message_length).c_str());
465 // Fail the test if an error wasn't handled.
466 ADD_FAILURE_AT(file, line)
467 << base::StringPiece(str.c_str() + message_start, message_length);
472 if (old_log_message_handler_ != NULL)
473 return (old_log_message_handler_)(severity, file, line, message_start, str);
479 void InstallationValidatorTest::set_validation_error_recipient(
480 ValidationErrorRecipient* recipient) {
481 validation_error_recipient_ = recipient;
485 // Populates |state| with the state of a valid installation of product
486 // |prod_type|. |inst_type| dictates properties of the installation
487 // (multi-install, ready-mode, etc).
488 void InstallationValidatorTest::MakeProductState(
489 BrowserDistribution::Type prod_type,
490 InstallationValidator::InstallationType inst_type,
494 FakeProductState* state) {
497 const bool is_multi_install =
498 prod_type == BrowserDistribution::CHROME_BINARIES ||
499 (prod_type == BrowserDistribution::CHROME_BROWSER &&
500 (inst_type & InstallationValidator::ProductBits::CHROME_MULTI) != 0) ||
501 (prod_type == BrowserDistribution::CHROME_FRAME &&
503 (InstallationValidator::ProductBits::CHROME_FRAME_MULTI |
504 InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE)) != 0);
506 const wchar_t* const* channels = &kChromeChannels[0];
507 if (prod_type == BrowserDistribution::CHROME_FRAME && !is_multi_install)
508 channels = &kChromeFrameChannels[0]; // SxS GCF has its own channel names.
509 const int channel_modifiers =
510 is_multi_install ? GetChannelModifiers(inst_type) : 0;
513 state->SetChannel(channels[channel], channel_modifiers);
514 state->SetVersion(chrome::kChromeVersion);
515 state->SetUninstallCommand(prod_type, install_level, chrome::kChromeVersion,
516 channel_modifiers, vehicle);
517 state->set_multi_install(is_multi_install);
518 if (prod_type == BrowserDistribution::CHROME_BINARIES) {
519 if (inst_type == InstallationValidator::CHROME_MULTI ||
521 InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI) {
522 state->AddQuickEnableCfCommand(prod_type, install_level,
523 chrome::kChromeVersion, channel_modifiers);
525 state->AddQueryEULAAcceptanceCommand(prod_type,
527 chrome::kChromeVersion,
530 if (prod_type == BrowserDistribution::CHROME_BINARIES) {
531 state->AddQuickEnableApplicationHostCommand(prod_type,
533 chrome::kChromeVersion,
536 if (prod_type == BrowserDistribution::CHROME_BROWSER) {
537 state->AddOsUpgradeCommand(prod_type,
539 chrome::kChromeVersion,
541 state->AddInstallExtensionCommand(prod_type,
543 chrome::kChromeVersion,
549 // Populates |state| with the state of a valid installation of |inst_type|.
550 void InstallationValidatorTest::MakeMachineState(
551 InstallationValidator::InstallationType inst_type,
555 FakeInstallationState* state) {
558 static const int kChromeMask =
559 (InstallationValidator::ProductBits::CHROME_SINGLE |
560 InstallationValidator::ProductBits::CHROME_MULTI);
561 static const int kChromeFrameMask =
562 (InstallationValidator::ProductBits::CHROME_FRAME_SINGLE |
563 InstallationValidator::ProductBits::CHROME_FRAME_MULTI |
564 InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE);
565 static const int kBinariesMask =
566 (InstallationValidator::ProductBits::CHROME_MULTI |
567 InstallationValidator::ProductBits::CHROME_FRAME_MULTI |
568 InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE);
570 FakeProductState prod_state;
572 if ((inst_type & kChromeMask) != 0) {
573 MakeProductState(BrowserDistribution::CHROME_BROWSER, inst_type,
574 install_level, channel, vehicle, &prod_state);
575 state->SetProductState(BrowserDistribution::CHROME_BROWSER, install_level,
579 if ((inst_type & kChromeFrameMask) != 0) {
580 MakeProductState(BrowserDistribution::CHROME_FRAME, inst_type,
581 install_level, channel, vehicle, &prod_state);
582 state->SetProductState(BrowserDistribution::CHROME_FRAME, install_level,
586 if ((inst_type & kBinariesMask) != 0) {
587 MakeProductState(BrowserDistribution::CHROME_BINARIES, inst_type,
588 install_level, channel, vehicle, &prod_state);
589 state->SetProductState(BrowserDistribution::CHROME_BINARIES, install_level,
594 void InstallationValidatorTest::TearDown() {
595 validation_error_recipient_ = NULL;
598 // Builds a proper machine state for a given InstallationType, then validates
600 TEST_P(InstallationValidatorTest, TestValidInstallation) {
601 const InstallationValidator::InstallationType inst_type = GetParam();
602 FakeInstallationState machine_state;
603 InstallationValidator::InstallationType type;
604 StrictMock<MockValidationErrorRecipient> recipient;
605 set_validation_error_recipient(&recipient);
607 MakeMachineState(inst_type, SYSTEM_LEVEL, STABLE_CHANNEL, GOOGLE_UPDATE,
609 EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState(
610 machine_state, true, &type));
611 EXPECT_EQ(inst_type, type);
614 // Run the test for all installation types.
615 INSTANTIATE_TEST_CASE_P(
616 AllValidInstallations,
617 InstallationValidatorTest,
618 Values(InstallationValidator::NO_PRODUCTS,
619 InstallationValidator::CHROME_SINGLE,
620 InstallationValidator::CHROME_MULTI,
621 InstallationValidator::CHROME_FRAME_SINGLE,
622 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE,
623 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI,
624 InstallationValidator::CHROME_FRAME_MULTI,
625 InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI,
626 InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI));