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 "testing/gtest/include/gtest/gtest.h"
17 class IndexedDBTransactionTest
: public testing::Test
{
19 IndexedDBTransactionTest() {
20 backing_store_
= new IndexedDBFakeBackingStore();
25 // DB is created here instead of the constructor to workaround a
26 // "peculiarity of C++". More info at
27 // https://code.google.com/p/googletest/wiki/FAQ#My_compiler_complains_that_a_constructor_(or_destructor)_cannot
28 IndexedDBFactory
* factory
= NULL
;
30 db_
= IndexedDBDatabase::Create(base::ASCIIToUTF16("db"),
33 IndexedDBDatabase::Identifier(),
38 void RunPostedTasks() { message_loop_
.RunUntilIdle(); }
39 void DummyOperation(IndexedDBTransaction
* transaction
) {}
42 scoped_refptr
<IndexedDBFakeBackingStore
> backing_store_
;
43 scoped_refptr
<IndexedDBDatabase
> db_
;
46 base::MessageLoop message_loop_
;
48 DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest
);
51 class IndexedDBTransactionTestMode
: public IndexedDBTransactionTest
,
52 public testing::WithParamInterface
<indexed_db::TransactionMode
> {
54 IndexedDBTransactionTestMode() {}
56 DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode
);
59 TEST_F(IndexedDBTransactionTest
, Timeout
) {
61 const std::set
<int64
> scope
;
62 const bool commit_success
= true;
63 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
65 new MockIndexedDBDatabaseCallbacks(),
67 indexed_db::TRANSACTION_READ_WRITE
,
69 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
70 db_
->TransactionCreated(transaction
);
72 // No conflicting transactions, so coordinator will start it immediately:
73 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
74 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
75 EXPECT_EQ(0, transaction
->diagnostics().tasks_scheduled
);
76 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
78 // Schedule a task - timer won't be started until it's processed.
79 transaction
->ScheduleTask(base::Bind(
80 &IndexedDBTransactionTest::DummyOperation
, base::Unretained(this)));
81 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
82 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
83 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
86 EXPECT_TRUE(transaction
->IsTimeoutTimerRunning());
88 transaction
->Timeout();
89 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
90 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
91 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
92 EXPECT_EQ(1, transaction
->diagnostics().tasks_completed
);
94 // This task will be ignored.
95 transaction
->ScheduleTask(base::Bind(
96 &IndexedDBTransactionTest::DummyOperation
, base::Unretained(this)));
97 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
98 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
99 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
100 EXPECT_EQ(1, transaction
->diagnostics().tasks_completed
);
103 TEST_F(IndexedDBTransactionTest
, NoTimeoutReadOnly
) {
105 const std::set
<int64
> scope
;
106 const bool commit_success
= true;
107 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
109 new MockIndexedDBDatabaseCallbacks(),
111 indexed_db::TRANSACTION_READ_ONLY
,
113 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
114 db_
->TransactionCreated(transaction
);
116 // No conflicting transactions, so coordinator will start it immediately:
117 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
118 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
120 // Schedule a task - timer won't be started until it's processed.
121 transaction
->ScheduleTask(base::Bind(
122 &IndexedDBTransactionTest::DummyOperation
, base::Unretained(this)));
123 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
125 // Transaction is read-only, so no need to time it out.
127 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
129 // Clean up to avoid leaks.
130 transaction
->Abort();
131 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
132 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
135 class AbortObserver
{
137 AbortObserver() : abort_task_called_(false) {}
139 void AbortTask(IndexedDBTransaction
* transaction
) {
140 abort_task_called_
= true;
143 bool abort_task_called() const { return abort_task_called_
; }
146 bool abort_task_called_
;
147 DISALLOW_COPY_AND_ASSIGN(AbortObserver
);
150 TEST_P(IndexedDBTransactionTestMode
, ScheduleNormalTask
) {
152 const std::set
<int64
> scope
;
153 const bool commit_failure
= false;
154 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
156 new MockIndexedDBDatabaseCallbacks(),
160 new IndexedDBFakeBackingStore::FakeTransaction(commit_failure
));
162 EXPECT_FALSE(transaction
->HasPendingTasks());
163 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
164 EXPECT_TRUE(transaction
->task_queue_
.empty());
165 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
166 EXPECT_EQ(0, transaction
->diagnostics().tasks_scheduled
);
167 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
169 db_
->TransactionCreated(transaction
);
171 EXPECT_FALSE(transaction
->HasPendingTasks());
172 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
173 EXPECT_TRUE(transaction
->task_queue_
.empty());
174 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
176 transaction
->ScheduleTask(
177 IndexedDBDatabase::NORMAL_TASK
,
178 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
179 base::Unretained(this)));
181 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
182 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
184 EXPECT_TRUE(transaction
->HasPendingTasks());
185 EXPECT_FALSE(transaction
->IsTaskQueueEmpty());
186 EXPECT_FALSE(transaction
->task_queue_
.empty());
187 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
189 // Pump the message loop so that the transaction completes all pending tasks,
190 // otherwise it will defer the commit.
191 base::MessageLoop::current()->RunUntilIdle();
192 EXPECT_FALSE(transaction
->HasPendingTasks());
193 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
194 EXPECT_TRUE(transaction
->task_queue_
.empty());
195 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
196 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
197 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
198 EXPECT_EQ(1, transaction
->diagnostics().tasks_completed
);
200 transaction
->Commit();
202 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
203 EXPECT_FALSE(transaction
->HasPendingTasks());
204 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
205 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
206 EXPECT_TRUE(transaction
->task_queue_
.empty());
207 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
208 EXPECT_EQ(1, transaction
->diagnostics().tasks_scheduled
);
209 EXPECT_EQ(1, transaction
->diagnostics().tasks_completed
);
212 TEST_F(IndexedDBTransactionTest
, SchedulePreemptiveTask
) {
214 const std::set
<int64
> scope
;
215 const bool commit_failure
= false;
216 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
218 new MockIndexedDBDatabaseCallbacks(),
220 indexed_db::TRANSACTION_VERSION_CHANGE
,
222 new IndexedDBFakeBackingStore::FakeTransaction(commit_failure
));
224 EXPECT_FALSE(transaction
->HasPendingTasks());
225 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
226 EXPECT_TRUE(transaction
->task_queue_
.empty());
227 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
228 EXPECT_EQ(0, transaction
->diagnostics().tasks_scheduled
);
229 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
231 db_
->TransactionCreated(transaction
);
233 EXPECT_FALSE(transaction
->HasPendingTasks());
234 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
235 EXPECT_TRUE(transaction
->task_queue_
.empty());
236 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
238 transaction
->ScheduleTask(
239 IndexedDBDatabase::PREEMPTIVE_TASK
,
240 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
241 base::Unretained(this)));
242 transaction
->AddPreemptiveEvent();
244 EXPECT_TRUE(transaction
->HasPendingTasks());
245 EXPECT_FALSE(transaction
->IsTaskQueueEmpty());
246 EXPECT_TRUE(transaction
->task_queue_
.empty());
247 EXPECT_FALSE(transaction
->preemptive_task_queue_
.empty());
249 // Pump the message loop so that the transaction completes all pending tasks,
250 // otherwise it will defer the commit.
251 base::MessageLoop::current()->RunUntilIdle();
252 EXPECT_TRUE(transaction
->HasPendingTasks());
253 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
254 EXPECT_TRUE(transaction
->task_queue_
.empty());
255 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
256 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
257 EXPECT_EQ(0, transaction
->diagnostics().tasks_scheduled
);
258 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
260 transaction
->DidCompletePreemptiveEvent();
261 transaction
->Commit();
263 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
264 EXPECT_FALSE(transaction
->HasPendingTasks());
265 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
266 EXPECT_TRUE(transaction
->IsTaskQueueEmpty());
267 EXPECT_TRUE(transaction
->task_queue_
.empty());
268 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
269 EXPECT_EQ(0, transaction
->diagnostics().tasks_scheduled
);
270 EXPECT_EQ(0, transaction
->diagnostics().tasks_completed
);
273 TEST_P(IndexedDBTransactionTestMode
, AbortTasks
) {
275 const std::set
<int64
> scope
;
276 const bool commit_failure
= false;
277 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
279 new MockIndexedDBDatabaseCallbacks(),
283 new IndexedDBFakeBackingStore::FakeTransaction(commit_failure
));
284 db_
->TransactionCreated(transaction
);
286 AbortObserver observer
;
287 transaction
->ScheduleTask(
288 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
289 base::Unretained(this)),
290 base::Bind(&AbortObserver::AbortTask
, base::Unretained(&observer
)));
292 // Pump the message loop so that the transaction completes all pending tasks,
293 // otherwise it will defer the commit.
294 base::MessageLoop::current()->RunUntilIdle();
296 EXPECT_FALSE(observer
.abort_task_called());
297 transaction
->Commit();
298 EXPECT_TRUE(observer
.abort_task_called());
299 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
300 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
303 TEST_P(IndexedDBTransactionTestMode
, AbortPreemptive
) {
305 const std::set
<int64
> scope
;
306 const bool commit_success
= true;
307 scoped_refptr
<IndexedDBTransaction
> transaction
= new IndexedDBTransaction(
309 new MockIndexedDBDatabaseCallbacks(),
313 new IndexedDBFakeBackingStore::FakeTransaction(commit_success
));
314 db_
->TransactionCreated(transaction
);
316 // No conflicting transactions, so coordinator will start it immediately:
317 EXPECT_EQ(IndexedDBTransaction::STARTED
, transaction
->state());
318 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
320 transaction
->ScheduleTask(
321 IndexedDBDatabase::PREEMPTIVE_TASK
,
322 base::Bind(&IndexedDBTransactionTest::DummyOperation
,
323 base::Unretained(this)));
324 EXPECT_EQ(0, transaction
->pending_preemptive_events_
);
325 transaction
->AddPreemptiveEvent();
326 EXPECT_EQ(1, transaction
->pending_preemptive_events_
);
330 transaction
->Abort();
331 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
332 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
333 EXPECT_EQ(0, transaction
->pending_preemptive_events_
);
334 EXPECT_TRUE(transaction
->preemptive_task_queue_
.empty());
335 EXPECT_TRUE(transaction
->task_queue_
.empty());
336 EXPECT_FALSE(transaction
->HasPendingTasks());
337 EXPECT_EQ(transaction
->diagnostics().tasks_completed
,
338 transaction
->diagnostics().tasks_scheduled
);
339 EXPECT_FALSE(transaction
->should_process_queue_
);
340 EXPECT_TRUE(transaction
->backing_store_transaction_begun_
);
341 EXPECT_TRUE(transaction
->used_
);
342 EXPECT_FALSE(transaction
->commit_pending_
);
344 // This task will be ignored.
345 transaction
->ScheduleTask(base::Bind(
346 &IndexedDBTransactionTest::DummyOperation
, base::Unretained(this)));
347 EXPECT_EQ(IndexedDBTransaction::FINISHED
, transaction
->state());
348 EXPECT_FALSE(transaction
->IsTimeoutTimerRunning());
349 EXPECT_FALSE(transaction
->HasPendingTasks());
350 EXPECT_EQ(transaction
->diagnostics().tasks_completed
,
351 transaction
->diagnostics().tasks_scheduled
);
354 static const indexed_db::TransactionMode kTestModes
[] = {
355 indexed_db::TRANSACTION_READ_ONLY
,
356 indexed_db::TRANSACTION_READ_WRITE
,
357 indexed_db::TRANSACTION_VERSION_CHANGE
360 INSTANTIATE_TEST_CASE_P(IndexedDBTransactions
,
361 IndexedDBTransactionTestMode
,
362 ::testing::ValuesIn(kTestModes
));
364 } // namespace content