f30f45128bb8a83a50385b9c4292f48abb593247
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_factory_unittest.cc
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.
4
5 #include "content/browser/indexed_db/indexed_db_factory.h"
6
7 #include "base/file_util.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/test/test_simple_task_runner.h"
13 #include "content/browser/indexed_db/indexed_db_connection.h"
14 #include "content/browser/indexed_db/indexed_db_context_impl.h"
15 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
16 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.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"
21 #include "webkit/common/database/database_identifier.h"
22
23 using base::ASCIIToUTF16;
24
25 namespace content {
26
27 namespace {
28
29 class MockIDBFactory : public IndexedDBFactory {
30  public:
31   MockIDBFactory(IndexedDBContextImpl* context) : IndexedDBFactory(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     scoped_refptr<IndexedDBBackingStore> backing_store =
40         OpenBackingStore(origin,
41                          data_directory,
42                          NULL /* request_context */,
43                          &data_loss,
44                          &data_loss_message,
45                          &disk_full);
46     EXPECT_EQ(blink::WebIDBDataLossNone, data_loss);
47     return backing_store;
48   }
49
50   void TestCloseBackingStore(IndexedDBBackingStore* backing_store) {
51     CloseBackingStore(backing_store->origin_url());
52   }
53
54   void TestReleaseBackingStore(IndexedDBBackingStore* backing_store,
55                                bool immediate) {
56     ReleaseBackingStore(backing_store->origin_url(), immediate);
57   }
58
59  private:
60   virtual ~MockIDBFactory() {}
61 };
62
63 }  // namespace
64
65 class IndexedDBFactoryTest : public testing::Test {
66  public:
67   IndexedDBFactoryTest() {
68     task_runner_ = new base::TestSimpleTaskRunner();
69     context_ = new IndexedDBContextImpl(base::FilePath(),
70                                         NULL /* special_storage_policy */,
71                                         NULL /* quota_manager_proxy */,
72                                         task_runner_.get());
73     idb_factory_ = new MockIDBFactory(context_.get());
74   }
75
76  protected:
77   // For timers to post events.
78   base::MessageLoop loop_;
79
80   MockIDBFactory* factory() const { return idb_factory_.get(); }
81   void clear_factory() { idb_factory_ = NULL; }
82   IndexedDBContextImpl* context() const { return context_.get(); }
83
84  private:
85   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
86   scoped_refptr<IndexedDBContextImpl> context_;
87   scoped_refptr<MockIDBFactory> idb_factory_;
88
89   DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
90 };
91
92 TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
93   GURL origin1("http://localhost:81");
94   GURL origin2("http://localhost:82");
95
96   base::ScopedTempDir temp_directory;
97   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
98   scoped_refptr<IndexedDBBackingStore> disk_store1 =
99       factory()->TestOpenBackingStore(origin1, temp_directory.path());
100
101   scoped_refptr<IndexedDBBackingStore> disk_store2 =
102       factory()->TestOpenBackingStore(origin1, temp_directory.path());
103   EXPECT_EQ(disk_store1.get(), disk_store2.get());
104
105   scoped_refptr<IndexedDBBackingStore> disk_store3 =
106       factory()->TestOpenBackingStore(origin2, temp_directory.path());
107
108   factory()->TestCloseBackingStore(disk_store1);
109   factory()->TestCloseBackingStore(disk_store3);
110
111   EXPECT_FALSE(disk_store1->HasOneRef());
112   EXPECT_FALSE(disk_store2->HasOneRef());
113   EXPECT_TRUE(disk_store3->HasOneRef());
114
115   disk_store2 = NULL;
116   EXPECT_TRUE(disk_store1->HasOneRef());
117 }
118
119 TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) {
120   GURL origin("http://localhost:81");
121
122   base::ScopedTempDir temp_directory;
123   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
124   scoped_refptr<IndexedDBBackingStore> store =
125       factory()->TestOpenBackingStore(origin, temp_directory.path());
126
127   // Give up the local refptr so that the factory has the only
128   // outstanding reference.
129   IndexedDBBackingStore* store_ptr = store.get();
130   store = NULL;
131   EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
132   factory()->TestReleaseBackingStore(store_ptr, false);
133   EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
134
135   factory()->TestOpenBackingStore(origin, temp_directory.path());
136   EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
137   factory()->TestReleaseBackingStore(store_ptr, false);
138   EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
139
140   // Take back a ref ptr and ensure that the actual close
141   // stops a running timer.
142   store = store_ptr;
143   factory()->TestCloseBackingStore(store_ptr);
144   EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
145 }
146
147 TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
148   GURL origin1("http://localhost:81");
149   GURL origin2("http://localhost:82");
150
151   scoped_refptr<IndexedDBBackingStore> mem_store1 =
152       factory()->TestOpenBackingStore(origin1, base::FilePath());
153
154   scoped_refptr<IndexedDBBackingStore> mem_store2 =
155       factory()->TestOpenBackingStore(origin1, base::FilePath());
156   EXPECT_EQ(mem_store1.get(), mem_store2.get());
157
158   scoped_refptr<IndexedDBBackingStore> mem_store3 =
159       factory()->TestOpenBackingStore(origin2, base::FilePath());
160
161   factory()->TestCloseBackingStore(mem_store1);
162   factory()->TestCloseBackingStore(mem_store3);
163
164   EXPECT_FALSE(mem_store1->HasOneRef());
165   EXPECT_FALSE(mem_store2->HasOneRef());
166   EXPECT_FALSE(mem_store3->HasOneRef());
167
168   clear_factory();
169   EXPECT_FALSE(mem_store1->HasOneRef());  // mem_store1 and 2
170   EXPECT_FALSE(mem_store2->HasOneRef());  // mem_store1 and 2
171   EXPECT_TRUE(mem_store3->HasOneRef());
172
173   mem_store2 = NULL;
174   EXPECT_TRUE(mem_store1->HasOneRef());
175 }
176
177 TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
178   base::ScopedTempDir temp_directory;
179   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
180   const base::FilePath base_path = temp_directory.path();
181
182   int limit = base::GetMaximumPathComponentLength(base_path);
183   EXPECT_GT(limit, 0);
184
185   std::string origin(limit + 1, 'x');
186   GURL too_long_origin("http://" + origin + ":81/");
187   scoped_refptr<IndexedDBBackingStore> diskStore1 =
188       factory()->TestOpenBackingStore(too_long_origin, base_path);
189   EXPECT_FALSE(diskStore1);
190
191   GURL ok_origin("http://someorigin.com:82/");
192   scoped_refptr<IndexedDBBackingStore> diskStore2 =
193       factory()->TestOpenBackingStore(ok_origin, base_path);
194   EXPECT_TRUE(diskStore2);
195 }
196
197 class DiskFullFactory : public IndexedDBFactory {
198  public:
199   DiskFullFactory(IndexedDBContextImpl* context) : IndexedDBFactory(context) {}
200
201  private:
202   virtual ~DiskFullFactory() {}
203   virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore(
204       const GURL& origin_url,
205       const base::FilePath& data_directory,
206       net::URLRequestContext* request_context,
207       blink::WebIDBDataLoss* data_loss,
208       std::string* data_loss_message,
209       bool* disk_full) OVERRIDE {
210     *disk_full = true;
211     return scoped_refptr<IndexedDBBackingStore>();
212   }
213 };
214
215 class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
216  public:
217   LookingForQuotaErrorMockCallbacks()
218       : IndexedDBCallbacks(NULL, 0, 0), error_called_(false) {}
219   virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE {
220     error_called_ = true;
221     EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError, error.code());
222   }
223   bool error_called() const { return error_called_; }
224
225  private:
226   virtual ~LookingForQuotaErrorMockCallbacks() {}
227   bool error_called_;
228 };
229
230 TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
231   const GURL origin("http://localhost:81");
232   base::ScopedTempDir temp_directory;
233   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
234
235   scoped_refptr<DiskFullFactory> factory = new DiskFullFactory(context());
236   scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks =
237       new LookingForQuotaErrorMockCallbacks;
238   scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks =
239       new IndexedDBDatabaseCallbacks(NULL, 0, 0);
240   const base::string16 name(ASCIIToUTF16("name"));
241   IndexedDBPendingConnection connection(callbacks,
242                                         dummy_database_callbacks,
243                                         0, /* child_process_id */
244                                         2, /* transaction_id */
245                                         1 /* version */);
246   factory->Open(name,
247                 connection,
248                 NULL /* request_context */,
249                 origin,
250                 temp_directory.path());
251   EXPECT_TRUE(callbacks->error_called());
252 }
253
254 TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
255   GURL origin("http://localhost:81");
256
257   base::ScopedTempDir temp_directory;
258   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
259
260   scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
261   scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
262       new MockIndexedDBDatabaseCallbacks());
263   const int64 transaction_id = 1;
264   IndexedDBPendingConnection connection(
265       callbacks,
266       db_callbacks,
267       0, /* child_process_id */
268       transaction_id,
269       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
270   factory()->Open(ASCIIToUTF16("db"),
271                   connection,
272                   NULL /* request_context */,
273                   origin,
274                   temp_directory.path());
275
276   EXPECT_TRUE(callbacks->connection());
277
278   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
279   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
280
281   callbacks->connection()->ForceClose();
282
283   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
284   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
285 }
286
287 TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
288   GURL origin("http://localhost:81");
289
290   base::ScopedTempDir temp_directory;
291   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
292
293   scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
294   scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
295       new MockIndexedDBDatabaseCallbacks());
296   const int64 transaction_id = 1;
297   IndexedDBPendingConnection connection(
298       callbacks,
299       db_callbacks,
300       0, /* child_process_id */
301       transaction_id,
302       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
303   factory()->Open(ASCIIToUTF16("db"),
304                   connection,
305                   NULL /* request_context */,
306                   origin,
307                   temp_directory.path());
308
309   EXPECT_TRUE(callbacks->connection());
310   IndexedDBBackingStore* store =
311       callbacks->connection()->database()->backing_store();
312   EXPECT_FALSE(store->HasOneRef());  // Factory and database.
313
314   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
315   callbacks->connection()->Close();
316   EXPECT_TRUE(store->HasOneRef());  // Factory.
317   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
318   EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
319   EXPECT_TRUE(store->close_timer()->IsRunning());
320
321   // Take a ref so it won't be destroyed out from under the test.
322   scoped_refptr<IndexedDBBackingStore> store_ref = store;
323   // Now simulate shutdown, which should stop the timer.
324   factory()->ContextDestroyed();
325   EXPECT_TRUE(store->HasOneRef());  // Local.
326   EXPECT_FALSE(store->close_timer()->IsRunning());
327   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
328   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
329 }
330
331 TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
332   GURL origin("http://localhost:81");
333
334   base::ScopedTempDir temp_directory;
335   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
336
337   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
338
339   const bool expect_connection = false;
340   scoped_refptr<MockIndexedDBCallbacks> callbacks(
341       new MockIndexedDBCallbacks(expect_connection));
342   factory()->DeleteDatabase(ASCIIToUTF16("db"),
343                             NULL /* request_context */,
344                             callbacks,
345                             origin,
346                             temp_directory.path());
347
348   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
349   EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
350
351   // Now simulate shutdown, which should stop the timer.
352   factory()->ContextDestroyed();
353
354   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
355   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
356 }
357
358 TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
359   GURL origin("http://localhost:81");
360
361   base::ScopedTempDir temp_directory;
362   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
363
364   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
365
366   const bool expect_connection = false;
367   scoped_refptr<MockIndexedDBCallbacks> callbacks(
368       new MockIndexedDBCallbacks(expect_connection));
369   factory()->GetDatabaseNames(
370       callbacks, origin, temp_directory.path(), NULL /* request_context */);
371
372   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
373   EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
374
375   // Now simulate shutdown, which should stop the timer.
376   factory()->ContextDestroyed();
377
378   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
379   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
380 }
381
382 TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
383   GURL origin("http://localhost:81");
384
385   base::ScopedTempDir temp_directory;
386   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
387
388   scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
389   scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
390       new MockIndexedDBDatabaseCallbacks());
391   const int64 transaction_id = 1;
392   IndexedDBPendingConnection connection(
393       callbacks,
394       db_callbacks,
395       0, /* child_process_id */
396       transaction_id,
397       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
398   factory()->Open(ASCIIToUTF16("db"),
399                   connection,
400                   NULL /* request_context */,
401                   origin,
402                   temp_directory.path());
403
404   EXPECT_TRUE(callbacks->connection());
405   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
406   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
407
408   callbacks->connection()->Close();
409
410   EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
411   EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
412
413   factory()->ForceClose(origin);
414
415   EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
416   EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
417
418   // Ensure it is safe if the store is not open.
419   factory()->ForceClose(origin);
420 }
421
422 class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
423  public:
424   virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
425                          const IndexedDBDatabaseMetadata& metadata) OVERRIDE {
426     EXPECT_TRUE(connection_.get());
427     EXPECT_FALSE(connection.get());
428   }
429
430   virtual void OnUpgradeNeeded(
431       int64 old_version,
432       scoped_ptr<IndexedDBConnection> connection,
433       const content::IndexedDBDatabaseMetadata& metadata) OVERRIDE {
434     connection_ = connection.Pass();
435   }
436
437  protected:
438   virtual ~UpgradeNeededCallbacks() {}
439 };
440
441 class ErrorCallbacks : public MockIndexedDBCallbacks {
442  public:
443   ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {}
444
445   virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE {
446     saw_error_= true;
447   }
448   bool saw_error() const { return saw_error_; }
449
450  private:
451   virtual ~ErrorCallbacks() {}
452   bool saw_error_;
453 };
454
455 TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
456   GURL origin("http://localhost:81");
457
458   base::ScopedTempDir temp_directory;
459   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
460
461   const base::string16 db_name(ASCIIToUTF16("db"));
462   const int64 db_version = 2;
463   const int64 transaction_id = 1;
464   scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks(
465       new MockIndexedDBDatabaseCallbacks());
466
467   // Open at version 2, then close.
468   {
469     scoped_refptr<MockIndexedDBCallbacks> callbacks(
470         new UpgradeNeededCallbacks());
471     IndexedDBPendingConnection connection(callbacks,
472                                           db_callbacks,
473                                           0, /* child_process_id */
474                                           transaction_id,
475                                           db_version);
476     factory()->Open(db_name,
477                     connection,
478                     NULL /* request_context */,
479                     origin,
480                     temp_directory.path());
481     EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
482
483     // Pump the message loop so the upgrade transaction can run.
484     base::MessageLoop::current()->RunUntilIdle();
485     EXPECT_TRUE(callbacks->connection());
486     callbacks->connection()->database()->Commit(transaction_id);
487
488     callbacks->connection()->Close();
489     EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
490   }
491
492   // Open at version < 2, which will fail; ensure factory doesn't retain
493   // the database object.
494   {
495     scoped_refptr<ErrorCallbacks> callbacks(new ErrorCallbacks());
496     IndexedDBPendingConnection connection(callbacks,
497                                           db_callbacks,
498                                           0, /* child_process_id */
499                                           transaction_id,
500                                           db_version - 1);
501     factory()->Open(db_name,
502                     connection,
503                     NULL /* request_context */,
504                     origin,
505                     temp_directory.path());
506     EXPECT_TRUE(callbacks->saw_error());
507     EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
508   }
509
510   // Terminate all pending-close timers.
511   factory()->ForceClose(origin);
512 }
513
514 }  // namespace content