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 "base/thread_task_runner_handle.h"
10 #include "content/browser/notifications/platform_notification_context_impl.h"
11 #include "content/browser/service_worker/embedded_worker_test_helper.h"
12 #include "content/browser/service_worker/service_worker_context_wrapper.h"
13 #include "content/common/service_worker/service_worker_types.h"
14 #include "content/public/browser/notification_database_data.h"
15 #include "content/public/test/test_browser_context.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "testing/gtest/include/gtest/gtest.h"
22 // Fake render process id to use in tests requiring one.
23 const int kFakeRenderProcessId
= 99;
25 // Fake Service Worker registration id to use in tests requiring one.
26 const int64_t kFakeServiceWorkerRegistrationId
= 42;
28 class PlatformNotificationContextTest
: public ::testing::Test
{
30 PlatformNotificationContextTest()
31 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
),
34 // Callback to provide when reading a single notification from the database.
35 void DidReadNotificationData(
36 bool success
, const NotificationDatabaseData
& database_data
) {
38 database_data_
= database_data
;
41 // Callback to provide when writing a notification to the database.
42 void DidWriteNotificationData(bool success
, int64_t notification_id
) {
44 notification_id_
= notification_id
;
47 // Callback to provide when deleting notification data from the database.
48 void DidDeleteNotificationData(bool success
) {
52 // Callback to provide when registering a Service Worker with a Service
53 // Worker Context. Will write the registration id to |store_registration_id|.
54 void DidRegisterServiceWorker(int64_t* store_registration_id
,
55 ServiceWorkerStatusCode status
,
56 const std::string
& status_message
,
57 int64_t service_worker_registration_id
) {
58 DCHECK(store_registration_id
);
59 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
61 *store_registration_id
= service_worker_registration_id
;
64 // Callback to provide when unregistering a Service Worker. Will write the
65 // resulting status code to |store_status|.
66 void DidUnregisterServiceWorker(ServiceWorkerStatusCode
* store_status
,
67 ServiceWorkerStatusCode status
) {
69 *store_status
= status
;
72 // Callback to provide when reading multiple notifications from the database.
73 // Will store the success value in the class member, and write the read
74 // notification datas to |store_notification_datas|.
75 void DidReadAllNotificationDatas(
76 std::vector
<NotificationDatabaseData
>* store_notification_datas
,
78 const std::vector
<NotificationDatabaseData
>& notification_datas
) {
79 DCHECK(store_notification_datas
);
82 *store_notification_datas
= notification_datas
;
86 // Creates a new PlatformNotificationContextImpl instance. When using this
87 // method, the underlying database will always be created in memory. The
88 // current message loop proxy will be used as the task runner.
89 PlatformNotificationContextImpl
* CreatePlatformNotificationContext() {
90 PlatformNotificationContextImpl
* context
=
91 new PlatformNotificationContextImpl(base::FilePath(),
94 context
->Initialize();
96 OverrideTaskRunnerForTesting(context
);
100 // Overrides the task runner in |context| with the current message loop
101 // proxy, to reduce the number of threads involved in the tests.
102 void OverrideTaskRunnerForTesting(PlatformNotificationContextImpl
* context
) {
103 context
->SetTaskRunnerForTesting(base::ThreadTaskRunnerHandle::Get());
106 // Returns the testing browsing context that can be used for this test.
107 BrowserContext
* browser_context() { return &browser_context_
; }
109 // Returns whether the last invoked callback finished successfully.
110 bool success() const { return success_
; }
112 // Returns the NotificationDatabaseData associated with the last invoked
113 // ReadNotificationData callback.
114 const NotificationDatabaseData
& database_data() const {
115 return database_data_
;
118 // Returns the notification id of the notification last written.
119 int64_t notification_id() const { return notification_id_
; }
122 TestBrowserThreadBundle thread_bundle_
;
123 TestBrowserContext browser_context_
;
126 NotificationDatabaseData database_data_
;
127 int64_t notification_id_
;
130 TEST_F(PlatformNotificationContextTest
, ReadNonExistentNotification
) {
131 scoped_refptr
<PlatformNotificationContextImpl
> context
=
132 CreatePlatformNotificationContext();
134 context
->ReadNotificationData(
135 42 /* notification_id */,
136 GURL("https://example.com"),
137 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
138 base::Unretained(this)));
140 base::RunLoop().RunUntilIdle();
142 // The read operation should have failed, as it does not exist.
143 ASSERT_FALSE(success());
146 TEST_F(PlatformNotificationContextTest
, WriteReadNotification
) {
147 scoped_refptr
<PlatformNotificationContextImpl
> context
=
148 CreatePlatformNotificationContext();
150 GURL
origin("https://example.com");
151 NotificationDatabaseData notification_database_data
;
152 notification_database_data
.origin
= origin
;
154 context
->WriteNotificationData(
156 notification_database_data
,
157 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
158 base::Unretained(this)));
160 base::RunLoop().RunUntilIdle();
162 // The write operation should have succeeded with a notification id.
163 ASSERT_TRUE(success());
164 EXPECT_GT(notification_id(), 0);
166 context
->ReadNotificationData(
169 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
170 base::Unretained(this)));
172 base::RunLoop().RunUntilIdle();
174 // The read operation should have succeeded, with the right notification.
175 ASSERT_TRUE(success());
177 const NotificationDatabaseData
& read_database_data
= database_data();
178 EXPECT_EQ(notification_database_data
.origin
, read_database_data
.origin
);
181 TEST_F(PlatformNotificationContextTest
, DeleteInvalidNotification
) {
182 scoped_refptr
<PlatformNotificationContextImpl
> context
=
183 CreatePlatformNotificationContext();
185 context
->DeleteNotificationData(
186 42 /* notification_id */,
187 GURL("https://example.com"),
188 base::Bind(&PlatformNotificationContextTest::DidDeleteNotificationData
,
189 base::Unretained(this)));
191 base::RunLoop().RunUntilIdle();
193 // The notification may not have existed, but since the goal of deleting data
194 // is to make sure that it's gone, the goal has been satisfied. As such,
195 // deleting a non-existent notification is considered to be a success.
196 EXPECT_TRUE(success());
199 TEST_F(PlatformNotificationContextTest
, DeleteNotification
) {
200 scoped_refptr
<PlatformNotificationContextImpl
> context
=
201 CreatePlatformNotificationContext();
203 GURL
origin("https://example.com");
204 NotificationDatabaseData notification_database_data
;
206 context
->WriteNotificationData(
208 notification_database_data
,
209 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
210 base::Unretained(this)));
212 base::RunLoop().RunUntilIdle();
214 // The write operation should have succeeded with a notification id.
215 ASSERT_TRUE(success());
216 EXPECT_GT(notification_id(), 0);
218 context
->DeleteNotificationData(
221 base::Bind(&PlatformNotificationContextTest::DidDeleteNotificationData
,
222 base::Unretained(this)));
224 base::RunLoop().RunUntilIdle();
226 // The notification existed, so it should have been removed successfully.
227 ASSERT_TRUE(success());
229 context
->ReadNotificationData(
232 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
233 base::Unretained(this)));
235 base::RunLoop().RunUntilIdle();
237 // The notification was removed, so we shouldn't be able to read it from
238 // the database anymore.
239 EXPECT_FALSE(success());
242 TEST_F(PlatformNotificationContextTest
, ServiceWorkerUnregistered
) {
243 scoped_ptr
<EmbeddedWorkerTestHelper
> embedded_worker_test_helper(
244 new EmbeddedWorkerTestHelper(base::FilePath(), kFakeRenderProcessId
));
246 // Manually create the PlatformNotificationContextImpl so that the Service
247 // Worker context wrapper can be passed in.
248 scoped_refptr
<PlatformNotificationContextImpl
> notification_context(
249 new PlatformNotificationContextImpl(
252 embedded_worker_test_helper
->context_wrapper()));
253 notification_context
->Initialize();
255 OverrideTaskRunnerForTesting(notification_context
.get());
257 GURL
origin("https://example.com");
258 GURL
script_url("https://example.com/worker.js");
260 int64_t service_worker_registration_id
= kInvalidServiceWorkerRegistrationId
;
262 // Register a Service Worker to get a valid registration id.
263 embedded_worker_test_helper
->context()->RegisterServiceWorker(
266 nullptr /* provider_host */,
267 base::Bind(&PlatformNotificationContextTest::DidRegisterServiceWorker
,
268 base::Unretained(this), &service_worker_registration_id
));
270 base::RunLoop().RunUntilIdle();
271 ASSERT_NE(service_worker_registration_id
,
272 kInvalidServiceWorkerRegistrationId
);
274 NotificationDatabaseData notification_database_data
;
276 // Create a notification for that Service Worker registration.
277 notification_context
->WriteNotificationData(
279 notification_database_data
,
280 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
281 base::Unretained(this)));
283 base::RunLoop().RunUntilIdle();
285 ASSERT_TRUE(success());
286 EXPECT_GT(notification_id(), 0);
288 ServiceWorkerStatusCode unregister_status
;
290 // Now drop the Service Worker registration which owns that notification.
291 embedded_worker_test_helper
->context()->UnregisterServiceWorker(
293 base::Bind(&PlatformNotificationContextTest::DidUnregisterServiceWorker
,
294 base::Unretained(this), &unregister_status
));
296 base::RunLoop().RunUntilIdle();
297 ASSERT_EQ(SERVICE_WORKER_OK
, unregister_status
);
299 // And verify that the associated notification has indeed been dropped.
300 notification_context
->ReadNotificationData(
303 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
304 base::Unretained(this)));
306 base::RunLoop().RunUntilIdle();
308 EXPECT_FALSE(success());
311 TEST_F(PlatformNotificationContextTest
, DestroyDatabaseOnStorageWiped
) {
312 scoped_refptr
<PlatformNotificationContextImpl
> context
=
313 CreatePlatformNotificationContext();
315 GURL
origin("https://example.com");
316 NotificationDatabaseData notification_database_data
;
318 context
->WriteNotificationData(
320 notification_database_data
,
321 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
322 base::Unretained(this)));
324 base::RunLoop().RunUntilIdle();
326 // The write operation should have succeeded with a notification id.
327 ASSERT_TRUE(success());
328 EXPECT_GT(notification_id(), 0);
330 // Call the OnStorageWiped override from the ServiceWorkerContextObserver,
331 // which indicates that the database should go away entirely.
332 context
->OnStorageWiped();
334 // Verify that reading notification data fails because the data does not
335 // exist anymore. Deliberately omit RunUntilIdle(), since this is unlikely to
336 // be the case when OnStorageWiped gets called in production.
337 context
->ReadNotificationData(
340 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
341 base::Unretained(this)));
343 base::RunLoop().RunUntilIdle();
345 EXPECT_FALSE(success());
348 TEST_F(PlatformNotificationContextTest
, DestroyOnDiskDatabase
) {
349 base::ScopedTempDir database_dir
;
350 ASSERT_TRUE(database_dir
.CreateUniqueTempDir());
352 // Manually construct the PlatformNotificationContextImpl because this test
353 // requires the database to be created on the filesystem.
354 scoped_refptr
<PlatformNotificationContextImpl
> context(
355 new PlatformNotificationContextImpl(database_dir
.path(),
359 OverrideTaskRunnerForTesting(context
.get());
361 // Trigger a read-operation to force creating the database.
362 context
->ReadNotificationData(
363 42 /* notification_id */,
364 GURL("https://example.com"),
365 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData
,
366 base::Unretained(this)));
368 base::RunLoop().RunUntilIdle();
370 EXPECT_FALSE(IsDirectoryEmpty(database_dir
.path()));
371 EXPECT_FALSE(success());
373 // Blow away the database by faking a Service Worker Context wipe-out.
374 context
->OnStorageWiped();
376 base::RunLoop().RunUntilIdle();
378 // The database's directory should be empty at this point.
379 EXPECT_TRUE(IsDirectoryEmpty(database_dir
.path()));
382 TEST_F(PlatformNotificationContextTest
, ReadAllServiceWorkerDataEmpty
) {
383 scoped_refptr
<PlatformNotificationContextImpl
> context
=
384 CreatePlatformNotificationContext();
386 GURL
origin("https://example.com");
388 std::vector
<NotificationDatabaseData
> notification_database_datas
;
389 context
->ReadAllNotificationDataForServiceWorkerRegistration(
391 kFakeServiceWorkerRegistrationId
,
392 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas
,
393 base::Unretained(this),
394 ¬ification_database_datas
));
396 base::RunLoop().RunUntilIdle();
398 EXPECT_TRUE(success());
399 EXPECT_EQ(0u, notification_database_datas
.size());
402 TEST_F(PlatformNotificationContextTest
, ReadAllServiceWorkerDataFilled
) {
403 scoped_refptr
<PlatformNotificationContextImpl
> context
=
404 CreatePlatformNotificationContext();
406 GURL
origin("https://example.com");
408 NotificationDatabaseData notification_database_data
;
409 notification_database_data
.origin
= origin
;
410 notification_database_data
.service_worker_registration_id
=
411 kFakeServiceWorkerRegistrationId
;
413 // Insert ten notifications into the database belonging to origin and the
414 // test Service Worker Registration id.
415 for (int i
= 0; i
< 10; ++i
) {
416 context
->WriteNotificationData(
418 notification_database_data
,
419 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData
,
420 base::Unretained(this)));
422 base::RunLoop().RunUntilIdle();
424 ASSERT_TRUE(success());
427 // Now read the notifications from the database again. There should be ten,
428 // all set with the correct origin and Service Worker Registration id.
429 std::vector
<NotificationDatabaseData
> notification_database_datas
;
430 context
->ReadAllNotificationDataForServiceWorkerRegistration(
432 kFakeServiceWorkerRegistrationId
,
433 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas
,
434 base::Unretained(this),
435 ¬ification_database_datas
));
437 base::RunLoop().RunUntilIdle();
439 ASSERT_TRUE(success());
440 ASSERT_EQ(10u, notification_database_datas
.size());
442 for (int i
= 0; i
< 10; ++i
) {
443 EXPECT_EQ(origin
, notification_database_datas
[i
].origin
);
444 EXPECT_EQ(kFakeServiceWorkerRegistrationId
,
445 notification_database_datas
[i
].service_worker_registration_id
);
449 } // namespace content