// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/shell_integration.h" #include <vector> #include "base/file_util.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_shortcut_win.h" #include "base/win/scoped_com_initializer.h" #include "base/win/windows_version.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/util_constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace { struct ShortcutTestObject { base::FilePath path; base::win::ShortcutProperties properties; }; class ShellIntegrationWinMigrateShortcutTest : public testing::Test { protected: virtual void SetUp() OVERRIDE { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); // A path to a random target. base::CreateTemporaryFileInDir(temp_dir_.path(), &other_target_); // This doesn't need to actually have a base name of "chrome.exe". base::CreateTemporaryFileInDir(temp_dir_.path(), &chrome_exe_); chrome_app_id_ = ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(), true); base::FilePath default_user_data_dir; chrome::GetDefaultUserDataDirectory(&default_user_data_dir); base::FilePath default_profile_path = default_user_data_dir.AppendASCII(chrome::kInitialProfile); app_list_app_id_ = ShellIntegration::GetAppListAppModelIdForProfile(default_profile_path); non_default_user_data_dir_ = base::FilePath(FILE_PATH_LITERAL("root")) .Append(FILE_PATH_LITERAL("Non Default Data Dir")); non_default_profile_ = L"NonDefault"; non_default_profile_chrome_app_id_ = ShellIntegration::GetChromiumModelIdForProfile( default_user_data_dir.Append(non_default_profile_)); non_default_user_data_dir_chrome_app_id_ = ShellIntegration::GetChromiumModelIdForProfile( non_default_user_data_dir_.AppendASCII(chrome::kInitialProfile)); non_default_user_data_dir_and_profile_chrome_app_id_ = ShellIntegration::GetChromiumModelIdForProfile( non_default_user_data_dir_.Append(non_default_profile_)); extension_id_ = L"chromiumexampleappidforunittests"; base::string16 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId( base::UTF16ToUTF8(extension_id_))); extension_app_id_ = ShellIntegration::GetAppModelIdForProfile(app_name, default_profile_path); non_default_profile_extension_app_id_ = ShellIntegration::GetAppModelIdForProfile( app_name, default_user_data_dir.Append(non_default_profile_)); CreateShortcuts(); } // Creates a test shortcut corresponding to |shortcut_properties| and resets // |shortcut_properties| after copying it to an internal structure for later // verification. void AddTestShortcutAndResetProperties( base::win::ShortcutProperties* shortcut_properties) { ShortcutTestObject shortcut_test_object; base::FilePath shortcut_path = temp_dir_.path().Append(L"Shortcut " + base::IntToString16(shortcuts_.size()) + installer::kLnkExt); shortcut_test_object.path = shortcut_path; shortcut_test_object.properties = *shortcut_properties; shortcuts_.push_back(shortcut_test_object); ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink( shortcut_path, *shortcut_properties, base::win::SHORTCUT_CREATE_ALWAYS)); shortcut_properties->options = 0U; } void CreateShortcuts() { // A temporary object to pass properties to // AddTestShortcutAndResetProperties(). base::win::ShortcutProperties temp_properties; // Shortcut 0 doesn't point to chrome.exe and thus should never be migrated. temp_properties.set_target(other_target_); temp_properties.set_app_id(L"Dumbo"); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 1 points to chrome.exe and thus should be migrated. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_dual_mode(false); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 2 points to chrome.exe, but already has the right appid and thus // should only be migrated if dual_mode is desired. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(chrome_app_id_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 3 is like shortcut 1, but it's appid is a prefix of the expected // appid instead of being totally different. base::string16 chrome_app_id_is_prefix(chrome_app_id_); chrome_app_id_is_prefix.push_back(L'1'); temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(chrome_app_id_is_prefix); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 4 is like shortcut 1, but it's appid is of the same size as the // expected appid. base::string16 same_size_as_chrome_app_id(L'1', chrome_app_id_.size()); temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(same_size_as_chrome_app_id); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 5 doesn't have an app_id, nor is dual_mode even set; they should // be set as expected upon migration. temp_properties.set_target(chrome_exe_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 6 has a non-default profile directory and so should get a non- // default app id. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_arguments( L"--profile-directory=" + non_default_profile_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 7 has a non-default user data directory and so should get a non- // default app id. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_arguments( L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\""); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 8 has a non-default user data directory as well as a non-default // profile directory and so should get a non-default app id. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_arguments( L"--user-data-dir=\"" + non_default_user_data_dir_.value() + L"\" " + L"--profile-directory=" + non_default_profile_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 9 is a shortcut to an app and should get an app id for that app // rather than the chrome app id. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_arguments( L"--app-id=" + extension_id_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); // Shortcut 10 is a shortcut to an app with a non-default profile and should // get an app id for that app with a non-default app id rather than the // chrome app id. temp_properties.set_target(chrome_exe_); temp_properties.set_app_id(L"Dumbo"); temp_properties.set_arguments( L"--app-id=" + extension_id_ + L" --profile-directory=" + non_default_profile_); ASSERT_NO_FATAL_FAILURE( AddTestShortcutAndResetProperties(&temp_properties)); } base::win::ScopedCOMInitializer com_initializer_; base::ScopedTempDir temp_dir_; // Test shortcuts. std::vector<ShortcutTestObject> shortcuts_; // The path to a fake chrome.exe. base::FilePath chrome_exe_; // The path to a random target. base::FilePath other_target_; // Chrome's AppUserModelId. base::string16 chrome_app_id_; // A profile that isn't the Default profile. base::string16 non_default_profile_; // A user data dir that isn't the default. base::FilePath non_default_user_data_dir_; // Chrome's AppUserModelId for the non-default profile. base::string16 non_default_profile_chrome_app_id_; // Chrome's AppUserModelId for the non-default user data dir. base::string16 non_default_user_data_dir_chrome_app_id_; // Chrome's AppUserModelId for the non-default user data dir and non-default // profile. base::string16 non_default_user_data_dir_and_profile_chrome_app_id_; // The app launcher's app id. base::string16 app_list_app_id_; // An example extension id of an example app. base::string16 extension_id_; // The app id of the example app for the default profile and user data dir. base::string16 extension_app_id_; // The app id of the example app for the non-default profile. base::string16 non_default_profile_extension_app_id_; }; } // namespace // Test migration when not checking for dual mode. TEST_F(ShellIntegrationWinMigrateShortcutTest, DontCheckDualMode) { if (base::win::GetVersion() < base::win::VERSION_WIN7) return; EXPECT_EQ(9, ShellIntegration::MigrateShortcutsInPathInternal( chrome_exe_, temp_dir_.path(), false)); // Only shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have been migrated. shortcuts_[1].properties.set_app_id(chrome_app_id_); shortcuts_[3].properties.set_app_id(chrome_app_id_); shortcuts_[4].properties.set_app_id(chrome_app_id_); shortcuts_[5].properties.set_app_id(chrome_app_id_); shortcuts_[6].properties.set_app_id(non_default_profile_chrome_app_id_); shortcuts_[7].properties.set_app_id(non_default_user_data_dir_chrome_app_id_); shortcuts_[8].properties.set_app_id( non_default_user_data_dir_and_profile_chrome_app_id_); shortcuts_[9].properties.set_app_id(extension_app_id_); shortcuts_[10].properties.set_app_id(non_default_profile_extension_app_id_); for (size_t i = 0; i < shortcuts_.size(); ++i) { // Dual mode should be false for all of these. shortcuts_[i].properties.set_dual_mode(false); base::win::ValidateShortcut(shortcuts_[i].path, shortcuts_[i].properties); } // Make sure shortcuts are not re-migrated. EXPECT_EQ(0, ShellIntegration::MigrateShortcutsInPathInternal( chrome_exe_, temp_dir_.path(), false)); } // Test migration when also checking for dual mode. TEST_F(ShellIntegrationWinMigrateShortcutTest, CheckDualMode) { if (base::win::GetVersion() < base::win::VERSION_WIN7) return; EXPECT_EQ(10, ShellIntegration::MigrateShortcutsInPathInternal( chrome_exe_, temp_dir_.path(), true)); // Shortcut 1, 3, 4, 5, 6, 7, 8, 9, and 10 should have had both their app_id // fixed and shortcut 1, 2, 3, 4, and 5 should also have had their dual_mode // property fixed. shortcuts_[1].properties.set_app_id(chrome_app_id_); shortcuts_[3].properties.set_app_id(chrome_app_id_); shortcuts_[4].properties.set_app_id(chrome_app_id_); shortcuts_[5].properties.set_app_id(chrome_app_id_); shortcuts_[6].properties.set_app_id(non_default_profile_chrome_app_id_); shortcuts_[7].properties.set_app_id(non_default_user_data_dir_chrome_app_id_); shortcuts_[8].properties.set_app_id( non_default_user_data_dir_and_profile_chrome_app_id_); shortcuts_[9].properties.set_app_id(extension_app_id_); shortcuts_[10].properties.set_app_id(non_default_profile_extension_app_id_); shortcuts_[1].properties.set_dual_mode(true); shortcuts_[2].properties.set_dual_mode(true); shortcuts_[3].properties.set_dual_mode(true); shortcuts_[4].properties.set_dual_mode(true); shortcuts_[5].properties.set_dual_mode(true); shortcuts_[6].properties.set_dual_mode(false); shortcuts_[7].properties.set_dual_mode(false); shortcuts_[8].properties.set_dual_mode(false); shortcuts_[9].properties.set_dual_mode(false); shortcuts_[10].properties.set_dual_mode(false); for (size_t i = 0; i < shortcuts_.size(); ++i) base::win::ValidateShortcut(shortcuts_[i].path, shortcuts_[i].properties); // Make sure shortcuts are not re-migrated. EXPECT_EQ(0, ShellIntegration::MigrateShortcutsInPathInternal( chrome_exe_, temp_dir_.path(), false)); } TEST(ShellIntegrationWinTest, GetAppModelIdForProfileTest) { const base::string16 base_app_id( BrowserDistribution::GetDistribution()->GetBaseAppId()); // Empty profile path should get chrome::kBrowserAppID base::FilePath empty_path; EXPECT_EQ(base_app_id, ShellIntegration::GetAppModelIdForProfile(base_app_id, empty_path)); // Default profile path should get chrome::kBrowserAppID base::FilePath default_user_data_dir; chrome::GetDefaultUserDataDirectory(&default_user_data_dir); base::FilePath default_profile_path = default_user_data_dir.AppendASCII(chrome::kInitialProfile); EXPECT_EQ(base_app_id, ShellIntegration::GetAppModelIdForProfile(base_app_id, default_profile_path)); // Non-default profile path should get chrome::kBrowserAppID joined with // profile info. base::FilePath profile_path(FILE_PATH_LITERAL("root")); profile_path = profile_path.Append(FILE_PATH_LITERAL("udd")); profile_path = profile_path.Append(FILE_PATH_LITERAL("User Data - Test")); EXPECT_EQ(base_app_id + L".udd.UserDataTest", ShellIntegration::GetAppModelIdForProfile(base_app_id, profile_path)); } TEST(ShellIntegrationWinTest, GetAppListAppModelIdForProfileTest) { base::string16 base_app_id( BrowserDistribution::GetDistribution()->GetBaseAppId()); base_app_id.append(L"AppList"); // Empty profile path should get chrome::kBrowserAppID + AppList base::FilePath empty_path; EXPECT_EQ(base_app_id, ShellIntegration::GetAppListAppModelIdForProfile(empty_path)); // Default profile path should get chrome::kBrowserAppID + AppList base::FilePath default_user_data_dir; chrome::GetDefaultUserDataDirectory(&default_user_data_dir); base::FilePath default_profile_path = default_user_data_dir.AppendASCII(chrome::kInitialProfile); EXPECT_EQ(base_app_id, ShellIntegration::GetAppListAppModelIdForProfile( default_profile_path)); // Non-default profile path should get chrome::kBrowserAppID + AppList joined // with profile info. base::FilePath profile_path(FILE_PATH_LITERAL("root")); profile_path = profile_path.Append(FILE_PATH_LITERAL("udd")); profile_path = profile_path.Append(FILE_PATH_LITERAL("User Data - Test")); EXPECT_EQ(base_app_id + L".udd.UserDataTest", ShellIntegration::GetAppListAppModelIdForProfile(profile_path)); }