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 16namespace content { 17 18class 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 33class 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 72class IndexedDBTransactionTestMode 73 : public IndexedDBTransactionTest, 74 public testing::WithParamInterface<blink::WebIDBTransactionMode> { 75 public: 76 IndexedDBTransactionTestMode() {} 77 private: 78 DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode); 79}; 80 81TEST_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 125TEST_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 157TEST_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 219TEST_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 280TEST_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 310TEST_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 361static const blink::WebIDBTransactionMode kTestModes[] = { 362 blink::WebIDBTransactionModeReadOnly, blink::WebIDBTransactionModeReadWrite, 363 blink::WebIDBTransactionModeVersionChange}; 364 365INSTANTIATE_TEST_CASE_P(IndexedDBTransactions, 366 IndexedDBTransactionTestMode, 367 ::testing::ValuesIn(kTestModes)); 368 369} // namespace content 370