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,
60 const wchar_t* const kChromeChannels
[] = {
66 const wchar_t* const kChromeFrameChannels
[] = {
72 class FakeProductState
: public ProductState
{
74 void SetChannel(const wchar_t* base
, int channel_modifiers
);
75 void SetVersion(const char* version
);
76 void SetUninstallCommand(BrowserDistribution::Type dist_type
,
79 int channel_modifiers
,
81 void AddInstallExtensionCommand(BrowserDistribution::Type dist_type
,
84 int channel_modifiers
);
85 void AddOsUpgradeCommand(BrowserDistribution::Type dist_type
,
88 int channel_modifiers
);
89 void AddQueryEULAAcceptanceCommand(BrowserDistribution::Type dist_type
,
92 int channel_modifiers
);
93 void AddQuickEnableApplicationHostCommand(BrowserDistribution::Type dist_type
,
96 int channel_modifiers
);
97 void set_multi_install(bool is_multi_install
) {
98 multi_install_
= is_multi_install
;
100 installer::AppCommands
& commands() { return commands_
; }
103 struct ChannelMethodForModifier
{
104 ChannelModifier modifier
;
105 bool (ChannelInfo::*method
)(bool value
);
108 static base::FilePath
GetSetupPath(
109 BrowserDistribution::Type dist_type
,
111 int channel_modifiers
);
113 static base::FilePath
GetSetupExePath(
114 BrowserDistribution::Type dist_type
,
117 int channel_modifiers
);
119 static const ChannelMethodForModifier kChannelMethods
[];
122 class FakeInstallationState
: public InstallationState
{
124 void SetProductState(BrowserDistribution::Type type
,
126 const ProductState
& product
) {
127 GetProducts(install_level
)[IndexFromDistType(type
)].CopyFrom(product
);
131 ProductState
* GetProducts(Level install_level
) {
132 return install_level
== USER_LEVEL
? user_products_
: system_products_
;
137 const FakeProductState::ChannelMethodForModifier
138 FakeProductState::kChannelMethods
[] = {
139 { CM_MULTI
, &ChannelInfo::SetMultiInstall
},
140 { CM_CHROME
, &ChannelInfo::SetChrome
},
141 { CM_CHROME_FRAME
, &ChannelInfo::SetChromeFrame
},
142 { CM_FULL
, &ChannelInfo::SetFullSuffix
}
146 base::FilePath
FakeProductState::GetSetupPath(
147 BrowserDistribution::Type dist_type
,
149 int channel_modifiers
) {
150 const bool is_multi_install
= (channel_modifiers
& CM_MULTI
) != 0;
151 return installer::GetChromeInstallPath(
152 install_level
== SYSTEM_LEVEL
,
153 BrowserDistribution::GetSpecificDistribution(is_multi_install
?
154 BrowserDistribution::CHROME_BINARIES
: dist_type
));
158 base::FilePath
FakeProductState::GetSetupExePath(
159 BrowserDistribution::Type dist_type
,
162 int channel_modifiers
) {
163 base::FilePath setup_path
= GetSetupPath(dist_type
, install_level
,
166 .AppendASCII(version
)
167 .Append(installer::kInstallerDir
)
168 .Append(installer::kSetupExe
);
171 // Sets the channel_ member of this instance according to a base channel value
172 // and a set of modifiers.
173 void FakeProductState::SetChannel(const wchar_t* base
, int channel_modifiers
) {
174 channel_
.set_value(base
);
175 for (size_t i
= 0; i
< arraysize(kChannelMethods
); ++i
) {
176 if ((channel_modifiers
& kChannelMethods
[i
].modifier
) != 0)
177 (channel_
.*kChannelMethods
[i
].method
)(true);
181 void FakeProductState::SetVersion(const char* version
) {
182 version_
.reset(version
== NULL
? NULL
: new Version(version
));
185 // Sets the uninstall command for this object.
186 void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type
,
189 int channel_modifiers
,
193 const bool is_multi_install
= (channel_modifiers
& CM_MULTI
) != 0;
194 uninstall_command_
= CommandLine(GetSetupExePath(dist_type
, install_level
,
195 version
, channel_modifiers
));
196 uninstall_command_
.AppendSwitch(installer::switches::kUninstall
);
197 if (install_level
== SYSTEM_LEVEL
)
198 uninstall_command_
.AppendSwitch(installer::switches::kSystemLevel
);
199 if (is_multi_install
) {
200 uninstall_command_
.AppendSwitch(installer::switches::kMultiInstall
);
201 if (dist_type
== BrowserDistribution::CHROME_BROWSER
)
202 uninstall_command_
.AppendSwitch(installer::switches::kChrome
);
203 else if (dist_type
== BrowserDistribution::CHROME_FRAME
)
204 uninstall_command_
.AppendSwitch(installer::switches::kChromeFrame
);
205 } else if (dist_type
== BrowserDistribution::CHROME_FRAME
) {
206 uninstall_command_
.AppendSwitch(installer::switches::kChromeFrame
);
209 uninstall_command_
.AppendSwitch(installer::switches::kMsi
);
212 // Adds the "install-extension" Google Update product command.
213 void FakeProductState::AddInstallExtensionCommand(
214 BrowserDistribution::Type dist_type
,
217 int channel_modifiers
) {
218 // Right now only Chrome browser uses this.
219 DCHECK_EQ(dist_type
, BrowserDistribution::CHROME_BROWSER
);
221 CommandLine
cmd_line(GetSetupPath(dist_type
, install_level
,
223 Append(installer::kChromeExe
));
224 cmd_line
.AppendSwitchASCII(::switches::kLimitedInstallFromWebstore
, "%1");
225 AppCommand
app_cmd(cmd_line
.GetCommandLineString());
226 app_cmd
.set_sends_pings(true);
227 app_cmd
.set_is_web_accessible(true);
228 app_cmd
.set_is_run_as_user(true);
229 commands_
.Set(installer::kCmdInstallExtension
, app_cmd
);
232 // Adds the "on-os-upgrade" Google Update product command.
233 void FakeProductState::AddOsUpgradeCommand(BrowserDistribution::Type dist_type
,
236 int channel_modifiers
) {
237 // Right now only Chrome browser uses this.
238 DCHECK_EQ(dist_type
, BrowserDistribution::CHROME_BROWSER
);
240 CommandLine
cmd_line(GetSetupExePath(dist_type
, install_level
, version
,
242 cmd_line
.AppendSwitch(installer::switches::kOnOsUpgrade
);
243 // Imitating ChromeBrowserOperations::AppendProductFlags().
244 if ((channel_modifiers
& CM_MULTI
) != 0) {
245 cmd_line
.AppendSwitch(installer::switches::kMultiInstall
);
246 cmd_line
.AppendSwitch(installer::switches::kChrome
);
248 if (install_level
== SYSTEM_LEVEL
)
249 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
250 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
251 AppCommand
app_cmd(cmd_line
.GetCommandLineString());
252 app_cmd
.set_is_auto_run_on_os_upgrade(true);
253 commands_
.Set(installer::kCmdOnOsUpgrade
, app_cmd
);
256 // Adds the "query-eula-acceptance" Google Update product command.
257 void FakeProductState::AddQueryEULAAcceptanceCommand(
258 BrowserDistribution::Type dist_type
,
261 int channel_modifiers
) {
262 DCHECK_EQ(dist_type
, BrowserDistribution::CHROME_BINARIES
);
264 CommandLine
cmd_line(GetSetupExePath(dist_type
, install_level
, version
,
266 cmd_line
.AppendSwitch(installer::switches::kQueryEULAAcceptance
);
267 if (install_level
== SYSTEM_LEVEL
)
268 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
269 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
270 AppCommand
app_cmd(cmd_line
.GetCommandLineString());
271 app_cmd
.set_is_web_accessible(true);
272 app_cmd
.set_is_run_as_user(true);
273 commands_
.Set(installer::kCmdQueryEULAAcceptance
, app_cmd
);
276 // Adds the "quick-enable-application-host" Google Update product command.
277 void FakeProductState::AddQuickEnableApplicationHostCommand(
278 BrowserDistribution::Type dist_type
,
281 int channel_modifiers
) {
282 DCHECK_EQ(dist_type
, BrowserDistribution::CHROME_BINARIES
);
283 DCHECK_NE(channel_modifiers
& CM_MULTI
, 0);
285 CommandLine
cmd_line(GetSetupExePath(dist_type
, install_level
, version
,
287 cmd_line
.AppendSwitch(installer::switches::kMultiInstall
);
288 cmd_line
.AppendSwitch(installer::switches::kChromeAppLauncher
);
289 cmd_line
.AppendSwitch(installer::switches::kEnsureGoogleUpdatePresent
);
290 AppCommand
app_cmd(cmd_line
.GetCommandLineString());
291 app_cmd
.set_sends_pings(true);
292 app_cmd
.set_is_web_accessible(true);
293 app_cmd
.set_is_run_as_user(true);
294 commands_
.Set(installer::kCmdQuickEnableApplicationHost
, app_cmd
);
299 // Fixture for testing the InstallationValidator. Errors logged by the
300 // validator are sent to an optional mock recipient (see
301 // set_validation_error_recipient) upon which expectations can be placed.
302 class InstallationValidatorTest
303 : public testing::TestWithParam
<InstallationValidator::InstallationType
> {
306 // These shouldn't need to be public, but there seems to be some interaction
307 // with parameterized tests that requires it.
308 static void SetUpTestCase();
309 static void TearDownTestCase();
311 // Returns the multi channel modifiers for a given installation type.
312 static int GetChannelModifiers(InstallationValidator::InstallationType type
);
315 typedef std::map
<InstallationValidator::InstallationType
, int>
316 InstallationTypeToModifiers
;
318 class ValidationErrorRecipient
{
320 virtual ~ValidationErrorRecipient() { }
321 virtual void ReceiveValidationError(const char* file
,
323 const char* message
) = 0;
325 class MockValidationErrorRecipient
: public ValidationErrorRecipient
{
327 MOCK_METHOD3(ReceiveValidationError
, void(const char* file
,
329 const char* message
));
333 static bool HandleLogMessage(int severity
,
336 size_t message_start
,
337 const std::string
& str
);
338 static void set_validation_error_recipient(
339 ValidationErrorRecipient
* recipient
);
340 static void MakeProductState(
341 BrowserDistribution::Type prod_type
,
342 InstallationValidator::InstallationType inst_type
,
346 FakeProductState
* state
);
347 static void MakeMachineState(
348 InstallationValidator::InstallationType inst_type
,
352 FakeInstallationState
* state
);
353 virtual void TearDown();
355 static logging::LogMessageHandlerFunction old_log_message_handler_
;
356 static ValidationErrorRecipient
* validation_error_recipient_
;
357 static InstallationTypeToModifiers
* type_to_modifiers_
;
361 logging::LogMessageHandlerFunction
362 InstallationValidatorTest::old_log_message_handler_
= NULL
;
365 InstallationValidatorTest::ValidationErrorRecipient
*
366 InstallationValidatorTest::validation_error_recipient_
= NULL
;
369 InstallationValidatorTest::InstallationTypeToModifiers
*
370 InstallationValidatorTest::type_to_modifiers_
= NULL
;
373 int InstallationValidatorTest::GetChannelModifiers(
374 InstallationValidator::InstallationType type
) {
375 DCHECK(type_to_modifiers_
);
376 DCHECK(type_to_modifiers_
->find(type
) != type_to_modifiers_
->end());
378 return (*type_to_modifiers_
)[type
];
382 void InstallationValidatorTest::SetUpTestCase() {
383 DCHECK(type_to_modifiers_
== NULL
);
384 old_log_message_handler_
= logging::GetLogMessageHandler();
385 logging::SetLogMessageHandler(&HandleLogMessage
);
387 type_to_modifiers_
= new InstallationTypeToModifiers();
388 InstallationTypeToModifiers
& ttm
= *type_to_modifiers_
;
389 ttm
[InstallationValidator::NO_PRODUCTS
] = 0;
390 ttm
[InstallationValidator::CHROME_SINGLE
] = 0;
391 ttm
[InstallationValidator::CHROME_MULTI
] = CM_MULTI
| CM_CHROME
;
392 ttm
[InstallationValidator::CHROME_FRAME_SINGLE
] = 0;
393 ttm
[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE
] = 0;
394 ttm
[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI
] =
395 CM_MULTI
| CM_CHROME
;
396 ttm
[InstallationValidator::CHROME_FRAME_MULTI
] = CM_MULTI
| CM_CHROME_FRAME
;
397 ttm
[InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI
] =
398 CM_MULTI
| CM_CHROME_FRAME
| CM_CHROME
;
402 void InstallationValidatorTest::TearDownTestCase() {
403 logging::SetLogMessageHandler(old_log_message_handler_
);
404 old_log_message_handler_
= NULL
;
406 delete type_to_modifiers_
;
407 type_to_modifiers_
= NULL
;
411 bool InstallationValidatorTest::HandleLogMessage(int severity
,
414 size_t message_start
,
415 const std::string
& str
) {
416 // All validation failures result in LOG(ERROR)
417 if (severity
== logging::LOG_ERROR
&& !str
.empty()) {
418 // Remove the trailing newline, if present.
419 size_t message_length
= str
.size() - message_start
;
420 if (*str
.rbegin() == '\n')
422 if (validation_error_recipient_
!= NULL
) {
423 validation_error_recipient_
->ReceiveValidationError(
424 file
, line
, str
.substr(message_start
, message_length
).c_str());
426 // Fail the test if an error wasn't handled.
427 ADD_FAILURE_AT(file
, line
)
428 << base::StringPiece(str
.c_str() + message_start
, message_length
);
433 if (old_log_message_handler_
!= NULL
)
434 return (old_log_message_handler_
)(severity
, file
, line
, message_start
, str
);
440 void InstallationValidatorTest::set_validation_error_recipient(
441 ValidationErrorRecipient
* recipient
) {
442 validation_error_recipient_
= recipient
;
446 // Populates |state| with the state of a valid installation of product
447 // |prod_type|. |inst_type| dictates properties of the installation
448 // (multi-install, etc).
449 void InstallationValidatorTest::MakeProductState(
450 BrowserDistribution::Type prod_type
,
451 InstallationValidator::InstallationType inst_type
,
455 FakeProductState
* state
) {
458 const bool is_multi_install
=
459 prod_type
== BrowserDistribution::CHROME_BINARIES
||
460 (prod_type
== BrowserDistribution::CHROME_BROWSER
&&
461 (inst_type
& InstallationValidator::ProductBits::CHROME_MULTI
) != 0) ||
462 (prod_type
== BrowserDistribution::CHROME_FRAME
&&
464 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
) != 0);
466 const wchar_t* const* channels
= &kChromeChannels
[0];
467 if (prod_type
== BrowserDistribution::CHROME_FRAME
&& !is_multi_install
)
468 channels
= &kChromeFrameChannels
[0]; // SxS GCF has its own channel names.
469 const int channel_modifiers
=
470 is_multi_install
? GetChannelModifiers(inst_type
) : 0;
473 state
->SetChannel(channels
[channel
], channel_modifiers
);
474 state
->SetVersion(chrome::kChromeVersion
);
475 state
->SetUninstallCommand(prod_type
, install_level
, chrome::kChromeVersion
,
476 channel_modifiers
, vehicle
);
477 state
->set_multi_install(is_multi_install
);
478 if (prod_type
== BrowserDistribution::CHROME_BINARIES
) {
479 state
->AddQueryEULAAcceptanceCommand(prod_type
,
481 chrome::kChromeVersion
,
484 if (prod_type
== BrowserDistribution::CHROME_BINARIES
) {
485 state
->AddQuickEnableApplicationHostCommand(prod_type
,
487 chrome::kChromeVersion
,
490 if (prod_type
== BrowserDistribution::CHROME_BROWSER
) {
491 state
->AddOsUpgradeCommand(prod_type
,
493 chrome::kChromeVersion
,
495 state
->AddInstallExtensionCommand(prod_type
,
497 chrome::kChromeVersion
,
503 // Populates |state| with the state of a valid installation of |inst_type|.
504 void InstallationValidatorTest::MakeMachineState(
505 InstallationValidator::InstallationType inst_type
,
509 FakeInstallationState
* state
) {
512 static const int kChromeMask
=
513 (InstallationValidator::ProductBits::CHROME_SINGLE
|
514 InstallationValidator::ProductBits::CHROME_MULTI
);
515 static const int kChromeFrameMask
=
516 (InstallationValidator::ProductBits::CHROME_FRAME_SINGLE
|
517 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
);
518 static const int kBinariesMask
=
519 (InstallationValidator::ProductBits::CHROME_MULTI
|
520 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
);
522 FakeProductState prod_state
;
524 if ((inst_type
& kChromeMask
) != 0) {
525 MakeProductState(BrowserDistribution::CHROME_BROWSER
, inst_type
,
526 install_level
, channel
, vehicle
, &prod_state
);
527 state
->SetProductState(BrowserDistribution::CHROME_BROWSER
, install_level
,
531 if ((inst_type
& kChromeFrameMask
) != 0) {
532 MakeProductState(BrowserDistribution::CHROME_FRAME
, inst_type
,
533 install_level
, channel
, vehicle
, &prod_state
);
534 state
->SetProductState(BrowserDistribution::CHROME_FRAME
, install_level
,
538 if ((inst_type
& kBinariesMask
) != 0) {
539 MakeProductState(BrowserDistribution::CHROME_BINARIES
, inst_type
,
540 install_level
, channel
, vehicle
, &prod_state
);
541 state
->SetProductState(BrowserDistribution::CHROME_BINARIES
, install_level
,
546 void InstallationValidatorTest::TearDown() {
547 validation_error_recipient_
= NULL
;
550 // Builds a proper machine state for a given InstallationType, then validates
552 TEST_P(InstallationValidatorTest
, TestValidInstallation
) {
553 const InstallationValidator::InstallationType inst_type
= GetParam();
554 FakeInstallationState machine_state
;
555 InstallationValidator::InstallationType type
;
556 StrictMock
<MockValidationErrorRecipient
> recipient
;
557 set_validation_error_recipient(&recipient
);
559 MakeMachineState(inst_type
, SYSTEM_LEVEL
, STABLE_CHANNEL
, GOOGLE_UPDATE
,
561 EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState(
562 machine_state
, true, &type
));
563 EXPECT_EQ(inst_type
, type
);
566 // Run the test for all installation types.
567 INSTANTIATE_TEST_CASE_P(
568 AllValidInstallations
,
569 InstallationValidatorTest
,
570 Values(InstallationValidator::NO_PRODUCTS
,
571 InstallationValidator::CHROME_SINGLE
,
572 InstallationValidator::CHROME_MULTI
,
573 InstallationValidator::CHROME_FRAME_SINGLE
,
574 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE
,
575 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI
,
576 InstallationValidator::CHROME_FRAME_MULTI
,
577 InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI
));