blob: 3584ced6bf3b7c96d3d04649a62067c1546b3158 [file] [log] [blame]
// Copyright 2020 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/web_applications/system_web_app_manager.h"
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/task_traits.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include "chrome/browser/web_applications/components/web_app_provider_base.h"
#include "chrome/browser/web_applications/pending_app_manager_impl.h"
#include "chrome/browser/web_applications/test/test_data_retriever.h"
#include "chrome/browser/web_applications/test/test_file_handler_manager.h"
#include "chrome/browser/web_applications/test/test_file_utils.h"
#include "chrome/browser/web_applications/test/test_os_integration_manager.h"
#include "chrome/browser/web_applications/test/test_pending_app_manager_impl.h"
#include "chrome/browser/web_applications/test/test_system_web_app_manager.h"
#include "chrome/browser/web_applications/test/test_web_app_database_factory.h"
#include "chrome/browser/web_applications/test/test_web_app_registry_controller.h"
#include "chrome/browser/web_applications/test/test_web_app_ui_manager.h"
#include "chrome/browser/web_applications/test/test_web_app_url_loader.h"
#include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_install_finalizer.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/common/chrome_features.h"
#include "content/public/test/test_utils.h"
#include "url/gurl.h"
namespace web_app {
namespace {
const char kSettingsAppNameForLogging[] = "OSSettings";
const char kDiscoverAppNameForLogging[] = "Discover";
GURL AppUrl1() {
return GURL(content::GetWebUIURL("system-app1"));
}
GURL AppIconUrl1() {
return GURL(content::GetWebUIURL("system-app1/app.ico"));
}
GURL AppUrl2() {
return GURL(content::GetWebUIURL("system-app2"));
}
GURL AppIconUrl2() {
return GURL(content::GetWebUIURL("system-app2/app.ico"));
}
GURL AppUrl3() {
return GURL(content::GetWebUIURL("system-app3"));
}
GURL AppIconUrl3() {
return GURL(content::GetWebUIURL("system-app3/app.ico"));
}
ExternalInstallOptions GetWindowedInstallOptions() {
ExternalInstallOptions options(AppUrl1(), DisplayMode::kStandalone,
ExternalInstallSource::kSystemInstalled);
options.add_to_applications_menu = true;
options.add_to_desktop = false;
options.add_to_quick_launch_bar = false;
options.add_to_search = true;
options.add_to_management = false;
options.is_disabled = false;
options.bypass_service_worker_check = true;
options.force_reinstall = true;
return options;
}
struct SystemAppData {
GURL url;
GURL icon_url;
ExternalInstallSource source;
};
std::unique_ptr<WebApplicationInfo> GetApp1WebApplicationInfo() {
std::unique_ptr<WebApplicationInfo> info =
std::make_unique<WebApplicationInfo>();
info->app_url = AppUrl1();
info->scope = AppUrl1().GetWithoutFilename();
info->title = base::UTF8ToUTF16("Foo Web App");
return info;
}
class TestDataRetrieverFactory {
public:
TestDataRetrieverFactory() = delete;
explicit TestDataRetrieverFactory(std::vector<SystemAppData> system_app_data)
: system_app_data_(std::move(system_app_data)) {}
std::unique_ptr<web_app::WebAppDataRetriever> CreateNextDataRetriever() {
size_t task_index = task_index_++;
auto data_retriever = std::make_unique<TestDataRetriever>();
data_retriever->SetEmptyRendererWebApplicationInfo();
// System apps require an icon specified in the manifest.
auto manifest = std::make_unique<blink::Manifest>();
manifest->start_url = GetSystemAppDataForTask(task_index).url;
manifest->scope = GetSystemAppDataForTask(task_index).url;
manifest->short_name =
base::NullableString16(base::ASCIIToUTF16("Manifest SWA Name"), false);
blink::Manifest::ImageResource icon;
icon.src = GetSystemAppDataForTask(task_index).icon_url;
icon.purpose.push_back(blink::Manifest::ImageResource::Purpose::ANY);
icon.sizes.emplace_back(gfx::Size(icon_size::k256, icon_size::k256));
manifest->icons.push_back(std::move(icon));
data_retriever->SetManifest(std::move(manifest),
/*is_installable=*/true);
// Every InstallTask starts with WebAppDataRetriever::GetIcons step.
data_retriever->SetGetIconsDelegate(base::BindLambdaForTesting(
[&, task_index](content::WebContents* web_contents,
const std::vector<GURL>& icon_urls,
bool skip_page_favicons) {
IconsMap icons_map;
AddIconToIconsMap(GetSystemAppDataForTask(task_index).icon_url,
icon_size::k256, SK_ColorBLUE, &icons_map);
return icons_map;
}));
return std::unique_ptr<WebAppDataRetriever>(std::move(data_retriever));
}
private:
const SystemAppData& GetSystemAppDataForTask(size_t task_index) const {
DCHECK(task_index < system_app_data_.size());
return system_app_data_[task_index];
}
size_t task_index_ = 0;
std::vector<SystemAppData> system_app_data_;
};
class SystemWebAppWaiter {
public:
explicit SystemWebAppWaiter(SystemWebAppManager* system_web_app_manager) {
system_web_app_manager->ResetOnAppsSynchronizedForTesting();
system_web_app_manager->on_apps_synchronized().Post(
FROM_HERE, base::BindLambdaForTesting([&]() {
// Wait one execution loop for on_apps_synchronized() to be
// called on all listeners.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, run_loop_.QuitClosure());
}));
}
void Wait() { run_loop_.Run(); }
private:
base::RunLoop run_loop_;
};
} // namespace
class SystemWebAppManagerTest : public WebAppTest {
public:
SystemWebAppManagerTest() {
scoped_feature_list_.InitWithFeatures(
{features::kSystemWebApps, features::kDesktopPWAsWithoutExtensions},
{});
}
~SystemWebAppManagerTest() override = default;
void SetUp() override {
WebAppTest::SetUp();
test_registry_controller_ =
std::make_unique<TestWebAppRegistryController>();
controller().SetUp(profile());
externally_installed_app_prefs_ =
std::make_unique<ExternallyInstalledWebAppPrefs>(profile()->GetPrefs());
test_file_handler_manager_ =
std::make_unique<TestFileHandlerManager>(profile());
icon_manager_ = std::make_unique<WebAppIconManager>(
profile(), controller().registrar(), std::make_unique<TestFileUtils>());
install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(
profile(), &icon_manager(), /*legacy_finalizer=*/nullptr);
install_manager_ = std::make_unique<WebAppInstallManager>(profile());
test_pending_app_manager_impl_ =
std::make_unique<TestPendingAppManagerImpl>(profile());
test_os_integration_manager_ =
std::make_unique<TestOsIntegrationManager>(profile());
test_system_web_app_manager_ =
std::make_unique<TestSystemWebAppManager>(profile());
test_ui_manager_ = std::make_unique<TestWebAppUiManager>();
install_finalizer().SetSubsystems(&controller().registrar(), &ui_manager(),
&controller().sync_bridge());
install_manager().SetUrlLoaderForTesting(
std::make_unique<TestWebAppUrlLoader>());
install_manager().SetSubsystems(&controller().registrar(),
&os_integration_manager(),
&install_finalizer());
auto url_loader = std::make_unique<TestWebAppUrlLoader>();
url_loader_ = url_loader.get();
pending_app_manager().SetUrlLoaderForTesting(std::move(url_loader));
pending_app_manager().SetSubsystems(
&controller().registrar(), &os_integration_manager(), &ui_manager(),
&install_finalizer(), &install_manager());
system_web_app_manager().SetSubsystems(
&pending_app_manager(), &controller().registrar(),
&controller().sync_bridge(), &ui_manager(), &file_handler_manager());
install_manager().Start();
install_finalizer().Start();
// TODO(https://crbug.com/1108611) we should use a single
// TestOsIntegrationManager
WebAppProviderBase::GetProviderBase(profile())
->os_integration_manager()
.SuppressOsHooksForTesting();
}
void TearDown() override {
DestroyManagers();
WebAppTest::TearDown();
}
void DestroyManagers() {
// The reverse order of creation:
test_ui_manager_.reset();
test_system_web_app_manager_.reset();
test_os_integration_manager_.reset();
test_pending_app_manager_impl_.reset();
install_manager_.reset();
install_finalizer_.reset();
icon_manager_.reset();
test_file_handler_manager_.reset();
externally_installed_app_prefs_.reset();
test_registry_controller_.reset();
}
protected:
TestWebAppRegistryController& controller() {
return *test_registry_controller_;
}
ExternallyInstalledWebAppPrefs& externally_installed_app_prefs() {
return *externally_installed_app_prefs_;
}
TestFileHandlerManager& file_handler_manager() {
return *test_file_handler_manager_;
}
WebAppIconManager& icon_manager() { return *icon_manager_; }
WebAppInstallFinalizer& install_finalizer() { return *install_finalizer_; }
WebAppInstallManager& install_manager() { return *install_manager_; }
TestPendingAppManagerImpl& pending_app_manager() {
return *test_pending_app_manager_impl_;
}
TestSystemWebAppManager& system_web_app_manager() {
return *test_system_web_app_manager_;
}
TestWebAppUiManager& ui_manager() { return *test_ui_manager_; }
TestOsIntegrationManager& os_integration_manager() {
return *test_os_integration_manager_;
}
TestWebAppUrlLoader& url_loader() { return *url_loader_; }
bool IsInstalled(const GURL& install_url) {
return controller().registrar().IsInstalled(
GenerateAppIdFromURL(install_url));
}
std::unique_ptr<WebApp> CreateWebApp(
const GURL& launch_url,
Source::Type source_type = Source::kDefault) {
const AppId app_id = GenerateAppIdFromURL(launch_url);
auto web_app = std::make_unique<WebApp>(app_id);
web_app->SetLaunchUrl(launch_url);
web_app->SetName("App Name");
web_app->AddSource(source_type);
web_app->SetDisplayMode(DisplayMode::kStandalone);
web_app->SetUserDisplayMode(DisplayMode::kStandalone);
return web_app;
}
std::unique_ptr<WebApp> CreateSystemWebApp(const GURL& launch_url) {
return CreateWebApp(launch_url, Source::Type::kSystem);
}
void InitRegistrarWithRegistry(const Registry& registry) {
controller().database_factory().WriteRegistry(registry);
controller().Init();
}
void InitRegistrarWithSystemApps(
std::vector<SystemAppData> system_app_data_list) {
DCHECK(controller().registrar().is_empty());
DCHECK(!system_app_data_list.empty());
Registry registry;
for (const SystemAppData& data : system_app_data_list) {
std::unique_ptr<WebApp> web_app = CreateSystemWebApp(data.url);
const AppId app_id = web_app->app_id();
registry.emplace(app_id, std::move(web_app));
externally_installed_app_prefs().Insert(
data.url, GenerateAppIdFromURL(data.url), data.source);
}
InitRegistrarWithRegistry(registry);
}
void InitEmptyRegistrar() {
Registry registry;
InitRegistrarWithRegistry(registry);
}
void PrepareSystemAppDataToRetrieve(
std::vector<SystemAppData> system_app_data) {
test_data_retriever_factory_ =
std::make_unique<TestDataRetrieverFactory>(std::move(system_app_data));
install_manager().SetDataRetrieverFactoryForTesting(
base::BindLambdaForTesting([this]() {
DCHECK(test_data_retriever_factory_);
return test_data_retriever_factory_->CreateNextDataRetriever();
}));
}
void PrepareLoadUrlResults(const std::vector<GURL>& urls) {
std::vector<WebAppUrlLoader::Result> load_results(
urls.size(), WebAppUrlLoader::Result::kUrlLoaded);
url_loader().AddPrepareForLoadResults(load_results);
for (const auto& url : urls) {
url_loader().SetNextLoadUrlResult(url,
WebAppUrlLoader::Result::kUrlLoaded);
}
}
void StartAndWaitForAppsToSynchronize() {
SystemWebAppWaiter waiter(&system_web_app_manager());
system_web_app_manager().Start();
waiter.Wait();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<TestWebAppRegistryController> test_registry_controller_;
std::unique_ptr<ExternallyInstalledWebAppPrefs>
externally_installed_app_prefs_;
std::unique_ptr<TestFileHandlerManager> test_file_handler_manager_;
std::unique_ptr<WebAppIconManager> icon_manager_;
std::unique_ptr<WebAppInstallFinalizer> install_finalizer_;
std::unique_ptr<WebAppInstallManager> install_manager_;
std::unique_ptr<TestPendingAppManagerImpl> test_pending_app_manager_impl_;
std::unique_ptr<TestSystemWebAppManager> test_system_web_app_manager_;
std::unique_ptr<TestWebAppUiManager> test_ui_manager_;
std::unique_ptr<TestOsIntegrationManager> test_os_integration_manager_;
TestWebAppUrlLoader* url_loader_ = nullptr;
std::unique_ptr<TestDataRetrieverFactory> test_data_retriever_factory_;
DISALLOW_COPY_AND_ASSIGN(SystemWebAppManagerTest);
};
// Test that System Apps are uninstalled with the feature disabled.
TEST_F(SystemWebAppManagerTest, Disabled) {
base::test::ScopedFeatureList disable_feature_list;
disable_feature_list.InitAndDisableFeature(features::kSystemWebApps);
InitRegistrarWithSystemApps(
{{AppUrl1(), AppIconUrl1(), ExternalInstallSource::kSystemInstalled}});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(std::move(system_apps));
StartAndWaitForAppsToSynchronize();
EXPECT_TRUE(pending_app_manager().install_requests().empty());
// We should try to uninstall the app that is no longer in the System App
// list.
EXPECT_EQ(std::vector<GURL>({AppUrl1()}),
pending_app_manager().uninstall_requests());
}
// Test that System Apps do install with the feature enabled.
TEST_F(SystemWebAppManagerTest, Enabled) {
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl1(), AppUrl2()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_apps.emplace(SystemAppType::DISCOVER,
SystemAppInfo(kDiscoverAppNameForLogging, AppUrl2()));
system_web_app_manager().SetSystemAppsForTesting(std::move(system_apps));
StartAndWaitForAppsToSynchronize();
EXPECT_FALSE(pending_app_manager().install_requests().empty());
}
// Test that System Apps do install with the feature enabled.
TEST_F(SystemWebAppManagerTest, InstallFromWebAppInfo) {
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl2()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(
SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1(),
base::BindRepeating(&GetApp1WebApplicationInfo)));
system_apps.emplace(SystemAppType::DISCOVER,
SystemAppInfo(kDiscoverAppNameForLogging, AppUrl2()));
system_web_app_manager().SetSystemAppsForTesting(std::move(system_apps));
StartAndWaitForAppsToSynchronize();
EXPECT_FALSE(pending_app_manager().install_requests().empty());
EXPECT_TRUE(IsInstalled(AppUrl1()));
EXPECT_TRUE(IsInstalled(AppUrl2()));
}
// Test that changing the set of System Apps uninstalls apps.
TEST_F(SystemWebAppManagerTest, UninstallAppInstalledInPreviousSession) {
// Simulate System Apps and a regular app that were installed in the
// previous session.
InitRegistrarWithSystemApps(
{{AppUrl1(), AppIconUrl1(), ExternalInstallSource::kSystemInstalled},
{AppUrl2(), AppIconUrl2(), ExternalInstallSource::kSystemInstalled},
{AppUrl3(), AppIconUrl3(), ExternalInstallSource::kInternalDefault}});
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(std::move(system_apps));
StartAndWaitForAppsToSynchronize();
// We should only try to install the app in the System App list.
std::vector<ExternalInstallOptions> expected_install_options_list;
expected_install_options_list.push_back(GetWindowedInstallOptions());
EXPECT_EQ(pending_app_manager().install_requests(),
expected_install_options_list);
// We should try to uninstall the app that is no longer in the System App
// list.
EXPECT_EQ(std::vector<GURL>({AppUrl2()}),
pending_app_manager().uninstall_requests());
}
TEST_F(SystemWebAppManagerTest, AlwaysUpdate) {
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kAlwaysUpdate);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
system_web_app_manager().set_current_version(base::Version("1.0.0.0"));
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(1u, pending_app_manager().install_requests().size());
// Create another app. The version hasn't changed but the app should still
// install.
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl1(), AppUrl2()});
system_apps.emplace(SystemAppType::DISCOVER,
SystemAppInfo(kDiscoverAppNameForLogging, AppUrl2()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
// This one returns because on_apps_synchronized runs immediately.
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(3u, pending_app_manager().install_requests().size());
{
// Disabling System Web Apps uninstalls without a version change.
base::test::ScopedFeatureList disable_feature_list;
disable_feature_list.InitWithFeatures({}, {features::kSystemWebApps});
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(2u, pending_app_manager().uninstall_requests().size());
}
// Re-enabling System Web Apps installs without a version change.
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl1(), AppUrl2()});
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(5u, pending_app_manager().install_requests().size());
}
TEST_F(SystemWebAppManagerTest, UpdateOnVersionChange) {
const std::vector<ExternalInstallOptions>& install_requests =
pending_app_manager().install_requests();
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kOnVersionChange);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
system_web_app_manager().set_current_version(base::Version("1.0.0.0"));
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(1u, install_requests.size());
EXPECT_TRUE(install_requests[0].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Create another app. The version hasn't changed, but we should immediately
// install anyway, as if a user flipped a chrome://flag. The first app won't
// force reinstall.
PrepareSystemAppDataToRetrieve({{AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl2()});
system_apps.emplace(SystemAppType::DISCOVER,
SystemAppInfo(kDiscoverAppNameForLogging, AppUrl2()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(3u, install_requests.size());
EXPECT_FALSE(install_requests[1].force_reinstall);
EXPECT_FALSE(install_requests[2].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
EXPECT_TRUE(IsInstalled(AppUrl2()));
// Bump the version number, and an update will trigger, and force
// reinstallation of both apps.
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl1(), AppUrl2()});
system_web_app_manager().set_current_version(base::Version("2.0.0.0"));
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(5u, install_requests.size());
EXPECT_TRUE(install_requests[3].force_reinstall);
EXPECT_TRUE(install_requests[4].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
EXPECT_TRUE(IsInstalled(AppUrl2()));
{
// Disabling System Web Apps uninstalls even without a version change.
base::test::ScopedFeatureList disable_feature_list;
disable_feature_list.InitWithFeatures({}, {features::kSystemWebApps});
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(2u, pending_app_manager().uninstall_requests().size());
EXPECT_FALSE(IsInstalled(AppUrl1()));
EXPECT_FALSE(IsInstalled(AppUrl2()));
}
// Re-enabling System Web Apps installs even without a version change.
PrepareSystemAppDataToRetrieve(
{{AppUrl1(), AppIconUrl1()}, {AppUrl2(), AppIconUrl2()}});
PrepareLoadUrlResults({AppUrl1(), AppUrl2()});
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(7u, install_requests.size());
EXPECT_FALSE(install_requests[5].force_reinstall);
EXPECT_FALSE(install_requests[6].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
EXPECT_TRUE(IsInstalled(AppUrl2()));
// Changing the install URL of a system app propagates even without a
// version change.
PrepareSystemAppDataToRetrieve({{AppUrl3(), AppIconUrl3()}});
PrepareLoadUrlResults({AppUrl3()});
system_apps.find(SystemAppType::SETTINGS)->second.install_url = AppUrl3();
system_web_app_manager().SetSystemAppsForTesting(system_apps);
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(9u, install_requests.size());
EXPECT_FALSE(install_requests[7].force_reinstall);
EXPECT_FALSE(install_requests[8].force_reinstall);
EXPECT_FALSE(IsInstalled(AppUrl1()));
EXPECT_TRUE(IsInstalled(AppUrl2()));
EXPECT_TRUE(IsInstalled(AppUrl3()));
}
TEST_F(SystemWebAppManagerTest, UpdateOnLocaleChange) {
const std::vector<ExternalInstallOptions>& install_requests =
pending_app_manager().install_requests();
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kOnVersionChange);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
// First execution.
system_web_app_manager().set_current_locale("en-US");
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(1u, install_requests.size());
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Change locale setting, should trigger reinstall.
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
system_web_app_manager().set_current_locale("ja");
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(2u, install_requests.size());
EXPECT_TRUE(install_requests[1].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Do not reinstall because locale is not changed.
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(3u, install_requests.size());
EXPECT_FALSE(install_requests[2].force_reinstall);
}
TEST_F(SystemWebAppManagerTest, InstallResultHistogram) {
base::HistogramTester histograms;
const std::string settings_app_install_result_histogram =
std::string(SystemWebAppManager::kInstallResultHistogramName) + ".Apps." +
kSettingsAppNameForLogging;
const std::string discover_app_install_result_histogram =
std::string(SystemWebAppManager::kInstallResultHistogramName) + ".Apps." +
kDiscoverAppNameForLogging;
// Profile category for Chrome OS testing environment is "Other".
const std::string profile_install_result_histogram =
std::string(SystemWebAppManager::kInstallResultHistogramName) +
".Profiles.Other";
InitEmptyRegistrar();
{
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallResultHistogramName, 0);
histograms.ExpectTotalCount(settings_app_install_result_histogram, 0);
histograms.ExpectTotalCount(profile_install_result_histogram, 0);
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallDurationHistogramName, 0);
StartAndWaitForAppsToSynchronize();
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallResultHistogramName, 1);
histograms.ExpectBucketCount(
SystemWebAppManager::kInstallResultHistogramName,
InstallResultCode::kSuccessNewInstall, 1);
histograms.ExpectTotalCount(settings_app_install_result_histogram, 1);
histograms.ExpectBucketCount(settings_app_install_result_histogram,
InstallResultCode::kSuccessNewInstall, 1);
histograms.ExpectTotalCount(profile_install_result_histogram, 1);
histograms.ExpectBucketCount(profile_install_result_histogram,
InstallResultCode::kSuccessNewInstall, 1);
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallDurationHistogramName, 1);
}
pending_app_manager().SetPreInstallCallback(base::BindLambdaForTesting(
[](const ExternalInstallOptions&) { return false; }));
{
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_apps.emplace(SystemAppType::DISCOVER,
SystemAppInfo(kDiscoverAppNameForLogging, AppUrl2()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
StartAndWaitForAppsToSynchronize();
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallResultHistogramName, 3);
histograms.ExpectBucketCount(
SystemWebAppManager::kInstallResultHistogramName,
InstallResultCode::kWebAppDisabled, 2);
histograms.ExpectTotalCount(settings_app_install_result_histogram, 2);
histograms.ExpectBucketCount(settings_app_install_result_histogram,
InstallResultCode::kWebAppDisabled, 1);
histograms.ExpectBucketCount(discover_app_install_result_histogram,
InstallResultCode::kWebAppDisabled, 1);
}
{
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallDurationHistogramName, 2);
histograms.ExpectBucketCount(
settings_app_install_result_histogram,
InstallResultCode::kCancelledOnWebAppProviderShuttingDown, 0);
histograms.ExpectBucketCount(
profile_install_result_histogram,
InstallResultCode::kCancelledOnWebAppProviderShuttingDown, 0);
{
SystemWebAppWaiter waiter(&system_web_app_manager());
system_web_app_manager().Start();
system_web_app_manager().Shutdown();
waiter.Wait();
}
histograms.ExpectBucketCount(
SystemWebAppManager::kInstallResultHistogramName,
InstallResultCode::kCancelledOnWebAppProviderShuttingDown, 1);
histograms.ExpectBucketCount(
SystemWebAppManager::kInstallResultHistogramName,
InstallResultCode::kWebAppDisabled, 2);
histograms.ExpectBucketCount(
settings_app_install_result_histogram,
InstallResultCode::kCancelledOnWebAppProviderShuttingDown, 1);
histograms.ExpectBucketCount(
profile_install_result_histogram,
InstallResultCode::kCancelledOnWebAppProviderShuttingDown, 1);
// If install was interrupted by shutdown, do not report duration.
histograms.ExpectTotalCount(
SystemWebAppManager::kInstallDurationHistogramName, 2);
}
}
TEST_F(SystemWebAppManagerTest, AbandonFailedInstalls) {
const std::vector<ExternalInstallOptions>& install_requests =
pending_app_manager().install_requests();
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kOnVersionChange);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
system_web_app_manager().set_current_version(base::Version("1.0.0.0"));
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(1u, install_requests.size());
EXPECT_TRUE(install_requests[0].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Bump the version number, and an update will trigger, and force
// reinstallation of both apps.
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
system_web_app_manager().set_current_version(base::Version("2.0.0.0"));
pending_app_manager().SetDropRequestsForTesting(true);
// Can't use the normal method because RunLoop::Run goes until
// on_app_synchronized is called, and this fails, never calling that.
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
// 1 successful, 1 abandoned, and 3 more abanonded retries is 5.
EXPECT_EQ(5u, install_requests.size());
EXPECT_TRUE(install_requests[1].force_reinstall);
EXPECT_TRUE(install_requests[2].force_reinstall);
EXPECT_TRUE(install_requests[3].force_reinstall);
EXPECT_TRUE(install_requests[4].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// If we don't abandon at the same version, it doesn't even attempt another
// request
pending_app_manager().SetDropRequestsForTesting(false);
system_web_app_manager().set_current_version(base::Version("2.0.0.0"));
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(5u, install_requests.size());
// Bump the version, and it works.
system_web_app_manager().set_current_version(base::Version("3.0.0.0"));
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(6u, install_requests.size());
}
// Same test, but for locale change.
TEST_F(SystemWebAppManagerTest, AbandonFailedInstallsLocaleChange) {
const std::vector<ExternalInstallOptions>& install_requests =
pending_app_manager().install_requests();
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kOnVersionChange);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
system_web_app_manager().set_current_version(base::Version("1.0.0.0"));
system_web_app_manager().set_current_locale("en/us");
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(1u, install_requests.size());
EXPECT_TRUE(install_requests[0].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Bump the version number, and an update will trigger, and force
// reinstallation of both apps.
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
system_web_app_manager().set_current_locale("en/au");
pending_app_manager().SetDropRequestsForTesting(true);
// Can't use the normal method because RunLoop::Run goes until
// on_app_synchronized is called, and this fails, never calling that.
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
// 1 successful, 1 abandoned, and 3 more abanonded retries is 5.
EXPECT_EQ(5u, install_requests.size());
EXPECT_TRUE(install_requests[1].force_reinstall);
EXPECT_TRUE(install_requests[2].force_reinstall);
EXPECT_TRUE(install_requests[3].force_reinstall);
EXPECT_TRUE(install_requests[4].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// If we don't abandon at the same version, it doesn't even attempt another
// request
pending_app_manager().SetDropRequestsForTesting(false);
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(5u, install_requests.size());
// Bump the version, and it works.
system_web_app_manager().set_current_locale("fr/fr");
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
}
TEST_F(SystemWebAppManagerTest, SucceedsAfterOneRetry) {
const std::vector<ExternalInstallOptions>& install_requests =
pending_app_manager().install_requests();
system_web_app_manager().SetUpdatePolicy(
SystemWebAppManager::UpdatePolicy::kOnVersionChange);
InitEmptyRegistrar();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
// Set up and install a baseline
base::flat_map<SystemAppType, SystemAppInfo> system_apps;
system_apps.emplace(SystemAppType::SETTINGS,
SystemAppInfo(kSettingsAppNameForLogging, AppUrl1()));
system_web_app_manager().SetSystemAppsForTesting(system_apps);
system_web_app_manager().set_current_version(base::Version("1.0.0.0"));
StartAndWaitForAppsToSynchronize();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(1u, install_requests.size());
EXPECT_TRUE(install_requests[0].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Bump the version number, and an update will trigger, and force
// reinstallation. But, this fails!
system_web_app_manager().set_current_version(base::Version("2.0.0.0"));
pending_app_manager().SetDropRequestsForTesting(true);
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(2u, install_requests.size());
EXPECT_TRUE(install_requests[1].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
system_web_app_manager().Start();
base::RunLoop().RunUntilIdle();
pending_app_manager().ClearSynchronizeRequestsForTesting();
// Retry a few times, but not until abandonment.
EXPECT_EQ(3u, install_requests.size());
EXPECT_TRUE(install_requests[0].force_reinstall);
EXPECT_TRUE(install_requests[1].force_reinstall);
EXPECT_TRUE(install_requests[2].force_reinstall);
EXPECT_TRUE(IsInstalled(AppUrl1()));
// Now we succeed at the same version
pending_app_manager().SetDropRequestsForTesting(false);
StartAndWaitForAppsToSynchronize();
pending_app_manager().ClearSynchronizeRequestsForTesting();
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
StartAndWaitForAppsToSynchronize();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(5u, install_requests.size());
EXPECT_TRUE(install_requests[3].force_reinstall);
EXPECT_FALSE(install_requests[4].force_reinstall);
// Bump the version number, and an update will trigger, and force
// reinstallation of both apps. This succeeds, everything works.
system_web_app_manager().set_current_version(base::Version("3.0.0.0"));
StartAndWaitForAppsToSynchronize();
pending_app_manager().ClearSynchronizeRequestsForTesting();
EXPECT_EQ(6u, install_requests.size());
EXPECT_TRUE(install_requests[5].force_reinstall);
PrepareSystemAppDataToRetrieve({{AppUrl1(), AppIconUrl1()}});
PrepareLoadUrlResults({AppUrl1()});
StartAndWaitForAppsToSynchronize();
EXPECT_EQ(7u, install_requests.size());
EXPECT_FALSE(install_requests[6].force_reinstall);
}
} // namespace web_app