blob: 26061954643c99a96da385c5176e6df7a074efbe [file] [log] [blame]
// 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/test/bind_test_util.h"
#include "base/test/mock_callback.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_factory_impl.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/browser_task_environment.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_ = std::make_unique<IndexedDBFakeBackingStore>();
factory_ = std::make_unique<MockIndexedDBFactory>();
std::unique_ptr<FakeIndexedDBMetadataCoding> metadata_coding =
std::make_unique<FakeIndexedDBMetadataCoding>();
metadata_coding_ = metadata_coding.get();
leveldb::Status s;
std::tie(db_, s) = IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
base::BindRepeating(&IndexedDBDatabaseTest::RunTasksForDatabase,
weak_factory_.GetWeakPtr(), true),
std::move(metadata_coding), IndexedDBDatabase::Identifier(),
&lock_manager_);
ASSERT_TRUE(s.ok());
}
void RunTasksForDatabase(bool async) {
if (!db_)
return;
if (async) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&IndexedDBDatabaseTest::RunTasksForDatabase,
weak_factory_.GetWeakPtr(), false));
return;
}
IndexedDBDatabase::RunTasksResult result;
leveldb::Status status;
std::tie(result, status) = db_->RunTasks();
switch (result) {
case IndexedDBDatabase::RunTasksResult::kDone:
return;
case IndexedDBDatabase::RunTasksResult::kError:
error_called_ = true;
return;
case IndexedDBDatabase::RunTasksResult::kCanBeDestroyed:
db_.reset();
metadata_coding_ = nullptr;
return;
}
}
void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
protected:
std::unique_ptr<IndexedDBFakeBackingStore> backing_store_;
std::unique_ptr<MockIndexedDBFactory> factory_;
std::unique_ptr<IndexedDBDatabase> db_;
FakeIndexedDBMetadataCoding* metadata_coding_ = nullptr;
bool error_called_ = false;
private:
BrowserTaskEnvironment task_environment_;
DisjointRangeLockManager lock_manager_;
base::WeakPtrFactory<IndexedDBDatabaseTest> weak_factory_{this};
};
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection1));
RunPostedTasks();
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection2));
RunPostedTasks();
EXPECT_TRUE(request1->connection());
request1->connection()->CloseAndReportForceClose();
EXPECT_FALSE(request1->connection()->IsConnected());
EXPECT_TRUE(request2->connection());
request2->connection()->CloseAndReportForceClose();
EXPECT_FALSE(request2->connection()->IsConnected());
RunPostedTasks();
EXPECT_FALSE(db_);
}
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(db_.get(), request->connection()->database().get());
const int64_t transaction_id = 123;
const std::vector<int64_t> scope;
const bool relaxed_durability = true;
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(),
relaxed_durability));
db_->RegisterAndScheduleTransaction(transaction);
request->connection()->CloseAndReportForceClose();
EXPECT_TRUE(callbacks->abort_called());
}
class MockCallbacks : public IndexedDBCallbacks {
public:
MockCallbacks()
: IndexedDBCallbacks(nullptr,
url::Origin(),
mojo::NullAssociatedRemote(),
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
bool deleted = false;
scoped_refptr<MockCallbacks> request2(new MockCallbacks());
db_->ScheduleDeleteDatabase(
IndexedDBOriginStateHandle(), request2,
base::BindLambdaForTesting([&]() { deleted = true; }));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
EXPECT_FALSE(request2->blocked_called());
request1->connection()->VersionChangeIgnored();
EXPECT_TRUE(request2->blocked_called());
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
db_->ForceCloseAndRunTasks();
RunPostedTasks();
EXPECT_FALSE(db_);
EXPECT_TRUE(deleted);
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection1));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection2));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 1UL);
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection3));
RunPostedTasks();
EXPECT_TRUE(request1->upgrade_called());
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 2UL);
db_->ForceCloseAndRunTasks();
RunPostedTasks();
EXPECT_FALSE(db_);
EXPECT_TRUE(callbacks1->forced_close_called());
EXPECT_TRUE(request1->error_called());
EXPECT_TRUE(callbacks2->forced_close_called());
EXPECT_TRUE(request2->error_called());
EXPECT_TRUE(callbacks3->forced_close_called());
EXPECT_TRUE(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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
bool deleted = false;
scoped_refptr<MockCallbacks> request2(new MockCallbacks());
db_->ScheduleDeleteDatabase(
IndexedDBOriginStateHandle(), request2,
base::BindLambdaForTesting([&]() { deleted = true; }));
RunPostedTasks();
EXPECT_FALSE(deleted);
db_->ForceCloseAndRunTasks();
RunPostedTasks();
EXPECT_FALSE(db_);
EXPECT_TRUE(deleted);
EXPECT_FALSE(request2->blocked_called());
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection2));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
db_->ForceCloseAndRunTasks();
RunPostedTasks();
EXPECT_FALSE(db_);
}
TEST_F(IndexedDBDatabaseTest, ForceCloseWhileOpenAndDeletePending) {
// Verify that pending connection requests are handled correctly during a
// ForceClose.
auto request1 = base::MakeRefCounted<MockIndexedDBCallbacks>();
auto callbacks1 = base::MakeRefCounted<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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL);
auto request2 = base::MakeRefCounted<MockIndexedDBCallbacks>(false);
auto callbacks2 = base::MakeRefCounted<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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection2));
RunPostedTasks();
bool deleted = false;
auto request3 = base::MakeRefCounted<MockCallbacks>();
db_->ScheduleDeleteDatabase(
IndexedDBOriginStateHandle(), request3,
base::BindLambdaForTesting([&]() { deleted = true; }));
RunPostedTasks();
EXPECT_FALSE(deleted);
EXPECT_EQ(db_->ConnectionCount(), 1UL);
EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL);
EXPECT_EQ(db_->PendingOpenDeleteCount(), 1UL);
db_->ForceCloseAndRunTasks();
RunPostedTasks();
EXPECT_TRUE(deleted);
EXPECT_FALSE(db_);
}
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_ = std::make_unique<IndexedDBFakeBackingStore>();
std::unique_ptr<FakeIndexedDBMetadataCoding> metadata_coding =
std::make_unique<FakeIndexedDBMetadataCoding>();
metadata_coding_ = metadata_coding.get();
leveldb::Status s;
std::tie(db_, s) = IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
ASCIIToUTF16("db"), backing_store_.get(), factory_.get(),
base::BindRepeating(
&IndexedDBDatabaseOperationTest::RunTasksForDatabase,
base::Unretained(this), true),
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_->ScheduleOpenConnection(IndexedDBOriginStateHandle(),
std::move(connection));
RunPostedTasks();
EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version);
EXPECT_TRUE(request_->connection());
transaction_ = request_->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));
// Run posted tasks to execute the dummy operation and ensure that it is
// stored in the connection.
RunPostedTasks();
}
void RunTasksForDatabase(bool async) {
if (!db_)
return;
if (async) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&IndexedDBDatabaseOperationTest::RunTasksForDatabase,
base::Unretained(this), false));
return;
}
IndexedDBDatabase::RunTasksResult result;
leveldb::Status status;
std::tie(result, status) = db_->RunTasks();
switch (result) {
case IndexedDBDatabase::RunTasksResult::kDone:
return;
case IndexedDBDatabase::RunTasksResult::kError:
error_called_ = true;
return;
case IndexedDBDatabase::RunTasksResult::kCanBeDestroyed:
db_.reset();
metadata_coding_ = nullptr;
return;
}
}
void RunPostedTasks() { base::RunLoop().RunUntilIdle(); }
private:
// Needs to outlive |db_|.
content::BrowserTaskEnvironment task_environment_;
protected:
std::unique_ptr<IndexedDBFakeBackingStore> backing_store_;
std::unique_ptr<IndexedDBDatabase> db_;
FakeIndexedDBMetadataCoding* metadata_coding_ = nullptr;
scoped_refptr<MockIndexedDBCallbacks> request_;
scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks_;
IndexedDBTransaction* transaction_ = nullptr;
DisjointRangeLockManager lock_manager_;
bool error_called_ = false;
leveldb::Status commit_success_;
private:
std::unique_ptr<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;
leveldb::Status s = db_->CreateObjectStoreOperation(
store_id, ASCIIToUTF16("store"), IndexedDBKeyPath(),
false /*auto_increment*/, transaction_);
EXPECT_TRUE(s.ok());
transaction_->SetCommitFlag();
RunPostedTasks();
EXPECT_FALSE(error_called_);
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;
leveldb::Status s = db_->CreateObjectStoreOperation(
store_id, ASCIIToUTF16("store"), IndexedDBKeyPath(),
false /*auto_increment*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
const int64_t index_id = 2002;
s = db_->CreateIndexOperation(store_id, index_id, ASCIIToUTF16("index"),
IndexedDBKeyPath(), false /*unique*/,
false /*multi_entry*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(
1ULL,
db_->metadata().object_stores.find(store_id)->second.indexes.size());
transaction_->SetCommitFlag();
RunPostedTasks();
EXPECT_FALSE(error_called_);
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;
leveldb::Status s = db_->CreateObjectStoreOperation(
store_id, ASCIIToUTF16("store"), IndexedDBKeyPath(),
false /*auto_increment*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
transaction_->SetCommitFlag();
RunPostedTasks();
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;
leveldb::Status s = db_->CreateObjectStoreOperation(
store_id, ASCIIToUTF16("store"), IndexedDBKeyPath(),
false /*auto_increment*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
const int64_t index_id = 2002;
s = db_->CreateIndexOperation(store_id, index_id, ASCIIToUTF16("index"),
IndexedDBKeyPath(), false /*unique*/,
false /*multi_entry*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(
1ULL,
db_->metadata().object_stores.find(store_id)->second.indexes.size());
transaction_->SetCommitFlag();
RunPostedTasks();
EXPECT_TRUE(error_called_);
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;
leveldb::Status s = db_->CreateObjectStoreOperation(
store_id, ASCIIToUTF16("store"), IndexedDBKeyPath(),
false /*auto_increment*/, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
IndexedDBValue value("value1", std::vector<IndexedDBBlobInfo>());
std::unique_ptr<IndexedDBKey> key(std::make_unique<IndexedDBKey>("key"));
std::vector<IndexedDBIndexKeys> index_keys;
base::MockCallback<blink::mojom::IDBTransaction::PutCallback> callback;
auto put_params = std::make_unique<IndexedDBDatabase::PutOperationParams>();
put_params->object_store_id = store_id;
put_params->value = value;
put_params->key = std::move(key);
put_params->put_mode = blink::mojom::IDBPutMode::AddOnly;
put_params->callback = callback.Get();
put_params->index_keys = index_keys;
s = db_->PutOperation(std::move(put_params), transaction_);
EXPECT_TRUE(s.ok());
s = db_->DeleteObjectStoreOperation(store_id, transaction_);
EXPECT_TRUE(s.ok());
EXPECT_EQ(0ULL, db_->metadata().object_stores.size());
transaction_->SetCommitFlag();
RunPostedTasks();
EXPECT_FALSE(error_called_);
EXPECT_TRUE(s.ok());
}
} // namespace content