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 <stdint.h>
8 #include <memory>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/debug/stack_trace.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/run_loop.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/test/bind_test_util.h"
18 #include "base/test/task_environment.h"
19 #include "components/services/storage/indexed_db/scopes/disjoint_range_lock_manager.h"
20 #include "content/browser/indexed_db/fake_indexed_db_metadata_coding.h"
21 #include "content/browser/indexed_db/indexed_db_class_factory.h"
22 #include "content/browser/indexed_db/indexed_db_connection.h"
23 #include "content/browser/indexed_db/indexed_db_database_error.h"
24 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
25 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
26 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
27 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
28 #include "content/browser/indexed_db/indexed_db_observer.h"
29 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
30 #include "content/browser/indexed_db/mock_indexed_db_factory.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
33 
34 namespace content {
35 namespace indexed_db_transaction_unittest {
36 namespace {
37 
SetToTrue(bool * value)38 void SetToTrue(bool* value) {
39   *value = true;
40 }
41 
42 }  // namespace
43 
44 class AbortObserver {
45  public:
AbortObserver()46   AbortObserver() : abort_task_called_(false) {}
47 
AbortTask()48   void AbortTask() { abort_task_called_ = true; }
49 
abort_task_called() const50   bool abort_task_called() const { return abort_task_called_; }
51 
52  private:
53   bool abort_task_called_;
54   DISALLOW_COPY_AND_ASSIGN(AbortObserver);
55 };
56 
57 class IndexedDBTransactionTest : public testing::Test {
58  public:
IndexedDBTransactionTest()59   IndexedDBTransactionTest()
60       : task_environment_(std::make_unique<base::test::TaskEnvironment>()),
61         backing_store_(new IndexedDBFakeBackingStore()),
62         factory_(new MockIndexedDBFactory()),
63         lock_manager_(kIndexedDBLockLevelCount) {}
64 
SetUp()65   void SetUp() override {
66     // DB is created here instead of the constructor to workaround a
67     // "peculiarity of C++". More info at
68     // https://github.com/google/googletest/blob/master/googletest/docs/FAQ.md#my-compiler-complains-that-a-constructor-or-destructor-cannot-return-a-value-whats-going-on
69     leveldb::Status s;
70     std::tie(db_, s) = IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
71         base::ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
72         CreateRunTasksCallback(),
73         std::make_unique<FakeIndexedDBMetadataCoding>(),
74         IndexedDBDatabase::Identifier(), &lock_manager_);
75     ASSERT_TRUE(s.ok());
76   }
77 
CreateRunTasksCallback()78   TasksAvailableCallback CreateRunTasksCallback() {
79     return base::BindRepeating(&IndexedDBTransactionTest::RunTasksForDatabase,
80                                base::Unretained(this), true);
81   }
82 
RunTasksForDatabase(bool async)83   void RunTasksForDatabase(bool async) {
84     if (!db_)
85       return;
86     if (async) {
87       base::SequencedTaskRunnerHandle::Get()->PostTask(
88           FROM_HERE,
89           base::BindOnce(&IndexedDBTransactionTest::RunTasksForDatabase,
90                          base::Unretained(this), false));
91       return;
92     }
93     IndexedDBDatabase::RunTasksResult result;
94     leveldb::Status status;
95     std::tie(result, status) = db_->RunTasks();
96     switch (result) {
97       case IndexedDBDatabase::RunTasksResult::kDone:
98         return;
99       case IndexedDBDatabase::RunTasksResult::kError:
100         error_called_ = true;
101         return;
102       case IndexedDBDatabase::RunTasksResult::kCanBeDestroyed:
103         db_.reset();
104         return;
105     }
106   }
107 
RunPostedTasks()108   void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
109 
DummyOperation(leveldb::Status result,IndexedDBTransaction * transaction)110   leveldb::Status DummyOperation(leveldb::Status result,
111                                  IndexedDBTransaction* transaction) {
112     return result;
113   }
AbortableOperation(AbortObserver * observer,IndexedDBTransaction * transaction)114   leveldb::Status AbortableOperation(AbortObserver* observer,
115                                      IndexedDBTransaction* transaction) {
116     transaction->ScheduleAbortTask(
117         base::BindOnce(&AbortObserver::AbortTask, base::Unretained(observer)));
118     return leveldb::Status::OK();
119   }
120 
CreateConnection()121   std::unique_ptr<IndexedDBConnection> CreateConnection() {
122     auto connection = std::unique_ptr<
123         IndexedDBConnection>(std::make_unique<IndexedDBConnection>(
124         IndexedDBOriginStateHandle(), IndexedDBClassFactory::Get(),
125         db_->AsWeakPtr(), base::DoNothing(), base::DoNothing(),
126         new MockIndexedDBDatabaseCallbacks()));
127     db_->AddConnectionForTesting(connection.get());
128     return connection;
129   }
130 
lock_manager()131   DisjointRangeLockManager* lock_manager() { return &lock_manager_; }
132 
133  protected:
134   std::unique_ptr<base::test::TaskEnvironment> task_environment_;
135   std::unique_ptr<IndexedDBFakeBackingStore> backing_store_;
136   std::unique_ptr<IndexedDBDatabase> db_;
137   std::unique_ptr<MockIndexedDBFactory> factory_;
138 
139   bool error_called_ = false;
140 
141  private:
142   DisjointRangeLockManager lock_manager_;
143 
144   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTest);
145 };
146 
147 class IndexedDBTransactionTestMode
148     : public IndexedDBTransactionTest,
149       public testing::WithParamInterface<blink::mojom::IDBTransactionMode> {
150  public:
IndexedDBTransactionTestMode()151   IndexedDBTransactionTestMode() {}
152 
153  private:
154   DISALLOW_COPY_AND_ASSIGN(IndexedDBTransactionTestMode);
155 };
156 
TEST_F(IndexedDBTransactionTest,Timeout)157 TEST_F(IndexedDBTransactionTest, Timeout) {
158   const int64_t id = 0;
159   const std::set<int64_t> scope;
160   const leveldb::Status commit_success = leveldb::Status::OK();
161   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
162   IndexedDBTransaction* transaction = connection->CreateTransaction(
163       id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
164       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
165   db_->RegisterAndScheduleTransaction(transaction);
166 
167   // No conflicting transactions, so coordinator will start it immediately:
168   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
169   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
170   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
171   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
172 
173   // Schedule a task - timer won't be started until it's processed.
174   transaction->ScheduleTask(
175       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
176                      base::Unretained(this), leveldb::Status::OK()));
177   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
178   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
179   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
180 
181   RunPostedTasks();
182   EXPECT_TRUE(transaction->IsTimeoutTimerRunning());
183 
184   transaction->Timeout();
185   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
186   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
187   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
188   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
189 
190   // This task will be ignored.
191   transaction->ScheduleTask(
192       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
193                      base::Unretained(this), leveldb::Status::OK()));
194   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
195   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
196   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
197   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
198 }
199 
TEST_F(IndexedDBTransactionTest,TimeoutPreemptive)200 TEST_F(IndexedDBTransactionTest, TimeoutPreemptive) {
201   const int64_t id = 0;
202   const std::set<int64_t> scope;
203   const leveldb::Status commit_success = leveldb::Status::OK();
204   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
205   IndexedDBTransaction* transaction = connection->CreateTransaction(
206       id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
207       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
208   db_->RegisterAndScheduleTransaction(transaction);
209 
210   // No conflicting transactions, so coordinator will start it immediately:
211   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
212   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
213   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
214   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
215 
216   // Add a preemptive task.
217   transaction->ScheduleTask(
218       blink::mojom::IDBTaskType::Preemptive,
219       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
220                      base::Unretained(this), leveldb::Status::OK()));
221   transaction->AddPreemptiveEvent();
222 
223   EXPECT_TRUE(transaction->HasPendingTasks());
224   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
225   EXPECT_TRUE(transaction->task_queue_.empty());
226   EXPECT_FALSE(transaction->preemptive_task_queue_.empty());
227 
228   // Pump the message loop so that the transaction completes all pending tasks,
229   // otherwise it will defer the commit.
230   RunPostedTasks();
231   EXPECT_TRUE(transaction->HasPendingTasks());
232   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
233   EXPECT_TRUE(transaction->task_queue_.empty());
234   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
235 
236   // Schedule a task - timer won't be started until preemptive tasks are done.
237   transaction->ScheduleTask(
238       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
239                      base::Unretained(this), leveldb::Status::OK()));
240   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
241   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
242   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
243 
244   // This shouldn't do anything - the preemptive task is still lurking.
245   RunPostedTasks();
246   EXPECT_TRUE(transaction->HasPendingTasks());
247   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
248   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
249   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
250 
251   // Finish the preemptive task, which unblocks regular tasks.
252   transaction->DidCompletePreemptiveEvent();
253   // TODO(dmurph): Should this explicit call be necessary?
254   transaction->RunTasks();
255 
256   // The task's completion should start the timer.
257   EXPECT_FALSE(transaction->HasPendingTasks());
258   EXPECT_TRUE(transaction->IsTimeoutTimerRunning());
259   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
260   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
261 }
262 
TEST_F(IndexedDBTransactionTest,NoTimeoutReadOnly)263 TEST_F(IndexedDBTransactionTest, NoTimeoutReadOnly) {
264   const int64_t id = 0;
265   const std::set<int64_t> scope;
266   const leveldb::Status commit_success = leveldb::Status::OK();
267   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
268   IndexedDBTransaction* transaction = connection->CreateTransaction(
269       id, scope, blink::mojom::IDBTransactionMode::ReadOnly,
270       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
271   db_->RegisterAndScheduleTransaction(transaction);
272 
273   // No conflicting transactions, so coordinator will start it immediately:
274   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
275   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
276 
277   // Schedule a task - timer won't be started until it's processed.
278   transaction->ScheduleTask(
279       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
280                      base::Unretained(this), leveldb::Status::OK()));
281   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
282 
283   // Transaction is read-only, so no need to time it out.
284   RunPostedTasks();
285   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
286 
287   // Clean up to avoid leaks.
288   transaction->Abort(IndexedDBDatabaseError(
289       IndexedDBDatabaseError(blink::mojom::IDBException::kAbortError,
290                              "Transaction aborted by user.")));
291   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
292   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
293 }
294 
TEST_P(IndexedDBTransactionTestMode,ScheduleNormalTask)295 TEST_P(IndexedDBTransactionTestMode, ScheduleNormalTask) {
296   const int64_t id = 0;
297   const std::set<int64_t> scope;
298   const leveldb::Status commit_success = leveldb::Status::OK();
299   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
300   IndexedDBTransaction* transaction = connection->CreateTransaction(
301       id, scope, GetParam(),
302       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
303 
304   EXPECT_FALSE(transaction->HasPendingTasks());
305   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
306   EXPECT_TRUE(transaction->task_queue_.empty());
307   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
308   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
309   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
310 
311   db_->RegisterAndScheduleTransaction(transaction);
312 
313   EXPECT_FALSE(transaction->HasPendingTasks());
314   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
315   EXPECT_TRUE(transaction->task_queue_.empty());
316   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
317 
318   transaction->ScheduleTask(
319       blink::mojom::IDBTaskType::Normal,
320       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
321                      base::Unretained(this), leveldb::Status::OK()));
322 
323   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
324   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
325 
326   EXPECT_TRUE(transaction->HasPendingTasks());
327   EXPECT_FALSE(transaction->IsTaskQueueEmpty());
328   EXPECT_FALSE(transaction->task_queue_.empty());
329   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
330 
331   // Pump the message loop so that the transaction completes all pending tasks,
332   // otherwise it will defer the commit.
333   base::RunLoop().RunUntilIdle();
334   EXPECT_FALSE(transaction->HasPendingTasks());
335   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
336   EXPECT_TRUE(transaction->task_queue_.empty());
337   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
338   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
339   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
340   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
341 
342   transaction->SetCommitFlag();
343   RunPostedTasks();
344   EXPECT_EQ(0UL, connection->transactions().size());
345 }
346 
TEST_P(IndexedDBTransactionTestMode,TaskFails)347 TEST_P(IndexedDBTransactionTestMode, TaskFails) {
348   const int64_t id = 0;
349   const std::set<int64_t> scope;
350   const leveldb::Status commit_success = leveldb::Status::OK();
351   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
352   IndexedDBTransaction* transaction = connection->CreateTransaction(
353       id, scope, GetParam(),
354       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
355 
356   EXPECT_FALSE(transaction->HasPendingTasks());
357   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
358   EXPECT_TRUE(transaction->task_queue_.empty());
359   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
360   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
361   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
362 
363   db_->RegisterAndScheduleTransaction(transaction);
364 
365   EXPECT_FALSE(transaction->HasPendingTasks());
366   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
367   EXPECT_TRUE(transaction->task_queue_.empty());
368   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
369 
370   transaction->ScheduleTask(
371       blink::mojom::IDBTaskType::Normal,
372       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
373                      base::Unretained(this),
374                      leveldb::Status::IOError("error")));
375 
376   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
377   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
378 
379   EXPECT_TRUE(transaction->HasPendingTasks());
380   EXPECT_FALSE(transaction->IsTaskQueueEmpty());
381   EXPECT_FALSE(transaction->task_queue_.empty());
382   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
383 
384   // Pump the message loop so that the transaction completes all pending tasks,
385   // otherwise it will defer the commit.
386   base::RunLoop().RunUntilIdle();
387   EXPECT_FALSE(transaction->HasPendingTasks());
388   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
389   EXPECT_TRUE(transaction->task_queue_.empty());
390   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
391   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
392   EXPECT_EQ(1, transaction->diagnostics().tasks_scheduled);
393   EXPECT_EQ(1, transaction->diagnostics().tasks_completed);
394 
395   EXPECT_TRUE(error_called_);
396 
397   transaction->SetCommitFlag();
398   RunPostedTasks();
399   EXPECT_EQ(0UL, connection->transactions().size());
400 }
401 
TEST_F(IndexedDBTransactionTest,SchedulePreemptiveTask)402 TEST_F(IndexedDBTransactionTest, SchedulePreemptiveTask) {
403   const int64_t id = 0;
404   const std::set<int64_t> scope;
405   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
406   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
407   IndexedDBTransaction* transaction = connection->CreateTransaction(
408       id, scope, blink::mojom::IDBTransactionMode::VersionChange,
409       new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
410 
411   EXPECT_FALSE(transaction->HasPendingTasks());
412   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
413   EXPECT_TRUE(transaction->task_queue_.empty());
414   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
415   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
416   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
417 
418   db_->RegisterAndScheduleTransaction(transaction);
419 
420   EXPECT_FALSE(transaction->HasPendingTasks());
421   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
422   EXPECT_TRUE(transaction->task_queue_.empty());
423   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
424 
425   transaction->ScheduleTask(
426       blink::mojom::IDBTaskType::Preemptive,
427       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
428                      base::Unretained(this), leveldb::Status::OK()));
429   transaction->AddPreemptiveEvent();
430 
431   EXPECT_TRUE(transaction->HasPendingTasks());
432   EXPECT_FALSE(transaction->IsTaskQueueEmpty());
433   EXPECT_TRUE(transaction->task_queue_.empty());
434   EXPECT_FALSE(transaction->preemptive_task_queue_.empty());
435 
436   // Pump the message loop so that the transaction completes all pending tasks,
437   // otherwise it will defer the commit.
438   base::RunLoop().RunUntilIdle();
439   EXPECT_TRUE(transaction->HasPendingTasks());
440   EXPECT_TRUE(transaction->IsTaskQueueEmpty());
441   EXPECT_TRUE(transaction->task_queue_.empty());
442   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
443   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
444   EXPECT_EQ(0, transaction->diagnostics().tasks_scheduled);
445   EXPECT_EQ(0, transaction->diagnostics().tasks_completed);
446 
447   transaction->DidCompletePreemptiveEvent();
448   transaction->SetCommitFlag();
449   RunPostedTasks();
450   EXPECT_TRUE(error_called_);
451 }
452 
TEST_P(IndexedDBTransactionTestMode,AbortTasks)453 TEST_P(IndexedDBTransactionTestMode, AbortTasks) {
454   const int64_t id = 0;
455   const std::set<int64_t> scope;
456   const leveldb::Status commit_failure = leveldb::Status::Corruption("Ouch.");
457   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
458   IndexedDBTransaction* transaction = connection->CreateTransaction(
459       id, scope, GetParam(),
460       new IndexedDBFakeBackingStore::FakeTransaction(commit_failure));
461   db_->RegisterAndScheduleTransaction(transaction);
462 
463   AbortObserver observer;
464   transaction->ScheduleTask(
465       base::BindOnce(&IndexedDBTransactionTest::AbortableOperation,
466                      base::Unretained(this), base::Unretained(&observer)));
467 
468   // Pump the message loop so that the transaction completes all pending tasks,
469   // otherwise it will defer the commit.
470   base::RunLoop().RunUntilIdle();
471 
472   EXPECT_FALSE(observer.abort_task_called());
473   transaction->SetCommitFlag();
474   RunPostedTasks();
475   EXPECT_TRUE(observer.abort_task_called());
476   EXPECT_TRUE(error_called_);
477 }
478 
TEST_P(IndexedDBTransactionTestMode,AbortPreemptive)479 TEST_P(IndexedDBTransactionTestMode, AbortPreemptive) {
480   const int64_t id = 0;
481   const std::set<int64_t> scope;
482   const leveldb::Status commit_success = leveldb::Status::OK();
483   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
484   IndexedDBTransaction* transaction = connection->CreateTransaction(
485       id, scope, GetParam(),
486       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
487   db_->RegisterAndScheduleTransaction(transaction);
488 
489   // No conflicting transactions, so coordinator will start it immediately:
490   EXPECT_EQ(IndexedDBTransaction::STARTED, transaction->state());
491   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
492 
493   transaction->ScheduleTask(
494       blink::mojom::IDBTaskType::Preemptive,
495       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
496                      base::Unretained(this), leveldb::Status::OK()));
497   EXPECT_EQ(0, transaction->pending_preemptive_events_);
498   transaction->AddPreemptiveEvent();
499   EXPECT_EQ(1, transaction->pending_preemptive_events_);
500 
501   RunPostedTasks();
502 
503   transaction->Abort(IndexedDBDatabaseError(
504       blink::mojom::IDBException::kAbortError, "Transaction aborted by user."));
505   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
506   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
507   EXPECT_EQ(0, transaction->pending_preemptive_events_);
508   EXPECT_TRUE(transaction->preemptive_task_queue_.empty());
509   EXPECT_TRUE(transaction->task_queue_.empty());
510   EXPECT_FALSE(transaction->HasPendingTasks());
511   EXPECT_EQ(transaction->diagnostics().tasks_completed,
512             transaction->diagnostics().tasks_scheduled);
513   EXPECT_TRUE(transaction->backing_store_transaction_begun_);
514   EXPECT_TRUE(transaction->used_);
515   EXPECT_FALSE(transaction->is_commit_pending_);
516 
517   // This task will be ignored.
518   transaction->ScheduleTask(
519       base::BindOnce(&IndexedDBTransactionTest::DummyOperation,
520                      base::Unretained(this), leveldb::Status::OK()));
521   EXPECT_EQ(IndexedDBTransaction::FINISHED, transaction->state());
522   EXPECT_FALSE(transaction->IsTimeoutTimerRunning());
523   EXPECT_FALSE(transaction->HasPendingTasks());
524   EXPECT_EQ(transaction->diagnostics().tasks_completed,
525             transaction->diagnostics().tasks_scheduled);
526 }
527 
TEST_F(IndexedDBTransactionTest,IndexedDBObserver)528 TEST_F(IndexedDBTransactionTest, IndexedDBObserver) {
529   const int64_t id = 0;
530   const std::set<int64_t> scope;
531   const leveldb::Status commit_success = leveldb::Status::OK();
532   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
533   IndexedDBTransaction* transaction = connection->CreateTransaction(
534       id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
535       new IndexedDBFakeBackingStore::FakeTransaction(commit_success));
536   ASSERT_TRUE(transaction);
537   db_->RegisterAndScheduleTransaction(transaction);
538 
539   EXPECT_EQ(0UL, transaction->pending_observers_.size());
540   EXPECT_EQ(0UL, connection->active_observers().size());
541 
542   // Add observers to pending observer list.
543   const int32_t observer_id1 = 1, observer_id2 = 2;
544   IndexedDBObserver::Options options(false, false, false, 0U);
545   transaction->AddPendingObserver(observer_id1, options);
546   transaction->AddPendingObserver(observer_id2, options);
547   EXPECT_EQ(2UL, transaction->pending_observers_.size());
548   EXPECT_EQ(0UL, connection->active_observers().size());
549 
550   // Before commit, observer would be in pending list of transaction.
551   std::vector<int32_t> observer_to_remove1 = {observer_id1};
552   connection->RemoveObservers(observer_to_remove1);
553   EXPECT_EQ(1UL, transaction->pending_observers_.size());
554   EXPECT_EQ(0UL, connection->active_observers().size());
555 
556   // After commit, observer moved to connection's active observer.
557   transaction->SetCommitFlag();
558   RunPostedTasks();
559   EXPECT_EQ(0UL, connection->transactions().size());
560   EXPECT_EQ(1UL, connection->active_observers().size());
561 
562   // Observer does not exist, so no change to active_observers.
563   connection->RemoveObservers(observer_to_remove1);
564   EXPECT_EQ(1UL, connection->active_observers().size());
565 
566   // Observer removed from connection's active observer.
567   std::vector<int32_t> observer_to_remove2 = {observer_id2};
568   connection->RemoveObservers(observer_to_remove2);
569   EXPECT_EQ(0UL, connection->active_observers().size());
570 }
571 
572 static const blink::mojom::IDBTransactionMode kTestModes[] = {
573     blink::mojom::IDBTransactionMode::ReadOnly,
574     blink::mojom::IDBTransactionMode::ReadWrite,
575     blink::mojom::IDBTransactionMode::VersionChange};
576 
577 INSTANTIATE_TEST_SUITE_P(IndexedDBTransactions,
578                          IndexedDBTransactionTestMode,
579                          ::testing::ValuesIn(kTestModes));
580 
TEST_F(IndexedDBTransactionTest,AbortCancelsLockRequest)581 TEST_F(IndexedDBTransactionTest, AbortCancelsLockRequest) {
582   const int64_t id = 0;
583   const int64_t object_store_id = 1ll;
584   const std::set<int64_t> scope = {object_store_id};
585   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
586   IndexedDBTransaction* transaction = connection->CreateTransaction(
587       id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
588       new IndexedDBFakeBackingStore::FakeTransaction(leveldb::Status::OK()));
589 
590   // Acquire a lock to block the transaction's lock acquisition.
591   bool locks_recieved = false;
592   std::vector<ScopesLockManager::ScopeLockRequest> lock_requests;
593   lock_requests.emplace_back(kDatabaseRangeLockLevel, GetDatabaseLockRange(id),
594                              ScopesLockManager::LockType::kShared);
595   lock_requests.emplace_back(kObjectStoreRangeLockLevel,
596                              GetObjectStoreLockRange(id, object_store_id),
597                              ScopesLockManager::LockType::kExclusive);
598   ScopesLocksHolder temp_lock_receiver;
599   lock_manager()->AcquireLocks(lock_requests,
600                                temp_lock_receiver.weak_factory.GetWeakPtr(),
601                                base::BindOnce(SetToTrue, &locks_recieved));
602   EXPECT_TRUE(locks_recieved);
603 
604   // Register the transaction, which should request locks and wait for
605   // |temp_lock_receiver| to release the locks.
606   db_->RegisterAndScheduleTransaction(transaction);
607   EXPECT_EQ(transaction->state(), IndexedDBTransaction::CREATED);
608 
609   // Abort the transaction, which should cancel the
610   // RegisterAndScheduleTransaction() pending lock request.
611   transaction->Abort(
612       IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError));
613   EXPECT_EQ(transaction->state(), IndexedDBTransaction::FINISHED);
614 
615   // Clear |temp_lock_receiver| so we can test later that all locks have
616   // cleared.
617   temp_lock_receiver.locks.clear();
618 
619   // Verify that the locks are available for acquisition again, as the
620   // transaction should have cancelled its lock request.
621   locks_recieved = false;
622   lock_manager()->AcquireLocks(lock_requests,
623                                temp_lock_receiver.weak_factory.GetWeakPtr(),
624                                base::BindOnce(SetToTrue, &locks_recieved));
625   EXPECT_TRUE(locks_recieved);
626 }
627 
TEST_F(IndexedDBTransactionTest,PostedStartTaskRunAfterAbort)628 TEST_F(IndexedDBTransactionTest, PostedStartTaskRunAfterAbort) {
629   int64_t id = 0;
630   const int64_t object_store_id = 1ll;
631   const std::set<int64_t> scope = {object_store_id};
632   std::unique_ptr<IndexedDBConnection> connection = CreateConnection();
633 
634   IndexedDBTransaction* transaction1 = connection->CreateTransaction(
635       id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
636       new IndexedDBFakeBackingStore::FakeTransaction(leveldb::Status::OK()));
637 
638   db_->RegisterAndScheduleTransaction(transaction1);
639   EXPECT_EQ(transaction1->state(), IndexedDBTransaction::STARTED);
640 
641   // Register another transaction, which will block on the first transaction.
642   IndexedDBTransaction* transaction2 = connection->CreateTransaction(
643       ++id, scope, blink::mojom::IDBTransactionMode::ReadWrite,
644       new IndexedDBFakeBackingStore::FakeTransaction(leveldb::Status::OK()));
645 
646   db_->RegisterAndScheduleTransaction(transaction2);
647   EXPECT_EQ(transaction2->state(), IndexedDBTransaction::CREATED);
648 
649   // Flush posted tasks before making the Abort calls since there are
650   // posted RunTasksForDatabase() tasks which, if we waited to run them
651   // until after Abort is called, would destroy our transactions and mask
652   // a potential race condition.
653   RunPostedTasks();
654 
655   // Abort all of the transactions, which should cause the second transaction's
656   // posted Start() task to run.
657   connection->AbortAllTransactions(
658       IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError));
659 
660   EXPECT_EQ(transaction2->state(), IndexedDBTransaction::FINISHED);
661 
662   // Run tasks to ensure Start() is called but does not DCHECK.
663   RunPostedTasks();
664 
665   // It's not safe to check the state of the transaction at this point since it
666   // is freed when the IndexedDBDatabase::RunTasks call happens via the posted
667   // RunTasksForDatabase task.
668 }
669 
670 }  // namespace indexed_db_transaction_unittest
671 }  // namespace content
672