Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_factory_unittest.cc
blob70f8ae64ad57a18dca100bb198c26fbe45722582
1 // Copyright 2013 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 "base/files/file_util.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/logging.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/test/test_simple_task_runner.h"
11 #include "content/browser/indexed_db/indexed_db_connection.h"
12 #include "content/browser/indexed_db/indexed_db_context_impl.h"
13 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
14 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
15 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
16 #include "storage/common/database/database_identifier.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
19 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
20 #include "url/gurl.h"
22 using base::ASCIIToUTF16;
24 namespace content {
26 namespace {
28 class MockIDBFactory : public IndexedDBFactoryImpl {
29 public:
30 explicit MockIDBFactory(IndexedDBContextImpl* context)
31 : IndexedDBFactoryImpl(context) {}
32 scoped_refptr<IndexedDBBackingStore> TestOpenBackingStore(
33 const GURL& origin,
34 const base::FilePath& data_directory) {
35 blink::WebIDBDataLoss data_loss =
36 blink::WebIDBDataLossNone;
37 std::string data_loss_message;
38 bool disk_full;
39 leveldb::Status s;
40 scoped_refptr<IndexedDBBackingStore> backing_store =
41 OpenBackingStore(origin,
42 data_directory,
43 NULL /* request_context */,
44 &data_loss,
45 &data_loss_message,
46 &disk_full,
47 &s);
48 EXPECT_EQ(blink::WebIDBDataLossNone, data_loss);
49 return backing_store;
52 void TestCloseBackingStore(IndexedDBBackingStore* backing_store) {
53 CloseBackingStore(backing_store->origin_url());
56 void TestReleaseBackingStore(IndexedDBBackingStore* backing_store,
57 bool immediate) {
58 ReleaseBackingStore(backing_store->origin_url(), immediate);
61 private:
62 ~MockIDBFactory() override {}
64 DISALLOW_COPY_AND_ASSIGN(MockIDBFactory);
67 } // namespace
69 class IndexedDBFactoryTest : public testing::Test {
70 public:
71 IndexedDBFactoryTest() {
72 task_runner_ = new base::TestSimpleTaskRunner();
73 context_ = new IndexedDBContextImpl(base::FilePath(),
74 NULL /* special_storage_policy */,
75 NULL /* quota_manager_proxy */,
76 task_runner_.get());
77 idb_factory_ = new MockIDBFactory(context_.get());
80 protected:
81 // For timers to post events.
82 base::MessageLoop loop_;
84 MockIDBFactory* factory() const { return idb_factory_.get(); }
85 void clear_factory() { idb_factory_ = NULL; }
86 IndexedDBContextImpl* context() const { return context_.get(); }
88 private:
89 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
90 scoped_refptr<IndexedDBContextImpl> context_;
91 scoped_refptr<MockIDBFactory> idb_factory_;
93 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
96 TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
97 GURL origin1("http://localhost:81");
98 GURL origin2("http://localhost:82");
100 base::ScopedTempDir temp_directory;
101 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
102 scoped_refptr<IndexedDBBackingStore> disk_store1 =
103 factory()->TestOpenBackingStore(origin1, temp_directory.path());
105 scoped_refptr<IndexedDBBackingStore> disk_store2 =
106 factory()->TestOpenBackingStore(origin1, temp_directory.path());
107 EXPECT_EQ(disk_store1.get(), disk_store2.get());
109 scoped_refptr<IndexedDBBackingStore> disk_store3 =
110 factory()->TestOpenBackingStore(origin2, temp_directory.path());
112 factory()->TestCloseBackingStore(disk_store1.get());
113 factory()->TestCloseBackingStore(disk_store3.get());
115 EXPECT_FALSE(disk_store1->HasOneRef());
116 EXPECT_FALSE(disk_store2->HasOneRef());
117 EXPECT_TRUE(disk_store3->HasOneRef());
119 disk_store2 = NULL;
120 EXPECT_TRUE(disk_store1->HasOneRef());
123 TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) {
124 GURL origin("http://localhost:81");
126 base::ScopedTempDir temp_directory;
127 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
128 scoped_refptr<IndexedDBBackingStore> store =
129 factory()->TestOpenBackingStore(origin, temp_directory.path());
131 // Give up the local refptr so that the factory has the only
132 // outstanding reference.
133 IndexedDBBackingStore* store_ptr = store.get();
134 store = NULL;
135 EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
136 factory()->TestReleaseBackingStore(store_ptr, false);
137 EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
139 factory()->TestOpenBackingStore(origin, temp_directory.path());
140 EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
141 factory()->TestReleaseBackingStore(store_ptr, false);
142 EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
144 // Take back a ref ptr and ensure that the actual close
145 // stops a running timer.
146 store = store_ptr;
147 factory()->TestCloseBackingStore(store_ptr);
148 EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
151 TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
152 GURL origin1("http://localhost:81");
153 GURL origin2("http://localhost:82");
155 scoped_refptr<IndexedDBBackingStore> mem_store1 =
156 factory()->TestOpenBackingStore(origin1, base::FilePath());
158 scoped_refptr<IndexedDBBackingStore> mem_store2 =
159 factory()->TestOpenBackingStore(origin1, base::FilePath());
160 EXPECT_EQ(mem_store1.get(), mem_store2.get());
162 scoped_refptr<IndexedDBBackingStore> mem_store3 =
163 factory()->TestOpenBackingStore(origin2, base::FilePath());
165 factory()->TestCloseBackingStore(mem_store1.get());
166 factory()->TestCloseBackingStore(mem_store3.get());
168 EXPECT_FALSE(mem_store1->HasOneRef());
169 EXPECT_FALSE(mem_store2->HasOneRef());
170 EXPECT_FALSE(mem_store3->HasOneRef());
172 clear_factory();
173 EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2
174 EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2
175 EXPECT_TRUE(mem_store3->HasOneRef());
177 mem_store2 = NULL;
178 EXPECT_TRUE(mem_store1->HasOneRef());
181 TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
182 base::ScopedTempDir temp_directory;
183 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
184 const base::FilePath base_path = temp_directory.path();
186 int limit = base::GetMaximumPathComponentLength(base_path);
187 EXPECT_GT(limit, 0);
189 std::string origin(limit + 1, 'x');
190 GURL too_long_origin("http://" + origin + ":81/");
191 scoped_refptr<IndexedDBBackingStore> diskStore1 =
192 factory()->TestOpenBackingStore(too_long_origin, base_path);
193 EXPECT_FALSE(diskStore1.get());
195 GURL ok_origin("http://someorigin.com:82/");
196 scoped_refptr<IndexedDBBackingStore> diskStore2 =
197 factory()->TestOpenBackingStore(ok_origin, base_path);
198 EXPECT_TRUE(diskStore2.get());
201 class DiskFullFactory : public IndexedDBFactoryImpl {
202 public:
203 explicit DiskFullFactory(IndexedDBContextImpl* context)
204 : IndexedDBFactoryImpl(context) {}
206 private:
207 ~DiskFullFactory() override {}
208 scoped_refptr<IndexedDBBackingStore> OpenBackingStore(
209 const GURL& origin_url,
210 const base::FilePath& data_directory,
211 net::URLRequestContext* request_context,
212 blink::WebIDBDataLoss* data_loss,
213 std::string* data_loss_message,
214 bool* disk_full,
215 leveldb::Status* s) override {
216 *disk_full = true;
217 *s = leveldb::Status::IOError("Disk is full");
218 return scoped_refptr<IndexedDBBackingStore>();
221 DISALLOW_COPY_AND_ASSIGN(DiskFullFactory);
224 class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
225 public:
226 LookingForQuotaErrorMockCallbacks()
227 : IndexedDBCallbacks(NULL, 0, 0), error_called_(false) {}
228 void OnError(const IndexedDBDatabaseError& error) override {
229 error_called_ = true;
230 EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError, error.code());
232 bool error_called() const { return error_called_; }
234 private:
235 ~LookingForQuotaErrorMockCallbacks() override {}
236 bool error_called_;
238 DISALLOW_COPY_AND_ASSIGN(LookingForQuotaErrorMockCallbacks);
241 TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
242 const GURL origin("http://localhost:81");
243 base::ScopedTempDir temp_directory;
244 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
246 scoped_refptr<DiskFullFactory> factory = new DiskFullFactory(context());
247 scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks =
248 new LookingForQuotaErrorMockCallbacks;
249 scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks =
250 new IndexedDBDatabaseCallbacks(NULL, 0, 0);
251 const base::string16 name(ASCIIToUTF16("name"));
252 IndexedDBPendingConnection connection(callbacks,
253 dummy_database_callbacks,
254 0, /* child_process_id */
255 2, /* transaction_id */
256 1 /* version */);
257 factory->Open(name,
258 connection,
259 NULL /* request_context */,
260 origin,
261 temp_directory.path());
262 EXPECT_TRUE(callbacks->error_called());
265 TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
266 GURL origin("http://localhost:81");
268 base::ScopedTempDir temp_directory;
269 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
271 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
272 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
273 new MockIndexedDBDatabaseCallbacks());
274 const int64 transaction_id = 1;
275 IndexedDBPendingConnection connection(
276 callbacks,
277 db_callbacks,
278 0, /* child_process_id */
279 transaction_id,
280 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
281 factory()->Open(ASCIIToUTF16("db"),
282 connection,
283 NULL /* request_context */,
284 origin,
285 temp_directory.path());
287 EXPECT_TRUE(callbacks->connection());
289 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
290 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
292 callbacks->connection()->ForceClose();
294 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
295 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
298 TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
299 GURL origin("http://localhost:81");
301 base::ScopedTempDir temp_directory;
302 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
304 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
305 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
306 new MockIndexedDBDatabaseCallbacks());
307 const int64 transaction_id = 1;
308 IndexedDBPendingConnection connection(
309 callbacks,
310 db_callbacks,
311 0, /* child_process_id */
312 transaction_id,
313 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
314 factory()->Open(ASCIIToUTF16("db"),
315 connection,
316 NULL /* request_context */,
317 origin,
318 temp_directory.path());
320 EXPECT_TRUE(callbacks->connection());
321 IndexedDBBackingStore* store =
322 callbacks->connection()->database()->backing_store();
323 EXPECT_FALSE(store->HasOneRef()); // Factory and database.
325 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
326 callbacks->connection()->Close();
327 EXPECT_TRUE(store->HasOneRef()); // Factory.
328 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
329 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
330 EXPECT_TRUE(store->close_timer()->IsRunning());
332 // Take a ref so it won't be destroyed out from under the test.
333 scoped_refptr<IndexedDBBackingStore> store_ref = store;
334 // Now simulate shutdown, which should stop the timer.
335 factory()->ContextDestroyed();
336 EXPECT_TRUE(store->HasOneRef()); // Local.
337 EXPECT_FALSE(store->close_timer()->IsRunning());
338 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
339 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
342 TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
343 GURL origin("http://localhost:81");
345 base::ScopedTempDir temp_directory;
346 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
348 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
350 const bool expect_connection = false;
351 scoped_refptr<MockIndexedDBCallbacks> callbacks(
352 new MockIndexedDBCallbacks(expect_connection));
353 factory()->DeleteDatabase(ASCIIToUTF16("db"),
354 NULL /* request_context */,
355 callbacks,
356 origin,
357 temp_directory.path());
359 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
360 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
362 // Now simulate shutdown, which should stop the timer.
363 factory()->ContextDestroyed();
365 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
366 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
369 TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
370 GURL origin("http://localhost:81");
372 base::ScopedTempDir temp_directory;
373 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
375 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
377 const bool expect_connection = false;
378 scoped_refptr<MockIndexedDBCallbacks> callbacks(
379 new MockIndexedDBCallbacks(expect_connection));
380 factory()->GetDatabaseNames(
381 callbacks, origin, temp_directory.path(), NULL /* request_context */);
383 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
384 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
386 // Now simulate shutdown, which should stop the timer.
387 factory()->ContextDestroyed();
389 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
390 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
393 TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
394 GURL origin("http://localhost:81");
396 base::ScopedTempDir temp_directory;
397 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
399 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
400 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
401 new MockIndexedDBDatabaseCallbacks());
402 const int64 transaction_id = 1;
403 IndexedDBPendingConnection connection(
404 callbacks,
405 db_callbacks,
406 0, /* child_process_id */
407 transaction_id,
408 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
409 factory()->Open(ASCIIToUTF16("db"),
410 connection,
411 NULL /* request_context */,
412 origin,
413 temp_directory.path());
415 EXPECT_TRUE(callbacks->connection());
416 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
417 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
419 callbacks->connection()->Close();
421 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
422 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
424 factory()->ForceClose(origin);
426 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
427 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
429 // Ensure it is safe if the store is not open.
430 factory()->ForceClose(origin);
433 class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
434 public:
435 UpgradeNeededCallbacks() {}
437 void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
438 const IndexedDBDatabaseMetadata& metadata) override {
439 EXPECT_TRUE(connection_.get());
440 EXPECT_FALSE(connection.get());
443 void OnUpgradeNeeded(
444 int64 old_version,
445 scoped_ptr<IndexedDBConnection> connection,
446 const content::IndexedDBDatabaseMetadata& metadata) override {
447 connection_ = connection.Pass();
450 protected:
451 ~UpgradeNeededCallbacks() override {}
453 private:
454 DISALLOW_COPY_AND_ASSIGN(UpgradeNeededCallbacks);
457 class ErrorCallbacks : public MockIndexedDBCallbacks {
458 public:
459 ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {}
461 void OnError(const IndexedDBDatabaseError& error) override {
462 saw_error_= true;
464 bool saw_error() const { return saw_error_; }
466 private:
467 ~ErrorCallbacks() override {}
468 bool saw_error_;
470 DISALLOW_COPY_AND_ASSIGN(ErrorCallbacks);
473 TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
474 GURL origin("http://localhost:81");
476 base::ScopedTempDir temp_directory;
477 ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
479 const base::string16 db_name(ASCIIToUTF16("db"));
480 const int64 db_version = 2;
481 const int64 transaction_id = 1;
482 scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks(
483 new MockIndexedDBDatabaseCallbacks());
485 // Open at version 2, then close.
487 scoped_refptr<MockIndexedDBCallbacks> callbacks(
488 new UpgradeNeededCallbacks());
489 IndexedDBPendingConnection connection(callbacks,
490 db_callbacks,
491 0, /* child_process_id */
492 transaction_id,
493 db_version);
494 factory()->Open(db_name,
495 connection,
496 NULL /* request_context */,
497 origin,
498 temp_directory.path());
499 EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
501 // Pump the message loop so the upgrade transaction can run.
502 base::MessageLoop::current()->RunUntilIdle();
503 EXPECT_TRUE(callbacks->connection());
504 callbacks->connection()->database()->Commit(transaction_id);
506 callbacks->connection()->Close();
507 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
510 // Open at version < 2, which will fail; ensure factory doesn't retain
511 // the database object.
513 scoped_refptr<ErrorCallbacks> callbacks(new ErrorCallbacks());
514 IndexedDBPendingConnection connection(callbacks,
515 db_callbacks,
516 0, /* child_process_id */
517 transaction_id,
518 db_version - 1);
519 factory()->Open(db_name,
520 connection,
521 NULL /* request_context */,
522 origin,
523 temp_directory.path());
524 EXPECT_TRUE(callbacks->saw_error());
525 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
528 // Terminate all pending-close timers.
529 factory()->ForceClose(origin);
532 } // namespace content