| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/indexed_db/indexed_db_database.h" |
| |
| #include <stdint.h> |
| #include <set> |
| #include <utility> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/browser/indexed_db/fake_indexed_db_metadata_coding.h" |
| #include "content/browser/indexed_db/indexed_db.h" |
| #include "content/browser/indexed_db/indexed_db_backing_store.h" |
| #include "content/browser/indexed_db/indexed_db_callbacks.h" |
| #include "content/browser/indexed_db/indexed_db_class_factory.h" |
| #include "content/browser/indexed_db/indexed_db_connection.h" |
| #include "content/browser/indexed_db/indexed_db_cursor.h" |
| #include "content/browser/indexed_db/indexed_db_fake_backing_store.h" |
| #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" |
| #include "content/browser/indexed_db/indexed_db_transaction.h" |
| #include "content/browser/indexed_db/indexed_db_value.h" |
| #include "content/browser/indexed_db/mock_indexed_db_callbacks.h" |
| #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h" |
| #include "content/browser/indexed_db/mock_indexed_db_factory.h" |
| #include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::ASCIIToUTF16; |
| using blink::IndexedDBDatabaseMetadata; |
| using blink::IndexedDBIndexKeys; |
| using blink::IndexedDBKey; |
| using blink::IndexedDBKeyPath; |
| |
| namespace { |
| const int kFakeChildProcessId = 0; |
| } |
| |
| namespace content { |
| namespace { |
| |
| void CreateAndBindTransactionPlaceholder( |
| base::WeakPtr<IndexedDBTransaction> transaction) {} |
| |
| } // namespace |
| |
| class IndexedDBDatabaseTest : public ::testing::Test { |
| public: |
| IndexedDBDatabaseTest() : lock_manager_(kIndexedDBLockLevelCount) {} |
| |
| void SetUp() override { |
| backing_store_ = new IndexedDBFakeBackingStore(); |
| factory_ = new MockIndexedDBFactory(); |
| std::unique_ptr<FakeIndexedDBMetadataCoding> metadata_coding = |
| std::make_unique<FakeIndexedDBMetadataCoding>(); |
| metadata_coding_ = metadata_coding.get(); |
| EXPECT_TRUE(backing_store_->HasOneRef()); |
| leveldb::Status s; |
| |
| std::tie(db_, s) = IndexedDBDatabase::Create( |
| ASCIIToUTF16("db"), backing_store_.get(), factory_.get(), |
| std::move(metadata_coding), IndexedDBDatabase::Identifier(), |
| &lock_manager_); |
| ASSERT_TRUE(s.ok()); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| } |
| |
| protected: |
| scoped_refptr<IndexedDBFakeBackingStore> backing_store_; |
| scoped_refptr<MockIndexedDBFactory> factory_; |
| scoped_refptr<IndexedDBDatabase> db_; |
| FakeIndexedDBMetadataCoding* metadata_coding_; |
| |
| private: |
| TestBrowserThreadBundle thread_bundle_; |
| DisjointRangeLockManager lock_manager_; |
| }; |
| |
| TEST_F(IndexedDBDatabaseTest, BackingStoreRetention) { |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| db_ = nullptr; |
| EXPECT_TRUE(backing_store_->HasOneRef()); // local |
| } |
| |
| TEST_F(IndexedDBDatabaseTest, ConnectionLifecycle) { |
| scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks()); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id1 = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection1( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id1, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection1)); |
| |
| EXPECT_FALSE(backing_store_->HasOneRef()); // db, connection count > 0 |
| |
| scoped_refptr<MockIndexedDBCallbacks> request2(new MockIndexedDBCallbacks()); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id2 = 2; |
| auto create_transaction_callback2 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection2( |
| std::make_unique<IndexedDBPendingConnection>( |
| request2, callbacks2, kFakeChildProcessId, transaction_id2, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback2))); |
| db_->OpenConnection(std::move(connection2)); |
| |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and connection |
| |
| request1->connection()->ForceClose(); |
| EXPECT_FALSE(request1->connection()->IsConnected()); |
| |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and connection |
| |
| request2->connection()->ForceClose(); |
| EXPECT_FALSE(request2->connection()->IsConnected()); |
| |
| EXPECT_TRUE(backing_store_->HasOneRef()); |
| EXPECT_FALSE(db_->backing_store()); |
| |
| db_ = nullptr; |
| } |
| |
| TEST_F(IndexedDBDatabaseTest, ForcedClose) { |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks( |
| new MockIndexedDBDatabaseCallbacks()); |
| scoped_refptr<MockIndexedDBCallbacks> request(new MockIndexedDBCallbacks()); |
| const int64_t upgrade_transaction_id = 3; |
| auto create_transaction_callback = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection( |
| std::make_unique<IndexedDBPendingConnection>( |
| request, callbacks, kFakeChildProcessId, upgrade_transaction_id, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback))); |
| db_->OpenConnection(std::move(connection)); |
| EXPECT_EQ(db_.get(), request->connection()->database()); |
| |
| const int64_t transaction_id = 123; |
| const std::vector<int64_t> scope; |
| IndexedDBTransaction* transaction = request->connection()->CreateTransaction( |
| transaction_id, std::set<int64_t>(scope.begin(), scope.end()), |
| blink::mojom::IDBTransactionMode::ReadOnly, |
| new IndexedDBBackingStore::Transaction(backing_store_.get())); |
| db_->RegisterAndScheduleTransaction(transaction); |
| |
| request->connection()->ForceClose(); |
| |
| EXPECT_TRUE(backing_store_->HasOneRef()); // local |
| EXPECT_TRUE(callbacks->abort_called()); |
| } |
| |
| class MockCallbacks : public IndexedDBCallbacks { |
| public: |
| MockCallbacks() |
| : IndexedDBCallbacks(nullptr, |
| url::Origin(), |
| nullptr, |
| base::ThreadTaskRunnerHandle::Get()) {} |
| |
| void OnBlocked(int64_t existing_version) override { blocked_called_ = true; } |
| void OnSuccess(int64_t result) override { success_called_ = true; } |
| void OnError(const IndexedDBDatabaseError& error) override { |
| error_called_ = true; |
| } |
| |
| bool blocked_called() const { return blocked_called_; } |
| bool success_called() const { return success_called_; } |
| bool error_called() const { return error_called_; } |
| |
| private: |
| ~MockCallbacks() override {} |
| |
| bool blocked_called_ = false; |
| bool success_called_ = false; |
| bool error_called_ = false; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockCallbacks); |
| }; |
| |
| TEST_F(IndexedDBDatabaseTest, PendingDelete) { |
| scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks()); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id1 = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id1, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| scoped_refptr<MockCallbacks> request2(new MockCallbacks()); |
| db_->DeleteDatabase(request2, false /* force_delete */); |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| EXPECT_FALSE(request2->blocked_called()); |
| db_->VersionChangeIgnored(); |
| EXPECT_TRUE(request2->blocked_called()); |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| db_->Close(request1->connection(), true /* forced */); |
| EXPECT_EQ(db_->ConnectionCount(), 0UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| EXPECT_FALSE(db_->backing_store()); |
| EXPECT_TRUE(backing_store_->HasOneRef()); // local |
| EXPECT_TRUE(request2->success_called()); |
| } |
| |
| TEST_F(IndexedDBDatabaseTest, OpenDeleteClear) { |
| const int64_t kDatabaseVersion = 1; |
| |
| scoped_refptr<MockIndexedDBCallbacks> request1( |
| new MockIndexedDBCallbacks(true)); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id1 = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection1( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id1, |
| kDatabaseVersion, std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection1)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| scoped_refptr<MockIndexedDBCallbacks> request2( |
| new MockIndexedDBCallbacks(false)); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id2 = 2; |
| auto create_transaction_callback2 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection2( |
| std::make_unique<IndexedDBPendingConnection>( |
| request2, callbacks2, kFakeChildProcessId, transaction_id2, |
| kDatabaseVersion, std::move(create_transaction_callback2))); |
| db_->OpenConnection(std::move(connection2)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 1UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| scoped_refptr<MockIndexedDBCallbacks> request3( |
| new MockIndexedDBCallbacks(false)); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks3( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id3 = 3; |
| auto create_transaction_callback3 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection3( |
| std::make_unique<IndexedDBPendingConnection>( |
| request3, callbacks3, kFakeChildProcessId, transaction_id3, |
| kDatabaseVersion, std::move(create_transaction_callback3))); |
| db_->OpenConnection(std::move(connection3)); |
| |
| // This causes the active request to call OnUpgradeNeeded on its callbacks. |
| // The Abort() triggered by ForceClose() assumes that the transaction was |
| // started and passed the connection along to the front end. |
| db_->CallUpgradeTransactionStartedForTesting( |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION); |
| EXPECT_TRUE(request1->upgrade_called()); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 2UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); |
| |
| db_->ForceClose(); |
| |
| EXPECT_TRUE(backing_store_->HasOneRef()); // local |
| EXPECT_TRUE(callbacks1->forced_close_called()); |
| EXPECT_TRUE(request1->error_called()); |
| EXPECT_TRUE(callbacks2->forced_close_called()); |
| EXPECT_FALSE(request2->error_called()); |
| EXPECT_TRUE(callbacks3->forced_close_called()); |
| EXPECT_FALSE(request3->error_called()); |
| } |
| |
| TEST_F(IndexedDBDatabaseTest, ForceDelete) { |
| scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks()); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id1 = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id1, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| scoped_refptr<MockCallbacks> request2(new MockCallbacks()); |
| db_->DeleteDatabase(request2, true /* force_delete */); |
| EXPECT_EQ(db_->ConnectionCount(), 0UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(request2->blocked_called()); |
| |
| EXPECT_FALSE(db_->backing_store()); |
| EXPECT_TRUE(backing_store_->HasOneRef()); // local |
| EXPECT_TRUE(request2->success_called()); |
| } |
| |
| TEST_F(IndexedDBDatabaseTest, ForceCloseWhileOpenPending) { |
| // Verify that pending connection requests are handled correctly during a |
| // ForceClose. |
| scoped_refptr<MockIndexedDBCallbacks> request1(new MockIndexedDBCallbacks()); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id1 = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id1, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| scoped_refptr<MockIndexedDBCallbacks> request2( |
| new MockIndexedDBCallbacks(false)); |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2( |
| new MockIndexedDBDatabaseCallbacks()); |
| const int64_t transaction_id2 = 2; |
| auto create_transaction_callback2 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection2( |
| std::make_unique<IndexedDBPendingConnection>( |
| request1, callbacks1, kFakeChildProcessId, transaction_id2, 3, |
| std::move(create_transaction_callback2))); |
| db_->OpenConnection(std::move(connection2)); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| EXPECT_FALSE(backing_store_->HasOneRef()); // local and db |
| |
| db_->ForceClose(); |
| EXPECT_EQ(db_->ConnectionCount(), 0UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| } |
| |
| leveldb::Status DummyOperation(IndexedDBTransaction* transaction) { |
| return leveldb::Status::OK(); |
| } |
| |
| class IndexedDBDatabaseOperationTest : public testing::Test { |
| public: |
| IndexedDBDatabaseOperationTest() |
| : lock_manager_(kIndexedDBLockLevelCount), |
| commit_success_(leveldb::Status::OK()), |
| factory_(new MockIndexedDBFactory()) {} |
| |
| void SetUp() override { |
| backing_store_ = new IndexedDBFakeBackingStore(); |
| std::unique_ptr<FakeIndexedDBMetadataCoding> metadata_coding = |
| std::make_unique<FakeIndexedDBMetadataCoding>(); |
| metadata_coding_ = metadata_coding.get(); |
| leveldb::Status s; |
| std::tie(db_, s) = IndexedDBDatabase::Create( |
| ASCIIToUTF16("db"), backing_store_.get(), factory_.get(), |
| std::move(metadata_coding), IndexedDBDatabase::Identifier(), |
| &lock_manager_); |
| ASSERT_TRUE(s.ok()); |
| |
| request_ = new MockIndexedDBCallbacks(); |
| callbacks_ = new MockIndexedDBDatabaseCallbacks(); |
| const int64_t transaction_id = 1; |
| auto create_transaction_callback1 = |
| base::BindOnce(&CreateAndBindTransactionPlaceholder); |
| std::unique_ptr<IndexedDBPendingConnection> connection( |
| std::make_unique<IndexedDBPendingConnection>( |
| request_, callbacks_, kFakeChildProcessId, transaction_id, |
| IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| std::move(create_transaction_callback1))); |
| db_->OpenConnection(std::move(connection)); |
| EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version); |
| |
| connection_ = std::make_unique<IndexedDBConnection>(kFakeChildProcessId, |
| db_, callbacks_); |
| transaction_ = connection_->CreateTransaction( |
| transaction_id, std::set<int64_t>() /*scope*/, |
| blink::mojom::IDBTransactionMode::VersionChange, |
| new IndexedDBFakeBackingStore::FakeTransaction(commit_success_)); |
| db_->RegisterAndScheduleTransaction(transaction_); |
| |
| // Add a dummy task which takes the place of the VersionChangeOperation |
| // which kicks off the upgrade. This ensures that the transaction has |
| // processed at least one task before the CreateObjectStore call. |
| transaction_->ScheduleTask(base::BindOnce(&DummyOperation)); |
| } |
| |
| void RunPostedTasks() { base::RunLoop().RunUntilIdle(); } |
| |
| private: |
| // Needs to outlive |db_|. |
| content::TestBrowserThreadBundle thread_bundle_; |
| |
| protected: |
| scoped_refptr<IndexedDBFakeBackingStore> backing_store_; |
| scoped_refptr<IndexedDBDatabase> db_; |
| FakeIndexedDBMetadataCoding* metadata_coding_; |
| scoped_refptr<MockIndexedDBCallbacks> request_; |
| scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_; |
| IndexedDBTransaction* transaction_; |
| std::unique_ptr<IndexedDBConnection> connection_; |
| DisjointRangeLockManager lock_manager_; |
| |
| leveldb::Status commit_success_; |
| |
| private: |
| scoped_refptr<MockIndexedDBFactory> factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationTest); |
| }; |
| |
| TEST_F(IndexedDBDatabaseOperationTest, CreateObjectStore) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"), |
| IndexedDBKeyPath(), false /*auto_increment*/); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| RunPostedTasks(); |
| transaction_->Commit(); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| } |
| |
| TEST_F(IndexedDBDatabaseOperationTest, CreateIndex) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"), |
| IndexedDBKeyPath(), false /*auto_increment*/); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| const int64_t index_id = 2002; |
| db_->CreateIndex(transaction_, store_id, index_id, ASCIIToUTF16("index"), |
| IndexedDBKeyPath(), false /*unique*/, false /*multi_entry*/); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| RunPostedTasks(); |
| transaction_->Commit(); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| } |
| |
| class IndexedDBDatabaseOperationAbortTest |
| : public IndexedDBDatabaseOperationTest { |
| public: |
| IndexedDBDatabaseOperationAbortTest() { |
| commit_success_ = leveldb::Status::NotFound("Bummer."); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseOperationAbortTest); |
| }; |
| |
| TEST_F(IndexedDBDatabaseOperationAbortTest, CreateObjectStore) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"), |
| IndexedDBKeyPath(), false /*auto_increment*/); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| RunPostedTasks(); |
| transaction_->Commit(); |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| } |
| |
| TEST_F(IndexedDBDatabaseOperationAbortTest, CreateIndex) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"), |
| IndexedDBKeyPath(), false /*auto_increment*/); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| const int64_t index_id = 2002; |
| db_->CreateIndex(transaction_, store_id, index_id, ASCIIToUTF16("index"), |
| IndexedDBKeyPath(), false /*unique*/, false /*multi_entry*/); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| RunPostedTasks(); |
| transaction_->Commit(); |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| } |
| |
| TEST_F(IndexedDBDatabaseOperationTest, CreatePutDelete) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| |
| // Creation is synchronous. |
| db_->CreateObjectStore(transaction_, store_id, ASCIIToUTF16("store"), |
| IndexedDBKeyPath(), false /*auto_increment*/); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| |
| |
| // Put is asynchronous |
| IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>()); |
| std::unique_ptr<IndexedDBKey> key(std::make_unique<IndexedDBKey>("key")); |
| std::vector<IndexedDBIndexKeys> index_keys; |
| scoped_refptr<MockIndexedDBCallbacks> request( |
| new MockIndexedDBCallbacks(false)); |
| db_->Put(transaction_, store_id, &value, std::move(key), |
| blink::mojom::IDBPutMode::AddOnly, request, index_keys); |
| |
| // Deletion is asynchronous. |
| db_->DeleteObjectStore(transaction_, store_id); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| |
| // This will execute the Put then Delete. |
| RunPostedTasks(); |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| |
| transaction_->Commit(); // Cleans up the object hierarchy. |
| } |
| |
| } // namespace content |