blob: 01fac604cd1e655eae4532ba8c5a14ea9fe8aea9 [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/web_app_database.h"
#include <memory>
#include <tuple>
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/proto/web_app.pb.h"
#include "chrome/browser/web_applications/test/test_web_app_database_factory.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/sync/model/model_type_store.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace web_app {
bool operator==(const WebApp::IconInfo& icon_info,
const WebApp::IconInfo& icon_info2) {
return std::tie(icon_info.url, icon_info.size_in_px) ==
std::tie(icon_info2.url, icon_info2.size_in_px);
}
bool operator==(const WebApp& web_app, const WebApp& web_app2) {
return std::tie(web_app.app_id(), web_app.name(), web_app.launch_url(),
web_app.description(), web_app.scope(), web_app.theme_color(),
web_app.icons()) ==
std::tie(web_app2.app_id(), web_app2.name(), web_app2.launch_url(),
web_app2.description(), web_app2.scope(),
web_app2.theme_color(), web_app2.icons());
}
bool operator!=(const WebApp& web_app, const WebApp& web_app2) {
return !operator==(web_app, web_app2);
}
namespace {
bool IsRegistryEqual(const Registry& registry, const Registry& registry2) {
if (registry.size() != registry2.size())
return false;
for (auto& kv : registry) {
const WebApp* web_app = kv.second.get();
const WebApp* web_app2 = registry2.at(web_app->app_id()).get();
if (*web_app != *web_app2)
return false;
}
return true;
}
} // namespace
class WebAppDatabaseTest : public testing::Test {
public:
WebAppDatabaseTest() {
database_factory_ = std::make_unique<TestWebAppDatabaseFactory>();
database_ = std::make_unique<WebAppDatabase>(database_factory_.get());
registrar_ = std::make_unique<WebAppRegistrar>(database_.get());
}
void InitRegistrar() {
base::RunLoop run_loop;
registrar_->Init(base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
run_loop.Run();
}
static std::unique_ptr<WebApp> CreateWebApp(const std::string& base_url,
int suffix) {
const auto launch_url = base_url + base::NumberToString(suffix);
const AppId app_id = GenerateAppIdFromURL(GURL(launch_url));
const std::string name = "Name" + base::NumberToString(suffix);
const std::string description =
"Description" + base::NumberToString(suffix);
const std::string scope =
base_url + "/scope" + base::NumberToString(suffix);
const base::Optional<SkColor> theme_color = suffix;
auto app = std::make_unique<WebApp>(app_id);
app->SetName(name);
app->SetDescription(description);
app->SetLaunchUrl(GURL(launch_url));
app->SetScope(GURL(scope));
app->SetThemeColor(theme_color);
const std::string icon_url =
base_url + "/icon" + base::NumberToString(suffix);
const int icon_size_in_px = 256;
WebApp::Icons icons;
icons.push_back({GURL(icon_url), icon_size_in_px});
app->SetIcons(std::move(icons));
return app;
}
Registry ReadRegistry() {
Registry registry;
base::RunLoop run_loop;
database_->ReadRegistry(base::BindLambdaForTesting([&](Registry r) {
registry = std::move(r);
run_loop.Quit();
}));
run_loop.Run();
return registry;
}
bool IsDatabaseRegistryEqualToRegistrar() {
Registry registry = ReadRegistry();
return IsRegistryEqual(registrar_->registry(), registry);
}
void WriteBatch(
std::unique_ptr<syncer::ModelTypeStore::WriteBatch> write_batch) {
base::RunLoop run_loop;
database_factory_->store()->CommitWriteBatch(
std::move(write_batch),
base::BindLambdaForTesting(
[&](const base::Optional<syncer::ModelError>& error) {
EXPECT_FALSE(error);
run_loop.Quit();
}));
run_loop.Run();
}
Registry WriteWebApps(const std::string& base_url, int num_apps) {
Registry registry;
auto write_batch = database_factory_->store()->CreateWriteBatch();
for (int i = 0; i < num_apps; ++i) {
auto app = CreateWebApp(base_url, i);
auto proto = WebAppDatabase::CreateWebAppProto(*app);
const auto app_id = app->app_id();
write_batch->WriteData(app_id, proto->SerializeAsString());
registry.emplace(app_id, std::move(app));
}
WriteBatch(std::move(write_batch));
return registry;
}
protected:
// Must be created before TestWebAppDatabaseFactory.
base::MessageLoop message_loop_;
std::unique_ptr<TestWebAppDatabaseFactory> database_factory_;
std::unique_ptr<WebAppDatabase> database_;
std::unique_ptr<WebAppRegistrar> registrar_;
};
TEST_F(WebAppDatabaseTest, WriteAndReadRegistry) {
InitRegistrar();
EXPECT_TRUE(registrar_->is_empty());
const int num_apps = 100;
const std::string base_url = "https://example.com/path";
auto app = CreateWebApp(base_url, 0);
auto app_id = app->app_id();
registrar_->RegisterApp(std::move(app));
EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
for (int i = 1; i <= num_apps; ++i) {
auto extra_app = CreateWebApp(base_url, i);
registrar_->RegisterApp(std::move(extra_app));
}
EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
registrar_->UnregisterApp(app_id);
EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
registrar_->UnregisterAll();
EXPECT_TRUE(IsDatabaseRegistryEqualToRegistrar());
}
TEST_F(WebAppDatabaseTest, OpenDatabaseAndReadRegistry) {
Registry registry = WriteWebApps("https://example.com/path", 100);
InitRegistrar();
EXPECT_TRUE(IsRegistryEqual(registrar_->registry(), registry));
}
TEST_F(WebAppDatabaseTest, WebAppWithoutOptionalFields) {
InitRegistrar();
const auto launch_url = GURL("https://example.com/");
const AppId app_id = GenerateAppIdFromURL(GURL(launch_url));
auto app = std::make_unique<WebApp>(app_id);
app->SetLaunchUrl(launch_url);
EXPECT_TRUE(app->name().empty());
EXPECT_TRUE(app->description().empty());
EXPECT_TRUE(app->scope().is_empty());
EXPECT_FALSE(app->theme_color().has_value());
// |icons| is mandatory data member for a representation in DB. If no icons,
// WebAppDatabase::CreateWebApp(from_proto) returns nullptr.
WebApp::Icons icons;
icons.push_back({GURL("https://example.com/icon"), 512});
app->SetIcons(std::move(icons));
registrar_->RegisterApp(std::move(app));
Registry registry = ReadRegistry();
EXPECT_EQ(1UL, registry.size());
std::unique_ptr<WebApp>& app_copy = registry.at(app_id);
// Mandatory members.
EXPECT_EQ(app_id, app_copy->app_id());
EXPECT_EQ(launch_url, app_copy->launch_url());
// No optional members.
EXPECT_TRUE(app_copy->name().empty());
EXPECT_TRUE(app_copy->description().empty());
EXPECT_TRUE(app_copy->scope().is_empty());
EXPECT_FALSE(app_copy->theme_color().has_value());
}
TEST_F(WebAppDatabaseTest, WebAppWithManyIcons) {
InitRegistrar();
const int num_icons = 32;
const std::string base_url = "https://example.com/path";
auto app = CreateWebApp(base_url, 0);
auto app_id = app->app_id();
WebApp::Icons icons;
for (int i = 1; i <= num_icons; ++i) {
const std::string icon_url =
base_url + "/icon" + base::NumberToString(num_icons);
// Let size equals the icon's number squared.
const int icon_size_in_px = i * i;
icons.push_back({GURL(icon_url), icon_size_in_px});
}
app->SetIcons(std::move(icons));
registrar_->RegisterApp(std::move(app));
Registry registry = ReadRegistry();
EXPECT_EQ(1UL, registry.size());
std::unique_ptr<WebApp>& app_copy = registry.at(app_id);
EXPECT_EQ(static_cast<unsigned>(num_icons), app_copy->icons().size());
for (int i = 1; i <= num_icons; ++i) {
const int icon_size_in_px = i * i;
EXPECT_EQ(icon_size_in_px, app_copy->icons()[i - 1].size_in_px);
}
}
} // namespace web_app