Commit 7deb98cb authored by Mike Jackson's avatar Mike Jackson Committed by Commit Bot

dwpas: Add unit tests for linux shortcuts code

In preparation for run on os login support for linux,
I'm adding tests to validate the existing functionality
of the linux shortcut code.

There are minimal product code changes, primarily to help
facilitate testing, or to remove dead code that was
only previously used by the old test code.

Bug: 897302
Change-Id: I8e08632227da675f19082c72d6fa611747d1f15c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2528877Reviewed-by: default avatarChase Phillips <cmp@chromium.org>
Commit-Queue: Mike Jackson <mjackson@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#830764}
parent cf9a3a64
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/nix/xdg_util.h" #include "base/nix/xdg_util.h"
#include "base/no_destructor.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h" #include "base/process/kill.h"
...@@ -40,6 +41,12 @@ constexpr const char* kCreateShortcutResult = ...@@ -40,6 +41,12 @@ constexpr const char* kCreateShortcutResult =
constexpr const char* kCreateShortcutIconResult = constexpr const char* kCreateShortcutIconResult =
"Apps.CreateShortcutIcon.Linux.Result"; "Apps.CreateShortcutIcon.Linux.Result";
// Testing hook for shell_integration_linux
web_app::LaunchXdgUtilityForTesting& GetInstalledLaunchXdgUtilityForTesting() {
static base::NoDestructor<web_app::LaunchXdgUtilityForTesting> instance;
return *instance;
}
// Result of creating app shortcut icon. // Result of creating app shortcut icon.
// Success is recorded for each icon image, but the first two errors // Success is recorded for each icon image, but the first two errors
// are per app, so the success/error ratio might not be very meaningful. // are per app, so the success/error ratio might not be very meaningful.
...@@ -81,6 +88,14 @@ void RecordCreateShortcut(CreateShortcutResult result) { ...@@ -81,6 +88,14 @@ void RecordCreateShortcut(CreateShortcutResult result) {
UMA_HISTOGRAM_ENUMERATION(kCreateShortcutResult, result); UMA_HISTOGRAM_ENUMERATION(kCreateShortcutResult, result);
} }
bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) {
if (GetInstalledLaunchXdgUtilityForTesting())
return std::move(GetInstalledLaunchXdgUtilityForTesting())
.Run(argv, exit_code);
return shell_integration_linux::LaunchXdgUtility(argv, exit_code);
}
const char kDirectoryFilename[] = "chrome-apps.directory"; const char kDirectoryFilename[] = "chrome-apps.directory";
std::string CreateShortcutIcon(const gfx::ImageFamily& icon_images, std::string CreateShortcutIcon(const gfx::ImageFamily& icon_images,
...@@ -132,8 +147,7 @@ std::string CreateShortcutIcon(const gfx::ImageFamily& icon_images, ...@@ -132,8 +147,7 @@ std::string CreateShortcutIcon(const gfx::ImageFamily& icon_images,
argv.push_back(temp_file_path.value()); argv.push_back(temp_file_path.value());
argv.push_back(icon_name); argv.push_back(icon_name);
int exit_code; int exit_code;
if (!shell_integration_linux::LaunchXdgUtility(argv, &exit_code) || if (!LaunchXdgUtility(argv, &exit_code) || exit_code) {
exit_code) {
LOG(WARNING) << "Could not install icon " << icon_name << ".png at size " LOG(WARNING) << "Could not install icon " << icon_name << ".png at size "
<< width << "."; << width << ".";
RecordCreateIcon(CreateShortcutIconResult::kFailToInstallIcon); RecordCreateIcon(CreateShortcutIconResult::kFailToInstallIcon);
...@@ -193,7 +207,8 @@ bool CreateShortcutOnDesktop(const base::FilePath& shortcut_filename, ...@@ -193,7 +207,8 @@ bool CreateShortcutOnDesktop(const base::FilePath& shortcut_filename,
// applications menu. If |directory_filename| is non-empty, creates a sub-menu // applications menu. If |directory_filename| is non-empty, creates a sub-menu
// with |directory_filename| and |directory_contents|, and stores the shortcut // with |directory_filename| and |directory_contents|, and stores the shortcut
// under the sub-menu. // under the sub-menu.
bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, bool CreateShortcutInApplicationsMenu(base::Environment* env,
const base::FilePath& shortcut_filename,
const std::string& contents, const std::string& contents,
const base::FilePath& directory_filename, const base::FilePath& directory_filename,
const std::string& directory_contents) { const std::string& directory_contents) {
...@@ -233,7 +248,7 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, ...@@ -233,7 +248,7 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename,
argv.push_back(temp_directory_path.value()); argv.push_back(temp_directory_path.value());
argv.push_back(temp_file_path.value()); argv.push_back(temp_file_path.value());
int exit_code; int exit_code;
shell_integration_linux::LaunchXdgUtility(argv, &exit_code); LaunchXdgUtility(argv, &exit_code);
if (exit_code != 0) { if (exit_code != 0) {
RecordCreateShortcut(CreateShortcutResult::kFailToInstallShortcut); RecordCreateShortcut(CreateShortcutResult::kFailToInstallShortcut);
...@@ -245,10 +260,8 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, ...@@ -245,10 +260,8 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename,
// manually run update-desktop-database on the user applications folder. // manually run update-desktop-database on the user applications folder.
// See this bug on xdg desktop-file-utils // See this bug on xdg desktop-file-utils
// https://gitlab.freedesktop.org/xdg/desktop-file-utils/issues/54 // https://gitlab.freedesktop.org/xdg/desktop-file-utils/issues/54
std::unique_ptr<base::Environment> env(base::Environment::Create());
base::FilePath user_applications_dir = base::FilePath user_applications_dir =
shell_integration_linux::GetDataWriteLocation(env.get()).Append( shell_integration_linux::GetDataWriteLocation(env).Append("applications");
"applications");
argv.clear(); argv.clear();
argv.push_back("update-desktop-database"); argv.push_back("update-desktop-database");
argv.push_back(user_applications_dir.value()); argv.push_back(user_applications_dir.value());
...@@ -258,7 +271,7 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, ...@@ -258,7 +271,7 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename,
// completes). Failure means the file type associations for this desktop entry // completes). Failure means the file type associations for this desktop entry
// may not show up in some file managers, but this is non-critical. // may not show up in some file managers, but this is non-critical.
int ignored_exit_code = 0; int ignored_exit_code = 0;
shell_integration_linux::LaunchXdgUtility(argv, &ignored_exit_code); LaunchXdgUtility(argv, &ignored_exit_code);
return true; return true;
} }
...@@ -267,6 +280,12 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, ...@@ -267,6 +280,12 @@ bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename,
namespace web_app { namespace web_app {
void SetLaunchXdgUtilityForTesting(
LaunchXdgUtilityForTesting launchXdgUtilityForTesting) {
GetInstalledLaunchXdgUtilityForTesting() =
std::move(launchXdgUtilityForTesting);
}
base::FilePath GetAppShortcutFilename(const base::FilePath& profile_path, base::FilePath GetAppShortcutFilename(const base::FilePath& profile_path,
const std::string& app_id) { const std::string& app_id) {
DCHECK(!app_id.empty()); DCHECK(!app_id.empty());
...@@ -309,10 +328,11 @@ bool DeleteShortcutInApplicationsMenu( ...@@ -309,10 +328,11 @@ bool DeleteShortcutInApplicationsMenu(
argv.push_back(directory_filename.value()); argv.push_back(directory_filename.value());
argv.push_back(shortcut_filename.value()); argv.push_back(shortcut_filename.value());
int exit_code; int exit_code;
return shell_integration_linux::LaunchXdgUtility(argv, &exit_code); return LaunchXdgUtility(argv, &exit_code);
} }
bool CreateDesktopShortcut(const ShortcutInfo& shortcut_info, bool CreateDesktopShortcut(base::Environment* env,
const ShortcutInfo& shortcut_info,
const ShortcutLocations& creation_locations) { const ShortcutLocations& creation_locations) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK); base::BlockingType::MAY_BLOCK);
...@@ -393,7 +413,7 @@ bool CreateDesktopShortcut(const ShortcutInfo& shortcut_info, ...@@ -393,7 +413,7 @@ bool CreateDesktopShortcut(const ShortcutInfo& shortcut_info,
base::JoinString(mime_types, ";"), base::JoinString(mime_types, ";"),
creation_locations.applications_menu_location == creation_locations.applications_menu_location ==
APP_MENU_LOCATION_HIDDEN); APP_MENU_LOCATION_HIDDEN);
success = CreateShortcutInApplicationsMenu(shortcut_filename, contents, success = CreateShortcutInApplicationsMenu(env, shortcut_filename, contents,
directory_filename, directory_filename,
directory_contents) && directory_contents) &&
success; success;
...@@ -407,18 +427,6 @@ ShortcutLocations GetExistingShortcutLocations( ...@@ -407,18 +427,6 @@ ShortcutLocations GetExistingShortcutLocations(
base::Environment* env, base::Environment* env,
const base::FilePath& profile_path, const base::FilePath& profile_path,
const std::string& extension_id) { const std::string& extension_id) {
base::FilePath desktop_path;
// If Get returns false, just leave desktop_path empty.
base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path);
return GetExistingShortcutLocations(env, profile_path, extension_id,
desktop_path);
}
ShortcutLocations GetExistingShortcutLocations(
base::Environment* env,
const base::FilePath& profile_path,
const std::string& extension_id,
const base::FilePath& desktop_path) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK); base::BlockingType::MAY_BLOCK);
...@@ -428,6 +436,9 @@ ShortcutLocations GetExistingShortcutLocations( ...@@ -428,6 +436,9 @@ ShortcutLocations GetExistingShortcutLocations(
ShortcutLocations locations; ShortcutLocations locations;
// Determine whether there is a shortcut on desktop. // Determine whether there is a shortcut on desktop.
base::FilePath desktop_path;
// If Get returns false, just leave desktop_path empty.
base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path);
if (!desktop_path.empty()) { if (!desktop_path.empty()) {
locations.on_desktop = locations.on_desktop =
base::PathExists(desktop_path.Append(shortcut_filename)); base::PathExists(desktop_path.Append(shortcut_filename));
...@@ -469,11 +480,11 @@ bool DeleteDesktopShortcuts(const base::FilePath& profile_path, ...@@ -469,11 +480,11 @@ bool DeleteDesktopShortcuts(const base::FilePath& profile_path,
return (deleted_from_desktop && deleted_from_application_menu); return (deleted_from_desktop && deleted_from_application_menu);
} }
bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { bool DeleteAllDesktopShortcuts(base::Environment* env,
const base::FilePath& profile_path) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK); base::BlockingType::MAY_BLOCK);
std::unique_ptr<base::Environment> env(base::Environment::Create());
bool result = true; bool result = true;
// Delete shortcuts from Desktop. // Delete shortcuts from Desktop.
base::FilePath desktop_path; base::FilePath desktop_path;
...@@ -489,7 +500,7 @@ bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { ...@@ -489,7 +500,7 @@ bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) {
// Delete shortcuts from |kDirectoryFilename|. // Delete shortcuts from |kDirectoryFilename|.
base::FilePath applications_menu = base::FilePath applications_menu =
shell_integration_linux::GetDataWriteLocation(env.get()); shell_integration_linux::GetDataWriteLocation(env);
applications_menu = applications_menu.AppendASCII("applications"); applications_menu = applications_menu.AppendASCII("applications");
std::vector<base::FilePath> shortcut_filenames_app_menu = std::vector<base::FilePath> shortcut_filenames_app_menu =
shell_integration_linux::GetExistingProfileShortcutFilenames( shell_integration_linux::GetExistingProfileShortcutFilenames(
...@@ -503,16 +514,35 @@ bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { ...@@ -503,16 +514,35 @@ bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path) {
return result; return result;
} }
void UpdateDesktopShortcuts(base::Environment* env,
const ShortcutInfo& shortcut_info) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
// Find out whether shortcuts are already installed.
ShortcutLocations creation_locations = GetExistingShortcutLocations(
env, shortcut_info.profile_path, shortcut_info.extension_id);
// Always create a hidden shortcut in applications if a visible one is not
// being created. This allows the operating system to identify the app, but
// not show it in the menu.
if (creation_locations.applications_menu_location == APP_MENU_LOCATION_NONE)
creation_locations.applications_menu_location = APP_MENU_LOCATION_HIDDEN;
CreateDesktopShortcut(env, shortcut_info, creation_locations);
}
namespace internals { namespace internals {
bool CreatePlatformShortcuts(const base::FilePath& web_app_path, bool CreatePlatformShortcuts(const base::FilePath& /*web_app_path*/,
const ShortcutLocations& creation_locations, const ShortcutLocations& creation_locations,
ShortcutCreationReason /*creation_reason*/, ShortcutCreationReason /*creation_reason*/,
const ShortcutInfo& shortcut_info) { const ShortcutInfo& shortcut_info) {
#if !BUILDFLAG(IS_CHROMEOS_ASH) #if !BUILDFLAG(IS_CHROMEOS_ASH)
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK); base::BlockingType::MAY_BLOCK);
return CreateDesktopShortcut(shortcut_info, creation_locations); std::unique_ptr<base::Environment> env(base::Environment::Create());
return CreateDesktopShortcut(env.get(), shortcut_info, creation_locations);
#else #else
return false; return false;
#endif #endif
...@@ -527,31 +557,19 @@ bool DeletePlatformShortcuts(const base::FilePath& web_app_path, ...@@ -527,31 +557,19 @@ bool DeletePlatformShortcuts(const base::FilePath& web_app_path,
return true; return true;
} }
void UpdatePlatformShortcuts(const base::FilePath& web_app_path, void UpdatePlatformShortcuts(const base::FilePath& /*web_app_path*/,
const base::string16& /*old_app_title*/, const base::string16& /*old_app_title*/,
const ShortcutInfo& shortcut_info) { const ShortcutInfo& shortcut_info) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, #if !BUILDFLAG(IS_CHROMEOS_ASH)
base::BlockingType::MAY_BLOCK);
std::unique_ptr<base::Environment> env(base::Environment::Create()); std::unique_ptr<base::Environment> env(base::Environment::Create());
UpdateDesktopShortcuts(env.get(), shortcut_info);
// Find out whether shortcuts are already installed. #endif
ShortcutLocations creation_locations = GetExistingShortcutLocations(
env.get(), shortcut_info.profile_path, shortcut_info.extension_id);
// Always create a hidden shortcut in applications if a visible one is not
// being created. This allows the operating system to identify the app, but
// not show it in the menu.
if (creation_locations.applications_menu_location == APP_MENU_LOCATION_NONE)
creation_locations.applications_menu_location = APP_MENU_LOCATION_HIDDEN;
CreatePlatformShortcuts(web_app_path, creation_locations,
SHORTCUT_CREATION_AUTOMATED, shortcut_info);
} }
void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) { void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {
#if !BUILDFLAG(IS_CHROMEOS_ASH) #if !BUILDFLAG(IS_CHROMEOS_ASH)
DeleteAllDesktopShortcuts(profile_path); std::unique_ptr<base::Environment> env(base::Environment::Create());
DeleteAllDesktopShortcuts(env.get(), profile_path);
#endif #endif
} }
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <string> #include <string>
#include "base/callback.h"
namespace base { namespace base {
class FilePath; class FilePath;
class Environment; class Environment;
...@@ -17,11 +19,19 @@ namespace web_app { ...@@ -17,11 +19,19 @@ namespace web_app {
struct ShortcutInfo; struct ShortcutInfo;
struct ShortcutLocations; struct ShortcutLocations;
using LaunchXdgUtilityForTesting =
base::RepeatingCallback<bool(const std::vector<std::string>&, int*)>;
// Test helper that hooking calls to shell_integration_linux::LaunchXdgUtility
void SetLaunchXdgUtilityForTesting(
LaunchXdgUtilityForTesting launchXdgUtilityForTesting);
// Create shortcuts on the desktop or in the application menu (as specified by // Create shortcuts on the desktop or in the application menu (as specified by
// |shortcut_info|), for the web page or extension in |shortcut_info|. // |shortcut_info|), for the web page or extension in |shortcut_info|.
// For extensions, duplicate shortcuts are avoided, so if a requested shortcut // For extensions, duplicate shortcuts are avoided, so if a requested shortcut
// already exists it is deleted first. // already exists it is deleted first.
bool CreateDesktopShortcut(const ShortcutInfo& shortcut_info, bool CreateDesktopShortcut(base::Environment* env,
const ShortcutInfo& shortcut_info,
const ShortcutLocations& creation_locations); const ShortcutLocations& creation_locations);
// Returns filename for .desktop file based on |profile_path| and // Returns filename for .desktop file based on |profile_path| and
...@@ -39,14 +49,8 @@ ShortcutLocations GetExistingShortcutLocations( ...@@ -39,14 +49,8 @@ ShortcutLocations GetExistingShortcutLocations(
const base::FilePath& profile_path, const base::FilePath& profile_path,
const std::string& extension_id); const std::string& extension_id);
// Version of GetExistingShortcutLocations which takes an explicit path void UpdateDesktopShortcuts(base::Environment* env,
// to the user's desktop directory. Useful for testing. const ShortcutInfo& shortcut_info);
// If |desktop_path| is empty, the desktop is not searched.
ShortcutLocations GetExistingShortcutLocations(
base::Environment* env,
const base::FilePath& profile_path,
const std::string& extension_id,
const base::FilePath& desktop_path);
// Delete any desktop shortcuts on desktop or in the application menu that have // Delete any desktop shortcuts on desktop or in the application menu that have
// been added for the extension with |extension_id| in |profile_path|. Returns // been added for the extension with |extension_id| in |profile_path|. Returns
...@@ -56,7 +60,8 @@ bool DeleteDesktopShortcuts(const base::FilePath& profile_path, ...@@ -56,7 +60,8 @@ bool DeleteDesktopShortcuts(const base::FilePath& profile_path,
// Delete any desktop shortcuts on desktop or in the application menu that have // Delete any desktop shortcuts on desktop or in the application menu that have
// for the profile in |profile_path|. Returns true on successful deletion. // for the profile in |profile_path|. Returns true on successful deletion.
bool DeleteAllDesktopShortcuts(const base::FilePath& profile_path); bool DeleteAllDesktopShortcuts(base::Environment* env,
const base::FilePath& profile_path);
} // namespace web_app } // namespace web_app
......
...@@ -17,12 +17,17 @@ ...@@ -17,12 +17,17 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_path_override.h" #include "base/test/scoped_path_override.h"
#include "chrome/browser/shell_integration_linux.h"
#include "chrome/browser/web_applications/components/web_app_id.h" #include "chrome/browser/web_applications/components/web_app_id.h"
#include "chrome/browser/web_applications/components/web_app_shortcut.h" #include "chrome/browser/web_applications/components/web_app_shortcut.h"
#include "chrome/browser/web_applications/components/web_application_info.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_constants.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -35,7 +40,7 @@ namespace { ...@@ -35,7 +40,7 @@ namespace {
// Provides mock environment variables values based on a stored map. // Provides mock environment variables values based on a stored map.
class MockEnvironment : public base::Environment { class MockEnvironment : public base::Environment {
public: public:
MockEnvironment() {} MockEnvironment() = default;
MockEnvironment(const MockEnvironment&) = delete; MockEnvironment(const MockEnvironment&) = delete;
MockEnvironment& operator=(const MockEnvironment&) = delete; MockEnvironment& operator=(const MockEnvironment&) = delete;
...@@ -68,113 +73,513 @@ class MockEnvironment : public base::Environment { ...@@ -68,113 +73,513 @@ class MockEnvironment : public base::Environment {
}; };
class ScopedDesktopPath {
public:
ScopedDesktopPath() { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); }
ScopedDesktopPath(const ScopedDesktopPath&) = delete;
ScopedDesktopPath& operator=(const ScopedDesktopPath&) = delete;
base::FilePath GetPath() {
base::FilePath desktop_path = temp_dir_.GetPath();
EXPECT_TRUE(base::CreateDirectory(desktop_path));
return desktop_path;
}
private:
base::ScopedTempDir temp_dir_;
};
class ScopedApplicationsPath {
public:
ScopedApplicationsPath() { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); }
ScopedApplicationsPath(const ScopedApplicationsPath&) = delete;
ScopedApplicationsPath& operator=(const ScopedApplicationsPath&) = delete;
base::FilePath GetPath() {
base::FilePath applications_path = temp_dir_.GetPath();
applications_path = applications_path.AppendASCII("applications");
EXPECT_TRUE(base::CreateDirectory(applications_path));
return applications_path;
}
base::FilePath GetDataHomePath() { return temp_dir_.GetPath(); }
private:
base::ScopedTempDir temp_dir_;
};
} // namespace } // namespace
TEST(ShellIntegrationTest, GetExistingShortcutLocations) { class WebAppShortcutLinuxTest : public WebAppTest {
base::FilePath kProfilePath("Profile 1"); public:
const char kExtensionId[] = "test_extension"; base::FilePath CreateShortcutInPath(const base::FilePath& path) {
const char kTemplateFilename[] = "chrome-test_extension-Profile_1.desktop"; EXPECT_TRUE(base::PathExists(path));
base::FilePath kTemplateFilepath(kTemplateFilename); base::FilePath shortcut_path = path.Append(GetTemplateFilename());
const char kNoDisplayDesktopFile[] = "[Desktop Entry]\nNoDisplay=true"; EXPECT_TRUE(base::WriteFile(shortcut_path, ""));
return shortcut_path;
}
std::unique_ptr<ShortcutInfo> GetShortcutInfo() {
auto shortcut_info = std::make_unique<ShortcutInfo>();
shortcut_info->extension_id = GetAppId();
shortcut_info->title = base::UTF8ToUTF16("app");
shortcut_info->profile_path =
base::FilePath("/a/b/c").Append(GetProfilePath());
gfx::ImageFamily image_family;
SquareSizePx icon_size_in_px = GetDesiredIconSizesForShortcut().back();
gfx::ImageSkia image_skia = CreateDefaultApplicationIcon(icon_size_in_px);
image_family.Add(gfx::Image(image_skia));
shortcut_info->favicon = std::move(image_family);
return shortcut_info;
}
std::string GetAppId() { return std::string("test_extension"); }
std::string GetTemplateFilename() {
return std::string("chrome-test_extension-Profile_1.desktop");
}
base::FilePath GetProfilePath() { return base::FilePath("Profile 1"); }
content::BrowserTaskEnvironment task_environment; void ValidateDeleteApplicationsLaunchXdgUtility(
const std::vector<std::string>& argv,
int* exit_code) {
EXPECT_TRUE(exit_code);
*exit_code = 0;
std::vector<std::string> expected_argv;
expected_argv.push_back("xdg-desktop-menu");
expected_argv.push_back("uninstall");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back("chrome-apps.directory");
expected_argv.push_back(GetTemplateFilename());
EXPECT_EQ(argv, expected_argv);
}
void ValidateCreateDesktopShortcutLaunchXdgUtility(
const std::vector<std::string>& argv,
int* exit_code,
const base::FilePath& expected_applications_path,
int invoke_count) {
EXPECT_TRUE(exit_code);
*exit_code = 0;
// There are 4 calls to this function:
// case 0) delete the existing shortcut
// case 1) install the icon for the new shortcut
// case 2) install the new shortcut
// case 3) update desktop database
EXPECT_LE(invoke_count, 4);
std::vector<std::string> expected_argv;
switch (invoke_count) {
case 0:
expected_argv.push_back("xdg-desktop-menu");
expected_argv.push_back("uninstall");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back(GetTemplateFilename());
break;
case 1:
// The icon is generated to a temporary path, but the file name
// should be known Confirm the file name is what we expect, and use
// the parameter passed in.
EXPECT_EQ(argv.size(), 8u);
EXPECT_TRUE(argv[6].find("chrome-test_extension-Profile_1.png") !=
std::string::npos);
expected_argv.push_back("xdg-icon-resource");
expected_argv.push_back("install");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back("--size");
expected_argv.push_back("512");
expected_argv.push_back(argv[6]);
expected_argv.push_back("chrome-test_extension-Profile_1");
break;
case 2:
// The desktop file is generated to a temporary path, but the file
// name should be known Confirm the file name is what we expect, and
// use the parameter passed in.
EXPECT_EQ(argv.size(), 6u);
EXPECT_TRUE(argv[4].find("chrome-apps.directory") != std::string::npos);
EXPECT_TRUE(argv[5].find(GetTemplateFilename()) != std::string::npos);
expected_argv.push_back("xdg-desktop-menu");
expected_argv.push_back("install");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back(argv[4]);
expected_argv.push_back(argv[5]);
break;
case 3:
expected_argv.push_back("update-desktop-database");
expected_argv.push_back(expected_applications_path.value());
break;
}
EXPECT_EQ(expected_argv, argv);
}
};
TEST_F(WebAppShortcutLinuxTest, GetExistingShortcutLocations) {
base::FilePath kTemplateFilepath(GetTemplateFilename());
// No existing shortcuts. // No existing shortcuts.
{ {
MockEnvironment env; MockEnvironment env;
ShortcutLocations result = ShortcutLocations result =
GetExistingShortcutLocations(&env, kProfilePath, kExtensionId); GetExistingShortcutLocations(&env, GetProfilePath(), GetAppId());
EXPECT_FALSE(result.on_desktop); EXPECT_FALSE(result.on_desktop);
EXPECT_EQ(APP_MENU_LOCATION_NONE, result.applications_menu_location); EXPECT_EQ(APP_MENU_LOCATION_NONE, result.applications_menu_location);
EXPECT_FALSE(result.in_quick_launch_bar); EXPECT_FALSE(result.in_quick_launch_bar);
EXPECT_FALSE(result.in_startup);
} }
// Shortcut on desktop. // Shortcut on desktop.
{ {
base::ScopedTempDir temp_dir; ScopedDesktopPath scoped_desktop_path;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); base::ScopedPathOverride user_desktop_override(
base::FilePath desktop_path = temp_dir.GetPath(); base::DIR_USER_DESKTOP, scoped_desktop_path.GetPath());
MockEnvironment env; MockEnvironment env;
ASSERT_TRUE(base::CreateDirectory(desktop_path));
ASSERT_TRUE(base::WriteFile(desktop_path.Append(kTemplateFilename), "")); CreateShortcutInPath(scoped_desktop_path.GetPath());
ShortcutLocations result = GetExistingShortcutLocations( ShortcutLocations result =
&env, kProfilePath, kExtensionId, desktop_path); GetExistingShortcutLocations(&env, GetProfilePath(), GetAppId());
EXPECT_TRUE(result.on_desktop); EXPECT_TRUE(result.on_desktop);
EXPECT_EQ(APP_MENU_LOCATION_NONE, result.applications_menu_location); EXPECT_EQ(APP_MENU_LOCATION_NONE, result.applications_menu_location);
EXPECT_FALSE(result.in_quick_launch_bar); EXPECT_FALSE(result.in_quick_launch_bar);
EXPECT_FALSE(result.in_startup);
} }
// Shortcut in applications directory. // Shortcut in applications directory.
{ {
base::ScopedTempDir temp_dir; ScopedApplicationsPath scoped_applications_path;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath apps_path = temp_dir.GetPath().Append("applications");
MockEnvironment env; MockEnvironment env;
env.Set("XDG_DATA_HOME", temp_dir.GetPath().value());
ASSERT_TRUE(base::CreateDirectory(apps_path)); env.Set("XDG_DATA_HOME",
ASSERT_TRUE(base::WriteFile(apps_path.Append(kTemplateFilename), "")); scoped_applications_path.GetDataHomePath().value());
CreateShortcutInPath(scoped_applications_path.GetPath());
ShortcutLocations result = ShortcutLocations result =
GetExistingShortcutLocations(&env, kProfilePath, kExtensionId); GetExistingShortcutLocations(&env, GetProfilePath(), GetAppId());
EXPECT_FALSE(result.on_desktop); EXPECT_FALSE(result.on_desktop);
EXPECT_EQ(APP_MENU_LOCATION_SUBDIR_CHROMEAPPS, EXPECT_EQ(APP_MENU_LOCATION_SUBDIR_CHROMEAPPS,
result.applications_menu_location); result.applications_menu_location);
EXPECT_FALSE(result.in_quick_launch_bar); EXPECT_FALSE(result.in_quick_launch_bar);
EXPECT_FALSE(result.in_startup);
} }
// Shortcut in applications directory with NoDisplay=true. // Shortcut in applications directory with NoDisplay=true.
{ {
base::ScopedTempDir temp_dir; ScopedApplicationsPath scoped_applications_path;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath apps_path = temp_dir.GetPath().Append("applications");
MockEnvironment env; MockEnvironment env;
env.Set("XDG_DATA_HOME", temp_dir.GetPath().value());
ASSERT_TRUE(base::CreateDirectory(apps_path)); env.Set("XDG_DATA_HOME",
ASSERT_TRUE(base::WriteFile(apps_path.Append(kTemplateFilename), scoped_applications_path.GetDataHomePath().value());
kNoDisplayDesktopFile)); ASSERT_TRUE(base::WriteFile(
scoped_applications_path.GetPath().Append(GetTemplateFilename()),
"[Desktop Entry]\nNoDisplay=true"));
ShortcutLocations result = ShortcutLocations result =
GetExistingShortcutLocations(&env, kProfilePath, kExtensionId); GetExistingShortcutLocations(&env, GetProfilePath(), GetAppId());
// Doesn't count as being in applications menu. // Doesn't count as being in applications menu.
EXPECT_FALSE(result.on_desktop); EXPECT_FALSE(result.on_desktop);
EXPECT_EQ(APP_MENU_LOCATION_HIDDEN, result.applications_menu_location); EXPECT_EQ(APP_MENU_LOCATION_HIDDEN, result.applications_menu_location);
EXPECT_FALSE(result.in_quick_launch_bar); EXPECT_FALSE(result.in_quick_launch_bar);
EXPECT_FALSE(result.in_startup);
} }
// Shortcut on desktop and in applications directory. // Shortcut on desktop and in applications directory.
{ {
base::ScopedTempDir temp_dir1; ScopedDesktopPath scoped_desktop_path;
ASSERT_TRUE(temp_dir1.CreateUniqueTempDir()); base::ScopedPathOverride user_desktop_override(
base::FilePath desktop_path = temp_dir1.GetPath(); base::DIR_USER_DESKTOP, scoped_desktop_path.GetPath());
ScopedApplicationsPath scoped_applications_path;
MockEnvironment env;
base::ScopedTempDir temp_dir2; env.Set("XDG_DATA_HOME",
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir()); scoped_applications_path.GetDataHomePath().value());
base::FilePath apps_path = temp_dir2.GetPath().Append("applications"); CreateShortcutInPath(scoped_desktop_path.GetPath());
CreateShortcutInPath(scoped_applications_path.GetPath());
MockEnvironment env; ShortcutLocations result =
ASSERT_TRUE(base::CreateDirectory(desktop_path)); GetExistingShortcutLocations(&env, GetProfilePath(), GetAppId());
ASSERT_TRUE(base::WriteFile(desktop_path.Append(kTemplateFilename), ""));
env.Set("XDG_DATA_HOME", temp_dir2.GetPath().value());
ASSERT_TRUE(base::CreateDirectory(apps_path));
ASSERT_TRUE(base::WriteFile(apps_path.Append(kTemplateFilename), ""));
ShortcutLocations result = GetExistingShortcutLocations(
&env, kProfilePath, kExtensionId, desktop_path);
EXPECT_TRUE(result.on_desktop); EXPECT_TRUE(result.on_desktop);
EXPECT_EQ(APP_MENU_LOCATION_SUBDIR_CHROMEAPPS, EXPECT_EQ(APP_MENU_LOCATION_SUBDIR_CHROMEAPPS,
result.applications_menu_location); result.applications_menu_location);
EXPECT_FALSE(result.in_quick_launch_bar); EXPECT_FALSE(result.in_quick_launch_bar);
EXPECT_FALSE(result.in_startup);
} }
} }
TEST(ShellIntegrationTest, GetExtensionShortcutFilename) { TEST_F(WebAppShortcutLinuxTest, GetExtensionShortcutFilename) {
base::FilePath kProfilePath("a/b/c/Profile Name?"); EXPECT_EQ(base::FilePath("chrome-extensionid-Profile_1.desktop"),
const char kExtensionId[] = "extensionid"; GetAppShortcutFilename(GetProfilePath(), "extensionid"));
EXPECT_EQ(base::FilePath("chrome-extensionid-Profile_Name_.desktop"), }
GetAppShortcutFilename(kProfilePath, kExtensionId));
TEST_F(WebAppShortcutLinuxTest, DeleteDesktopShortcuts) {
ScopedDesktopPath scoped_desktop_path;
base::FilePath desktop_shortcut_path =
CreateShortcutInPath(scoped_desktop_path.GetPath());
base::ScopedPathOverride user_desktop_override(base::DIR_USER_DESKTOP,
scoped_desktop_path.GetPath());
int invoke_count = 0;
auto DeleteApplicationsLaunchXdgUtility = base::BindLambdaForTesting(
[&](const std::vector<std::string>& argv, int* exit_code) -> bool {
EXPECT_EQ(invoke_count, 0);
invoke_count++;
ValidateDeleteApplicationsLaunchXdgUtility(argv, exit_code);
return true;
});
SetLaunchXdgUtilityForTesting(DeleteApplicationsLaunchXdgUtility);
EXPECT_TRUE(base::PathExists(desktop_shortcut_path));
EXPECT_TRUE(DeleteDesktopShortcuts(GetProfilePath(), GetAppId()));
EXPECT_EQ(invoke_count, 1);
EXPECT_FALSE(base::PathExists(desktop_shortcut_path));
}
TEST_F(WebAppShortcutLinuxTest, DeleteAllDesktopShortcuts) {
ScopedDesktopPath scoped_desktop_path;
base::FilePath desktop_shortcut_path =
CreateShortcutInPath(scoped_desktop_path.GetPath());
base::ScopedPathOverride user_desktop_override(base::DIR_USER_DESKTOP,
scoped_desktop_path.GetPath());
ScopedApplicationsPath scoped_applications_path;
base::FilePath application_shortcut_path =
CreateShortcutInPath(scoped_applications_path.GetPath());
MockEnvironment env;
env.Set("XDG_DATA_HOME", scoped_applications_path.GetDataHomePath().value());
int invoke_count = 0;
auto DeleteApplicationsLaunchXdgUtility = base::BindLambdaForTesting(
[&](const std::vector<std::string>& argv, int* exit_code) -> bool {
EXPECT_EQ(invoke_count, 0);
invoke_count++;
ValidateDeleteApplicationsLaunchXdgUtility(argv, exit_code);
return true;
});
SetLaunchXdgUtilityForTesting(DeleteApplicationsLaunchXdgUtility);
EXPECT_TRUE(base::PathExists(desktop_shortcut_path));
EXPECT_TRUE(DeleteAllDesktopShortcuts(&env, GetProfilePath()));
EXPECT_EQ(invoke_count, 1);
EXPECT_FALSE(base::PathExists(desktop_shortcut_path));
}
TEST_F(WebAppShortcutLinuxTest, CreateDesktopShortcut) {
ScopedDesktopPath scoped_desktop_path;
ScopedApplicationsPath scoped_applications_path;
base::ScopedPathOverride user_desktop_override(base::DIR_USER_DESKTOP,
scoped_desktop_path.GetPath());
MockEnvironment env;
env.Set("XDG_DATA_HOME", scoped_applications_path.GetDataHomePath().value());
int invoke_count = 0;
LaunchXdgUtilityForTesting CreateDesktopShortcutLaunchXdgUtility =
base::BindLambdaForTesting([&](const std::vector<std::string>& argv,
int* exit_code) -> bool {
ValidateCreateDesktopShortcutLaunchXdgUtility(
argv, exit_code, scoped_applications_path.GetPath(), invoke_count);
invoke_count++;
if (invoke_count < 4)
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
return true;
});
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
std::unique_ptr<ShortcutInfo> shortcut_info = GetShortcutInfo();
ShortcutLocations locations;
locations.on_desktop = true;
locations.applications_menu_location = APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
EXPECT_TRUE(CreateDesktopShortcut(&env, *shortcut_info, locations));
EXPECT_EQ(invoke_count, 4);
// At this point, we've already validated creation in the Application menu
// because of the hook into XdgUtilityForTesting.
// Validate the shortcut was created, and the contents are what we expect
// them to be.
std::string expected_contents =
shell_integration_linux::GetDesktopFileContents(
shell_integration_linux::internal::GetChromeExePath(),
GenerateApplicationNameFromInfo(*shortcut_info), shortcut_info->url,
shortcut_info->extension_id, shortcut_info->title,
"chrome-test_extension-Profile_1", shortcut_info->profile_path, "",
"", false);
base::FilePath desktop_shortcut_path =
scoped_desktop_path.GetPath().Append(GetTemplateFilename());
ASSERT_TRUE(base::PathExists(desktop_shortcut_path));
std::string actual_contents;
ASSERT_TRUE(base::ReadFileToString(desktop_shortcut_path, &actual_contents));
EXPECT_EQ(expected_contents, actual_contents);
}
TEST_F(WebAppShortcutLinuxTest, CreateDesktopShortcutEmptyExtension) {
ScopedDesktopPath scoped_desktop_path;
ScopedApplicationsPath scoped_applications_path;
base::ScopedPathOverride user_desktop_override(base::DIR_USER_DESKTOP,
scoped_desktop_path.GetPath());
MockEnvironment env;
env.Set("XDG_DATA_HOME", scoped_applications_path.GetDataHomePath().value());
int invoke_count = 0;
LaunchXdgUtilityForTesting CreateDesktopShortcutLaunchXdgUtility =
base::BindLambdaForTesting([&](const std::vector<std::string>& argv,
int* exit_code) -> bool {
EXPECT_TRUE(exit_code);
*exit_code = 0;
// There are 3 calls to this function:
// case 0) install the icon for the new shortcut
// case 1) install the new shortcut
// case 2) update desktop database
EXPECT_LE(invoke_count, 3);
std::vector<std::string> expected_argv;
switch (invoke_count) {
case 0:
// The icon is generated to a temporary path, but the file name
// should be known Confirm the file name is what we expect, and use
// the parameter passed in.
EXPECT_EQ(argv.size(), 8u);
EXPECT_TRUE(argv[6].find("chrome-https___example.com_.png") !=
std::string::npos);
expected_argv.push_back("xdg-icon-resource");
expected_argv.push_back("install");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back("--size");
expected_argv.push_back("512");
expected_argv.push_back(argv[6]);
expected_argv.push_back("chrome-https___example.com_");
break;
case 1:
// The desktop file is generated to a temporary path, but the file
// name should be known Confirm the file name is what we expect, and
// use the parameter passed in.
EXPECT_EQ(argv.size(), 6u);
EXPECT_TRUE(argv[4].find("chrome-apps.directory") !=
std::string::npos);
EXPECT_TRUE(argv[5].find("chrome-https___example.com_.desktop") !=
std::string::npos);
expected_argv.push_back("xdg-desktop-menu");
expected_argv.push_back("install");
expected_argv.push_back("--mode");
expected_argv.push_back("user");
expected_argv.push_back(argv[4]);
expected_argv.push_back(argv[5]);
break;
case 2:
expected_argv.push_back("update-desktop-database");
expected_argv.push_back(scoped_applications_path.GetPath().value());
break;
}
invoke_count++;
EXPECT_EQ(expected_argv, argv);
if (invoke_count < 3)
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
return true;
});
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
std::unique_ptr<ShortcutInfo> shortcut_info = GetShortcutInfo();
shortcut_info->extension_id = "";
shortcut_info->url = GURL("https://example.com");
ShortcutLocations locations;
locations.on_desktop = true;
locations.applications_menu_location = APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
EXPECT_TRUE(CreateDesktopShortcut(&env, *shortcut_info, locations));
EXPECT_EQ(invoke_count, 3);
// At this point, we've already validated creation in the Application menu
// because of the hook into XdgUtilityForTesting.
// Validate the shortcut was created, and the contents are what we expect
// them to be.
std::string expected_contents =
shell_integration_linux::GetDesktopFileContents(
shell_integration_linux::internal::GetChromeExePath(),
GenerateApplicationNameFromInfo(*shortcut_info), shortcut_info->url,
shortcut_info->extension_id, shortcut_info->title,
"chrome-https___example.com_", shortcut_info->profile_path, "", "",
false);
base::FilePath desktop_shortcut_path = scoped_desktop_path.GetPath().Append(
"chrome-https___example.com_.desktop");
ASSERT_TRUE(base::PathExists(desktop_shortcut_path));
std::string actual_contents;
ASSERT_TRUE(base::ReadFileToString(desktop_shortcut_path, &actual_contents));
EXPECT_EQ(expected_contents, actual_contents);
}
TEST_F(WebAppShortcutLinuxTest, UpdateDesktopShortcuts) {
ScopedDesktopPath scoped_desktop_path;
ScopedApplicationsPath scoped_applications_path;
base::ScopedPathOverride user_desktop_override(base::DIR_USER_DESKTOP,
scoped_desktop_path.GetPath());
MockEnvironment env;
env.Set("XDG_DATA_HOME", scoped_applications_path.GetDataHomePath().value());
CreateShortcutInPath(scoped_desktop_path.GetPath());
CreateShortcutInPath(scoped_applications_path.GetPath());
int invoke_count = 0;
LaunchXdgUtilityForTesting CreateDesktopShortcutLaunchXdgUtility =
base::BindLambdaForTesting([&](const std::vector<std::string>& argv,
int* exit_code) -> bool {
ValidateCreateDesktopShortcutLaunchXdgUtility(
argv, exit_code, scoped_applications_path.GetPath(), invoke_count);
invoke_count++;
if (invoke_count < 4)
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
return true;
});
SetLaunchXdgUtilityForTesting(CreateDesktopShortcutLaunchXdgUtility);
std::unique_ptr<ShortcutInfo> shortcut_info = GetShortcutInfo();
UpdateDesktopShortcuts(&env, *shortcut_info);
EXPECT_EQ(invoke_count, 4);
// At this point, we've already validated creation in the Application menu
// because of the hook into XdgUtilityForTesting.
// Validate the shortcut was created, and the contents are what we expect
// them to be.
std::string expected_contents =
shell_integration_linux::GetDesktopFileContents(
shell_integration_linux::internal::GetChromeExePath(),
GenerateApplicationNameFromInfo(*shortcut_info), shortcut_info->url,
shortcut_info->extension_id, shortcut_info->title,
"chrome-test_extension-Profile_1", shortcut_info->profile_path, "",
"", false);
base::FilePath desktop_shortcut_path =
scoped_desktop_path.GetPath().Append(GetTemplateFilename());
ASSERT_TRUE(base::PathExists(desktop_shortcut_path));
std::string actual_contents;
ASSERT_TRUE(base::ReadFileToString(desktop_shortcut_path, &actual_contents));
EXPECT_EQ(expected_contents, actual_contents);
} }
} // namespace web_app } // namespace web_app
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment