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 AddOsUpgradeCommand(BrowserDistribution::Type dist_type
,
84 int channel_modifiers
);
85 void set_multi_install(bool is_multi_install
) {
86 multi_install_
= is_multi_install
;
88 installer::AppCommands
& commands() { return commands_
; }
91 struct ChannelMethodForModifier
{
92 ChannelModifier modifier
;
93 bool (ChannelInfo::*method
)(bool value
);
96 static base::FilePath
GetSetupPath(
97 BrowserDistribution::Type dist_type
,
99 int channel_modifiers
);
101 static base::FilePath
GetSetupExePath(
102 BrowserDistribution::Type dist_type
,
105 int channel_modifiers
);
107 static const ChannelMethodForModifier kChannelMethods
[];
110 class FakeInstallationState
: public InstallationState
{
112 void SetProductState(BrowserDistribution::Type type
,
114 const ProductState
& product
) {
115 GetProducts(install_level
)[IndexFromDistType(type
)].CopyFrom(product
);
119 ProductState
* GetProducts(Level install_level
) {
120 return install_level
== USER_LEVEL
? user_products_
: system_products_
;
125 const FakeProductState::ChannelMethodForModifier
126 FakeProductState::kChannelMethods
[] = {
127 { CM_MULTI
, &ChannelInfo::SetMultiInstall
},
128 { CM_CHROME
, &ChannelInfo::SetChrome
},
129 { CM_CHROME_FRAME
, &ChannelInfo::SetChromeFrame
},
130 { CM_FULL
, &ChannelInfo::SetFullSuffix
}
134 base::FilePath
FakeProductState::GetSetupPath(
135 BrowserDistribution::Type dist_type
,
137 int channel_modifiers
) {
138 const bool is_multi_install
= (channel_modifiers
& CM_MULTI
) != 0;
139 return installer::GetChromeInstallPath(
140 install_level
== SYSTEM_LEVEL
,
141 BrowserDistribution::GetSpecificDistribution(is_multi_install
?
142 BrowserDistribution::CHROME_BINARIES
: dist_type
));
146 base::FilePath
FakeProductState::GetSetupExePath(
147 BrowserDistribution::Type dist_type
,
150 int channel_modifiers
) {
151 base::FilePath setup_path
= GetSetupPath(dist_type
, install_level
,
154 .AppendASCII(version
)
155 .Append(installer::kInstallerDir
)
156 .Append(installer::kSetupExe
);
159 // Sets the channel_ member of this instance according to a base channel value
160 // and a set of modifiers.
161 void FakeProductState::SetChannel(const wchar_t* base
, int channel_modifiers
) {
162 channel_
.set_value(base
);
163 for (size_t i
= 0; i
< arraysize(kChannelMethods
); ++i
) {
164 if ((channel_modifiers
& kChannelMethods
[i
].modifier
) != 0)
165 (channel_
.*kChannelMethods
[i
].method
)(true);
169 void FakeProductState::SetVersion(const char* version
) {
170 version_
.reset(version
== NULL
? NULL
: new Version(version
));
173 // Sets the uninstall command for this object.
174 void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type
,
177 int channel_modifiers
,
181 const bool is_multi_install
= (channel_modifiers
& CM_MULTI
) != 0;
182 uninstall_command_
= base::CommandLine(
183 GetSetupExePath(dist_type
, install_level
, version
, channel_modifiers
));
184 uninstall_command_
.AppendSwitch(installer::switches::kUninstall
);
185 if (install_level
== SYSTEM_LEVEL
)
186 uninstall_command_
.AppendSwitch(installer::switches::kSystemLevel
);
187 if (is_multi_install
) {
188 uninstall_command_
.AppendSwitch(installer::switches::kMultiInstall
);
189 if (dist_type
== BrowserDistribution::CHROME_BROWSER
)
190 uninstall_command_
.AppendSwitch(installer::switches::kChrome
);
191 else if (dist_type
== BrowserDistribution::CHROME_FRAME
)
192 uninstall_command_
.AppendSwitch(installer::switches::kChromeFrame
);
193 } else if (dist_type
== BrowserDistribution::CHROME_FRAME
) {
194 uninstall_command_
.AppendSwitch(installer::switches::kChromeFrame
);
197 uninstall_command_
.AppendSwitch(installer::switches::kMsi
);
200 // Adds the "on-os-upgrade" Google Update product command.
201 void FakeProductState::AddOsUpgradeCommand(BrowserDistribution::Type dist_type
,
204 int channel_modifiers
) {
205 // Right now only Chrome browser uses this.
206 DCHECK_EQ(dist_type
, BrowserDistribution::CHROME_BROWSER
);
208 base::CommandLine
cmd_line(
209 GetSetupExePath(dist_type
, install_level
, version
, channel_modifiers
));
210 cmd_line
.AppendSwitch(installer::switches::kOnOsUpgrade
);
211 // Imitating ChromeBrowserOperations::AppendProductFlags().
212 if ((channel_modifiers
& CM_MULTI
) != 0) {
213 cmd_line
.AppendSwitch(installer::switches::kMultiInstall
);
214 cmd_line
.AppendSwitch(installer::switches::kChrome
);
216 if (install_level
== SYSTEM_LEVEL
)
217 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
218 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
219 AppCommand
app_cmd(cmd_line
.GetCommandLineString());
220 app_cmd
.set_is_auto_run_on_os_upgrade(true);
221 commands_
.Set(installer::kCmdOnOsUpgrade
, app_cmd
);
226 // Fixture for testing the InstallationValidator. Errors logged by the
227 // validator are sent to an optional mock recipient (see
228 // set_validation_error_recipient) upon which expectations can be placed.
229 class InstallationValidatorTest
230 : public testing::TestWithParam
<InstallationValidator::InstallationType
> {
233 // These shouldn't need to be public, but there seems to be some interaction
234 // with parameterized tests that requires it.
235 static void SetUpTestCase();
236 static void TearDownTestCase();
238 // Returns the multi channel modifiers for a given installation type.
239 static int GetChannelModifiers(InstallationValidator::InstallationType type
);
242 typedef std::map
<InstallationValidator::InstallationType
, int>
243 InstallationTypeToModifiers
;
245 class ValidationErrorRecipient
{
247 virtual ~ValidationErrorRecipient() { }
248 virtual void ReceiveValidationError(const char* file
,
250 const char* message
) = 0;
252 class MockValidationErrorRecipient
: public ValidationErrorRecipient
{
254 MOCK_METHOD3(ReceiveValidationError
, void(const char* file
,
256 const char* message
));
260 static bool HandleLogMessage(int severity
,
263 size_t message_start
,
264 const std::string
& str
);
265 static void set_validation_error_recipient(
266 ValidationErrorRecipient
* recipient
);
267 static void MakeProductState(
268 BrowserDistribution::Type prod_type
,
269 InstallationValidator::InstallationType inst_type
,
273 FakeProductState
* state
);
274 static void MakeMachineState(
275 InstallationValidator::InstallationType inst_type
,
279 FakeInstallationState
* state
);
280 virtual void TearDown();
282 static logging::LogMessageHandlerFunction old_log_message_handler_
;
283 static ValidationErrorRecipient
* validation_error_recipient_
;
284 static InstallationTypeToModifiers
* type_to_modifiers_
;
288 logging::LogMessageHandlerFunction
289 InstallationValidatorTest::old_log_message_handler_
= NULL
;
292 InstallationValidatorTest::ValidationErrorRecipient
*
293 InstallationValidatorTest::validation_error_recipient_
= NULL
;
296 InstallationValidatorTest::InstallationTypeToModifiers
*
297 InstallationValidatorTest::type_to_modifiers_
= NULL
;
300 int InstallationValidatorTest::GetChannelModifiers(
301 InstallationValidator::InstallationType type
) {
302 DCHECK(type_to_modifiers_
);
303 DCHECK(type_to_modifiers_
->find(type
) != type_to_modifiers_
->end());
305 return (*type_to_modifiers_
)[type
];
309 void InstallationValidatorTest::SetUpTestCase() {
310 DCHECK(type_to_modifiers_
== NULL
);
311 old_log_message_handler_
= logging::GetLogMessageHandler();
312 logging::SetLogMessageHandler(&HandleLogMessage
);
314 type_to_modifiers_
= new InstallationTypeToModifiers();
315 InstallationTypeToModifiers
& ttm
= *type_to_modifiers_
;
316 ttm
[InstallationValidator::NO_PRODUCTS
] = 0;
317 ttm
[InstallationValidator::CHROME_SINGLE
] = 0;
318 ttm
[InstallationValidator::CHROME_MULTI
] = CM_MULTI
| CM_CHROME
;
319 ttm
[InstallationValidator::CHROME_FRAME_SINGLE
] = 0;
320 ttm
[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE
] = 0;
321 ttm
[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI
] =
322 CM_MULTI
| CM_CHROME
;
323 ttm
[InstallationValidator::CHROME_FRAME_MULTI
] = CM_MULTI
| CM_CHROME_FRAME
;
324 ttm
[InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI
] =
325 CM_MULTI
| CM_CHROME_FRAME
| CM_CHROME
;
329 void InstallationValidatorTest::TearDownTestCase() {
330 logging::SetLogMessageHandler(old_log_message_handler_
);
331 old_log_message_handler_
= NULL
;
333 delete type_to_modifiers_
;
334 type_to_modifiers_
= NULL
;
338 bool InstallationValidatorTest::HandleLogMessage(int severity
,
341 size_t message_start
,
342 const std::string
& str
) {
343 // All validation failures result in LOG(ERROR)
344 if (severity
== logging::LOG_ERROR
&& !str
.empty()) {
345 // Remove the trailing newline, if present.
346 size_t message_length
= str
.size() - message_start
;
347 if (*str
.rbegin() == '\n')
349 if (validation_error_recipient_
!= NULL
) {
350 validation_error_recipient_
->ReceiveValidationError(
351 file
, line
, str
.substr(message_start
, message_length
).c_str());
353 // Fail the test if an error wasn't handled.
354 ADD_FAILURE_AT(file
, line
)
355 << base::StringPiece(str
.c_str() + message_start
, message_length
);
360 if (old_log_message_handler_
!= NULL
)
361 return (old_log_message_handler_
)(severity
, file
, line
, message_start
, str
);
367 void InstallationValidatorTest::set_validation_error_recipient(
368 ValidationErrorRecipient
* recipient
) {
369 validation_error_recipient_
= recipient
;
373 // Populates |state| with the state of a valid installation of product
374 // |prod_type|. |inst_type| dictates properties of the installation
375 // (multi-install, etc).
376 void InstallationValidatorTest::MakeProductState(
377 BrowserDistribution::Type prod_type
,
378 InstallationValidator::InstallationType inst_type
,
382 FakeProductState
* state
) {
385 const bool is_multi_install
=
386 prod_type
== BrowserDistribution::CHROME_BINARIES
||
387 (prod_type
== BrowserDistribution::CHROME_BROWSER
&&
388 (inst_type
& InstallationValidator::ProductBits::CHROME_MULTI
) != 0) ||
389 (prod_type
== BrowserDistribution::CHROME_FRAME
&&
391 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
) != 0);
393 const wchar_t* const* channels
= &kChromeChannels
[0];
394 if (prod_type
== BrowserDistribution::CHROME_FRAME
&& !is_multi_install
)
395 channels
= &kChromeFrameChannels
[0]; // SxS GCF has its own channel names.
396 const int channel_modifiers
=
397 is_multi_install
? GetChannelModifiers(inst_type
) : 0;
400 state
->SetChannel(channels
[channel
], channel_modifiers
);
401 state
->SetVersion(chrome::kChromeVersion
);
402 state
->SetUninstallCommand(prod_type
, install_level
, chrome::kChromeVersion
,
403 channel_modifiers
, vehicle
);
404 state
->set_multi_install(is_multi_install
);
405 if (prod_type
== BrowserDistribution::CHROME_BROWSER
) {
406 state
->AddOsUpgradeCommand(prod_type
,
408 chrome::kChromeVersion
,
414 // Populates |state| with the state of a valid installation of |inst_type|.
415 void InstallationValidatorTest::MakeMachineState(
416 InstallationValidator::InstallationType inst_type
,
420 FakeInstallationState
* state
) {
423 static const int kChromeMask
=
424 (InstallationValidator::ProductBits::CHROME_SINGLE
|
425 InstallationValidator::ProductBits::CHROME_MULTI
);
426 static const int kChromeFrameMask
=
427 (InstallationValidator::ProductBits::CHROME_FRAME_SINGLE
|
428 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
);
429 static const int kBinariesMask
=
430 (InstallationValidator::ProductBits::CHROME_MULTI
|
431 InstallationValidator::ProductBits::CHROME_FRAME_MULTI
);
433 FakeProductState prod_state
;
435 if ((inst_type
& kChromeMask
) != 0) {
436 MakeProductState(BrowserDistribution::CHROME_BROWSER
, inst_type
,
437 install_level
, channel
, vehicle
, &prod_state
);
438 state
->SetProductState(BrowserDistribution::CHROME_BROWSER
, install_level
,
442 if ((inst_type
& kChromeFrameMask
) != 0) {
443 MakeProductState(BrowserDistribution::CHROME_FRAME
, inst_type
,
444 install_level
, channel
, vehicle
, &prod_state
);
445 state
->SetProductState(BrowserDistribution::CHROME_FRAME
, install_level
,
449 if ((inst_type
& kBinariesMask
) != 0) {
450 MakeProductState(BrowserDistribution::CHROME_BINARIES
, inst_type
,
451 install_level
, channel
, vehicle
, &prod_state
);
452 state
->SetProductState(BrowserDistribution::CHROME_BINARIES
, install_level
,
457 void InstallationValidatorTest::TearDown() {
458 validation_error_recipient_
= NULL
;
461 // Builds a proper machine state for a given InstallationType, then validates
463 TEST_P(InstallationValidatorTest
, TestValidInstallation
) {
464 const InstallationValidator::InstallationType inst_type
= GetParam();
465 FakeInstallationState machine_state
;
466 InstallationValidator::InstallationType type
;
467 StrictMock
<MockValidationErrorRecipient
> recipient
;
468 set_validation_error_recipient(&recipient
);
470 MakeMachineState(inst_type
, SYSTEM_LEVEL
, STABLE_CHANNEL
, GOOGLE_UPDATE
,
472 EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState(
473 machine_state
, true, &type
));
474 EXPECT_EQ(inst_type
, type
);
477 // Run the test for all installation types.
478 INSTANTIATE_TEST_CASE_P(
479 AllValidInstallations
,
480 InstallationValidatorTest
,
481 Values(InstallationValidator::NO_PRODUCTS
,
482 InstallationValidator::CHROME_SINGLE
,
483 InstallationValidator::CHROME_MULTI
,
484 InstallationValidator::CHROME_FRAME_SINGLE
,
485 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE
,
486 InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI
,
487 InstallationValidator::CHROME_FRAME_MULTI
,
488 InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI
));