OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/notifications/notification_database.h" | 5 #include "content/browser/notifications/notification_database.h" |
6 | 6 |
7 #include "base/files/scoped_temp_dir.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/browser/notifications/notification_database_data.h" |
| 11 #include "content/public/common/platform_notification_data.h" |
8 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
9 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 13 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
10 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 14 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| 15 #include "url/gurl.h" |
11 | 16 |
12 namespace content { | 17 namespace content { |
13 | 18 |
14 class NotificationDatabaseTest : public ::testing::Test { | 19 class NotificationDatabaseTest : public ::testing::Test { |
15 protected: | 20 protected: |
16 // Creates a new NotificationDatabase instance in memory. | 21 // Creates a new NotificationDatabase instance in memory. |
17 NotificationDatabase* CreateDatabaseInMemory() { | 22 NotificationDatabase* CreateDatabaseInMemory() { |
18 return new NotificationDatabase(base::FilePath()); | 23 return new NotificationDatabase(base::FilePath()); |
19 } | 24 } |
20 | 25 |
(...skipping 15 matching lines...) Expand all Loading... |
36 | 41 |
37 // Writes a LevelDB key-value pair directly to the LevelDB backing the | 42 // Writes a LevelDB key-value pair directly to the LevelDB backing the |
38 // notification database in |database|. | 43 // notification database in |database|. |
39 void WriteLevelDBKeyValuePair(NotificationDatabase* database, | 44 void WriteLevelDBKeyValuePair(NotificationDatabase* database, |
40 const std::string& key, | 45 const std::string& key, |
41 const std::string& value) { | 46 const std::string& value) { |
42 leveldb::Status status = | 47 leveldb::Status status = |
43 database->GetDBForTesting()->Put(leveldb::WriteOptions(), key, value); | 48 database->GetDBForTesting()->Put(leveldb::WriteOptions(), key, value); |
44 ASSERT_TRUE(status.ok()); | 49 ASSERT_TRUE(status.ok()); |
45 } | 50 } |
46 | |
47 // Increments the next notification id value in the database. Normally this | |
48 // is managed by the NotificationDatabase when writing notification data. | |
49 // | |
50 // TODO(peter): Stop doing this manually when writing notification data will | |
51 // do this for us, except for tests verifying corruption behavior. | |
52 void IncrementNextNotificationId(NotificationDatabase* database) { | |
53 int64_t next_notification_id; | |
54 ASSERT_EQ(NotificationDatabase::STATUS_OK, | |
55 database->GetNextNotificationId(&next_notification_id)); | |
56 | |
57 leveldb::WriteBatch batch; | |
58 database->WriteNextNotificationId(&batch, next_notification_id + 1); | |
59 | |
60 leveldb::Status status = | |
61 database->GetDBForTesting()->Write(leveldb::WriteOptions(), &batch); | |
62 | |
63 ASSERT_TRUE(status.ok()); | |
64 } | |
65 }; | 51 }; |
66 | 52 |
67 TEST_F(NotificationDatabaseTest, OpenCloseMemory) { | 53 TEST_F(NotificationDatabaseTest, OpenCloseMemory) { |
68 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); | 54 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
69 | 55 |
70 // Should return false because the database does not exist in memory. | 56 // Should return false because the database does not exist in memory. |
71 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, | 57 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
72 database->Open(false /* create_if_missing */)); | 58 database->Open(false /* create_if_missing */)); |
73 | 59 |
74 // Should return true, indicating that the database could be created. | 60 // Should return true, indicating that the database could be created. |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 ASSERT_EQ(NotificationDatabase::STATUS_OK, database->Destroy()); | 111 ASSERT_EQ(NotificationDatabase::STATUS_OK, database->Destroy()); |
126 EXPECT_FALSE(IsDatabaseOpen(database.get())); | 112 EXPECT_FALSE(IsDatabaseOpen(database.get())); |
127 | 113 |
128 // Try to re-open the database (but not re-create it). This should fail as | 114 // Try to re-open the database (but not re-create it). This should fail as |
129 // the files associated with the database should have been blown away. | 115 // the files associated with the database should have been blown away. |
130 database.reset(CreateDatabaseOnFileSystem(database_dir.path())); | 116 database.reset(CreateDatabaseOnFileSystem(database_dir.path())); |
131 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, | 117 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
132 database->Open(false /* create_if_missing */)); | 118 database->Open(false /* create_if_missing */)); |
133 } | 119 } |
134 | 120 |
135 TEST_F(NotificationDatabaseTest, GetNextNotificationIdIncrements) { | 121 TEST_F(NotificationDatabaseTest, NotificationIdIncrements) { |
136 base::ScopedTempDir database_dir; | 122 base::ScopedTempDir database_dir; |
137 ASSERT_TRUE(database_dir.CreateUniqueTempDir()); | 123 ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
138 | 124 |
139 scoped_ptr<NotificationDatabase> database( | 125 scoped_ptr<NotificationDatabase> database( |
140 CreateDatabaseOnFileSystem(database_dir.path())); | 126 CreateDatabaseOnFileSystem(database_dir.path())); |
141 | 127 |
142 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 128 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
143 database->Open(true /* create_if_missing */)); | 129 database->Open(true /* create_if_missing */)); |
144 | 130 |
| 131 GURL origin("https://example.com"); |
| 132 |
| 133 NotificationDatabaseData database_data; |
145 int64_t notification_id = 0; | 134 int64_t notification_id = 0; |
146 | 135 |
147 // Verify that getting two ids on the same database instance results in | 136 // Verify that getting two ids on the same database instance results in |
148 // incrementing values. Notification ids will start at 1. | 137 // incrementing values. Notification ids will start at 1. |
149 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 138 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
150 database->GetNextNotificationId(¬ification_id)); | 139 database->WriteNotificationData(origin, |
151 EXPECT_EQ(1, notification_id); | 140 database_data, |
152 | 141 ¬ification_id)); |
153 ASSERT_NO_FATAL_FAILURE(IncrementNextNotificationId(database.get())); | 142 EXPECT_EQ(notification_id, 1); |
154 | 143 |
155 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 144 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
156 database->GetNextNotificationId(¬ification_id)); | 145 database->WriteNotificationData(origin, |
157 EXPECT_EQ(2, notification_id); | 146 database_data, |
158 | 147 ¬ification_id)); |
159 ASSERT_NO_FATAL_FAILURE(IncrementNextNotificationId(database.get())); | 148 EXPECT_EQ(notification_id, 2); |
160 | 149 |
161 database.reset(CreateDatabaseOnFileSystem(database_dir.path())); | 150 database.reset(CreateDatabaseOnFileSystem(database_dir.path())); |
162 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 151 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
163 database->Open(false /* create_if_missing */)); | 152 database->Open(false /* create_if_missing */)); |
164 | 153 |
165 // Verify that the next notification id was stored in the database, and | 154 // Verify that the next notification id was stored in the database, and |
166 // continues where we expect it to be, even after closing and opening it. | 155 // continues where we expect it to be, even after closing and opening it. |
167 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 156 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
168 database->GetNextNotificationId(¬ification_id)); | 157 database->WriteNotificationData(origin, |
169 EXPECT_EQ(3, notification_id); | 158 database_data, |
170 } | 159 ¬ification_id)); |
171 | 160 EXPECT_EQ(notification_id, 3); |
172 TEST_F(NotificationDatabaseTest, GetNextNotificationIdCorruption) { | 161 } |
173 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); | 162 |
174 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 163 TEST_F(NotificationDatabaseTest, NotificationIdCorruption) { |
175 database->Open(true /* create_if_missing */)); | 164 base::ScopedTempDir database_dir; |
176 | 165 ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
177 int64_t notification_id = 0; | 166 |
178 | 167 scoped_ptr<NotificationDatabase> database( |
179 ASSERT_EQ(NotificationDatabase::STATUS_OK, | 168 CreateDatabaseOnFileSystem(database_dir.path())); |
180 database->GetNextNotificationId(¬ification_id)); | 169 |
181 EXPECT_EQ(1, notification_id); | 170 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
182 | 171 database->Open(true /* create_if_missing */)); |
183 // Deliberately write an invalid value as the next notification id to the | 172 |
184 // database, which should cause GetNextNotificationId to realize that | 173 GURL origin("https://example.com"); |
185 // something is wrong with the data it's reading. | 174 |
| 175 NotificationDatabaseData database_data; |
| 176 int64_t notification_id = 0; |
| 177 |
| 178 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 179 database->WriteNotificationData(origin, |
| 180 database_data, |
| 181 ¬ification_id)); |
| 182 EXPECT_EQ(notification_id, 1); |
| 183 |
| 184 // Deliberately write an invalid value as the next notification id. When |
| 185 // re-opening the database, the Open() method should realize that an invalid |
| 186 // value is being read, and mark the database as corrupted. |
186 ASSERT_NO_FATAL_FAILURE(WriteLevelDBKeyValuePair(database.get(), | 187 ASSERT_NO_FATAL_FAILURE(WriteLevelDBKeyValuePair(database.get(), |
187 "NEXT_NOTIFICATION_ID", | 188 "NEXT_NOTIFICATION_ID", |
188 "-42")); | 189 "-42")); |
189 | 190 |
| 191 database.reset(CreateDatabaseOnFileSystem(database_dir.path())); |
190 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_CORRUPTED, | 192 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_CORRUPTED, |
191 database->GetNextNotificationId(¬ification_id)); | 193 database->Open(false /* create_if_missing */)); |
| 194 } |
| 195 |
| 196 TEST_F(NotificationDatabaseTest, ReadInvalidNotificationData) { |
| 197 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 198 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 199 database->Open(true /* create_if_missing */)); |
| 200 |
| 201 NotificationDatabaseData database_data; |
| 202 |
| 203 // Reading the notification data for a notification that does not exist should |
| 204 // return the ERROR_NOT_FOUND status code. |
| 205 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| 206 database->ReadNotificationData(9001, |
| 207 GURL("https://chrome.com"), |
| 208 &database_data)); |
| 209 } |
| 210 |
| 211 TEST_F(NotificationDatabaseTest, ReadNotificationDataDifferentOrigin) { |
| 212 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 213 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 214 database->Open(true /* create_if_missing */)); |
| 215 |
| 216 int64_t notification_id = 0; |
| 217 GURL origin("https://example.com"); |
| 218 |
| 219 NotificationDatabaseData database_data, read_database_data; |
| 220 database_data.notification_data.title = base::UTF8ToUTF16("My Notification"); |
| 221 |
| 222 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 223 database->WriteNotificationData(origin, |
| 224 database_data, |
| 225 ¬ification_id)); |
| 226 |
| 227 // Reading the notification from the database when given a different origin |
| 228 // should return the ERROR_NOT_FOUND status code. |
| 229 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| 230 database->ReadNotificationData(notification_id, |
| 231 GURL("https://chrome.com"), |
| 232 &read_database_data)); |
| 233 |
| 234 // However, reading the notification from the database with the same origin |
| 235 // should return STATUS_OK and the associated notification data. |
| 236 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 237 database->ReadNotificationData(notification_id, |
| 238 origin, |
| 239 &read_database_data)); |
| 240 |
| 241 EXPECT_EQ(database_data.notification_data.title, |
| 242 read_database_data.notification_data.title); |
| 243 } |
| 244 |
| 245 TEST_F(NotificationDatabaseTest, ReadNotificationDataReflection) { |
| 246 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 247 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 248 database->Open(true /* create_if_missing */)); |
| 249 |
| 250 int64_t notification_id = 0; |
| 251 |
| 252 GURL origin("https://example.com"); |
| 253 |
| 254 PlatformNotificationData notification_data; |
| 255 notification_data.title = base::UTF8ToUTF16("My Notification"); |
| 256 notification_data.direction = |
| 257 PlatformNotificationData::NotificationDirectionRightToLeft; |
| 258 notification_data.lang = "nl-NL"; |
| 259 notification_data.body = base::UTF8ToUTF16("Hello, world!"); |
| 260 notification_data.tag = "replace id"; |
| 261 notification_data.icon = GURL("https://example.com/icon.png"); |
| 262 notification_data.silent = true; |
| 263 |
| 264 NotificationDatabaseData database_data; |
| 265 database_data.notification_id = notification_id; |
| 266 database_data.origin = origin; |
| 267 database_data.service_worker_registration_id = 42; |
| 268 database_data.notification_data = notification_data; |
| 269 |
| 270 // Write the constructed notification to the database, and then immediately |
| 271 // read it back from the database again as well. |
| 272 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 273 database->WriteNotificationData(origin, |
| 274 database_data, |
| 275 ¬ification_id)); |
| 276 |
| 277 NotificationDatabaseData read_database_data; |
| 278 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 279 database->ReadNotificationData(notification_id, |
| 280 origin, |
| 281 &read_database_data)); |
| 282 |
| 283 // Verify that all members retrieved from the database are exactly the same |
| 284 // as the ones that were written to it. This tests the serialization behavior. |
| 285 |
| 286 EXPECT_EQ(database_data.notification_id, read_database_data.notification_id); |
| 287 EXPECT_EQ(database_data.origin, read_database_data.origin); |
| 288 EXPECT_EQ(database_data.service_worker_registration_id, |
| 289 read_database_data.service_worker_registration_id); |
| 290 |
| 291 const PlatformNotificationData& read_notification_data = |
| 292 read_database_data.notification_data; |
| 293 |
| 294 EXPECT_EQ(notification_data.title, read_notification_data.title); |
| 295 EXPECT_EQ(notification_data.direction, read_notification_data.direction); |
| 296 EXPECT_EQ(notification_data.lang, read_notification_data.lang); |
| 297 EXPECT_EQ(notification_data.body, read_notification_data.body); |
| 298 EXPECT_EQ(notification_data.tag, read_notification_data.tag); |
| 299 EXPECT_EQ(notification_data.icon, read_notification_data.icon); |
| 300 EXPECT_EQ(notification_data.silent, read_notification_data.silent); |
| 301 } |
| 302 |
| 303 TEST_F(NotificationDatabaseTest, ReadWriteMultipleNotificationData) { |
| 304 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 305 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 306 database->Open(true /* create_if_missing */)); |
| 307 |
| 308 NotificationDatabaseData database_data; |
| 309 GURL origin("https://example.com"); |
| 310 |
| 311 // Write ten notifications to the database, each with a unique title and |
| 312 // notification id (it is the responsibility of the user to increment this). |
| 313 for (int i = 1; i <= 10; ++i) { |
| 314 database_data.notification_id = i; |
| 315 database_data.notification_data.title = base::IntToString16(i); |
| 316 |
| 317 int64_t notification_id = 0; |
| 318 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 319 database->WriteNotificationData(origin, |
| 320 database_data, |
| 321 ¬ification_id)); |
| 322 EXPECT_EQ(notification_id, i); |
| 323 } |
| 324 |
| 325 // Read the ten notifications from the database, and verify that the titles |
| 326 // of each of them matches with how they were created. |
| 327 for (int i = 1; i <= 10; ++i) { |
| 328 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 329 database->ReadNotificationData(i /* notification_id */, |
| 330 origin, |
| 331 &database_data)); |
| 332 |
| 333 EXPECT_EQ(base::IntToString16(i), database_data.notification_data.title); |
| 334 } |
| 335 } |
| 336 |
| 337 TEST_F(NotificationDatabaseTest, DeleteInvalidNotificationData) { |
| 338 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 339 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 340 database->Open(true /* create_if_missing */)); |
| 341 |
| 342 // Deleting non-existing notifications is not considered to be a failure. |
| 343 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 344 database->DeleteNotificationData(9001, |
| 345 GURL("https://chrome.com"))); |
| 346 } |
| 347 |
| 348 TEST_F(NotificationDatabaseTest, DeleteNotificationDataSameOrigin) { |
| 349 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 350 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 351 database->Open(true /* create_if_missing */)); |
| 352 |
| 353 int64_t notification_id = 0; |
| 354 |
| 355 NotificationDatabaseData database_data; |
| 356 GURL origin("https://example.com"); |
| 357 |
| 358 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 359 database->WriteNotificationData(origin, |
| 360 database_data, |
| 361 ¬ification_id)); |
| 362 |
| 363 // Reading a notification after writing one should succeed. |
| 364 EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| 365 database->ReadNotificationData(notification_id, |
| 366 origin, |
| 367 &database_data)); |
| 368 |
| 369 // Delete the notification which was just written to the database, and verify |
| 370 // that reading it again will fail. |
| 371 EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| 372 database->DeleteNotificationData(notification_id, origin)); |
| 373 EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| 374 database->ReadNotificationData(notification_id, |
| 375 origin, |
| 376 &database_data)); |
| 377 } |
| 378 |
| 379 TEST_F(NotificationDatabaseTest, DeleteNotificationDataDifferentOrigin) { |
| 380 scoped_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| 381 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 382 database->Open(true /* create_if_missing */)); |
| 383 |
| 384 int64_t notification_id = 0; |
| 385 |
| 386 NotificationDatabaseData database_data; |
| 387 GURL origin("https://example.com"); |
| 388 |
| 389 ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| 390 database->WriteNotificationData(origin, |
| 391 database_data, |
| 392 ¬ification_id)); |
| 393 |
| 394 // Attempting to delete the notification with a different origin, but with the |
| 395 // same |notification_id|, should not return an error (the notification could |
| 396 // not be found, but that's not considered a failure). However, it should not |
| 397 // remove the notification either. |
| 398 EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| 399 database->DeleteNotificationData(notification_id, |
| 400 GURL("https://chrome.com"))); |
| 401 |
| 402 EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| 403 database->ReadNotificationData(notification_id, |
| 404 origin, |
| 405 &database_data)); |
192 } | 406 } |
193 | 407 |
194 } // namespace content | 408 } // namespace content |
OLD | NEW |