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