// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/indexed_db/indexed_db_transaction.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/indexed_db/indexed_db_fake_backing_store.h" #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { namespace { class IndexedDBTransactionTest : public testing::Test { public: IndexedDBTransactionTest() { IndexedDBFactory* factory = NULL; backing_store_ = new IndexedDBFakeBackingStore(); db_ = IndexedDBDatabase::Create(base::ASCIIToUTF16("db"), backing_store_, factory, IndexedDBDatabase::Identifier()); } void RunPostedTasks() { message_loop_.RunUntilIdle(); } void DummyOperation(IndexedDBTransaction* transaction) {} protected: scoped_refptr backing_store_; scoped_refptr db_; private: base::MessageLoop message_loop_; DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest); }; TEST_F(IndexedDBTransactionTest, Timeout) { const int64 id = 0; const std::set scope; const bool commit_success = true; scoped_refptr transaction = new IndexedDBTransaction( id, new MockIndexedDBDatabaseCallbacks(), scope, indexed_db::TRANSACTION_READ_ONLY, db_, new IndexedDBFakeBackingStore::FakeTransaction(commit_success)); db_->TransactionCreated(transaction); // No conflicting transactions, so coordinator will start it immediately: EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state()); EXPECT_FALSE(transaction->IsTimeoutTimerRunning()); // Schedule a task - timer won't be started until it's processed. transaction->ScheduleTask(base::Bind( &IndexedDBTransactionTest::DummyOperation, base::Unretained(this))); EXPECT_FALSE(transaction->IsTimeoutTimerRunning()); RunPostedTasks(); EXPECT_TRUE(transaction->IsTimeoutTimerRunning()); // Abort should cancel the timer. transaction->Abort(); EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state()); EXPECT_FALSE(transaction->IsTimeoutTimerRunning()); // This task will be ignored. transaction->ScheduleTask(base::Bind( &IndexedDBTransactionTest::DummyOperation, base::Unretained(this))); EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state()); EXPECT_FALSE(transaction->IsTimeoutTimerRunning()); } class AbortObserver { public: AbortObserver() : abort_task_called_(false) {} void AbortTask(IndexedDBTransaction* transaction) { abort_task_called_ = true; } bool abort_task_called() const { return abort_task_called_; } private: bool abort_task_called_; DISALLOW_COPY_AND_ASSIGN(AbortObserver); }; TEST_F(IndexedDBTransactionTest, AbortTasks) { const int64 id = 0; const std::set scope; const bool commit_failure = false; scoped_refptr transaction = new IndexedDBTransaction( id, new MockIndexedDBDatabaseCallbacks(), scope, indexed_db::TRANSACTION_READ_ONLY, db_, new IndexedDBFakeBackingStore::FakeTransaction(commit_failure)); db_->TransactionCreated(transaction); AbortObserver observer; transaction->ScheduleTask( base::Bind(&IndexedDBTransactionTest::DummyOperation, base::Unretained(this)), base::Bind(&AbortObserver::AbortTask, base::Unretained(&observer))); // Pump the message loop so that the transaction completes all pending tasks, // otherwise it will defer the commit. base::MessageLoop::current()->RunUntilIdle(); EXPECT_FALSE(observer.abort_task_called()); transaction->Commit(); EXPECT_TRUE(observer.abort_task_called()); } } // namespace } // namespace content