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.
5 #include "content/browser/indexed_db/indexed_db_transaction.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"
20 AbortObserver() : abort_task_called_(false) {}
22 void AbortTask(IndexedDBTransaction
* transaction
) {
23 abort_task_called_
= true;
26 bool abort_task_called() const { return abort_task_called_
; }
29 bool abort_task_called_
;
30 DISALLOW_COPY_AND_ASSIGN(AbortObserver
);
33 class IndexedDBTransactionTest
: public testing::Test
{
35 IndexedDBTransactionTest() : factory_(new MockIndexedDBFactory()) {
36 backing_store_
= new IndexedDBFakeBackingStore();
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
45 db_
= IndexedDBDatabase::Create(base::ASCIIToUTF16("db"),
48 IndexedDBDatabase::Identifier(),
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
)));
62 scoped_refptr
<IndexedDBFakeBackingStore
> backing_store_
;
63 scoped_refptr
<IndexedDBDatabase
> db_
;
66 base::MessageLoop message_loop_
;
67 scoped_refptr
<MockIndexedDBFactory
> factory_
;
69 DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest
);
72 class IndexedDBTransactionTestMode
73 : public IndexedDBTransactionTest
,
74 public testing::WithParamInterface
<blink::WebIDBTransactionMode
> {
76 IndexedDBTransactionTestMode() {}
78 DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode
);
81 TEST_F(IndexedDBTransactionTest
, Timeout
) {
83 const std::set
<int64
> scope
;
84 const leveldb::Status commit_success
= leveldb::Status::OK();
85 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
87 new MockIndexedDBDatabaseCallbacks(),
89 blink::WebIDBTransactionModeReadWrite
,
91 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
92 db_
->TransactionCreated(transaction
.get());
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
);
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
);
108 EXPECT_TRUE(transaction
->IsTimeoutTimerRunning());
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
);
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
);
125 TEST_F(IndexedDBTransactionTest
, NoTimeoutReadOnly
) {
127 const std::set
<int64
> scope
;
128 const leveldb::Status commit_success
= leveldb::Status::OK();
129 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
131 new MockIndexedDBDatabaseCallbacks(),
133 blink::WebIDBTransactionModeReadOnly
,
135 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
136 db_
->TransactionCreated(transaction
.get());
138 // No conflicting transactions, so coordinator will start it immediately:
139 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
140 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
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());
147 // Transaction is read-only, so no need to time it out.
149 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
151 // Clean up to avoid leaks.
152 transaction
->Abort();
153 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
154 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
157 TEST_P(IndexedDBTransactionTestMode
, ScheduleNormalTask
) {
159 const std::set
<int64
> scope
;
160 const leveldb::Status commit_success
= leveldb::Status::OK();
161 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
163 new MockIndexedDBDatabaseCallbacks(),
167 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
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
);
176 db_
->TransactionCreated(transaction
.get());
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());
183 transaction
->ScheduleTask(
184 blink::WebIDBTaskTypeNormal
,
185 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
186 base::Unretained(this)));
188 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
189 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
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());
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
);
207 transaction
->Commit();
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
);
219 TEST_F(IndexedDBTransactionTest
, SchedulePreemptiveTask
) {
221 const std::set
<int64
> scope
;
222 const leveldb::Status commit_failure
= leveldb::Status::Corruption("Ouch.");
223 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
225 new MockIndexedDBDatabaseCallbacks(),
227 blink::WebIDBTransactionModeVersionChange
,
229 new IndexedDBFakeBackingStore::FakeTransaction(commit_failure
));
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
);
238 db_
->TransactionCreated(transaction
.get());
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());
245 transaction
->ScheduleTask(
246 blink::WebIDBTaskTypePreemptive
,
247 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
248 base::Unretained(this)));
249 transaction
->AddPreemptiveEvent();
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());
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
);
267 transaction
->DidCompletePreemptiveEvent();
268 transaction
->Commit();
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
);
280 TEST_P(IndexedDBTransactionTestMode
, AbortTasks
) {
282 const std::set
<int64
> scope
;
283 const leveldb::Status commit_failure
= leveldb::Status::Corruption("Ouch.");
284 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
286 new MockIndexedDBDatabaseCallbacks(),
290 new IndexedDBFakeBackingStore::FakeTransaction(commit_failure
));
291 db_
->TransactionCreated(transaction
.get());
293 AbortObserver observer
;
294 transaction
->ScheduleTask(
295 base::Bind(&IndexedDBTransactionTest::AbortableOperation
,
296 base::Unretained(this),
297 base::Unretained(&observer
)));
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();
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());
310 TEST_P(IndexedDBTransactionTestMode
, AbortPreemptive
) {
312 const std::set
<int64
> scope
;
313 const leveldb::Status commit_success
= leveldb::Status::OK();
314 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
316 new MockIndexedDBDatabaseCallbacks(),
320 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
321 db_
->TransactionCreated(transaction
.get());
323 // No conflicting transactions, so coordinator will start it immediately:
324 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
325 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
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_
);
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_
);
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
);
361 static const blink::WebIDBTransactionMode kTestModes
[] = {
362 blink::WebIDBTransactionModeReadOnly
, blink::WebIDBTransactionModeReadWrite
,
363 blink::WebIDBTransactionModeVersionChange
};
365 INSTANTIATE_TEST_CASE_P(IndexedDBTransactions
,
366 IndexedDBTransactionTestMode
,
367 ::testing::ValuesIn(kTestModes
));
369 } // namespace content