Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / notifications / notification_database_unittest.cc
bloba4dbff33b2379cd6dd6e5c3941739488f8399a3b
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.
5 #include "content/browser/notifications/notification_database.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/public/browser/notification_database_data.h"
11 #include "content/public/common/platform_notification_data.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/leveldatabase/src/include/leveldb/db.h"
14 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
15 #include "url/gurl.h"
17 namespace content {
19 const int kExampleServiceWorkerRegistrationId = 42;
21 const struct {
22 const char* origin;
23 int64_t service_worker_registration_id;
24 } kExampleNotificationData[] = {
25 { "https://example.com", 0 },
26 { "https://example.com", kExampleServiceWorkerRegistrationId },
27 { "https://example.com", kExampleServiceWorkerRegistrationId },
28 { "https://example.com", kExampleServiceWorkerRegistrationId + 1 },
29 { "https://chrome.com", 0 },
30 { "https://chrome.com", 0 },
31 { "https://chrome.com", kExampleServiceWorkerRegistrationId }
34 class NotificationDatabaseTest : public ::testing::Test {
35 protected:
36 // Creates a new NotificationDatabase instance in memory.
37 NotificationDatabase* CreateDatabaseInMemory() {
38 return new NotificationDatabase(base::FilePath());
41 // Creates a new NotificationDatabase instance in |path|.
42 NotificationDatabase* CreateDatabaseOnFileSystem(
43 const base::FilePath& path) {
44 return new NotificationDatabase(path);
47 // Creates a new notification for |service_worker_registration_id| belonging
48 // to |origin| and writes it to the database. The written notification id
49 // will be stored in |notification_id|.
50 void CreateAndWriteNotification(NotificationDatabase* database,
51 const GURL& origin,
52 int64_t service_worker_registration_id,
53 int64_t* notification_id) {
54 NotificationDatabaseData database_data;
55 database_data.origin = origin;
56 database_data.service_worker_registration_id =
57 service_worker_registration_id;
59 ASSERT_EQ(NotificationDatabase::STATUS_OK,
60 database->WriteNotificationData(origin,
61 database_data,
62 notification_id));
65 // Populates |database| with a series of example notifications that differ in
66 // their origin and Service Worker registration id.
67 void PopulateDatabaseWithExampleData(NotificationDatabase* database) {
68 int64_t notification_id;
69 for (size_t i = 0; i < arraysize(kExampleNotificationData); ++i) {
70 ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification(
71 database,
72 GURL(kExampleNotificationData[i].origin),
73 kExampleNotificationData[i].service_worker_registration_id,
74 &notification_id));
78 // Returns if |database| has been opened.
79 bool IsDatabaseOpen(NotificationDatabase* database) {
80 return database->IsOpen();
83 // Returns if |database| is an in-memory only database.
84 bool IsInMemoryDatabase(NotificationDatabase* database) {
85 return database->IsInMemoryDatabase();
88 // Writes a LevelDB key-value pair directly to the LevelDB backing the
89 // notification database in |database|.
90 void WriteLevelDBKeyValuePair(NotificationDatabase* database,
91 const std::string& key,
92 const std::string& value) {
93 leveldb::Status status =
94 database->GetDBForTesting()->Put(leveldb::WriteOptions(), key, value);
95 ASSERT_TRUE(status.ok());
99 TEST_F(NotificationDatabaseTest, OpenCloseMemory) {
100 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
102 // Should return false because the database does not exist in memory.
103 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
104 database->Open(false /* create_if_missing */));
106 // Should return true, indicating that the database could be created.
107 EXPECT_EQ(NotificationDatabase::STATUS_OK,
108 database->Open(true /* create_if_missing */));
110 EXPECT_TRUE(IsDatabaseOpen(database.get()));
111 EXPECT_TRUE(IsInMemoryDatabase(database.get()));
113 // Verify that in-memory databases do not persist when being re-created.
114 database.reset(CreateDatabaseInMemory());
116 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
117 database->Open(false /* create_if_missing */));
120 TEST_F(NotificationDatabaseTest, OpenCloseFileSystem) {
121 base::ScopedTempDir database_dir;
122 ASSERT_TRUE(database_dir.CreateUniqueTempDir());
124 scoped_ptr<NotificationDatabase> database(
125 CreateDatabaseOnFileSystem(database_dir.path()));
127 // Should return false because the database does not exist on the file system.
128 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
129 database->Open(false /* create_if_missing */));
131 // Should return true, indicating that the database could be created.
132 EXPECT_EQ(NotificationDatabase::STATUS_OK,
133 database->Open(true /* create_if_missing */));
135 EXPECT_TRUE(IsDatabaseOpen(database.get()));
136 EXPECT_FALSE(IsInMemoryDatabase(database.get()));
138 // Close the database, and re-open it without attempting to create it because
139 // the files on the file system should still exist as expected.
140 database.reset(CreateDatabaseOnFileSystem(database_dir.path()));
141 EXPECT_EQ(NotificationDatabase::STATUS_OK,
142 database->Open(false /* create_if_missing */));
145 TEST_F(NotificationDatabaseTest, DestroyDatabase) {
146 base::ScopedTempDir database_dir;
147 ASSERT_TRUE(database_dir.CreateUniqueTempDir());
149 scoped_ptr<NotificationDatabase> database(
150 CreateDatabaseOnFileSystem(database_dir.path()));
152 EXPECT_EQ(NotificationDatabase::STATUS_OK,
153 database->Open(true /* create_if_missing */));
154 EXPECT_TRUE(IsDatabaseOpen(database.get()));
156 // Destroy the database. This will immediately close it as well.
157 ASSERT_EQ(NotificationDatabase::STATUS_OK, database->Destroy());
158 EXPECT_FALSE(IsDatabaseOpen(database.get()));
160 // Try to re-open the database (but not re-create it). This should fail as
161 // the files associated with the database should have been blown away.
162 database.reset(CreateDatabaseOnFileSystem(database_dir.path()));
163 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
164 database->Open(false /* create_if_missing */));
167 TEST_F(NotificationDatabaseTest, NotificationIdIncrements) {
168 base::ScopedTempDir database_dir;
169 ASSERT_TRUE(database_dir.CreateUniqueTempDir());
171 scoped_ptr<NotificationDatabase> database(
172 CreateDatabaseOnFileSystem(database_dir.path()));
174 ASSERT_EQ(NotificationDatabase::STATUS_OK,
175 database->Open(true /* create_if_missing */));
177 GURL origin("https://example.com");
179 int64_t notification_id = 0;
181 // Verify that getting two ids on the same database instance results in
182 // incrementing values. Notification ids will start at 1.
183 ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification(
184 database.get(), origin, 0 /* sw_registration_id */, &notification_id));
185 EXPECT_EQ(notification_id, 1);
187 ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification(
188 database.get(), origin, 0 /* sw_registration_id */, &notification_id));
189 EXPECT_EQ(notification_id, 2);
191 database.reset(CreateDatabaseOnFileSystem(database_dir.path()));
192 ASSERT_EQ(NotificationDatabase::STATUS_OK,
193 database->Open(false /* create_if_missing */));
195 // Verify that the next notification id was stored in the database, and
196 // continues where we expect it to be, even after closing and opening it.
197 ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification(
198 database.get(), origin, 0 /* sw_registration_id */, &notification_id));
199 EXPECT_EQ(notification_id, 3);
202 TEST_F(NotificationDatabaseTest, NotificationIdIncrementsStorage) {
203 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
204 ASSERT_EQ(NotificationDatabase::STATUS_OK,
205 database->Open(true /* create_if_missing */));
207 GURL origin("https://example.com");
209 NotificationDatabaseData database_data;
210 database_data.notification_id = -1;
212 int64_t notification_id = 0;
213 ASSERT_EQ(NotificationDatabase::STATUS_OK,
214 database->WriteNotificationData(origin,
215 database_data,
216 &notification_id));
218 ASSERT_EQ(NotificationDatabase::STATUS_OK,
219 database->ReadNotificationData(notification_id,
220 origin,
221 &database_data));
223 EXPECT_EQ(notification_id, database_data.notification_id);
226 TEST_F(NotificationDatabaseTest, NotificationIdCorruption) {
227 base::ScopedTempDir database_dir;
228 ASSERT_TRUE(database_dir.CreateUniqueTempDir());
230 scoped_ptr<NotificationDatabase> database(
231 CreateDatabaseOnFileSystem(database_dir.path()));
233 ASSERT_EQ(NotificationDatabase::STATUS_OK,
234 database->Open(true /* create_if_missing */));
236 GURL origin("https://example.com");
238 NotificationDatabaseData database_data;
239 int64_t notification_id = 0;
241 ASSERT_EQ(NotificationDatabase::STATUS_OK,
242 database->WriteNotificationData(origin,
243 database_data,
244 &notification_id));
245 EXPECT_EQ(notification_id, 1);
247 // Deliberately write an invalid value as the next notification id. When
248 // re-opening the database, the Open() method should realize that an invalid
249 // value is being read, and mark the database as corrupted.
250 ASSERT_NO_FATAL_FAILURE(WriteLevelDBKeyValuePair(database.get(),
251 "NEXT_NOTIFICATION_ID",
252 "-42"));
254 database.reset(CreateDatabaseOnFileSystem(database_dir.path()));
255 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_CORRUPTED,
256 database->Open(false /* create_if_missing */));
259 TEST_F(NotificationDatabaseTest, ReadInvalidNotificationData) {
260 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
261 ASSERT_EQ(NotificationDatabase::STATUS_OK,
262 database->Open(true /* create_if_missing */));
264 NotificationDatabaseData database_data;
266 // Reading the notification data for a notification that does not exist should
267 // return the ERROR_NOT_FOUND status code.
268 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
269 database->ReadNotificationData(9001,
270 GURL("https://chrome.com"),
271 &database_data));
274 TEST_F(NotificationDatabaseTest, ReadNotificationDataDifferentOrigin) {
275 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
276 ASSERT_EQ(NotificationDatabase::STATUS_OK,
277 database->Open(true /* create_if_missing */));
279 int64_t notification_id = 0;
280 GURL origin("https://example.com");
282 NotificationDatabaseData database_data, read_database_data;
283 database_data.notification_data.title = base::UTF8ToUTF16("My Notification");
285 ASSERT_EQ(NotificationDatabase::STATUS_OK,
286 database->WriteNotificationData(origin,
287 database_data,
288 &notification_id));
290 // Reading the notification from the database when given a different origin
291 // should return the ERROR_NOT_FOUND status code.
292 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
293 database->ReadNotificationData(notification_id,
294 GURL("https://chrome.com"),
295 &read_database_data));
297 // However, reading the notification from the database with the same origin
298 // should return STATUS_OK and the associated notification data.
299 ASSERT_EQ(NotificationDatabase::STATUS_OK,
300 database->ReadNotificationData(notification_id,
301 origin,
302 &read_database_data));
304 EXPECT_EQ(database_data.notification_data.title,
305 read_database_data.notification_data.title);
308 TEST_F(NotificationDatabaseTest, ReadNotificationDataReflection) {
309 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
310 ASSERT_EQ(NotificationDatabase::STATUS_OK,
311 database->Open(true /* create_if_missing */));
313 int64_t notification_id = 0;
315 GURL origin("https://example.com");
317 PlatformNotificationData notification_data;
318 notification_data.title = base::UTF8ToUTF16("My Notification");
319 notification_data.direction =
320 PlatformNotificationData::DIRECTION_RIGHT_TO_LEFT;
321 notification_data.lang = "nl-NL";
322 notification_data.body = base::UTF8ToUTF16("Hello, world!");
323 notification_data.tag = "replace id";
324 notification_data.icon = GURL("https://example.com/icon.png");
325 notification_data.silent = true;
327 NotificationDatabaseData database_data;
328 database_data.notification_id = notification_id;
329 database_data.origin = origin;
330 database_data.service_worker_registration_id = 42;
331 database_data.notification_data = notification_data;
333 // Write the constructed notification to the database, and then immediately
334 // read it back from the database again as well.
335 ASSERT_EQ(NotificationDatabase::STATUS_OK,
336 database->WriteNotificationData(origin,
337 database_data,
338 &notification_id));
340 NotificationDatabaseData read_database_data;
341 ASSERT_EQ(NotificationDatabase::STATUS_OK,
342 database->ReadNotificationData(notification_id,
343 origin,
344 &read_database_data));
346 // Verify that all members retrieved from the database are exactly the same
347 // as the ones that were written to it. This tests the serialization behavior.
349 EXPECT_EQ(notification_id, read_database_data.notification_id);
351 EXPECT_EQ(database_data.origin, read_database_data.origin);
352 EXPECT_EQ(database_data.service_worker_registration_id,
353 read_database_data.service_worker_registration_id);
355 const PlatformNotificationData& read_notification_data =
356 read_database_data.notification_data;
358 EXPECT_EQ(notification_data.title, read_notification_data.title);
359 EXPECT_EQ(notification_data.direction, read_notification_data.direction);
360 EXPECT_EQ(notification_data.lang, read_notification_data.lang);
361 EXPECT_EQ(notification_data.body, read_notification_data.body);
362 EXPECT_EQ(notification_data.tag, read_notification_data.tag);
363 EXPECT_EQ(notification_data.icon, read_notification_data.icon);
364 EXPECT_EQ(notification_data.silent, read_notification_data.silent);
367 TEST_F(NotificationDatabaseTest, ReadWriteMultipleNotificationData) {
368 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
369 ASSERT_EQ(NotificationDatabase::STATUS_OK,
370 database->Open(true /* create_if_missing */));
372 GURL origin("https://example.com");
373 int64_t notification_id = 0;
375 // Write ten notifications to the database, each with a unique title and
376 // notification id (it is the responsibility of the user to increment this).
377 for (int i = 1; i <= 10; ++i) {
378 ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification(
379 database.get(), origin, i /* sw_registration_id */, &notification_id));
380 EXPECT_EQ(notification_id, i);
383 NotificationDatabaseData database_data;
385 // Read the ten notifications from the database, and verify that the titles
386 // of each of them matches with how they were created.
387 for (int i = 1; i <= 10; ++i) {
388 ASSERT_EQ(NotificationDatabase::STATUS_OK,
389 database->ReadNotificationData(i /* notification_id */,
390 origin,
391 &database_data));
393 EXPECT_EQ(i, database_data.service_worker_registration_id);
397 TEST_F(NotificationDatabaseTest, DeleteInvalidNotificationData) {
398 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
399 ASSERT_EQ(NotificationDatabase::STATUS_OK,
400 database->Open(true /* create_if_missing */));
402 // Deleting non-existing notifications is not considered to be a failure.
403 ASSERT_EQ(NotificationDatabase::STATUS_OK,
404 database->DeleteNotificationData(9001,
405 GURL("https://chrome.com")));
408 TEST_F(NotificationDatabaseTest, DeleteNotificationDataSameOrigin) {
409 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
410 ASSERT_EQ(NotificationDatabase::STATUS_OK,
411 database->Open(true /* create_if_missing */));
413 int64_t notification_id = 0;
415 NotificationDatabaseData database_data;
416 GURL origin("https://example.com");
418 ASSERT_EQ(NotificationDatabase::STATUS_OK,
419 database->WriteNotificationData(origin,
420 database_data,
421 &notification_id));
423 // Reading a notification after writing one should succeed.
424 EXPECT_EQ(NotificationDatabase::STATUS_OK,
425 database->ReadNotificationData(notification_id,
426 origin,
427 &database_data));
429 // Delete the notification which was just written to the database, and verify
430 // that reading it again will fail.
431 EXPECT_EQ(NotificationDatabase::STATUS_OK,
432 database->DeleteNotificationData(notification_id, origin));
433 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND,
434 database->ReadNotificationData(notification_id,
435 origin,
436 &database_data));
439 TEST_F(NotificationDatabaseTest, DeleteNotificationDataDifferentOrigin) {
440 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
441 ASSERT_EQ(NotificationDatabase::STATUS_OK,
442 database->Open(true /* create_if_missing */));
444 int64_t notification_id = 0;
446 NotificationDatabaseData database_data;
447 GURL origin("https://example.com");
449 ASSERT_EQ(NotificationDatabase::STATUS_OK,
450 database->WriteNotificationData(origin,
451 database_data,
452 &notification_id));
454 // Attempting to delete the notification with a different origin, but with the
455 // same |notification_id|, should not return an error (the notification could
456 // not be found, but that's not considered a failure). However, it should not
457 // remove the notification either.
458 EXPECT_EQ(NotificationDatabase::STATUS_OK,
459 database->DeleteNotificationData(notification_id,
460 GURL("https://chrome.com")));
462 EXPECT_EQ(NotificationDatabase::STATUS_OK,
463 database->ReadNotificationData(notification_id,
464 origin,
465 &database_data));
468 TEST_F(NotificationDatabaseTest, ReadAllNotificationData) {
469 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
470 ASSERT_EQ(NotificationDatabase::STATUS_OK,
471 database->Open(true /* create_if_missing */));
473 ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get()));
475 std::vector<NotificationDatabaseData> notifications;
476 ASSERT_EQ(NotificationDatabase::STATUS_OK,
477 database->ReadAllNotificationData(&notifications));
479 EXPECT_EQ(arraysize(kExampleNotificationData), notifications.size());
482 TEST_F(NotificationDatabaseTest, ReadAllNotificationDataEmpty) {
483 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
484 ASSERT_EQ(NotificationDatabase::STATUS_OK,
485 database->Open(true /* create_if_missing */));
487 std::vector<NotificationDatabaseData> notifications;
488 ASSERT_EQ(NotificationDatabase::STATUS_OK,
489 database->ReadAllNotificationData(&notifications));
491 EXPECT_EQ(0u, notifications.size());
494 TEST_F(NotificationDatabaseTest, ReadAllNotificationDataForOrigin) {
495 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
496 ASSERT_EQ(NotificationDatabase::STATUS_OK,
497 database->Open(true /* create_if_missing */));
499 ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get()));
501 GURL origin("https://example.com");
503 std::vector<NotificationDatabaseData> notifications;
504 ASSERT_EQ(NotificationDatabase::STATUS_OK,
505 database->ReadAllNotificationDataForOrigin(origin, &notifications));
507 EXPECT_EQ(4u, notifications.size());
510 TEST_F(NotificationDatabaseTest,
511 ReadAllNotificationDataForServiceWorkerRegistration) {
512 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
513 ASSERT_EQ(NotificationDatabase::STATUS_OK,
514 database->Open(true /* create_if_missing */));
516 ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get()));
518 GURL origin("https://example.com:443");
520 std::vector<NotificationDatabaseData> notifications;
521 ASSERT_EQ(NotificationDatabase::STATUS_OK,
522 database->ReadAllNotificationDataForServiceWorkerRegistration(
523 origin, kExampleServiceWorkerRegistrationId, &notifications));
525 EXPECT_EQ(2u, notifications.size());
528 TEST_F(NotificationDatabaseTest, DeleteAllNotificationDataForOrigin) {
529 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
530 ASSERT_EQ(NotificationDatabase::STATUS_OK,
531 database->Open(true /* create_if_missing */));
533 ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get()));
535 GURL origin("https://example.com:443");
537 std::set<int64_t> deleted_notification_set;
538 ASSERT_EQ(NotificationDatabase::STATUS_OK,
539 database->DeleteAllNotificationDataForOrigin(
540 origin, &deleted_notification_set));
542 EXPECT_EQ(4u, deleted_notification_set.size());
544 std::vector<NotificationDatabaseData> notifications;
545 ASSERT_EQ(NotificationDatabase::STATUS_OK,
546 database->ReadAllNotificationDataForOrigin(origin, &notifications));
548 EXPECT_EQ(0u, notifications.size());
551 TEST_F(NotificationDatabaseTest, DeleteAllNotificationDataForOriginEmpty) {
552 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
553 ASSERT_EQ(NotificationDatabase::STATUS_OK,
554 database->Open(true /* create_if_missing */));
556 GURL origin("https://example.com");
558 std::set<int64_t> deleted_notification_set;
559 ASSERT_EQ(NotificationDatabase::STATUS_OK,
560 database->DeleteAllNotificationDataForOrigin(
561 origin, &deleted_notification_set));
563 EXPECT_EQ(0u, deleted_notification_set.size());
566 TEST_F(NotificationDatabaseTest,
567 DeleteAllNotificationDataForServiceWorkerRegistration) {
568 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
569 ASSERT_EQ(NotificationDatabase::STATUS_OK,
570 database->Open(true /* create_if_missing */));
572 ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get()));
574 GURL origin("https://example.com:443");
575 std::set<int64_t> deleted_notification_set;
576 ASSERT_EQ(NotificationDatabase::STATUS_OK,
577 database->DeleteAllNotificationDataForServiceWorkerRegistration(
578 origin,
579 kExampleServiceWorkerRegistrationId,
580 &deleted_notification_set));
582 EXPECT_EQ(2u, deleted_notification_set.size());
584 std::vector<NotificationDatabaseData> notifications;
585 ASSERT_EQ(NotificationDatabase::STATUS_OK,
586 database->ReadAllNotificationDataForServiceWorkerRegistration(
587 origin, kExampleServiceWorkerRegistrationId, &notifications));
589 EXPECT_EQ(0u, notifications.size());
592 } // namespace content