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_factory.h" 20 #include "content/browser/indexed_db/indexed_db_fake_backing_store.h" 21 #include "content/browser/indexed_db/indexed_db_transaction.h" 22 #include "content/browser/indexed_db/indexed_db_value.h" 23 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h" 24 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 using base::ASCIIToUTF16; 28 29 namespace { 30 const int kFakeChildProcessId = 0; 31 } 32 33 namespace content { 34 35 TEST(IndexedDBDatabaseTest, BackingStoreRetention) { 36 scoped_refptr<IndexedDBFakeBackingStore> backing_store = 37 new IndexedDBFakeBackingStore(); 38 EXPECT_TRUE(backing_store->HasOneRef()); 39 40 IndexedDBFactory* factory = 0; 41 leveldb::Status s; 42 scoped_refptr<IndexedDBDatabase> db = 43 IndexedDBDatabase::Create(ASCIIToUTF16("db"), 44 backing_store, 45 factory, 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 54 TEST(IndexedDBDatabaseTest, ConnectionLifecycle) { 55 scoped_refptr<IndexedDBFakeBackingStore> backing_store = 56 new IndexedDBFakeBackingStore(); 57 EXPECT_TRUE(backing_store->HasOneRef()); // local 58 59 IndexedDBFactory* factory = 0; 60 leveldb::Status s; 61 scoped_refptr<IndexedDBDatabase> db = 62 IndexedDBDatabase::Create(ASCIIToUTF16("db"), 63 backing_store, 64 factory, 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 112 TEST(IndexedDBDatabaseTest, ForcedClose) { 113 scoped_refptr<IndexedDBFakeBackingStore> backing_store = 114 new IndexedDBFakeBackingStore(); 115 EXPECT_TRUE(backing_store->HasOneRef()); 116 117 IndexedDBFactory* factory = 0; 118 leveldb::Status s; 119 scoped_refptr<IndexedDBDatabase> database = 120 IndexedDBDatabase::Create(ASCIIToUTF16("db"), 121 backing_store, 122 factory, 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, 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 indexed_db::TRANSACTION_READ_ONLY); 147 148 request->connection()->ForceClose(); 149 150 EXPECT_TRUE(backing_store->HasOneRef()); // local 151 EXPECT_TRUE(callbacks->abort_called()); 152 } 153 154 class 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 178 TEST(IndexedDBDatabaseTest, PendingDelete) { 179 scoped_refptr<IndexedDBFakeBackingStore> backing_store = 180 new IndexedDBFakeBackingStore(); 181 EXPECT_TRUE(backing_store->HasOneRef()); // local 182 183 IndexedDBFactory* factory = 0; 184 leveldb::Status s; 185 scoped_refptr<IndexedDBDatabase> db = 186 IndexedDBDatabase::Create(ASCIIToUTF16("db"), 187 backing_store, 188 factory, 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_TRUE(request2->blocked_called()); 212 EXPECT_FALSE(backing_store->HasOneRef()); // local and db 213 214 db->Close(request1->connection(), true /* forced */); 215 216 EXPECT_FALSE(db->backing_store()); 217 EXPECT_TRUE(backing_store->HasOneRef()); // local 218 EXPECT_TRUE(request2->success_called()); 219 } 220 221 void DummyOperation(IndexedDBTransaction* transaction) { 222 } 223 224 class IndexedDBDatabaseOperationTest : public testing::Test { 225 public: 226 IndexedDBDatabaseOperationTest() : commit_success_(leveldb::Status::OK()) {} 227 228 virtual void SetUp() { 229 backing_store_ = new IndexedDBFakeBackingStore(); 230 leveldb::Status s; 231 db_ = IndexedDBDatabase::Create(ASCIIToUTF16("db"), 232 backing_store_, 233 NULL /*factory*/, 234 IndexedDBDatabase::Identifier(), 235 &s); 236 ASSERT_TRUE(s.ok()); 237 238 request_ = new MockIndexedDBCallbacks(); 239 callbacks_ = new MockIndexedDBDatabaseCallbacks(); 240 const int64 transaction_id = 1; 241 db_->OpenConnection(IndexedDBPendingConnection( 242 request_, 243 callbacks_, 244 kFakeChildProcessId, 245 transaction_id, 246 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)); 247 EXPECT_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION, 248 db_->metadata().int_version); 249 250 transaction_ = new IndexedDBTransaction( 251 transaction_id, 252 callbacks_, 253 std::set<int64>() /*scope*/, 254 indexed_db::TRANSACTION_VERSION_CHANGE, 255 db_, 256 new IndexedDBFakeBackingStore::FakeTransaction(commit_success_)); 257 db_->TransactionCreated(transaction_); 258 259 // Add a dummy task which takes the place of the VersionChangeOperation 260 // which kicks off the upgrade. This ensures that the transaction has 261 // processed at least one task before the CreateObjectStore call. 262 transaction_->ScheduleTask(base::Bind(&DummyOperation)); 263 } 264 265 void RunPostedTasks() { base::RunLoop().RunUntilIdle(); } 266 267 protected: 268 scoped_refptr<IndexedDBFakeBackingStore> backing_store_; 269 scoped_refptr<IndexedDBDatabase> db_; 270 scoped_refptr<MockIndexedDBCallbacks> request_; 271 scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_; 272 scoped_refptr<IndexedDBTransaction> transaction_; 273 274 leveldb::Status commit_success_; 275 276 private: 277 base::MessageLoop message_loop_; 278 279 DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest); 280 }; 281 282 TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) { 283 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 284 const int64 store_id = 1001; 285 db_->CreateObjectStore(transaction_->id(), 286 store_id, 287 ASCIIToUTF16("store"), 288 IndexedDBKeyPath(), 289 false /*auto_increment*/); 290 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 291 RunPostedTasks(); 292 transaction_->Commit(); 293 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 294 } 295 296 TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) { 297 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 298 const int64 store_id = 1001; 299 db_->CreateObjectStore(transaction_->id(), 300 store_id, 301 ASCIIToUTF16("store"), 302 IndexedDBKeyPath(), 303 false /*auto_increment*/); 304 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 305 const int64 index_id = 2002; 306 db_->CreateIndex(transaction_->id(), 307 store_id, 308 index_id, 309 ASCIIToUTF16("index"), 310 IndexedDBKeyPath(), 311 false /*unique*/, 312 false /*multi_entry*/); 313 EXPECT_EQ( 314 1ULL, 315 db_->metadata().object_stores.find(store_id)->second.indexes.size()); 316 RunPostedTasks(); 317 transaction_->Commit(); 318 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 319 EXPECT_EQ( 320 1ULL, 321 db_->metadata().object_stores.find(store_id)->second.indexes.size()); 322 } 323 324 class IndexedDBDatabaseOperationAbortTest 325 : public IndexedDBDatabaseOperationTest { 326 public: 327 IndexedDBDatabaseOperationAbortTest() { 328 commit_success_ = leveldb::Status::NotFound("Bummer."); 329 } 330 331 private: 332 DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationAbortTest); 333 }; 334 335 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) { 336 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 337 const int64 store_id = 1001; 338 db_->CreateObjectStore(transaction_->id(), 339 store_id, 340 ASCIIToUTF16("store"), 341 IndexedDBKeyPath(), 342 false /*auto_increment*/); 343 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 344 RunPostedTasks(); 345 transaction_->Commit(); 346 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 347 } 348 349 TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) { 350 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 351 const int64 store_id = 1001; 352 db_->CreateObjectStore(transaction_->id(), 353 store_id, 354 ASCIIToUTF16("store"), 355 IndexedDBKeyPath(), 356 false /*auto_increment*/); 357 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 358 const int64 index_id = 2002; 359 db_->CreateIndex(transaction_->id(), 360 store_id, 361 index_id, 362 ASCIIToUTF16("index"), 363 IndexedDBKeyPath(), 364 false /*unique*/, 365 false /*multi_entry*/); 366 EXPECT_EQ( 367 1ULL, 368 db_->metadata().object_stores.find(store_id)->second.indexes.size()); 369 RunPostedTasks(); 370 transaction_->Commit(); 371 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 372 } 373 374 TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) { 375 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 376 const int64 store_id = 1001; 377 378 // Creation is synchronous. 379 db_->CreateObjectStore(transaction_->id(), 380 store_id, 381 ASCIIToUTF16("store"), 382 IndexedDBKeyPath(), 383 false /*auto_increment*/); 384 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 385 386 387 // Put is asynchronous 388 IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>()); 389 ScopedVector<webkit_blob::BlobDataHandle> handles; 390 scoped_ptr<IndexedDBKey> key(new IndexedDBKey("key")); 391 std::vector<IndexedDBDatabase::IndexKeys> index_keys; 392 scoped_refptr<MockIndexedDBCallbacks> request( 393 new MockIndexedDBCallbacks(false)); 394 db_->Put(transaction_->id(), 395 store_id, 396 &value, 397 &handles, 398 key.Pass(), 399 IndexedDBDatabase::ADD_ONLY, 400 request, 401 index_keys); 402 403 // Deletion is asynchronous. 404 db_->DeleteObjectStore(transaction_->id(), 405 store_id); 406 EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); 407 408 // This will execute the Put then Delete. 409 RunPostedTasks(); 410 EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); 411 412 transaction_->Commit(); // Cleans up the object hierarchy. 413 } 414 415 } // namespace content 416