blob: bb8a4e8b8e6e2e4378246ada8c474d7261b2f782 [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 "content/browser/notifications/notification_storage.h"
#include "base/bind.h"
#include "base/guid.h"
#include "base/run_loop.h"
#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "url/gurl.h"
namespace content {
class NotificationStorageTest : public ::testing::Test {
public:
NotificationStorageTest()
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
origin_(GURL("https://example.com")),
success_(false),
service_worker_registration_id_(
blink::mojom::kInvalidServiceWorkerRegistrationId) {
helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
storage_ =
std::make_unique<NotificationStorage>(helper_->context_wrapper());
}
void DidRegisterServiceWorker(base::OnceClosure quit_closure,
blink::ServiceWorkerStatusCode status,
const std::string& status_message,
int64_t service_worker_registration_id) {
DCHECK(service_worker_registration_id_);
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status) << status_message;
service_worker_registration_id_ = service_worker_registration_id;
std::move(quit_closure).Run();
}
void DidFindServiceWorkerRegistration(
scoped_refptr<ServiceWorkerRegistration>* out_service_worker_registration,
base::OnceClosure quit_closure,
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
DCHECK(out_service_worker_registration);
EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
<< blink::ServiceWorkerStatusToString(status);
*out_service_worker_registration = service_worker_registration;
std::move(quit_closure).Run();
}
// Registers a Service Worker for the testing origin and returns its
// |service_worker_registration_id|. If registration failed, this will be
// blink::mojom::kInvalidServiceWorkerRegistrationId. The
// ServiceWorkerRegistration will be kept alive for the test's lifetime.
int64_t RegisterServiceWorker() {
GURL script_url = origin_;
{
blink::mojom::ServiceWorkerRegistrationOptions options;
options.scope = origin_;
base::RunLoop run_loop;
helper_->context()->RegisterServiceWorker(
script_url, options,
base::BindOnce(&NotificationStorageTest::DidRegisterServiceWorker,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
if (service_worker_registration_id_ ==
blink::mojom::kInvalidServiceWorkerRegistrationId) {
ADD_FAILURE() << "Could not obtain a valid Service Worker registration";
return blink::mojom::kInvalidServiceWorkerRegistrationId;
}
scoped_refptr<ServiceWorkerRegistration> service_worker_registration;
{
base::RunLoop run_loop;
helper_->context()->storage()->FindRegistrationForId(
service_worker_registration_id_, origin_,
base::BindOnce(
&NotificationStorageTest::DidFindServiceWorkerRegistration,
base::Unretained(this), &service_worker_registration,
run_loop.QuitClosure()));
run_loop.Run();
}
// Wait for the worker to be activated.
thread_bundle_.RunUntilIdle();
if (!service_worker_registration) {
ADD_FAILURE() << "Could not find the new Service Worker registration.";
return blink::mojom::kInvalidServiceWorkerRegistrationId;
}
service_worker_registrations_.push_back(
std::move(service_worker_registration));
return service_worker_registration_id_;
}
void DidWriteNotificationDataSynchronous(base::OnceClosure quit_closure,
bool success,
const std::string& notification_id) {
success_ = success;
notification_id_ = notification_id;
std::move(quit_closure).Run();
}
void WriteNotificationDataSynchronous(const NotificationDatabaseData& data) {
base::RunLoop run_loop;
storage_->WriteNotificationData(
data, base::Bind(
&NotificationStorageTest::DidWriteNotificationDataSynchronous,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void DidReadNotificationDataAndRecordInteractionSynchronous(
base::OnceClosure quit_closure,
bool success,
const NotificationDatabaseData& data) {
success_ = success;
out_data_ = data;
std::move(quit_closure).Run();
}
NotificationDatabaseData ReadNotificationDataAndRecordInteractionSynchronous(
int64_t service_worker_registration_id,
const std::string& notification_id,
PlatformNotificationContext::Interaction interaction) {
base::RunLoop run_loop;
storage_->ReadNotificationDataAndRecordInteraction(
service_worker_registration_id, notification_id, interaction,
base::Bind(&NotificationStorageTest::
DidReadNotificationDataAndRecordInteractionSynchronous,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
return out_data_;
}
// Generates a random notification ID. The format of the ID is opaque.
std::string GenerateNotificationId() { return base::GenerateGUID(); }
protected:
TestBrowserThreadBundle thread_bundle_; // Must be first member
std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
GURL origin_;
TestBrowserContext browser_context_;
bool success_;
int64_t service_worker_registration_id_;
NotificationDatabaseData out_data_;
private:
std::unique_ptr<NotificationStorage> storage_;
std::string notification_id_;
// Vector of ServiceWorkerRegistration instances that have to be kept alive
// for the lifetime of this test.
std::vector<scoped_refptr<ServiceWorkerRegistration>>
service_worker_registrations_;
};
TEST_F(NotificationStorageTest, WriteReadNotification) {
NotificationDatabaseData data;
data.notification_id = GenerateNotificationId();
data.origin = origin_;
data.service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
data.service_worker_registration_id);
WriteNotificationDataSynchronous(data);
ASSERT_TRUE(success_);
NotificationDatabaseData read_data =
ReadNotificationDataAndRecordInteractionSynchronous(
data.service_worker_registration_id, data.notification_id,
PlatformNotificationContext::Interaction::NONE);
ASSERT_TRUE(success_);
EXPECT_EQ(data.origin, read_data.origin);
EXPECT_EQ(data.notification_id, read_data.notification_id);
EXPECT_EQ(data.service_worker_registration_id,
read_data.service_worker_registration_id);
}
TEST_F(NotificationStorageTest, ReadInvalidNotification) {
int64_t service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
service_worker_registration_id);
ReadNotificationDataAndRecordInteractionSynchronous(
service_worker_registration_id, "bad_id",
PlatformNotificationContext::Interaction::NONE);
ASSERT_FALSE(success_);
}
TEST_F(NotificationStorageTest, ReadAndUpdateInteraction) {
NotificationDatabaseData data, read_data;
data.notification_id = GenerateNotificationId();
data.origin = origin_;
data.service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
data.service_worker_registration_id);
WriteNotificationDataSynchronous(data);
ASSERT_TRUE(success_);
// Check that the time deltas have not yet been set.
EXPECT_FALSE(read_data.time_until_first_click_millis.has_value());
EXPECT_FALSE(read_data.time_until_last_click_millis.has_value());
EXPECT_FALSE(read_data.time_until_close_millis.has_value());
// Check that when a notification has an interaction, the appropriate field is
// updated on the read.
read_data = ReadNotificationDataAndRecordInteractionSynchronous(
data.service_worker_registration_id, data.notification_id,
PlatformNotificationContext::Interaction::CLICKED);
ASSERT_TRUE(success_);
EXPECT_EQ(1, read_data.num_clicks);
read_data = ReadNotificationDataAndRecordInteractionSynchronous(
data.service_worker_registration_id, data.notification_id,
PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED);
ASSERT_TRUE(success_);
EXPECT_EQ(1, read_data.num_action_button_clicks);
read_data = ReadNotificationDataAndRecordInteractionSynchronous(
data.service_worker_registration_id, data.notification_id,
PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED);
ASSERT_TRUE(success_);
EXPECT_EQ(2, read_data.num_action_button_clicks);
// Check that the click timestamps are correctly updated.
EXPECT_TRUE(read_data.time_until_first_click_millis.has_value());
EXPECT_TRUE(read_data.time_until_last_click_millis.has_value());
// Check that when a read with a CLOSED interaction occurs, the correct
// field is updated.
read_data = ReadNotificationDataAndRecordInteractionSynchronous(
data.service_worker_registration_id, data.notification_id,
PlatformNotificationContext::Interaction::CLOSED);
ASSERT_TRUE(success_);
EXPECT_EQ(true, read_data.time_until_close_millis.has_value());
}
} // namespace content