1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/run_loop.h"
9 #include "content/browser/notifications/platform_notification_context_impl.h"
10 #include "content/browser/service_worker/embedded_worker_test_helper.h"
11 #include "content/browser/service_worker/service_worker_context_wrapper.h"
12 #include "content/common/service_worker/service_worker_types.h"
13 #include "content/public/browser/notification_database_data.h"
14 #include "content/public/test/test_browser_thread_bundle.h"
15 #include "testing/gtest/include/gtest/gtest.h"
20 // Fake render process id to use in tests requiring one.
21 const int kFakeRenderProcessId
= 99;
23 // Fake Service Worker registration id to use in tests requiring one.
24 const int64_t kFakeServiceWorkerRegistrationId
= 42;
26 class PlatformNotificationContextTest
: public ::testing::Test
{
28 PlatformNotificationContextTest()
29 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
),
32 // Callback to provide when reading a single notification from the database.
33 void DidReadNotificationData(
34 bool success
, const NotificationDatabaseData
& database_data
) {
36 database_data_
= database_data
;
39 // Callback to provide when writing a notification to the database.
40 void DidWriteNotificationData(bool success
, int64_t notification_id
) {
42 notification_id_
= notification_id
;
45 // Callback to provide when deleting notification data from the database.
46 void DidDeleteNotificationData(bool success
) {
50 // Callback to provide when registering a Service Worker with a Service
51 // Worker Context. Will write the registration id to |store_registration_id|.
52 void DidRegisterServiceWorker(int64_t* store_registration_id
,
53 ServiceWorkerStatusCode status
,
54 const std::string
& status_message
,
55 int64_t service_worker_registration_id
) {
56 DCHECK(store_registration_id
);
57 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
59 *store_registration_id
= service_worker_registration_id
;
62 // Callback to provide when unregistering a Service Worker. Will write the
63 // resulting status code to |store_status|.
64 void DidUnregisterServiceWorker(ServiceWorkerStatusCode
* store_status
,
65 ServiceWorkerStatusCode status
) {
67 *store_status
= status
;
70 // Callback to provide when reading multiple notifications from the database.
71 // Will store the success value in the class member, and write the read
72 // notification datas to |store_notification_datas|.
73 void DidReadAllNotificationDatas(
74 std::vector
<NotificationDatabaseData
>* store_notification_datas
,
76 const std::vector
<NotificationDatabaseData
>& notification_datas
) {
77 DCHECK(store_notification_datas
);
80 *store_notification_datas
= notification_datas
;
84 // Creates a new PlatformNotificationContextImpl instance. When using this
85 // method, the underlying database will always be created in memory. The
86 // current message loop proxy will be used as the task runner.
87 PlatformNotificationContextImpl
* CreatePlatformNotificationContext() {
88 PlatformNotificationContextImpl
* context
=
89 new PlatformNotificationContextImpl(base::FilePath(), nullptr);
90 context
->Initialize();
92 OverrideTaskRunnerForTesting(context
);
96 // Overrides the task runner in |context| with the current message loop
97 // proxy, to reduce the number of threads involved in the tests.
98 void OverrideTaskRunnerForTesting(PlatformNotificationContextImpl
* context
) {
99 context
->SetTaskRunnerForTesting(base::MessageLoopProxy::current());
102 // Returns whether the last invoked callback finished successfully.
103 bool success() const { return success_
; }
105 // Returns the NotificationDatabaseData associated with the last invoked
106 // ReadNotificationData callback.
107 const NotificationDatabaseData
& database_data() const {
108 return database_data_
;
111 // Returns the notification id of the notification last written.
112 int64_t notification_id() const { return notification_id_
; }
115 TestBrowserThreadBundle thread_bundle_
;
118 NotificationDatabaseData database_data_
;
119 int64_t notification_id_
;
122 TEST_F(PlatformNotificationContextTest
, ReadNonExistentNotification
) {
123 scoped_refptr
<PlatformNotificationContextImpl
> context
=
124 CreatePlatformNotificationContext();
126 context
->ReadNotificationData(
127 42 /* notification_id */,
128 GURL("https://example.com"),
129 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
130 base::Unretained(this)));
132 base::RunLoop().RunUntilIdle();
134 // The read operation should have failed, as it does not exist.
135 ASSERT_FALSE(success());
138 TEST_F(PlatformNotificationContextTest
, WriteReadNotification
) {
139 scoped_refptr
<PlatformNotificationContextImpl
> context
=
140 CreatePlatformNotificationContext();
142 GURL
origin("https://example.com");
143 NotificationDatabaseData notification_database_data
;
144 notification_database_data
.origin
= origin
;
146 context
->WriteNotificationData(
148 notification_database_data
,
149 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
150 base::Unretained(this)));
152 base::RunLoop().RunUntilIdle();
154 // The write operation should have succeeded with a notification id.
155 ASSERT_TRUE(success());
156 EXPECT_GT(notification_id(), 0);
158 context
->ReadNotificationData(
161 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
162 base::Unretained(this)));
164 base::RunLoop().RunUntilIdle();
166 // The read operation should have succeeded, with the right notification.
167 ASSERT_TRUE(success());
169 const NotificationDatabaseData
& read_database_data
= database_data();
170 EXPECT_EQ(notification_database_data
.origin
, read_database_data
.origin
);
173 TEST_F(PlatformNotificationContextTest
, DeleteInvalidNotification
) {
174 scoped_refptr
<PlatformNotificationContextImpl
> context
=
175 CreatePlatformNotificationContext();
177 context
->DeleteNotificationData(
178 42 /* notification_id */,
179 GURL("https://example.com"),
180 base::Bind(&PlatformNotificationContextTest::DidDeleteNotificationData
,
181 base::Unretained(this)));
183 base::RunLoop().RunUntilIdle();
185 // The notification may not have existed, but since the goal of deleting data
186 // is to make sure that it's gone, the goal has been satisfied. As such,
187 // deleting a non-existent notification is considered to be a success.
188 EXPECT_TRUE(success());
191 TEST_F(PlatformNotificationContextTest
, DeleteNotification
) {
192 scoped_refptr
<PlatformNotificationContextImpl
> context
=
193 CreatePlatformNotificationContext();
195 GURL
origin("https://example.com");
196 NotificationDatabaseData notification_database_data
;
198 context
->WriteNotificationData(
200 notification_database_data
,
201 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
202 base::Unretained(this)));
204 base::RunLoop().RunUntilIdle();
206 // The write operation should have succeeded with a notification id.
207 ASSERT_TRUE(success());
208 EXPECT_GT(notification_id(), 0);
210 context
->DeleteNotificationData(
213 base::Bind(&PlatformNotificationContextTest::DidDeleteNotificationData
,
214 base::Unretained(this)));
216 base::RunLoop().RunUntilIdle();
218 // The notification existed, so it should have been removed successfully.
219 ASSERT_TRUE(success());
221 context
->ReadNotificationData(
224 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
225 base::Unretained(this)));
227 base::RunLoop().RunUntilIdle();
229 // The notification was removed, so we shouldn't be able to read it from
230 // the database anymore.
231 EXPECT_FALSE(success());
234 TEST_F(PlatformNotificationContextTest
, ServiceWorkerUnregistered
) {
235 scoped_ptr
<EmbeddedWorkerTestHelper
> embedded_worker_test_helper(
236 new EmbeddedWorkerTestHelper(base::FilePath(), kFakeRenderProcessId
));
238 // Manually create the PlatformNotificationContextImpl so that the Service
239 // Worker context wrapper can be passed in.
240 scoped_refptr
<PlatformNotificationContextImpl
> notification_context(
241 new PlatformNotificationContextImpl(
243 embedded_worker_test_helper
->context_wrapper()));
244 notification_context
->Initialize();
246 OverrideTaskRunnerForTesting(notification_context
.get());
248 GURL
origin("https://example.com");
249 GURL
script_url("https://example.com/worker.js");
251 int64_t service_worker_registration_id
= kInvalidServiceWorkerRegistrationId
;
253 // Register a Service Worker to get a valid registration id.
254 embedded_worker_test_helper
->context()->RegisterServiceWorker(
257 nullptr /* provider_host */,
258 base::Bind(&PlatformNotificationContextTest::DidRegisterServiceWorker
,
259 base::Unretained(this), &service_worker_registration_id
));
261 base::RunLoop().RunUntilIdle();
262 ASSERT_NE(service_worker_registration_id
,
263 kInvalidServiceWorkerRegistrationId
);
265 NotificationDatabaseData notification_database_data
;
267 // Create a notification for that Service Worker registration.
268 notification_context
->WriteNotificationData(
270 notification_database_data
,
271 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
272 base::Unretained(this)));
274 base::RunLoop().RunUntilIdle();
276 ASSERT_TRUE(success());
277 EXPECT_GT(notification_id(), 0);
279 ServiceWorkerStatusCode unregister_status
;
281 // Now drop the Service Worker registration which owns that notification.
282 embedded_worker_test_helper
->context()->UnregisterServiceWorker(
284 base::Bind(&PlatformNotificationContextTest::DidUnregisterServiceWorker
,
285 base::Unretained(this), &unregister_status
));
287 base::RunLoop().RunUntilIdle();
288 ASSERT_EQ(SERVICE_WORKER_OK
, unregister_status
);
290 // And verify that the associated notification has indeed been dropped.
291 notification_context
->ReadNotificationData(
294 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
295 base::Unretained(this)));
297 base::RunLoop().RunUntilIdle();
299 EXPECT_FALSE(success());
302 TEST_F(PlatformNotificationContextTest
, DestroyDatabaseOnStorageWiped
) {
303 scoped_refptr
<PlatformNotificationContextImpl
> context
=
304 CreatePlatformNotificationContext();
306 GURL
origin("https://example.com");
307 NotificationDatabaseData notification_database_data
;
309 context
->WriteNotificationData(
311 notification_database_data
,
312 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
313 base::Unretained(this)));
315 base::RunLoop().RunUntilIdle();
317 // The write operation should have succeeded with a notification id.
318 ASSERT_TRUE(success());
319 EXPECT_GT(notification_id(), 0);
321 // Call the OnStorageWiped override from the ServiceWorkerContextObserver,
322 // which indicates that the database should go away entirely.
323 context
->OnStorageWiped();
325 // Verify that reading notification data fails because the data does not
326 // exist anymore. Deliberately omit RunUntilIdle(), since this is unlikely to
327 // be the case when OnStorageWiped gets called in production.
328 context
->ReadNotificationData(
331 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
332 base::Unretained(this)));
334 base::RunLoop().RunUntilIdle();
336 EXPECT_FALSE(success());
339 TEST_F(PlatformNotificationContextTest
, DestroyOnDiskDatabase
) {
340 base::ScopedTempDir database_dir
;
341 ASSERT_TRUE(database_dir
.CreateUniqueTempDir());
343 // Manually construct the PlatformNotificationContextImpl because this test
344 // requires the database to be created on the filesystem.
345 scoped_refptr
<PlatformNotificationContextImpl
> context(
346 new PlatformNotificationContextImpl(database_dir
.path(), nullptr));
348 OverrideTaskRunnerForTesting(context
.get());
350 // Trigger a read-operation to force creating the database.
351 context
->ReadNotificationData(
352 42 /* notification_id */,
353 GURL("https://example.com"),
354 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
355 base::Unretained(this)));
357 base::RunLoop().RunUntilIdle();
359 EXPECT_FALSE(IsDirectoryEmpty(database_dir
.path()));
360 EXPECT_FALSE(success());
362 // Blow away the database by faking a Service Worker Context wipe-out.
363 context
->OnStorageWiped();
365 base::RunLoop().RunUntilIdle();
367 // The database's directory should be empty at this point.
368 EXPECT_TRUE(IsDirectoryEmpty(database_dir
.path()));
371 TEST_F(PlatformNotificationContextTest
, ReadAllServiceWorkerDataEmpty
) {
372 scoped_refptr
<PlatformNotificationContextImpl
> context
=
373 CreatePlatformNotificationContext();
375 GURL
origin("https://example.com");
377 std::vector
<NotificationDatabaseData
> notification_database_datas
;
378 context
->ReadAllNotificationDataForServiceWorkerRegistration(
380 kFakeServiceWorkerRegistrationId
,
381 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas
,
382 base::Unretained(this),
383 ¬ification_database_datas
));
385 base::RunLoop().RunUntilIdle();
387 EXPECT_TRUE(success());
388 EXPECT_EQ(0u, notification_database_datas
.size());
391 TEST_F(PlatformNotificationContextTest
, ReadAllServiceWorkerDataFilled
) {
392 scoped_refptr
<PlatformNotificationContextImpl
> context
=
393 CreatePlatformNotificationContext();
395 GURL
origin("https://example.com");
397 NotificationDatabaseData notification_database_data
;
398 notification_database_data
.origin
= origin
;
399 notification_database_data
.service_worker_registration_id
=
400 kFakeServiceWorkerRegistrationId
;
402 // Insert ten notifications into the database belonging to origin and the
403 // test Service Worker Registration id.
404 for (int i
= 0; i
< 10; ++i
) {
405 context
->WriteNotificationData(
407 notification_database_data
,
408 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
409 base::Unretained(this)));
411 base::RunLoop().RunUntilIdle();
413 ASSERT_TRUE(success());
416 // Now read the notifications from the database again. There should be ten,
417 // all set with the correct origin and Service Worker Registration id.
418 std::vector
<NotificationDatabaseData
> notification_database_datas
;
419 context
->ReadAllNotificationDataForServiceWorkerRegistration(
421 kFakeServiceWorkerRegistrationId
,
422 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas
,
423 base::Unretained(this),
424 ¬ification_database_datas
));
426 base::RunLoop().RunUntilIdle();
428 ASSERT_TRUE(success());
429 ASSERT_EQ(10u, notification_database_datas
.size());
431 for (int i
= 0; i
< 10; ++i
) {
432 EXPECT_EQ(origin
, notification_database_datas
[i
].origin
);
433 EXPECT_EQ(kFakeServiceWorkerRegistrationId
,
434 notification_database_datas
[i
].service_worker_registration_id
);
438 } // namespace content