blob: 032cc47a7b64d4a284769472ea85dad8b4cea054 [file] [log] [blame]
// Copyright 2018 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/externally_managed_app_manager.h"
#include <algorithm>
#include <sstream>
#include <vector>
#include "base/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
#include "chrome/browser/web_applications/test/fake_externally_managed_app_manager.h"
#include "chrome/browser/web_applications/test/fake_web_app_registry_controller.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/webapps/browser/install_result_code.h"
namespace web_app {
class ExternallyManagedAppManagerTest : public WebAppTest {
protected:
void SetUp() override {
WebAppTest::SetUp();
fake_registry_controller_ =
std::make_unique<FakeWebAppRegistryController>();
controller().SetUp(profile());
externally_installed_app_prefs_ =
std::make_unique<ExternallyInstalledWebAppPrefs>(profile()->GetPrefs());
externally_managed_app_manager_ =
std::make_unique<FakeExternallyManagedAppManager>(profile());
externally_managed_app_manager().SetSubsystems(&app_registrar(), nullptr,
nullptr, nullptr);
externally_managed_app_manager().SetHandleInstallRequestCallback(
base::BindLambdaForTesting(
[this](const ExternalInstallOptions& install_options)
-> ExternallyManagedAppManager::InstallResult {
const GURL& install_url = install_options.install_url;
if (!app_registrar().GetAppById(GenerateAppId(
/*manifest_id=*/absl::nullopt, install_url))) {
std::unique_ptr<WebApp> web_app =
test::CreateWebApp(install_url, Source::kDefault);
controller().RegisterApp(std::move(web_app));
externally_installed_app_prefs().Insert(
install_url,
GenerateAppId(/*manifest_id=*/absl::nullopt, install_url),
install_options.install_source);
++deduped_install_count_;
}
return ExternallyManagedAppManager::InstallResult(
webapps::InstallResultCode::kSuccessNewInstall);
}));
externally_managed_app_manager().SetHandleUninstallRequestCallback(
base::BindLambdaForTesting(
[this](const GURL& app_url,
ExternalInstallSource install_source) -> bool {
absl::optional<AppId> app_id =
app_registrar().LookupExternalAppId(app_url);
if (app_id) {
controller().UnregisterApp(*app_id);
deduped_uninstall_count_++;
}
return true;
}));
controller().Init();
}
void DestroyExternallyManagedAppManager() {
externally_managed_app_manager_.reset();
}
void Sync(std::vector<GURL> urls) {
ResetCounts();
std::vector<ExternalInstallOptions> install_options_list;
for (const auto& url : urls) {
install_options_list.emplace_back(
url, DisplayMode::kStandalone,
ExternalInstallSource::kInternalDefault);
}
base::RunLoop run_loop;
externally_managed_app_manager().SynchronizeInstalledApps(
std::move(install_options_list),
ExternalInstallSource::kInternalDefault,
base::BindLambdaForTesting(
[&run_loop, urls](
std::map<GURL, ExternallyManagedAppManager::InstallResult>
install_results,
std::map<GURL, bool> uninstall_results) { run_loop.Quit(); }));
// Wait for SynchronizeInstalledApps to finish.
run_loop.Run();
}
void Expect(int deduped_install_count,
int deduped_uninstall_count,
std::vector<GURL> installed_app_urls) {
EXPECT_EQ(deduped_install_count, deduped_install_count_);
EXPECT_EQ(deduped_uninstall_count, deduped_uninstall_count_);
std::map<AppId, GURL> apps = app_registrar().GetExternallyInstalledApps(
ExternalInstallSource::kInternalDefault);
std::vector<GURL> urls;
for (auto it : apps)
urls.push_back(it.second);
std::sort(urls.begin(), urls.end());
EXPECT_EQ(installed_app_urls, urls);
}
void ResetCounts() {
deduped_install_count_ = 0;
deduped_uninstall_count_ = 0;
}
FakeWebAppRegistryController& controller() {
return *fake_registry_controller_;
}
web_app::WebAppRegistrar& app_registrar() { return controller().registrar(); }
ExternallyInstalledWebAppPrefs& externally_installed_app_prefs() {
return *externally_installed_app_prefs_;
}
FakeExternallyManagedAppManager& externally_managed_app_manager() {
return *externally_managed_app_manager_;
}
private:
int deduped_install_count_ = 0;
int deduped_uninstall_count_ = 0;
std::unique_ptr<FakeWebAppRegistryController> fake_registry_controller_;
std::unique_ptr<ExternallyInstalledWebAppPrefs>
externally_installed_app_prefs_;
std::unique_ptr<FakeExternallyManagedAppManager>
externally_managed_app_manager_;
};
// Test that destroying ExternallyManagedAppManager during a synchronize call
// that installs an app doesn't crash. Regression test for
// https://crbug.com/962808
TEST_F(ExternallyManagedAppManagerTest, DestroyDuringInstallInSynchronize) {
std::vector<ExternalInstallOptions> install_options_list;
install_options_list.emplace_back(GURL("https://foo.example"),
DisplayMode::kStandalone,
ExternalInstallSource::kInternalDefault);
install_options_list.emplace_back(GURL("https://bar.example"),
DisplayMode::kStandalone,
ExternalInstallSource::kInternalDefault);
externally_managed_app_manager().SynchronizeInstalledApps(
std::move(install_options_list), ExternalInstallSource::kInternalDefault,
// ExternallyManagedAppManager gives no guarantees about whether its
// pending callbacks will be run or not when it gets destroyed.
base::DoNothing());
DestroyExternallyManagedAppManager();
base::RunLoop().RunUntilIdle();
}
// Test that destroying ExternallyManagedAppManager during a synchronize call
// that uninstalls an app doesn't crash. Regression test for
// https://crbug.com/962808
TEST_F(ExternallyManagedAppManagerTest, DestroyDuringUninstallInSynchronize) {
// Install an app that will be uninstalled next.
{
std::vector<ExternalInstallOptions> install_options_list;
install_options_list.emplace_back(GURL("https://foo.example"),
DisplayMode::kStandalone,
ExternalInstallSource::kInternalDefault);
base::RunLoop run_loop;
externally_managed_app_manager().SynchronizeInstalledApps(
std::move(install_options_list),
ExternalInstallSource::kInternalDefault,
base::BindLambdaForTesting(
[&](std::map<GURL, ExternallyManagedAppManager::InstallResult>
install_results,
std::map<GURL, bool> uninstall_results) { run_loop.Quit(); }));
run_loop.Run();
}
externally_managed_app_manager().SynchronizeInstalledApps(
std::vector<ExternalInstallOptions>(),
ExternalInstallSource::kInternalDefault,
// ExternallyManagedAppManager gives no guarantees about whether its
// pending callbacks will be run or not when it gets destroyed.
base::DoNothing());
DestroyExternallyManagedAppManager();
base::RunLoop().RunUntilIdle();
}
TEST_F(ExternallyManagedAppManagerTest, SynchronizeInstalledApps) {
GURL a("https://a.example.com/");
GURL b("https://b.example.com/");
GURL c("https://c.example.com/");
GURL d("https://d.example.com/");
GURL e("https://e.example.com/");
Sync(std::vector<GURL>{a, b, d});
Expect(3, 0, std::vector<GURL>{a, b, d});
Sync(std::vector<GURL>{b, e});
Expect(1, 2, std::vector<GURL>{b, e});
Sync(std::vector<GURL>{e});
Expect(0, 1, std::vector<GURL>{e});
Sync(std::vector<GURL>{c});
Expect(1, 1, std::vector<GURL>{c});
Sync(std::vector<GURL>{e, a, d});
Expect(3, 1, std::vector<GURL>{a, d, e});
Sync(std::vector<GURL>{c, a, b, d, e});
Expect(2, 0, std::vector<GURL>{a, b, c, d, e});
Sync(std::vector<GURL>{});
Expect(0, 5, std::vector<GURL>{});
// The remaining code tests duplicate inputs.
Sync(std::vector<GURL>{b, a, b, c});
Expect(3, 0, std::vector<GURL>{a, b, c});
Sync(std::vector<GURL>{e, a, e, e, e, a});
Expect(1, 2, std::vector<GURL>{a, e});
Sync(std::vector<GURL>{b, c, d});
Expect(3, 2, std::vector<GURL>{b, c, d});
Sync(std::vector<GURL>{a, a, a, a, a, a});
Expect(1, 3, std::vector<GURL>{a});
Sync(std::vector<GURL>{});
Expect(0, 1, std::vector<GURL>{});
}
} // namespace web_app