Home | History | Annotate | Download | only in indexed_db
      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_database.h"
      6 
      7 #include <set>
      8 
      9 #include "base/auto_reset.h"
     10 #include "base/logging.h"
     11 #include "base/run_loop.h"
     12 #include "base/strings/string16.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "content/browser/indexed_db/indexed_db.h"
     15 #include "content/browser/indexed_db/indexed_db_backing_store.h"
     16 #include "content/browser/indexed_db/indexed_db_callbacks.h"
     17 #include "content/browser/indexed_db/indexed_db_connection.h"
     18 #include "content/browser/indexed_db/indexed_db_cursor.h"
     19 #include "content/browser/indexed_db/indexed_db_factory.h"
     20 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
     21 #include "content/browser/indexed_db/indexed_db_transaction.h"
     22 #include "content/browser/indexed_db/indexed_db_value.h"
     23 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
     24 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 using base::ASCIIToUTF16;
     28 
     29 namespace {
     30 const int kFakeChildProcessId = 0;
     31 }
     32 
     33 namespace content {
     34 
     35 TEST(IndexedDBDatabaseTest, BackingStoreRetention) {
     36   scoped_refptr<IndexedDBFakeBackingStore> backing_store =
     37       new IndexedDBFakeBackingStore();
     38   EXPECT_TRUE(backing_store->HasOneRef());
     39 
     40   IndexedDBFactory* factory = 0;
     41   leveldb::Status s;
     42   scoped_refptr<IndexedDBDatabase> db =
     43       IndexedDBDatabase::Create(ASCIIToUTF16("db"),
     44                                 backing_store,
     45                                 factory,
     46                                 IndexedDBDatabase::Identifier(),
     47                                 &s);
     48   ASSERT_TRUE(s.ok());
     49   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
     50   db = NULL;
     51   EXPECT_TRUE(backing_store->HasOneRef());  // local
     52 }
     53 
     54 TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
     55   scoped_refptr<IndexedDBFakeBackingStore> backing_store =
     56       new IndexedDBFakeBackingStore();
     57   EXPECT_TRUE(backing_store->HasOneRef());  // local
     58 
     59   IndexedDBFactory* factory = 0;
     60   leveldb::Status s;
     61   scoped_refptr<IndexedDBDatabase> db =
     62       IndexedDBDatabase::Create(ASCIIToUTF16("db"),
     63                                 backing_store,
     64                                 factory,
     65                                 IndexedDBDatabase::Identifier(),
     66                                 &s);
     67   ASSERT_TRUE(s.ok());
     68   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
     69 
     70   scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
     71   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
     72       new MockIndexedDBDatabaseCallbacks());
     73   const int64 transaction_id1 = 1;
     74   IndexedDBPendingConnection connection1(
     75       request1,
     76       callbacks1,
     77       kFakeChildProcessId,
     78       transaction_id1,
     79       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
     80   db->OpenConnection(connection1);
     81 
     82   EXPECT_FALSE(backing_store->HasOneRef());  // db, connection count > 0
     83 
     84   scoped_refptr<MockIndexedDBCallbacks> request2(new MockIndexedDBCallbacks());
     85   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
     86       new MockIndexedDBDatabaseCallbacks());
     87   const int64 transaction_id2 = 2;
     88   IndexedDBPendingConnection connection2(
     89       request2,
     90       callbacks2,
     91       kFakeChildProcessId,
     92       transaction_id2,
     93       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
     94   db->OpenConnection(connection2);
     95 
     96   EXPECT_FALSE(backing_store->HasOneRef());  // local and connection
     97 
     98   request1->connection()->ForceClose();
     99   EXPECT_FALSE(request1->connection()->IsConnected());
    100 
    101   EXPECT_FALSE(backing_store->HasOneRef());  // local and connection
    102 
    103   request2->connection()->ForceClose();
    104   EXPECT_FALSE(request2->connection()->IsConnected());
    105 
    106   EXPECT_TRUE(backing_store->HasOneRef());
    107   EXPECT_FALSE(db->backing_store());
    108 
    109   db = NULL;
    110 }
    111 
    112 TEST(IndexedDBDatabaseTest, ForcedClose) {
    113   scoped_refptr<IndexedDBFakeBackingStore> backing_store =
    114       new IndexedDBFakeBackingStore();
    115   EXPECT_TRUE(backing_store->HasOneRef());
    116 
    117   IndexedDBFactory* factory = 0;
    118   leveldb::Status s;
    119   scoped_refptr<IndexedDBDatabase> database =
    120       IndexedDBDatabase::Create(ASCIIToUTF16("db"),
    121                                 backing_store,
    122                                 factory,
    123                                 IndexedDBDatabase::Identifier(),
    124                                 &s);
    125   ASSERT_TRUE(s.ok());
    126   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
    127 
    128   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks(
    129       new MockIndexedDBDatabaseCallbacks());
    130   scoped_refptr<MockIndexedDBCallbacks> request(new MockIndexedDBCallbacks());
    131   const int64 upgrade_transaction_id = 3;
    132   IndexedDBPendingConnection connection(
    133       request,
    134       callbacks,
    135       kFakeChildProcessId,
    136       upgrade_transaction_id,
    137       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
    138   database->OpenConnection(connection);
    139   EXPECT_EQ(database, request->connection()->database());
    140 
    141   const int64 transaction_id = 123;
    142   const std::vector<int64> scope;
    143   database->CreateTransaction(transaction_id,
    144                               request->connection(),
    145                               scope,
    146                               indexed_db::TRANSACTION_READ_ONLY);
    147 
    148   request->connection()->ForceClose();
    149 
    150   EXPECT_TRUE(backing_store->HasOneRef());  // local
    151   EXPECT_TRUE(callbacks->abort_called());
    152 }
    153 
    154 class MockDeleteCallbacks : public IndexedDBCallbacks {
    155  public:
    156   MockDeleteCallbacks()
    157       : IndexedDBCallbacks(NULL, 0, 0),
    158         blocked_called_(false),
    159         success_called_(false) {}
    160 
    161   virtual void OnBlocked(int64 existing_version) OVERRIDE {
    162     blocked_called_ = true;
    163   }
    164   virtual void OnSuccess(int64 result) OVERRIDE { success_called_ = true; }
    165 
    166   bool blocked_called() const { return blocked_called_; }
    167   bool success_called() const { return success_called_; }
    168 
    169  private:
    170   virtual ~MockDeleteCallbacks() {}
    171 
    172   bool blocked_called_;
    173   bool success_called_;
    174 
    175   DISALLOW_COPY_AND_ASSIGN(MockDeleteCallbacks);
    176 };
    177 
    178 TEST(IndexedDBDatabaseTest, PendingDelete) {
    179   scoped_refptr<IndexedDBFakeBackingStore> backing_store =
    180       new IndexedDBFakeBackingStore();
    181   EXPECT_TRUE(backing_store->HasOneRef());  // local
    182 
    183   IndexedDBFactory* factory = 0;
    184   leveldb::Status s;
    185   scoped_refptr<IndexedDBDatabase> db =
    186       IndexedDBDatabase::Create(ASCIIToUTF16("db"),
    187                                 backing_store,
    188                                 factory,
    189                                 IndexedDBDatabase::Identifier(),
    190                                 &s);
    191   ASSERT_TRUE(s.ok());
    192   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
    193 
    194   scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
    195   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
    196       new MockIndexedDBDatabaseCallbacks());
    197   const int64 transaction_id1 = 1;
    198   IndexedDBPendingConnection connection(
    199       request1,
    200       callbacks1,
    201       kFakeChildProcessId,
    202       transaction_id1,
    203       IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
    204   db->OpenConnection(connection);
    205 
    206   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
    207 
    208   scoped_refptr<MockDeleteCallbacks> request2(new MockDeleteCallbacks());
    209   db->DeleteDatabase(request2);
    210 
    211   EXPECT_TRUE(request2->blocked_called());
    212   EXPECT_FALSE(backing_store->HasOneRef());  // local and db
    213 
    214   db->Close(request1->connection(), true /* forced */);
    215 
    216   EXPECT_FALSE(db->backing_store());
    217   EXPECT_TRUE(backing_store->HasOneRef());  // local
    218   EXPECT_TRUE(request2->success_called());
    219 }
    220 
    221 void DummyOperation(IndexedDBTransaction* transaction) {
    222 }
    223 
    224 class IndexedDBDatabaseOperationTest : public testing::Test {
    225  public:
    226   IndexedDBDatabaseOperationTest() : commit_success_(leveldb::Status::OK()) {}
    227 
    228   virtual void SetUp() {
    229     backing_store_ = new IndexedDBFakeBackingStore();
    230     leveldb::Status s;
    231     db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"),
    232                                     backing_store_,
    233                                     NULL /*factory*/,
    234                                     IndexedDBDatabase::Identifier(),
    235                                     &s);
    236     ASSERT_TRUE(s.ok());
    237 
    238     request_ = new MockIndexedDBCallbacks();
    239     callbacks_ = new MockIndexedDBDatabaseCallbacks();
    240     const int64 transaction_id = 1;
    241     db_->OpenConnection(IndexedDBPendingConnection(
    242         request_,
    243         callbacks_,
    244         kFakeChildProcessId,
    245         transaction_id,
    246         IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION));
    247     EXPECT_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
    248               db_->metadata().int_version);
    249 
    250     transaction_ = new IndexedDBTransaction(
    251         transaction_id,
    252         callbacks_,
    253         std::set<int64>() /*scope*/,
    254         indexed_db::TRANSACTION_VERSION_CHANGE,
    255         db_,
    256         new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
    257     db_->TransactionCreated(transaction_);
    258 
    259     // Add a dummy task which takes the place of the VersionChangeOperation
    260     // which kicks off the upgrade. This ensures that the transaction has
    261     // processed at least one task before the CreateObjectStore call.
    262     transaction_->ScheduleTask(base::Bind(&DummyOperation));
    263   }
    264 
    265   void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
    266 
    267  protected:
    268   scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
    269   scoped_refptr<IndexedDBDatabase> db_;
    270   scoped_refptr<MockIndexedDBCallbacks> request_;
    271   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
    272   scoped_refptr<IndexedDBTransaction> transaction_;
    273 
    274   leveldb::Status commit_success_;
    275 
    276  private:
    277   base::MessageLoop message_loop_;
    278 
    279   DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest);
    280 };
    281 
    282 TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) {
    283   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    284   const int64 store_id = 1001;
    285   db_->CreateObjectStore(transaction_->id(),
    286                          store_id,
    287                          ASCIIToUTF16("store"),
    288                          IndexedDBKeyPath(),
    289                          false /*auto_increment*/);
    290   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    291   RunPostedTasks();
    292   transaction_->Commit();
    293   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    294 }
    295 
    296 TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) {
    297   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    298   const int64 store_id = 1001;
    299   db_->CreateObjectStore(transaction_->id(),
    300                          store_id,
    301                          ASCIIToUTF16("store"),
    302                          IndexedDBKeyPath(),
    303                          false /*auto_increment*/);
    304   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    305   const int64 index_id = 2002;
    306   db_->CreateIndex(transaction_->id(),
    307                    store_id,
    308                    index_id,
    309                    ASCIIToUTF16("index"),
    310                    IndexedDBKeyPath(),
    311                    false /*unique*/,
    312                    false /*multi_entry*/);
    313   EXPECT_EQ(
    314       1ULL,
    315       db_->metadata().object_stores.find(store_id)->second.indexes.size());
    316   RunPostedTasks();
    317   transaction_->Commit();
    318   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    319   EXPECT_EQ(
    320       1ULL,
    321       db_->metadata().object_stores.find(store_id)->second.indexes.size());
    322 }
    323 
    324 class IndexedDBDatabaseOperationAbortTest
    325     : public IndexedDBDatabaseOperationTest {
    326  public:
    327   IndexedDBDatabaseOperationAbortTest() {
    328     commit_success_ = leveldb::Status::NotFound("Bummer.");
    329   }
    330 
    331  private:
    332   DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationAbortTest);
    333 };
    334 
    335 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) {
    336   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    337   const int64 store_id = 1001;
    338   db_->CreateObjectStore(transaction_->id(),
    339                          store_id,
    340                          ASCIIToUTF16("store"),
    341                          IndexedDBKeyPath(),
    342                          false /*auto_increment*/);
    343   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    344   RunPostedTasks();
    345   transaction_->Commit();
    346   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    347 }
    348 
    349 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) {
    350   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    351   const int64 store_id = 1001;
    352   db_->CreateObjectStore(transaction_->id(),
    353                          store_id,
    354                          ASCIIToUTF16("store"),
    355                          IndexedDBKeyPath(),
    356                          false /*auto_increment*/);
    357   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    358   const int64 index_id = 2002;
    359   db_->CreateIndex(transaction_->id(),
    360                    store_id,
    361                    index_id,
    362                    ASCIIToUTF16("index"),
    363                    IndexedDBKeyPath(),
    364                    false /*unique*/,
    365                    false /*multi_entry*/);
    366   EXPECT_EQ(
    367       1ULL,
    368       db_->metadata().object_stores.find(store_id)->second.indexes.size());
    369   RunPostedTasks();
    370   transaction_->Commit();
    371   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    372 }
    373 
    374 TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) {
    375   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    376   const int64 store_id = 1001;
    377 
    378   // Creation is synchronous.
    379   db_->CreateObjectStore(transaction_->id(),
    380                          store_id,
    381                          ASCIIToUTF16("store"),
    382                          IndexedDBKeyPath(),
    383                          false /*auto_increment*/);
    384   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    385 
    386 
    387   // Put is asynchronous
    388   IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>());
    389   ScopedVector<webkit_blob::BlobDataHandle> handles;
    390   scoped_ptr<IndexedDBKey> key(new IndexedDBKey("key"));
    391   std::vector<IndexedDBDatabase::IndexKeys> index_keys;
    392   scoped_refptr<MockIndexedDBCallbacks> request(
    393       new MockIndexedDBCallbacks(false));
    394   db_->Put(transaction_->id(),
    395            store_id,
    396            &value,
    397            &handles,
    398            key.Pass(),
    399            IndexedDBDatabase::ADD_ONLY,
    400            request,
    401            index_keys);
    402 
    403   // Deletion is asynchronous.
    404   db_->DeleteObjectStore(transaction_->id(),
    405                          store_id);
    406   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
    407 
    408   // This will execute the Put then Delete.
    409   RunPostedTasks();
    410   EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
    411 
    412   transaction_->Commit();  // Cleans up the object hierarchy.
    413 }
    414 
    415 }  // namespace content
    416