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_database.h"
6
7#include <set>
8
9#include "base/auto_reset.h"
10#include "base/logging.h"
11#include "base/run_loop.h"
12#include "base/strings/string16.h"
13#include "base/strings/utf_string_conversions.h"
14#include "content/browser/indexed_db/indexed_db.h"
15#include "content/browser/indexed_db/indexed_db_backing_store.h"
16#include "content/browser/indexed_db/indexed_db_callbacks.h"
17#include "content/browser/indexed_db/indexed_db_connection.h"
18#include "content/browser/indexed_db/indexed_db_cursor.h"
19#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
20#include "content/browser/indexed_db/indexed_db_transaction.h"
21#include "content/browser/indexed_db/indexed_db_value.h"
22#include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
23#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
24#include "content/browser/indexed_db/mock_indexed_db_factory.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27using base::ASCIIToUTF16;
28
29namespace {
30const int kFakeChildProcessId = 0;
31}
32
33namespace content {
34
35TEST(IndexedDBDatabaseTest, BackingStoreRetention) {
36  scoped_refptr<IndexedDBFakeBackingStore> backing_store =
37      new IndexedDBFakeBackingStore();
38  EXPECT_TRUE(backing_store->HasOneRef());
39
40  scoped_refptr<MockIndexedDBFactory> factory = new MockIndexedDBFactory();
41  leveldb::Status s;
42  scoped_refptr<IndexedDBDatabase> db =
43      IndexedDBDatabase::Create(ASCIIToUTF16("db"),
44                                backing_store.get(),
45                                factory.get(),
46                                IndexedDBDatabase::Identifier(),
47                                &s);
48  ASSERT_TRUE(s.ok());
49  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
50  db = NULL;
51  EXPECT_TRUE(backing_store->HasOneRef());  // local
52}
53
54TEST(IndexedDBDatabaseTest, ConnectionLifecycle) {
55  scoped_refptr<IndexedDBFakeBackingStore> backing_store =
56      new IndexedDBFakeBackingStore();
57  EXPECT_TRUE(backing_store->HasOneRef());  // local
58
59  scoped_refptr<MockIndexedDBFactory> factory = new MockIndexedDBFactory();
60  leveldb::Status s;
61  scoped_refptr<IndexedDBDatabase> db =
62      IndexedDBDatabase::Create(ASCIIToUTF16("db"),
63                                backing_store.get(),
64                                factory.get(),
65                                IndexedDBDatabase::Identifier(),
66                                &s);
67  ASSERT_TRUE(s.ok());
68  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
69
70  scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
71  scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
72      new MockIndexedDBDatabaseCallbacks());
73  const int64 transaction_id1 = 1;
74  IndexedDBPendingConnection connection1(
75      request1,
76      callbacks1,
77      kFakeChildProcessId,
78      transaction_id1,
79      IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
80  db->OpenConnection(connection1);
81
82  EXPECT_FALSE(backing_store->HasOneRef());  // db, connection count > 0
83
84  scoped_refptr<MockIndexedDBCallbacks> request2(new MockIndexedDBCallbacks());
85  scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
86      new MockIndexedDBDatabaseCallbacks());
87  const int64 transaction_id2 = 2;
88  IndexedDBPendingConnection connection2(
89      request2,
90      callbacks2,
91      kFakeChildProcessId,
92      transaction_id2,
93      IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
94  db->OpenConnection(connection2);
95
96  EXPECT_FALSE(backing_store->HasOneRef());  // local and connection
97
98  request1->connection()->ForceClose();
99  EXPECT_FALSE(request1->connection()->IsConnected());
100
101  EXPECT_FALSE(backing_store->HasOneRef());  // local and connection
102
103  request2->connection()->ForceClose();
104  EXPECT_FALSE(request2->connection()->IsConnected());
105
106  EXPECT_TRUE(backing_store->HasOneRef());
107  EXPECT_FALSE(db->backing_store());
108
109  db = NULL;
110}
111
112TEST(IndexedDBDatabaseTest, ForcedClose) {
113  scoped_refptr<IndexedDBFakeBackingStore> backing_store =
114      new IndexedDBFakeBackingStore();
115  EXPECT_TRUE(backing_store->HasOneRef());
116
117  scoped_refptr<MockIndexedDBFactory> factory = new MockIndexedDBFactory();
118  leveldb::Status s;
119  scoped_refptr<IndexedDBDatabase> database =
120      IndexedDBDatabase::Create(ASCIIToUTF16("db"),
121                                backing_store.get(),
122                                factory.get(),
123                                IndexedDBDatabase::Identifier(),
124                                &s);
125  ASSERT_TRUE(s.ok());
126  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
127
128  scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks(
129      new MockIndexedDBDatabaseCallbacks());
130  scoped_refptr<MockIndexedDBCallbacks> request(new MockIndexedDBCallbacks());
131  const int64 upgrade_transaction_id = 3;
132  IndexedDBPendingConnection connection(
133      request,
134      callbacks,
135      kFakeChildProcessId,
136      upgrade_transaction_id,
137      IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
138  database->OpenConnection(connection);
139  EXPECT_EQ(database.get(), request->connection()->database());
140
141  const int64 transaction_id = 123;
142  const std::vector<int64> scope;
143  database->CreateTransaction(transaction_id,
144                              request->connection(),
145                              scope,
146                              blink::WebIDBTransactionModeReadOnly);
147
148  request->connection()->ForceClose();
149
150  EXPECT_TRUE(backing_store->HasOneRef());  // local
151  EXPECT_TRUE(callbacks->abort_called());
152}
153
154class MockDeleteCallbacks : public IndexedDBCallbacks {
155 public:
156  MockDeleteCallbacks()
157      : IndexedDBCallbacks(NULL, 0, 0),
158        blocked_called_(false),
159        success_called_(false) {}
160
161  virtual void OnBlocked(int64 existing_version) OVERRIDE {
162    blocked_called_ = true;
163  }
164  virtual void OnSuccess(int64 result) OVERRIDE { success_called_ = true; }
165
166  bool blocked_called() const { return blocked_called_; }
167  bool success_called() const { return success_called_; }
168
169 private:
170  virtual ~MockDeleteCallbacks() {}
171
172  bool blocked_called_;
173  bool success_called_;
174
175  DISALLOW_COPY_AND_ASSIGN(MockDeleteCallbacks);
176};
177
178TEST(IndexedDBDatabaseTest, PendingDelete) {
179  scoped_refptr<IndexedDBFakeBackingStore> backing_store =
180      new IndexedDBFakeBackingStore();
181  EXPECT_TRUE(backing_store->HasOneRef());  // local
182
183  scoped_refptr<MockIndexedDBFactory> factory = new MockIndexedDBFactory();
184  leveldb::Status s;
185  scoped_refptr<IndexedDBDatabase> db =
186      IndexedDBDatabase::Create(ASCIIToUTF16("db"),
187                                backing_store.get(),
188                                factory.get(),
189                                IndexedDBDatabase::Identifier(),
190                                &s);
191  ASSERT_TRUE(s.ok());
192  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
193
194  scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks());
195  scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
196      new MockIndexedDBDatabaseCallbacks());
197  const int64 transaction_id1 = 1;
198  IndexedDBPendingConnection connection(
199      request1,
200      callbacks1,
201      kFakeChildProcessId,
202      transaction_id1,
203      IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
204  db->OpenConnection(connection);
205
206  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
207
208  scoped_refptr<MockDeleteCallbacks> request2(new MockDeleteCallbacks());
209  db->DeleteDatabase(request2);
210
211  EXPECT_FALSE(request2->blocked_called());
212  db->VersionChangeIgnored();
213  EXPECT_TRUE(request2->blocked_called());
214
215  EXPECT_FALSE(backing_store->HasOneRef());  // local and db
216
217  db->Close(request1->connection(), true /* forced */);
218
219  EXPECT_FALSE(db->backing_store());
220  EXPECT_TRUE(backing_store->HasOneRef());  // local
221  EXPECT_TRUE(request2->success_called());
222}
223
224void DummyOperation(IndexedDBTransaction* transaction) {
225}
226
227class IndexedDBDatabaseOperationTest : public testing::Test {
228 public:
229  IndexedDBDatabaseOperationTest()
230      : commit_success_(leveldb::Status::OK()),
231        factory_(new MockIndexedDBFactory()) {}
232
233  virtual void SetUp() {
234    backing_store_ = new IndexedDBFakeBackingStore();
235    leveldb::Status s;
236    db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"),
237                                    backing_store_.get(),
238                                    factory_.get(),
239                                    IndexedDBDatabase::Identifier(),
240                                    &s);
241    ASSERT_TRUE(s.ok());
242
243    request_ = new MockIndexedDBCallbacks();
244    callbacks_ = new MockIndexedDBDatabaseCallbacks();
245    const int64 transaction_id = 1;
246    db_->OpenConnection(IndexedDBPendingConnection(
247        request_,
248        callbacks_,
249        kFakeChildProcessId,
250        transaction_id,
251        IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION));
252    EXPECT_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
253              db_->metadata().int_version);
254
255    transaction_ = new IndexedDBTransaction(
256        transaction_id,
257        callbacks_,
258        std::set<int64>() /*scope*/,
259        blink::WebIDBTransactionModeVersionChange,
260        db_.get(),
261        new IndexedDBFakeBackingStore::FakeTransaction(commit_success_));
262    db_->TransactionCreated(transaction_.get());
263
264    // Add a dummy task which takes the place of the VersionChangeOperation
265    // which kicks off the upgrade. This ensures that the transaction has
266    // processed at least one task before the CreateObjectStore call.
267    transaction_->ScheduleTask(base::Bind(&DummyOperation));
268  }
269
270  void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
271
272 protected:
273  scoped_refptr<IndexedDBFakeBackingStore> backing_store_;
274  scoped_refptr<IndexedDBDatabase> db_;
275  scoped_refptr<MockIndexedDBCallbacks> request_;
276  scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
277  scoped_refptr<IndexedDBTransaction> transaction_;
278
279  leveldb::Status commit_success_;
280
281 private:
282  base::MessageLoop message_loop_;
283  scoped_refptr<MockIndexedDBFactory> factory_;
284
285  DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest);
286};
287
288TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) {
289  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
290  const int64 store_id = 1001;
291  db_->CreateObjectStore(transaction_->id(),
292                         store_id,
293                         ASCIIToUTF16("store"),
294                         IndexedDBKeyPath(),
295                         false /*auto_increment*/);
296  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
297  RunPostedTasks();
298  transaction_->Commit();
299  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
300}
301
302TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) {
303  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
304  const int64 store_id = 1001;
305  db_->CreateObjectStore(transaction_->id(),
306                         store_id,
307                         ASCIIToUTF16("store"),
308                         IndexedDBKeyPath(),
309                         false /*auto_increment*/);
310  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
311  const int64 index_id = 2002;
312  db_->CreateIndex(transaction_->id(),
313                   store_id,
314                   index_id,
315                   ASCIIToUTF16("index"),
316                   IndexedDBKeyPath(),
317                   false /*unique*/,
318                   false /*multi_entry*/);
319  EXPECT_EQ(
320      1ULL,
321      db_->metadata().object_stores.find(store_id)->second.indexes.size());
322  RunPostedTasks();
323  transaction_->Commit();
324  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
325  EXPECT_EQ(
326      1ULL,
327      db_->metadata().object_stores.find(store_id)->second.indexes.size());
328}
329
330class IndexedDBDatabaseOperationAbortTest
331    : public IndexedDBDatabaseOperationTest {
332 public:
333  IndexedDBDatabaseOperationAbortTest() {
334    commit_success_ = leveldb::Status::NotFound("Bummer.");
335  }
336
337 private:
338  DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationAbortTest);
339};
340
341TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) {
342  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
343  const int64 store_id = 1001;
344  db_->CreateObjectStore(transaction_->id(),
345                         store_id,
346                         ASCIIToUTF16("store"),
347                         IndexedDBKeyPath(),
348                         false /*auto_increment*/);
349  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
350  RunPostedTasks();
351  transaction_->Commit();
352  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
353}
354
355TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) {
356  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
357  const int64 store_id = 1001;
358  db_->CreateObjectStore(transaction_->id(),
359                         store_id,
360                         ASCIIToUTF16("store"),
361                         IndexedDBKeyPath(),
362                         false /*auto_increment*/);
363  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
364  const int64 index_id = 2002;
365  db_->CreateIndex(transaction_->id(),
366                   store_id,
367                   index_id,
368                   ASCIIToUTF16("index"),
369                   IndexedDBKeyPath(),
370                   false /*unique*/,
371                   false /*multi_entry*/);
372  EXPECT_EQ(
373      1ULL,
374      db_->metadata().object_stores.find(store_id)->second.indexes.size());
375  RunPostedTasks();
376  transaction_->Commit();
377  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
378}
379
380TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) {
381  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
382  const int64 store_id = 1001;
383
384  // Creation is synchronous.
385  db_->CreateObjectStore(transaction_->id(),
386                         store_id,
387                         ASCIIToUTF16("store"),
388                         IndexedDBKeyPath(),
389                         false /*auto_increment*/);
390  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
391
392
393  // Put is asynchronous
394  IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>());
395  ScopedVector<storage::BlobDataHandle> handles;
396  scoped_ptr<IndexedDBKey> key(new IndexedDBKey("key"));
397  std::vector<IndexedDBDatabase::IndexKeys> index_keys;
398  scoped_refptr<MockIndexedDBCallbacks> request(
399      new MockIndexedDBCallbacks(false));
400  db_->Put(transaction_->id(),
401           store_id,
402           &value,
403           &handles,
404           key.Pass(),
405           blink::WebIDBPutModeAddOnly,
406           request,
407           index_keys);
408
409  // Deletion is asynchronous.
410  db_->DeleteObjectStore(transaction_->id(),
411                         store_id);
412  EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
413
414  // This will execute the Put then Delete.
415  RunPostedTasks();
416  EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
417
418  transaction_->Commit();  // Cleans up the object hierarchy.
419}
420
421}  // namespace content
422