| // Copyright 2013 The Chromium Authors |
| // 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/instance/database.h" |
| |
| #include <stdint.h> |
| |
| #include <array> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "base/auto_reset.h" |
| #include "base/containers/span.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/task/updateable_sequenced_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/gmock_callback_support.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/task_environment.h" |
| #include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h" |
| #include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" |
| #include "components/services/storage/public/cpp/buckets/bucket_locator.h" |
| #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" |
| #include "content/browser/indexed_db/indexed_db_value.h" |
| #include "content/browser/indexed_db/instance/backing_store.h" |
| #include "content/browser/indexed_db/instance/bucket_context.h" |
| #include "content/browser/indexed_db/instance/connection.h" |
| #include "content/browser/indexed_db/instance/cursor.h" |
| #include "content/browser/indexed_db/instance/database_callbacks.h" |
| #include "content/browser/indexed_db/instance/factory_client.h" |
| #include "content/browser/indexed_db/instance/fake_transaction.h" |
| #include "content/browser/indexed_db/instance/mock_factory_client.h" |
| #include "content/browser/indexed_db/instance/mock_file_system_access_context.h" |
| #include "content/browser/indexed_db/instance/transaction.h" |
| #include "content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "storage/browser/test/mock_quota_manager.h" |
| #include "storage/browser/test/mock_quota_manager_proxy.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| |
| using blink::IndexedDBDatabaseMetadata; |
| using blink::IndexedDBIndexKeys; |
| using blink::IndexedDBKey; |
| using blink::IndexedDBKeyPath; |
| |
| namespace content::indexed_db { |
| |
| namespace { |
| constexpr int64_t kTestObjectStoreId = 1001; |
| constexpr int64_t kTestIndexId = 2002; |
| constexpr char kTestForceCloseMessage[] = |
| "The database's connection is force-closed."; |
| |
| // Contains a record's keys and value that tests use to populate the database. |
| struct TestIDBRecord { |
| TestIDBRecord(IndexedDBKey primary_key, |
| const IndexedDBValue& value, |
| std::optional<IndexedDBKey> index_key) |
| : primary_key(std::move(primary_key)), |
| value(value.Clone()), |
| index_key(std::move(index_key)) {} |
| |
| TestIDBRecord(const TestIDBRecord& other) { |
| primary_key = other.primary_key.Clone(); |
| value = other.value.Clone(); |
| if (other.index_key) { |
| index_key = other.index_key->Clone(); |
| } |
| } |
| |
| IndexedDBKey primary_key; |
| IndexedDBValue value; |
| // Optional. Tests may skip index creation. |
| std::optional<IndexedDBKey> index_key; |
| }; |
| |
| // Contains the options used to create an object store. Initializes members |
| // to reasonable defaults that tests may override. |
| struct TestObjectStoreParameters { |
| int64_t object_store_id = 0; |
| std::u16string name{u"store"}; |
| IndexedDBKeyPath key_path; |
| bool auto_increment = false; |
| }; |
| |
| // Contains the options used to create an index. Optional. Test setup skips |
| // index creation when `index_id` is `kInvalidId`. Initializes members to |
| // reasonable defaults that tests may override. |
| struct TestIndexParameters { |
| int64_t index_id = blink::IndexedDBIndexMetadata::kInvalidId; |
| std::u16string name{u"index"}; |
| bool unique = false; |
| bool multi_entry = false; |
| IndexedDBKeyPath key_path; |
| bool auto_increment = false; |
| }; |
| |
| // Describes how test setup should create and populate an object store and |
| // optionally an index. |
| struct TestDatabaseParameters { |
| TestObjectStoreParameters object_store_parameters; |
| TestIndexParameters index_parameters; |
| std::vector<TestIDBRecord> records; |
| }; |
| |
| // Contains the arguments needed to call `Database::GetAllOperation`. |
| // Initializes members to reasonable defaults that tests may override. |
| struct TestGetAllParameters { |
| blink::mojom::IDBGetAllResultType result_type = |
| blink::mojom::IDBGetAllResultType::Keys; |
| |
| blink::IndexedDBKeyRange key_range; |
| |
| int64_t max_count = std::numeric_limits<int64_t>::max(); |
| |
| blink::mojom::IDBCursorDirection direction = |
| blink::mojom::IDBCursorDirection::Next; |
| }; |
| |
| // `Database::GetAllOperation` streams record results to a sink. This fake |
| // implementation enables test to wait for all the results and inspect them. |
| class FakeGetAllResultSink final |
| : public blink::mojom::IDBDatabaseGetAllResultSink { |
| public: |
| FakeGetAllResultSink() = default; |
| |
| ~FakeGetAllResultSink() final { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| FakeGetAllResultSink(const FakeGetAllResultSink&) = delete; |
| FakeGetAllResultSink& operator=(const FakeGetAllResultSink&) = delete; |
| |
| blink::mojom::IDBError* GetError() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(is_done_); |
| return error_.get(); |
| } |
| |
| const std::vector<blink::mojom::IDBRecordPtr>& GetResults() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(is_done_); |
| return records_; |
| } |
| |
| void BindReceiver( |
| mojo::PendingAssociatedReceiver<blink::mojom::IDBDatabaseGetAllResultSink> |
| receiver) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!is_done_); |
| receiver_.Bind(std::move(receiver)); |
| } |
| |
| void WaitForResults() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| run_loop_.Run(); |
| CHECK(is_done_); |
| } |
| |
| private: |
| void ReceiveResults(std::vector<blink::mojom::IDBRecordPtr> records, |
| bool done) final { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!is_done_); |
| |
| if (records_.empty()) { |
| records_ = std::move(records); |
| } else { |
| records_.reserve(records_.size() + records.size()); |
| for (auto& record : records) { |
| records_.emplace_back(std::move(record)); |
| } |
| } |
| |
| if (done) { |
| is_done_ = true; |
| run_loop_.Quit(); |
| } |
| } |
| |
| void OnError(blink::mojom::IDBErrorPtr error) final { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!is_done_); |
| |
| error_ = std::move(error); |
| is_done_ = true; |
| run_loop_.Quit(); |
| } |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| mojo::AssociatedReceiver<blink::mojom::IDBDatabaseGetAllResultSink> receiver_{ |
| this}; |
| |
| // Used to wait until all results have been received. |
| base::RunLoop run_loop_; |
| bool is_done_ = false; |
| |
| // Store results and error for later inspection. Must not be accessed until |
| // `is_done_` is true. |
| std::vector<blink::mojom::IDBRecordPtr> records_; |
| blink::mojom::IDBErrorPtr error_; |
| }; |
| |
| void ExpectEqualsIndexedDBKey(const IndexedDBKey& expected_primary_key, |
| const IndexedDBKey& actual_primary_key) { |
| ASSERT_EQ(actual_primary_key.IsValid(), expected_primary_key.IsValid()); |
| |
| if (expected_primary_key.IsValid()) { |
| EXPECT_TRUE(actual_primary_key.Equals(expected_primary_key)) |
| << "Expected " << expected_primary_key.DebugString() << " but got " |
| << actual_primary_key.DebugString(); |
| } |
| } |
| |
| void ExpectEqualsOptionalIndexedDBKey( |
| const std::optional<IndexedDBKey>& expected_primary_key, |
| const std::optional<IndexedDBKey>& actual_primary_key) { |
| ASSERT_EQ(actual_primary_key.has_value(), expected_primary_key.has_value()); |
| |
| if (expected_primary_key.has_value()) { |
| ASSERT_NO_FATAL_FAILURE( |
| ExpectEqualsIndexedDBKey(*expected_primary_key, *actual_primary_key)); |
| } |
| } |
| |
| void ExpectEqualsIDBReturnValuePtr( |
| const blink::mojom::IDBReturnValuePtr& expected_return_value, |
| const blink::mojom::IDBReturnValuePtr& actual_return_value) { |
| ASSERT_EQ(actual_return_value.is_null(), expected_return_value.is_null()); |
| |
| if (!expected_return_value.is_null()) { |
| // Verify the value bits. |
| ASSERT_EQ(actual_return_value->value.is_null(), |
| expected_return_value->value.is_null()); |
| |
| if (!expected_return_value->value.is_null()) { |
| EXPECT_EQ(actual_return_value->value->bits, |
| expected_return_value->value->bits); |
| |
| // Verify the external objects. |
| EXPECT_EQ(actual_return_value->value->external_objects.size(), |
| expected_return_value->value->external_objects.size()); |
| } |
| |
| // Verify the return value primary key and key path. |
| ASSERT_NO_FATAL_FAILURE(ExpectEqualsIndexedDBKey( |
| actual_return_value->primary_key, expected_return_value->primary_key)); |
| |
| EXPECT_EQ(expected_return_value->key_path, actual_return_value->key_path); |
| } |
| } |
| |
| // Creates a `IDBReturnValuePtr` with the given bits. `primary_key`, |
| // `key_path` are optional, required only for object stores that generate keys. |
| // `external_objects` is not set and remains empty. |
| blink::mojom::IDBReturnValuePtr CreateIDBReturnValuePtr( |
| const std::string& bits, |
| const IndexedDBKey* primary_key = nullptr, |
| IndexedDBKeyPath key_path = {}) { |
| blink::mojom::IDBReturnValuePtr result = blink::mojom::IDBReturnValue::New(); |
| result->value = blink::mojom::IDBValue::New(); |
| result->value->bits.assign(bits.begin(), bits.end()); |
| if (primary_key) { |
| result->primary_key = primary_key->Clone(); |
| } |
| result->key_path = std::move(key_path); |
| return result; |
| } |
| |
| } // namespace |
| |
| class DatabaseTest : public ::testing::Test { |
| public: |
| DatabaseTest() = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>( |
| /*is_incognito=*/false, temp_dir_.GetPath(), |
| base::SingleThreadTaskRunner::GetCurrentDefault(), |
| /*special_storage_policy=*/nullptr); |
| |
| quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>( |
| quota_manager_.get(), |
| base::SingleThreadTaskRunner::GetCurrentDefault().get()); |
| |
| BucketContext::Delegate delegate; |
| delegate.on_ready_for_destruction = |
| base::BindOnce(&DatabaseTest::OnBucketContextReadyForDestruction, |
| weak_factory_.GetWeakPtr()); |
| |
| mojo::PendingRemote<storage::mojom::FileSystemAccessContext> fsa_context; |
| file_system_access_context_ = |
| std::make_unique<test::MockFileSystemAccessContext>(); |
| file_system_access_context_->Clone( |
| fsa_context.InitWithNewPipeAndPassReceiver()); |
| |
| bucket_context_ = std::make_unique<BucketContext>( |
| storage::BucketInfo(), temp_dir_.GetPath(), std::move(delegate), |
| scoped_refptr<base::UpdateableSequencedTaskRunner>(), |
| quota_manager_proxy_, |
| /*blob_storage_context=*/mojo::NullRemote(), |
| /*file_system_access_context=*/std::move(fsa_context)); |
| |
| bucket_context_->InitBackingStoreIfNeeded(true); |
| db_ = bucket_context_->CreateAndAddDatabase(u"db"); |
| } |
| |
| void TearDown() override { db_ = nullptr; } |
| |
| void OnBucketContextReadyForDestruction() { bucket_context_.reset(); } |
| |
| void RunPostedTasks() { |
| base::RunLoop run_loop; |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| protected: |
| base::test::TaskEnvironment task_environment_; |
| |
| base::ScopedTempDir temp_dir_; |
| std::unique_ptr<BucketContext> bucket_context_; |
| std::unique_ptr<test::MockFileSystemAccessContext> |
| file_system_access_context_; |
| scoped_refptr<storage::MockQuotaManager> quota_manager_; |
| scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_; |
| |
| // As this is owned by `bucket_context_`, tests that cause the database to |
| // be destroyed must manually reset this to null to avoid triggering dangling |
| // pointer warnings. |
| raw_ptr<Database> db_ = nullptr; |
| |
| base::WeakPtrFactory<DatabaseTest> weak_factory_{this}; |
| }; |
| |
| TEST_F(DatabaseTest, ConnectionLifecycle) { |
| MockMojoDatabaseCallbacks database_callbacks; |
| MockFactoryClient request1; |
| const int64_t transaction_id1 = 1; |
| auto connection1 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection1)); |
| RunPostedTasks(); |
| |
| MockMojoDatabaseCallbacks database_callbacks2; |
| MockFactoryClient request2; |
| const int64_t transaction_id2 = 2; |
| auto connection2 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request2), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks2.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id2, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection2)); |
| RunPostedTasks(); |
| db_ = nullptr; |
| |
| EXPECT_TRUE(request1.connection()); |
| request1.connection()->CloseAndReportForceClose(kTestForceCloseMessage); |
| EXPECT_FALSE(request1.connection()->IsConnected()); |
| |
| EXPECT_TRUE(request2.connection()); |
| request2.connection()->CloseAndReportForceClose(kTestForceCloseMessage); |
| EXPECT_FALSE(request2.connection()->IsConnected()); |
| |
| RunPostedTasks(); |
| |
| EXPECT_TRUE(bucket_context_->GetDatabasesForTesting().empty()); |
| } |
| |
| TEST_F(DatabaseTest, ForcedClose) { |
| MockMojoDatabaseCallbacks database_callbacks; |
| MockFactoryClient request; |
| const int64_t upgrade_transaction_id = 3; |
| auto connection = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks.BindNewEndpointAndPassDedicatedRemote()), |
| upgrade_transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_, request.connection()->database().get()); |
| |
| request.connection()->CreateTransaction( |
| mojo::NullAssociatedReceiver(), /*transaction_id=*/123, |
| /*object_store_ids=*/{}, blink::mojom::IDBTransactionMode::ReadOnly, |
| blink::mojom::IDBTransactionDurability::Relaxed); |
| db_ = nullptr; |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(database_callbacks, ForcedClose) |
| .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); |
| request.connection()->CloseAndReportForceClose(kTestForceCloseMessage); |
| run_loop.Run(); |
| } |
| |
| namespace { |
| |
| class FakeFactoryClient : public FactoryClient { |
| public: |
| FakeFactoryClient() : FactoryClient(mojo::NullAssociatedRemote()) {} |
| ~FakeFactoryClient() override = default; |
| |
| FakeFactoryClient(const FakeFactoryClient&) = delete; |
| FakeFactoryClient& operator=(const FakeFactoryClient&) = delete; |
| |
| void OnBlocked(int64_t existing_version) override { blocked_called_ = true; } |
| void OnDeleteSuccess(int64_t old_version) override { success_called_ = true; } |
| void OnError(const DatabaseError& 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: |
| bool blocked_called_ = false; |
| bool success_called_ = false; |
| bool error_called_ = false; |
| }; |
| |
| } // namespace |
| |
| TEST_F(DatabaseTest, PendingDelete) { |
| MockFactoryClient request1; |
| const int64_t transaction_id1 = 1; |
| MockMojoDatabaseCallbacks database_callbacks1; |
| auto connection = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks1.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| base::RunLoop run_loop; |
| FakeFactoryClient request2; |
| db_->ScheduleDeleteDatabase(std::make_unique<ThunkFactoryClient>(request2), |
| run_loop.QuitClosure()); |
| 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(kTestForceCloseMessage); |
| db_ = nullptr; |
| |
| run_loop.Run(); |
| EXPECT_FALSE(db_); |
| |
| EXPECT_TRUE(request2.success_called()); |
| } |
| |
| TEST_F(DatabaseTest, OpenDeleteClear) { |
| const int64_t kDatabaseVersion = 1; |
| |
| MockFactoryClient request1( |
| /*expect_connection=*/true); |
| MockMojoDatabaseCallbacks database_callbacks1; |
| const int64_t transaction_id1 = 1; |
| auto connection1 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks1.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, kDatabaseVersion, mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection1)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| MockFactoryClient request2( |
| /*expect_connection=*/false); |
| MockMojoDatabaseCallbacks database_callbacks2; |
| const int64_t transaction_id2 = 2; |
| auto connection2 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request2), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks2.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id2, kDatabaseVersion, mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection2)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 1UL); |
| |
| MockFactoryClient request3( |
| /*expect_connection=*/false); |
| MockMojoDatabaseCallbacks database_callbacks3; |
| const int64_t transaction_id3 = 3; |
| auto connection3 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request3), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks3.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id3, kDatabaseVersion, mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection3)); |
| RunPostedTasks(); |
| |
| EXPECT_TRUE(request1.upgrade_called()); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 2UL); |
| |
| EXPECT_CALL(database_callbacks1, ForcedClose); |
| EXPECT_CALL(database_callbacks2, ForcedClose); |
| EXPECT_CALL(database_callbacks3, ForcedClose); |
| |
| db_->ForceCloseAndRunTasks(kTestForceCloseMessage); |
| db_ = nullptr; |
| database_callbacks1.FlushForTesting(); |
| |
| EXPECT_TRUE(request1.error_called()); |
| EXPECT_TRUE(request2.error_called()); |
| EXPECT_TRUE(request3.error_called()); |
| } |
| |
| TEST_F(DatabaseTest, ForceDelete) { |
| MockFactoryClient request1; |
| MockMojoDatabaseCallbacks database_callbacks; |
| const int64_t transaction_id1 = 1; |
| auto connection = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| base::RunLoop run_loop; |
| FakeFactoryClient request2; |
| db_->ScheduleDeleteDatabase(std::make_unique<ThunkFactoryClient>(request2), |
| run_loop.QuitClosure()); |
| RunPostedTasks(); |
| EXPECT_FALSE(run_loop.AnyQuitCalled()); |
| db_->ForceCloseAndRunTasks(kTestForceCloseMessage); |
| db_ = nullptr; |
| run_loop.Run(); |
| EXPECT_FALSE(db_); |
| EXPECT_FALSE(request2.blocked_called()); |
| EXPECT_TRUE(request2.success_called()); |
| } |
| |
| TEST_F(DatabaseTest, ForceCloseWhileOpenPending) { |
| // Verify that pending connection requests are handled correctly during a |
| // ForceClose. |
| MockFactoryClient request1; |
| MockMojoDatabaseCallbacks database_callbacks1; |
| const int64_t transaction_id1 = 1; |
| auto connection1 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks1.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection1)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| MockFactoryClient request2(/*expect_connection=*/false); |
| MockMojoDatabaseCallbacks database_callbacks2; |
| |
| const int64_t transaction_id2 = 2; |
| auto connection2 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request2), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks2.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id2, 3, mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection2)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| db_->ForceCloseAndRunTasks(kTestForceCloseMessage); |
| db_ = nullptr; |
| RunPostedTasks(); |
| EXPECT_FALSE(db_); |
| } |
| |
| TEST_F(DatabaseTest, ForceCloseWhileOpenAndDeletePending) { |
| // Verify that pending connection requests are handled correctly during a |
| // ForceClose. |
| MockFactoryClient request1; |
| MockMojoDatabaseCallbacks database_callbacks1; |
| const int64_t transaction_id1 = 1; |
| auto connection1 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request1), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks1.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id1, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection1)); |
| RunPostedTasks(); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 0UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 0UL); |
| |
| MockFactoryClient request2(false); |
| MockMojoDatabaseCallbacks database_callbacks2; |
| const int64_t transaction_id2 = 2; |
| auto connection2 = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request2), |
| std::make_unique<DatabaseCallbacks>( |
| database_callbacks2.BindNewEndpointAndPassDedicatedRemote()), |
| transaction_id2, 3, mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection2)); |
| RunPostedTasks(); |
| |
| base::RunLoop run_loop; |
| auto request3 = std::make_unique<FakeFactoryClient>(); |
| db_->ScheduleDeleteDatabase(std::move(request3), run_loop.QuitClosure()); |
| RunPostedTasks(); |
| EXPECT_FALSE(run_loop.AnyQuitCalled()); |
| |
| EXPECT_EQ(db_->ConnectionCount(), 1UL); |
| EXPECT_EQ(db_->ActiveOpenDeleteCount(), 1UL); |
| EXPECT_EQ(db_->PendingOpenDeleteCount(), 1UL); |
| |
| db_->ForceCloseAndRunTasks(kTestForceCloseMessage); |
| db_ = nullptr; |
| run_loop.Run(); |
| } |
| |
| Status DummyOperation(Transaction* transaction) { |
| return Status::OK(); |
| } |
| |
| class DatabaseOperationTest : public DatabaseTest { |
| public: |
| DatabaseOperationTest() = default; |
| DatabaseOperationTest(const DatabaseOperationTest&) = delete; |
| DatabaseOperationTest& operator=(const DatabaseOperationTest&) = delete; |
| |
| void SetUp() override { |
| DatabaseTest::SetUp(); |
| |
| const int64_t transaction_id = 1; |
| auto connection = std::make_unique<PendingConnection>( |
| std::make_unique<ThunkFactoryClient>(request_), |
| std::make_unique<DatabaseCallbacks>(mojo::NullAssociatedRemote()), |
| transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION, |
| mojo::NullAssociatedReceiver()); |
| db_->ScheduleOpenConnection(std::move(connection)); |
| RunPostedTasks(); |
| EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version); |
| |
| EXPECT_TRUE(request_.connection()); |
| transaction_ = request_.connection()->CreateVersionChangeTransaction( |
| transaction_id, /*scope=*/std::set<int64_t>(), |
| std::make_unique<FakeTransaction>( |
| commit_success_, |
| db_->backing_store_db()->CreateTransaction( |
| blink::mojom::IDBTransactionDurability::Relaxed, |
| blink::mojom::IDBTransactionMode::VersionChange))); |
| |
| std::vector<PartitionedLockManager::PartitionedLockRequest> lock_requests = |
| db_->BuildLockRequestsForTransaction( |
| blink::mojom::IDBTransactionMode::VersionChange, /*scope=*/{}); |
| db_->lock_manager().AcquireLocks( |
| std::move(lock_requests), *transaction_->mutable_locks_receiver(), |
| base::BindOnce(&Transaction::Start, transaction_->AsWeakPtr())); |
| |
| // 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(); |
| } |
| |
| // Populates an object store and optionally an index with `database_records`. |
| // After setup, calls `Database::GetAllOperation` with `get_all_parameters`. |
| // Verifies that the results match `expected_results`. |
| void TestGetAll( |
| TestDatabaseParameters database_parameters, |
| TestGetAllParameters get_all_parameters, |
| base::span<const blink::mojom::IDBRecordPtr> expected_results) { |
| // Create the object store. |
| ASSERT_EQ(0u, db_->metadata().object_stores.size()); |
| const auto& object_store_parameters = |
| database_parameters.object_store_parameters; |
| const int64_t store_id = object_store_parameters.object_store_id; |
| Status status = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, object_store_parameters.name, |
| object_store_parameters.key_path, |
| object_store_parameters.auto_increment); |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| EXPECT_EQ(1u, db_->metadata().object_stores.size()); |
| |
| // Optionally, create an index when the test provides a valid index id. |
| const blink::IndexedDBIndexMetadata index( |
| database_parameters.index_parameters.name, |
| database_parameters.index_parameters.index_id, |
| database_parameters.index_parameters.key_path, |
| database_parameters.index_parameters.unique, |
| database_parameters.index_parameters.multi_entry); |
| const int64_t index_id = index.id; |
| const bool has_index = |
| index_id != blink::IndexedDBIndexMetadata::kInvalidId; |
| if (has_index) { |
| status = |
| transaction_->BackingStoreTransaction()->CreateIndex(store_id, index); |
| } |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| |
| // Populate the object store and optionally the index with the provided |
| // records. |
| for (TestIDBRecord& record : database_parameters.records) { |
| std::vector<IndexedDBIndexKeys> index_keys; |
| ASSERT_EQ(record.index_key.has_value(), has_index); |
| if (has_index) { |
| IndexedDBIndexKeys index_key{index_id, {}}; |
| index_key.keys.emplace_back(std::move(*record.index_key)); |
| index_keys.emplace_back(std::move(index_key)); |
| } |
| |
| testing::NiceMock< |
| base::MockCallback<blink::mojom::IDBTransaction::PutCallback>> |
| callback; |
| |
| // Set in-flight memory to a reasonably large number to prevent underflow |
| // in `PutOperation` |
| transaction_->in_flight_memory_ += 1000; |
| |
| status = transaction_->DoPut( |
| store_id, record.value.Clone(), std::move(record.primary_key), |
| blink::mojom::IDBPutMode::AddOnly, std::move(index_keys), |
| callback.Get(), transaction_); |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| } |
| |
| // Call `Database::GetAllOperation` with the provided parameters. |
| FakeGetAllResultSink result_sink; |
| blink::mojom::IDBDatabase::GetAllCallback get_all_callback = base::BindOnce( |
| &FakeGetAllResultSink::BindReceiver, base::Unretained(&result_sink)); |
| |
| std::unique_ptr<Database::GetAllResultSinkWrapper> result_sink_wrapper = |
| std::make_unique<Database::GetAllResultSinkWrapper>( |
| transaction_->AsWeakPtr(), std::move(get_all_callback)); |
| result_sink_wrapper->UseDedicatedReceiverForTesting(); |
| |
| status = db_->GetAllOperation( |
| store_id, index_id, std::move(get_all_parameters.key_range), |
| get_all_parameters.result_type, get_all_parameters.max_count, |
| get_all_parameters.direction, std::move(result_sink_wrapper), |
| transaction_); |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| |
| result_sink.WaitForResults(); |
| EXPECT_EQ(result_sink.GetError(), nullptr); |
| |
| // Verify that the actual results match the expected results. |
| const std::vector<blink::mojom::IDBRecordPtr>& actual_results = |
| result_sink.GetResults(); |
| ASSERT_EQ(actual_results.size(), expected_results.size()); |
| |
| for (size_t i = 0u; i < expected_results.size(); ++i) { |
| ASSERT_FALSE(actual_results[i].is_null()); |
| |
| // Verify the primary key. |
| ASSERT_NO_FATAL_FAILURE(ExpectEqualsOptionalIndexedDBKey( |
| expected_results[i]->primary_key, actual_results[i]->primary_key)); |
| |
| // Verify the record value. |
| ASSERT_NO_FATAL_FAILURE(ExpectEqualsIDBReturnValuePtr( |
| expected_results[i]->return_value, actual_results[i]->return_value)); |
| |
| // Verify the index key. |
| ASSERT_NO_FATAL_FAILURE(ExpectEqualsOptionalIndexedDBKey( |
| expected_results[i]->index_key, actual_results[i]->index_key)); |
| } |
| |
| // Perform cleanup. |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| |
| // A transaction error would have resulted in a deleted db. |
| EXPECT_FALSE(bucket_context_->GetDatabasesForTesting().empty()); |
| } |
| |
| protected: |
| MockFactoryClient request_; |
| |
| // As this is owned by `Connection`, tests that cause the transaction |
| // to be committed must manually reset this to null to avoid triggering |
| // dangling pointer warnings. |
| raw_ptr<Transaction> transaction_ = nullptr; |
| Status commit_success_; |
| }; |
| |
| TEST_F(DatabaseOperationTest, CreateObjectStore) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| Status s = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| EXPECT_TRUE(s.ok()); |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| EXPECT_TRUE(bucket_context_); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| } |
| |
| TEST_F(DatabaseOperationTest, CreateIndex) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| Status s = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| const int64_t index_id = 2002; |
| s = transaction_->BackingStoreTransaction()->CreateIndex( |
| store_id, blink::IndexedDBIndexMetadata( |
| u"index", index_id, IndexedDBKeyPath(), /*unique=*/false, |
| /*multi_entry=*/false)); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| EXPECT_TRUE(bucket_context_); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| } |
| |
| class DatabaseOperationAbortTest : public DatabaseOperationTest { |
| public: |
| DatabaseOperationAbortTest() { |
| commit_success_ = Status::NotFound("Bummer."); |
| } |
| |
| DatabaseOperationAbortTest(const DatabaseOperationAbortTest&) = delete; |
| DatabaseOperationAbortTest& operator=(const DatabaseOperationAbortTest&) = |
| delete; |
| }; |
| |
| TEST_F(DatabaseOperationAbortTest, CreateObjectStore) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| Status s = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| db_ = nullptr; |
| transaction_->SetCommitFlag(); |
| RunPostedTasks(); |
| // A transaction error results in a deleted db. |
| EXPECT_TRUE(bucket_context_->GetDatabasesForTesting().empty()); |
| } |
| |
| TEST_F(DatabaseOperationAbortTest, CreateIndex) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| Status s = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| const int64_t index_id = 2002; |
| s = transaction_->BackingStoreTransaction()->CreateIndex( |
| store_id, blink::IndexedDBIndexMetadata( |
| u"index", index_id, IndexedDBKeyPath(), /*unique=*/false, |
| /*multi_entry=*/false)); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ( |
| 1ULL, |
| db_->metadata().object_stores.find(store_id)->second.indexes.size()); |
| db_ = nullptr; |
| transaction_->SetCommitFlag(); |
| RunPostedTasks(); |
| // A transaction error results in a deleted db. |
| EXPECT_TRUE(bucket_context_->GetDatabasesForTesting().empty()); |
| } |
| |
| TEST_F(DatabaseOperationTest, CreatePutDelete) { |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| const int64_t store_id = 1001; |
| |
| Status s = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| store_id, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| EXPECT_TRUE(s.ok()); |
| EXPECT_EQ(1ULL, db_->metadata().object_stores.size()); |
| |
| base::MockCallback<blink::mojom::IDBTransaction::PutCallback> callback; |
| |
| // Set in-flight memory to a reasonably large number to prevent underflow in |
| // `PutOperation` |
| transaction_->in_flight_memory_ += 1000; |
| |
| s = transaction_->DoPut( |
| store_id, IndexedDBValue("value1", {}), IndexedDBKey("key"), |
| blink::mojom::IDBPutMode::AddOnly, std::vector<IndexedDBIndexKeys>(), |
| callback.Get(), transaction_); |
| EXPECT_TRUE(s.ok()); |
| |
| s = transaction_->BackingStoreTransaction()->DeleteObjectStore(store_id); |
| EXPECT_TRUE(s.ok()); |
| |
| EXPECT_EQ(0ULL, db_->metadata().object_stores.size()); |
| |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| // A transaction error would have resulted in a deleted db. |
| EXPECT_FALSE(bucket_context_->GetDatabasesForTesting().empty()); |
| EXPECT_TRUE(s.ok()); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWhenEmpty) { |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/{}, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| }, |
| /*expected_results=*/{})); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllValuesWhenEmpty) { |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/{}, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Values, |
| }, |
| /*expected_results=*/{})); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeys) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllValues) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value3"), |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Values, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllRecords) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/CreateIDBReturnValuePtr("value3"), |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllKeys) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllValues) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value3"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/std::nullopt, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Values, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllRecords) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/CreateIDBReturnValuePtr("value3"), |
| IndexedDBKey{"index_key1"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| IndexedDBKey{"index_key2"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| IndexedDBKey{"index_key3"}), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWithRange) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| .key_range = |
| { |
| IndexedDBKey("key2"), |
| IndexedDBKey("key9"), |
| /*lower_open=*/false, |
| /*upper_open=*/false, |
| }, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWithRangeThatDoesNotExist) { |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| .key_range = |
| { |
| IndexedDBKey("key7"), |
| IndexedDBKey("key9"), |
| /*lower_open=*/false, |
| /*upper_open=*/false, |
| }, |
| }, |
| /*expected_results=*/{})); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWithInvalidRange) { |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| .key_range = |
| { |
| IndexedDBKey("key9"), |
| IndexedDBKey("key7"), |
| /*lower_open=*/false, |
| /*upper_open=*/false, |
| }, |
| }, |
| /*expected_results=*/{})); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWithMaxCount) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/nullptr, |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Keys, |
| .max_count = 2, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllRecordsWithPrevDirection) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key3"}, |
| /*value=*/CreateIDBReturnValuePtr("value3"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| /*index_key=*/std::nullopt), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| /*index_key=*/std::nullopt), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| /*index_key=*/std::nullopt, |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| .direction = blink::mojom::IDBCursorDirection::Prev, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllRecordsWithNextNoDuplicateDirection) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| IndexedDBKey{"index_key1"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| IndexedDBKey{"index_key2"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key4"}, |
| /*value=*/CreateIDBReturnValuePtr("value4"), |
| IndexedDBKey{"index_key3"}), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key4"}, |
| {"value4", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key5"}, |
| {"value5", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key6"}, |
| {"value6", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| .direction = blink::mojom::IDBCursorDirection::NextNoDuplicate, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllRecordsWithPrevNoDuplicateDirection) { |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key4"}, |
| /*value=*/CreateIDBReturnValuePtr("value4"), |
| IndexedDBKey{"index_key3"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key2"}, |
| /*value=*/CreateIDBReturnValuePtr("value2"), |
| IndexedDBKey{"index_key2"}), |
| blink::mojom::IDBRecord::New(IndexedDBKey{"key1"}, |
| /*value=*/CreateIDBReturnValuePtr("value1"), |
| IndexedDBKey{"index_key1"}), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| IndexedDBKey{"key1"}, |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| { |
| IndexedDBKey{"key2"}, |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key3"}, |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| IndexedDBKey{"key4"}, |
| {"value4", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key5"}, |
| {"value5", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| IndexedDBKey{"key6"}, |
| {"value6", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| .direction = blink::mojom::IDBCursorDirection::PrevNoDuplicate, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, ObjectStoreGetAllKeysWithInvalidObjectStoreId) { |
| ASSERT_EQ(0u, db_->metadata().object_stores.size()); |
| |
| // Call `Database::GetAllOperation` with an invalid object store id, which |
| // must fail with an invalid argument status. |
| FakeGetAllResultSink result_sink; |
| blink::mojom::IDBDatabase::GetAllCallback get_all_callback = base::BindOnce( |
| &FakeGetAllResultSink::BindReceiver, base::Unretained(&result_sink)); |
| |
| std::unique_ptr<Database::GetAllResultSinkWrapper> result_sink_wrapper = |
| std::make_unique<Database::GetAllResultSinkWrapper>( |
| transaction_->AsWeakPtr(), std::move(get_all_callback)); |
| result_sink_wrapper->UseDedicatedReceiverForTesting(); |
| |
| TestGetAllParameters get_all_parameters; |
| |
| Status status = db_->GetAllOperation( |
| kTestObjectStoreId, |
| /*index_id=*/blink::IndexedDBIndexMetadata::kInvalidId, |
| std::move(get_all_parameters.key_range), get_all_parameters.result_type, |
| get_all_parameters.max_count, get_all_parameters.direction, |
| std::move(result_sink_wrapper), transaction_); |
| ASSERT_TRUE(status.IsInvalidArgument()) << status.ToString(); |
| |
| // Verify that the result sink received an error. |
| result_sink.WaitForResults(); |
| ASSERT_NE(result_sink.GetError(), nullptr); |
| EXPECT_EQ(result_sink.GetError()->error_code, |
| blink::mojom::IDBException::kUnknownError); |
| |
| // Perform cleanup. |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllKeysWithInvalidIndexId) { |
| // Create an object store. |
| ASSERT_EQ(0u, db_->metadata().object_stores.size()); |
| Status status = transaction_->BackingStoreTransaction()->CreateObjectStore( |
| kTestObjectStoreId, u"store", IndexedDBKeyPath(), |
| /*auto_increment=*/false); |
| ASSERT_TRUE(status.ok()) << status.ToString(); |
| ASSERT_EQ(1u, db_->metadata().object_stores.size()); |
| |
| // Call `Database::GetAllOperation` with an invalid index id, which must fail |
| // with an invalid argument status. |
| FakeGetAllResultSink result_sink; |
| blink::mojom::IDBDatabase::GetAllCallback get_all_callback = base::BindOnce( |
| &FakeGetAllResultSink::BindReceiver, base::Unretained(&result_sink)); |
| |
| std::unique_ptr<Database::GetAllResultSinkWrapper> result_sink_wrapper = |
| std::make_unique<Database::GetAllResultSinkWrapper>( |
| transaction_->AsWeakPtr(), std::move(get_all_callback)); |
| result_sink_wrapper->UseDedicatedReceiverForTesting(); |
| |
| TestGetAllParameters get_all_parameters; |
| |
| status = db_->GetAllOperation( |
| kTestObjectStoreId, kTestIndexId, std::move(get_all_parameters.key_range), |
| get_all_parameters.result_type, get_all_parameters.max_count, |
| get_all_parameters.direction, std::move(result_sink_wrapper), |
| transaction_); |
| ASSERT_TRUE(status.IsInvalidArgument()) << status.ToString(); |
| |
| // Verify that the result sink received an error. |
| result_sink.WaitForResults(); |
| ASSERT_NE(result_sink.GetError(), nullptr); |
| EXPECT_EQ(result_sink.GetError()->error_code, |
| blink::mojom::IDBException::kUnknownError); |
| |
| // Perform cleanup. |
| transaction_->SetCommitFlag(); |
| transaction_ = nullptr; |
| RunPostedTasks(); |
| } |
| |
| TEST_F(DatabaseOperationTest, |
| ObjectStoreGetAllRecordsWithMultipleResultChunks) { |
| // Generate 2.5 chunks of results. |
| const size_t record_count = (blink::mojom::kIDBGetAllChunkSize * 2) + |
| (blink::mojom::kIDBGetAllChunkSize / 2); |
| |
| std::vector<TestIDBRecord> database_records; |
| std::vector<blink::mojom::IDBRecordPtr> expected_results; |
| |
| for (size_t i = 0u; i < record_count; ++i) { |
| const std::string primary_key = base::StringPrintf("key%zu", i); |
| const std::string value = base::StringPrintf("value%zu", i); |
| |
| database_records.emplace_back(IndexedDBKey{primary_key}, |
| IndexedDBValue{value, {}}, |
| /*index_key=*/std::nullopt); |
| |
| expected_results.emplace_back( |
| blink::mojom::IDBRecord::New(IndexedDBKey{primary_key}, |
| /*value=*/CreateIDBReturnValuePtr(value), |
| /*index_key=*/std::nullopt)); |
| } |
| |
| // Sort the expected results by primary key. |
| std::sort(expected_results.begin(), expected_results.end(), |
| [](const blink::mojom::IDBRecordPtr& left, |
| const blink::mojom::IDBRecordPtr& right) { |
| return left->primary_key->IsLessThan(*right->primary_key); |
| }); |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| }, |
| /*index_parameters=*/{}, |
| std::move(database_records), |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| }, |
| expected_results)); |
| } |
| |
| TEST_F(DatabaseOperationTest, IndexGetAllRecordsWithAutoIncrementingKeys) { |
| const IndexedDBKeyPath object_store_key_path{u"id"}; |
| |
| const auto expected_generated_keys = std::to_array<IndexedDBKey>({ |
| IndexedDBKey(1.0, blink::mojom::IDBKeyType::Number), |
| IndexedDBKey(2.0, blink::mojom::IDBKeyType::Number), |
| IndexedDBKey(3.0, blink::mojom::IDBKeyType::Number), |
| }); |
| |
| const blink::mojom::IDBRecordPtr expected_results[] = { |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/expected_generated_keys[2].Clone(), |
| /*value=*/ |
| CreateIDBReturnValuePtr("value3", &expected_generated_keys[2], |
| object_store_key_path), |
| /*index_key=*/IndexedDBKey{"index_key1"}), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/expected_generated_keys[1].Clone(), |
| /*value=*/ |
| CreateIDBReturnValuePtr("value2", &expected_generated_keys[1], |
| object_store_key_path), |
| /*index_key=*/IndexedDBKey{"index_key2"}), |
| blink::mojom::IDBRecord::New( |
| /*primary_key=*/expected_generated_keys[0].Clone(), |
| /*value=*/ |
| CreateIDBReturnValuePtr("value1", &expected_generated_keys[0], |
| object_store_key_path), |
| /*index_key=*/IndexedDBKey{"index_key3"}), |
| }; |
| |
| ASSERT_NO_FATAL_FAILURE(TestGetAll( |
| /*database_parameters=*/ |
| { |
| /*object_store_parameters=*/ |
| { |
| .object_store_id = kTestObjectStoreId, |
| .key_path = object_store_key_path, |
| .auto_increment = true, |
| }, |
| /*index_parameters=*/ |
| { |
| .index_id = kTestIndexId, |
| }, |
| /*database_records=*/ |
| { |
| { |
| /*primary_key (generated)=*/IndexedDBKey(), |
| {"value1", /*external_objects=*/{}}, |
| IndexedDBKey("index_key3"), |
| }, |
| { |
| /*primary_key (generated)=*/IndexedDBKey(), |
| {"value2", /*external_objects=*/{}}, |
| IndexedDBKey("index_key2"), |
| }, |
| { |
| /*primary_key (generated)=*/IndexedDBKey(), |
| {"value3", /*external_objects=*/{}}, |
| IndexedDBKey("index_key1"), |
| }, |
| }, |
| }, |
| /*get_all_parameters=*/ |
| { |
| .result_type = blink::mojom::IDBGetAllResultType::Records, |
| }, |
| expected_results)); |
| } |
| |
| } // namespace content::indexed_db |