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