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_transaction.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
     12 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
     13 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace content {
     17 
     18 class AbortObserver {
     19  public:
     20   AbortObserver() : abort_task_called_(false) {}
     21 
     22   void AbortTask(IndexedDBTransaction* transaction) {
     23     abort_task_called_ = true;
     24   }
     25 
     26   bool abort_task_called() const { return abort_task_called_; }
     27 
     28  private:
     29   bool abort_task_called_;
     30   DISALLOW_COPY_AND_ASSIGN(AbortObserver);
     31 };
     32 
     33 class IndexedDBTransactionTest : public testing::Test {
     34  public:
     35   IndexedDBTransactionTest() : factory_(new MockIndexedDBFactory()) {
     36     backing_store_ = new IndexedDBFakeBackingStore();
     37     CreateDB();
     38   }
     39 
     40   void CreateDB() {
     41     // DB is created here instead of the constructor to workaround a
     42     // "peculiarity of C++". More info at
     43     // https://code.google.com/p/googletest/wiki/FAQ#My_compiler_complains_that_a_constructor_(or_destructor)_cannot
     44     leveldb::Status s;
     45     db_ = IndexedDBDatabase::Create(base::ASCIIToUTF16("db"),
     46                                     backing_store_.get(),
     47                                     factory_.get(),
     48                                     IndexedDBDatabase::Identifier(),
     49                                     &s);
     50     ASSERT_TRUE(s.ok());
     51   }
     52 
     53   void RunPostedTasks() { message_loop_.RunUntilIdle(); }
     54   void DummyOperation(IndexedDBTransaction* transaction) {}
     55   void AbortableOperation(AbortObserver* observer,
     56                           IndexedDBTransaction* transaction) {
     57     transaction->ScheduleAbortTask(
     58         base::Bind(&AbortObserver::AbortTask, base::Unretained(observer)));
     59   }
     60 
     61  protected:
     62   scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
     63   scoped_refptr<IndexedDBDatabase> db_;
     64 
     65  private:
     66   base::MessageLoop message_loop_;
     67   scoped_refptr<MockIndexedDBFactory> factory_;
     68 
     69   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest);
     70 };
     71 
     72 class IndexedDBTransactionTestMode
     73     : public IndexedDBTransactionTest,
     74       public testing::WithParamInterface<blink::WebIDBTransactionMode> {
     75  public:
     76   IndexedDBTransactionTestMode() {}
     77  private:
     78   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode);
     79 };
     80 
     81 TEST_F(IndexedDBTransactionTest, Timeout) {
     82   const int64 id = 0;
     83   const std::set<int64> scope;
     84   const leveldb::Status commit_success = leveldb::Status::OK();
     85   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
     86       id,
     87       new MockIndexedDBDatabaseCallbacks(),
     88       scope,
     89       blink::WebIDBTransactionModeReadWrite,
     90       db_.get(),
     91       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
     92   db_->TransactionCreated(transaction.get());
     93 
     94   // No conflicting transactions, so coordinator will start it immediately:
     95   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
     96   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
     97   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
     98   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
     99 
    100   // Schedule a task - timer won't be started until it's processed.
    101   transaction->ScheduleTask(base::Bind(
    102       &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
    103   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    104   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    105   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    106 
    107   RunPostedTasks();
    108   EXPECT_TRUE(transaction->IsTimeoutTimerRunning());
    109 
    110   transaction->Timeout();
    111   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    112   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    113   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    114   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
    115 
    116   // This task will be ignored.
    117   transaction->ScheduleTask(base::Bind(
    118       &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
    119   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    120   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    121   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    122   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
    123 }
    124 
    125 TEST_F(IndexedDBTransactionTest, NoTimeoutReadOnly) {
    126   const int64 id = 0;
    127   const std::set<int64> scope;
    128   const leveldb::Status commit_success = leveldb::Status::OK();
    129   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
    130       id,
    131       new MockIndexedDBDatabaseCallbacks(),
    132       scope,
    133       blink::WebIDBTransactionModeReadOnly,
    134       db_.get(),
    135       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
    136   db_->TransactionCreated(transaction.get());
    137 
    138   // No conflicting transactions, so coordinator will start it immediately:
    139   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
    140   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    141 
    142   // Schedule a task - timer won't be started until it's processed.
    143   transaction->ScheduleTask(base::Bind(
    144       &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
    145   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    146 
    147   // Transaction is read-only, so no need to time it out.
    148   RunPostedTasks();
    149   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    150 
    151   // Clean up to avoid leaks.
    152   transaction->Abort();
    153   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    154   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    155 }
    156 
    157 TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) {
    158   const int64 id = 0;
    159   const std::set<int64> scope;
    160   const leveldb::Status commit_success = leveldb::Status::OK();
    161   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
    162       id,
    163       new MockIndexedDBDatabaseCallbacks(),
    164       scope,
    165       GetParam(),
    166       db_.get(),
    167       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
    168 
    169   EXPECT_FALSE(transaction->HasPendingTasks());
    170   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    171   EXPECT_TRUE(transaction->task_queue_.empty());
    172   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    173   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
    174   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    175 
    176   db_->TransactionCreated(transaction.get());
    177 
    178   EXPECT_FALSE(transaction->HasPendingTasks());
    179   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    180   EXPECT_TRUE(transaction->task_queue_.empty());
    181   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    182 
    183   transaction->ScheduleTask(
    184       blink::WebIDBTaskTypeNormal,
    185       base::Bind(&IndexedDBTransactionTest::DummyOperation,
    186                  base::Unretained(this)));
    187 
    188   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    189   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    190 
    191   EXPECT_TRUE(transaction->HasPendingTasks());
    192   EXPECT_FALSE(transaction->IsTaskQueueEmpty());
    193   EXPECT_FALSE(transaction->task_queue_.empty());
    194   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    195 
    196   // Pump the message loop so that the transaction completes all pending tasks,
    197   // otherwise it will defer the commit.
    198   base::MessageLoop::current()->RunUntilIdle();
    199   EXPECT_FALSE(transaction->HasPendingTasks());
    200   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    201   EXPECT_TRUE(transaction->task_queue_.empty());
    202   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    203   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
    204   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    205   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
    206 
    207   transaction->Commit();
    208 
    209   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    210   EXPECT_FALSE(transaction->HasPendingTasks());
    211   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    212   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    213   EXPECT_TRUE(transaction->task_queue_.empty());
    214   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    215   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
    216   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
    217 }
    218 
    219 TEST_F(IndexedDBTransactionTest, SchedulePreemptiveTask) {
    220   const int64 id = 0;
    221   const std::set<int64> scope;
    222   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
    223   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
    224       id,
    225       new MockIndexedDBDatabaseCallbacks(),
    226       scope,
    227       blink::WebIDBTransactionModeVersionChange,
    228       db_.get(),
    229       new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
    230 
    231   EXPECT_FALSE(transaction->HasPendingTasks());
    232   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    233   EXPECT_TRUE(transaction->task_queue_.empty());
    234   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    235   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
    236   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    237 
    238   db_->TransactionCreated(transaction.get());
    239 
    240   EXPECT_FALSE(transaction->HasPendingTasks());
    241   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    242   EXPECT_TRUE(transaction->task_queue_.empty());
    243   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    244 
    245   transaction->ScheduleTask(
    246       blink::WebIDBTaskTypePreemptive,
    247       base::Bind(&IndexedDBTransactionTest::DummyOperation,
    248                  base::Unretained(this)));
    249   transaction->AddPreemptiveEvent();
    250 
    251   EXPECT_TRUE(transaction->HasPendingTasks());
    252   EXPECT_FALSE(transaction->IsTaskQueueEmpty());
    253   EXPECT_TRUE(transaction->task_queue_.empty());
    254   EXPECT_FALSE(transaction->preemptive_task_queue_.empty());
    255 
    256   // Pump the message loop so that the transaction completes all pending tasks,
    257   // otherwise it will defer the commit.
    258   base::MessageLoop::current()->RunUntilIdle();
    259   EXPECT_TRUE(transaction->HasPendingTasks());
    260   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    261   EXPECT_TRUE(transaction->task_queue_.empty());
    262   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    263   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
    264   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
    265   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    266 
    267   transaction->DidCompletePreemptiveEvent();
    268   transaction->Commit();
    269 
    270   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    271   EXPECT_FALSE(transaction->HasPendingTasks());
    272   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    273   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
    274   EXPECT_TRUE(transaction->task_queue_.empty());
    275   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    276   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
    277   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
    278 }
    279 
    280 TEST_P(IndexedDBTransactionTestMode, AbortTasks) {
    281   const int64 id = 0;
    282   const std::set<int64> scope;
    283   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
    284   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
    285       id,
    286       new MockIndexedDBDatabaseCallbacks(),
    287       scope,
    288       GetParam(),
    289       db_.get(),
    290       new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
    291   db_->TransactionCreated(transaction.get());
    292 
    293   AbortObserver observer;
    294   transaction->ScheduleTask(
    295       base::Bind(&IndexedDBTransactionTest::AbortableOperation,
    296                  base::Unretained(this),
    297                  base::Unretained(&observer)));
    298 
    299   // Pump the message loop so that the transaction completes all pending tasks,
    300   // otherwise it will defer the commit.
    301   base::MessageLoop::current()->RunUntilIdle();
    302 
    303   EXPECT_FALSE(observer.abort_task_called());
    304   transaction->Commit();
    305   EXPECT_TRUE(observer.abort_task_called());
    306   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    307   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    308 }
    309 
    310 TEST_P(IndexedDBTransactionTestMode, AbortPreemptive) {
    311   const int64 id = 0;
    312   const std::set<int64> scope;
    313   const leveldb::Status commit_success = leveldb::Status::OK();
    314   scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
    315       id,
    316       new MockIndexedDBDatabaseCallbacks(),
    317       scope,
    318       GetParam(),
    319       db_.get(),
    320       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
    321   db_->TransactionCreated(transaction.get());
    322 
    323   // No conflicting transactions, so coordinator will start it immediately:
    324   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
    325   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    326 
    327   transaction->ScheduleTask(
    328       blink::WebIDBTaskTypePreemptive,
    329       base::Bind(&IndexedDBTransactionTest::DummyOperation,
    330                  base::Unretained(this)));
    331   EXPECT_EQ(0, transaction->pending_preemptive_events_);
    332   transaction->AddPreemptiveEvent();
    333   EXPECT_EQ(1, transaction->pending_preemptive_events_);
    334 
    335   RunPostedTasks();
    336 
    337   transaction->Abort();
    338   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    339   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    340   EXPECT_EQ(0, transaction->pending_preemptive_events_);
    341   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
    342   EXPECT_TRUE(transaction->task_queue_.empty());
    343   EXPECT_FALSE(transaction->HasPendingTasks());
    344   EXPECT_EQ(transaction->diagnostics().tasks_completed,
    345             transaction->diagnostics().tasks_scheduled);
    346   EXPECT_FALSE(transaction->should_process_queue_);
    347   EXPECT_TRUE(transaction->backing_store_transaction_begun_);
    348   EXPECT_TRUE(transaction->used_);
    349   EXPECT_FALSE(transaction->commit_pending_);
    350 
    351   // This task will be ignored.
    352   transaction->ScheduleTask(base::Bind(
    353       &IndexedDBTransactionTest::DummyOperation, base::Unretained(this)));
    354   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
    355   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
    356   EXPECT_FALSE(transaction->HasPendingTasks());
    357   EXPECT_EQ(transaction->diagnostics().tasks_completed,
    358             transaction->diagnostics().tasks_scheduled);
    359 }
    360 
    361 static const blink::WebIDBTransactionMode kTestModes[] = {
    362     blink::WebIDBTransactionModeReadOnly, blink::WebIDBTransactionModeReadWrite,
    363     blink::WebIDBTransactionModeVersionChange};
    364 
    365 INSTANTIATE_TEST_CASE_P(IndexedDBTransactions,
    366                         IndexedDBTransactionTestMode,
    367                         ::testing::ValuesIn(kTestModes));
    368 
    369 }  // namespace content
    370