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 "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
18 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
20 #include "webkit/common/database/database_identifier.h"
22 using base::ASCIIToUTF16
;
28 class MockIDBFactory
: public IndexedDBFactoryImpl
{
30 explicit MockIDBFactory(IndexedDBContextImpl
* context
)
31 : IndexedDBFactoryImpl(context
) {}
32 scoped_refptr
<IndexedDBBackingStore
> TestOpenBackingStore(
34 const base::FilePath
& data_directory
) {
35 blink::WebIDBDataLoss data_loss
=
36 blink::WebIDBDataLossNone
;
37 std::string data_loss_message
;
40 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
41 OpenBackingStore(origin
,
43 NULL
/* request_context */,
48 EXPECT_EQ(blink::WebIDBDataLossNone
, data_loss
);
52 void TestCloseBackingStore(IndexedDBBackingStore
* backing_store
) {
53 CloseBackingStore(backing_store
->origin_url());
56 void TestReleaseBackingStore(IndexedDBBackingStore
* backing_store
,
58 ReleaseBackingStore(backing_store
->origin_url(), immediate
);
62 virtual ~MockIDBFactory() {}
64 DISALLOW_COPY_AND_ASSIGN(MockIDBFactory
);
69 class IndexedDBFactoryTest
: public testing::Test
{
71 IndexedDBFactoryTest() {
72 task_runner_
= new base::TestSimpleTaskRunner();
73 context_
= new IndexedDBContextImpl(base::FilePath(),
74 NULL
/* special_storage_policy */,
75 NULL
/* quota_manager_proxy */,
77 idb_factory_
= new MockIDBFactory(context_
.get());
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(); }
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());
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();
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.
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());
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());
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
);
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
{
203 explicit DiskFullFactory(IndexedDBContextImpl
* context
)
204 : IndexedDBFactoryImpl(context
) {}
207 virtual ~DiskFullFactory() {}
208 virtual 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
,
215 leveldb::Status
* s
) OVERRIDE
{
217 *s
= leveldb::Status::IOError("Disk is full");
218 return scoped_refptr
<IndexedDBBackingStore
>();
221 DISALLOW_COPY_AND_ASSIGN(DiskFullFactory
);
224 class LookingForQuotaErrorMockCallbacks
: public IndexedDBCallbacks
{
226 LookingForQuotaErrorMockCallbacks()
227 : IndexedDBCallbacks(NULL
, 0, 0), error_called_(false) {}
228 virtual 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_
; }
235 virtual ~LookingForQuotaErrorMockCallbacks() {}
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 */
259 NULL
/* request_context */,
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(
278 0, /* child_process_id */
280 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
281 factory()->Open(ASCIIToUTF16("db"),
283 NULL
/* request_context */,
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(
311 0, /* child_process_id */
313 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
314 factory()->Open(ASCIIToUTF16("db"),
316 NULL
/* request_context */,
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 */,
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(
406 0, /* child_process_id */
408 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
409 factory()->Open(ASCIIToUTF16("db"),
411 NULL
/* request_context */,
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
{
435 UpgradeNeededCallbacks() {}
437 virtual void OnSuccess(scoped_ptr
<IndexedDBConnection
> connection
,
438 const IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
439 EXPECT_TRUE(connection_
.get());
440 EXPECT_FALSE(connection
.get());
443 virtual void OnUpgradeNeeded(
445 scoped_ptr
<IndexedDBConnection
> connection
,
446 const content::IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
447 connection_
= connection
.Pass();
451 virtual ~UpgradeNeededCallbacks() {}
454 DISALLOW_COPY_AND_ASSIGN(UpgradeNeededCallbacks
);
457 class ErrorCallbacks
: public MockIndexedDBCallbacks
{
459 ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {}
461 virtual void OnError(const IndexedDBDatabaseError
& error
) OVERRIDE
{
464 bool saw_error() const { return saw_error_
; }
467 virtual ~ErrorCallbacks() {}
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
,
491 0, /* child_process_id */
494 factory()->Open(db_name
,
496 NULL
/* request_context */,
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
,
516 0, /* child_process_id */
519 factory()->Open(db_name
,
521 NULL
/* request_context */,
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