Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(358)

Unified Diff: content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc

Issue 2784003002: [IndexedDB] Mojo testing harness. (Closed)
Patch Set: comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7911bf328748cc3ebff689c2a45d7401aca0b17e
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -0,0 +1,431 @@
+// Copyright 2017 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_dispatcher_host.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/utf_offset_string_conversions.h"
+#include "base/test/test_simple_task_runner.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/indexed_db/indexed_db_callbacks.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_pending_connection.h"
+#include "content/browser/indexed_db/mock_mojo_indexed_db_callbacks.h"
+#include "content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h"
+#include "content/common/indexed_db/indexed_db.mojom.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding.h"
+#include "net/url_request/url_request_test_util.h"
+#include "storage/browser/test/mock_quota_manager_proxy.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
+#include "url/origin.h"
+
+using indexed_db::mojom::Callbacks;
+using indexed_db::mojom::CallbacksAssociatedPtrInfo;
+using indexed_db::mojom::DatabaseAssociatedPtr;
+using indexed_db::mojom::DatabaseAssociatedRequest;
+using indexed_db::mojom::DatabaseCallbacks;
+using indexed_db::mojom::DatabaseCallbacksAssociatedPtrInfo;
+using indexed_db::mojom::FactoryAssociatedPtr;
+using indexed_db::mojom::FactoryAssociatedRequest;
+using indexed_db::mojom::KeyPath;
+using indexed_db::mojom::Value;
+using indexed_db::mojom::ValuePtr;
+using mojo::StrongAssociatedBindingPtr;
+using testing::_;
+
+namespace content {
+namespace {
+
+ACTION_TEMPLATE(MoveArg,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(out)) {
+ *out = std::move(*::testing::get<k>(args));
+};
+
+MATCHER_P(IsAssociatedInterfacePtrInfoValid,
+ tf,
+ std::string(negation ? "isn't" : "is") + " " +
+ std::string(tf ? "valid" : "invalid")) {
+ return tf == arg->is_valid();
+}
+
+MATCHER_P(MatchesIDBKey, key, "") {
+ return arg.Equals(key);
+}
+
+static const char kDatabaseName[] = "db";
+static const char kOrigin[] = "https://www.example.com";
+static const int kFakeProcessId = 2;
+
+base::FilePath CreateAndReturnTempDir(base::ScopedTempDir* temp_dir) {
+ CHECK(temp_dir->CreateUniqueTempDir());
+ return temp_dir->GetPath();
+}
+
+// Represents a transaction from the renderer side.
+class TestTransaction {
+ public:
+ using CallbackList = std::list<std::unique_ptr<MockMojoIndexedDBCallbacks>>;
+
+ TestTransaction(::indexed_db::mojom::Database* database,
+ int64_t transaction_id)
+ : database_(database), transaction_id_(transaction_id) {}
+ TestTransaction(TestTransaction&& other) = default;
+
+ ~TestTransaction() {}
+
+ void EnqueueCreateObjectStore(int64_t object_store_id,
+ base::string16 name,
+ const content::IndexedDBKeyPath& key_path,
+ bool auto_increment) {
+ database_->CreateObjectStore(transaction_id_, object_store_id, name,
+ key_path, auto_increment);
+ }
+
+ void EnqueueCommit() { database_->Commit(transaction_id_); }
+
+ CallbackList::iterator EnqueuePut(
+ int64_t object_store_id,
+ ValuePtr value,
+ const content::IndexedDBKey& key,
+ blink::WebIDBPutMode mode,
+ const std::vector<content::IndexedDBIndexKeys>& index_keys) {
+ // Create our callback.
+ CallbacksAssociatedPtrInfo callbacks_ptr_info;
+ std::unique_ptr<MockMojoIndexedDBCallbacks> callbacks(
+ new MockMojoIndexedDBCallbacks(mojo::MakeRequest(&callbacks_ptr_info)));
+ callback_list_.push_back(std::move(callbacks));
+ database_->Put(transaction_id_, object_store_id, std::move(value), key,
+ mode, index_keys, std::move(callbacks_ptr_info));
+ return --callback_list_.end();
+ }
+
+ void ExpectTxnComplete(
+ MockMojoIndexedDBDatabaseCallbacks* connection_callbacks) {
+ EXPECT_CALL(*connection_callbacks, Complete(transaction_id_)).Times(1);
+ }
+
+ MockMojoIndexedDBCallbacks* GetCallbacksObject(CallbackList::iterator it) {
+ return (*it).get();
+ }
+
+ private:
+ ::indexed_db::mojom::Database* database_;
+ int64_t transaction_id_;
+ CallbackList callback_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTransaction);
+};
+
+// Represents a database connection from the renderer side.
+class TestDatabaseConnection {
Reilly Grant (use Gerrit) 2017/05/12 01:46:52 I recommend against using a class like this becaus
+ public:
+ TestDatabaseConnection(url::Origin origin,
+ base::string16 db_name,
+ int64_t version,
+ int64_t upgrade_txn_id)
+ : origin_(std::move(origin)),
+ db_name_(std::move(db_name)),
+ version_(version),
+ upgrade_txn_id_(upgrade_txn_id){};
+ TestDatabaseConnection(TestDatabaseConnection&& other) = default;
+
+ ~TestDatabaseConnection() {}
+
+ // The factory has to be flushed or the io thread run for the message to be
+ // sent.
+ void QueueOpenRequestAndCreateCallbacks(FactoryAssociatedPtr* factory) {
+ ASSERT_FALSE(IsCallbackBound()) << "Connection already opened.";
+
+ CallbacksAssociatedPtrInfo callbacks_ptr_info;
+ DatabaseCallbacksAssociatedPtrInfo database_callbacks_ptr_info;
+
+ open_callbacks_.reset(new MockMojoIndexedDBCallbacks(
+ ::mojo::MakeRequest(&callbacks_ptr_info)));
+ connection_callbacks_.reset(new MockMojoIndexedDBDatabaseCallbacks(
+ ::mojo::MakeRequest(&database_callbacks_ptr_info)));
+
+ (*factory)->Open(std::move(callbacks_ptr_info),
+ std::move(database_callbacks_ptr_info), origin_, db_name_,
+ version_, upgrade_txn_id_);
+ }
+
+ void ExpectUpgradeCall(int64_t old_version) {
+ EXPECT_FALSE(database_info_.is_valid());
+ EXPECT_CALL(*open_callbacks_,
+ MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
+ old_version, blink::kWebIDBDataLossNone,
+ std::string(""), _))
+ .WillOnce(testing::DoAll(MoveArg<0>(&database_info_),
+ testing::SaveArg<4>(&metadata_)));
+ }
+
+ void ExpectDatabaseSuccess(bool with_database) {
+ if (with_database) {
+ EXPECT_CALL(
+ *open_callbacks_,
+ MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(true), _))
+ .WillOnce(testing::DoAll(MoveArg<0>(&database_info_),
+ testing::SaveArg<1>(&metadata_)));
+ } else {
+ EXPECT_CALL(
+ *open_callbacks_,
+ MockedSuccessDatabase(IsAssociatedInterfacePtrInfoValid(false), _));
+ }
+ }
+
+ void ValidateMetadata() {
+ EXPECT_EQ(db_name_, metadata_.name);
+ EXPECT_EQ(version_, metadata_.version);
+ }
+
+ bool IsCallbackBound() { return open_callbacks_ && connection_callbacks_; }
+
+ bool IsDatabaseBound() { return database_.is_bound(); }
+
+ void CreateDatabasePtr() {
+ ASSERT_TRUE(database_info_.is_valid());
+ database_.Bind(std::move(database_info_));
+ }
+
+ void FlushDatabaseMessages() { database_.FlushForTesting(); }
+
+ ::indexed_db::mojom::Database* database() {
+ EXPECT_TRUE(IsDatabaseBound());
+ return database_.get();
+ }
+
+ // Callbacks given to the 'open' call - used for upgrading & recieving the db.
+ MockMojoIndexedDBCallbacks* open_callbacks() { return open_callbacks_.get(); }
+
+ // Callbacks given to the 'open' call for interacting with connection.
+ MockMojoIndexedDBDatabaseCallbacks* connection_callbacks() {
+ return connection_callbacks_.get();
+ }
+
+ const IndexedDBDatabaseMetadata& metadata() { return metadata_; }
+
+ private:
+ url::Origin origin_;
+ base::string16 db_name_;
+ int64_t version_;
+ int64_t upgrade_txn_id_;
+
+ DatabaseAssociatedPtr database_;
+ IndexedDBDatabaseMetadata metadata_;
+
+ ::indexed_db::mojom::DatabaseAssociatedPtrInfo database_info_;
+
+ std::unique_ptr<MockMojoIndexedDBCallbacks> open_callbacks_;
+ std::unique_ptr<MockMojoIndexedDBDatabaseCallbacks> connection_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDatabaseConnection);
+};
+
+} // namespace
+
+class IndexedDBDispatcherHostTest : public testing::Test {
+ public:
+ IndexedDBDispatcherHostTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ db_task_runner_(new base::TestSimpleTaskRunner()),
+ request_context_getter_(
+ new net::TestURLRequestContextGetter(db_task_runner_)),
+ special_storage_policy_(new MockSpecialStoragePolicy()),
+ quota_manager_proxy_(
+ new MockQuotaManagerProxy(nullptr, db_task_runner_.get())),
+ context_impl_(
+ new IndexedDBContextImpl(CreateAndReturnTempDir(&temp_dir_),
+ special_storage_policy_.get(),
+ quota_manager_proxy_.get(),
+ db_task_runner_.get())),
+ blob_storage_(ChromeBlobStorageContext::GetFor(&browser_context_)),
+ host_(new IndexedDBDispatcherHost(kFakeProcessId,
+ request_context_getter_.get(),
+ context_impl_.get(),
+ blob_storage_.get())) {
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void TearDown() override {
+ host_.reset();
+ base::RunLoop().RunUntilIdle();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ // We leak files if this doesn't return true.
+ ASSERT_TRUE(temp_dir_.Delete());
+ }
+
+ void SetUp() override {
+ FactoryAssociatedRequest request =
+ ::mojo::MakeIsolatedRequest(&idb_mojo_factory_);
+ host_->AddBinding(std::move(request));
+ }
+
+ protected:
+ TestBrowserThreadBundle thread_bundle_;
+ TestBrowserContext browser_context_;
+
+ base::ScopedTempDir temp_dir_;
+ scoped_refptr<base::TestSimpleTaskRunner> db_task_runner_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
+ scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
+ scoped_refptr<IndexedDBContextImpl> context_impl_;
+ scoped_refptr<ChromeBlobStorageContext> blob_storage_;
+ std::unique_ptr<IndexedDBDispatcherHost, BrowserThread::DeleteOnIOThread>
+ host_;
+ FactoryAssociatedPtr idb_mojo_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcherHostTest);
+};
+
+TEST_F(IndexedDBDispatcherHostTest, CloseConnectionBeforeUpgrade) {
+ TestDatabaseConnection connection(url::Origin(GURL(kOrigin)),
+ base::UTF8ToUTF16(kDatabaseName), 1, 1);
+
+ // Queue open request message.
+ connection.QueueOpenRequestAndCreateCallbacks(&idb_mojo_factory_);
+
+ // Create mock expectation.
+ connection.ExpectUpgradeCall(IndexedDBDatabaseMetadata::NO_VERSION);
+
+ // Send message.
+ idb_mojo_factory_.FlushForTesting();
+
+ // Run database tasks and io tasks to send message back.
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ connection.ValidateMetadata();
+}
+
+TEST_F(IndexedDBDispatcherHostTest, CloseAfterUpgrade) {
+ // Open connection.
+ TestDatabaseConnection connection(url::Origin(GURL(kOrigin)),
+ base::UTF8ToUTF16(kDatabaseName), 1, 1);
+ connection.QueueOpenRequestAndCreateCallbacks(&idb_mojo_factory_);
+ connection.ExpectUpgradeCall(IndexedDBDatabaseMetadata::NO_VERSION);
+ idb_mojo_factory_.FlushForTesting();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ connection.ValidateMetadata();
+ connection.CreateDatabasePtr();
+
+ // Create object store.
+ TestTransaction txn(connection.database(), 1);
+ txn.EnqueueCreateObjectStore(10, base::UTF8ToUTF16(kDatabaseName),
+ content::IndexedDBKeyPath(), false);
+ txn.EnqueueCommit();
+ txn.ExpectTxnComplete(connection.connection_callbacks());
+ connection.ExpectDatabaseSuccess(/* with_database */ false);
+
+ connection.FlushDatabaseMessages();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(IndexedDBDispatcherHostTest, OpenNewConnectionWhileUpgrading) {
+ // Open connection 1.
+ TestDatabaseConnection connection1(url::Origin(GURL(kOrigin)),
+ base::UTF8ToUTF16(kDatabaseName), 1, 1);
+ connection1.QueueOpenRequestAndCreateCallbacks(&idb_mojo_factory_);
+ connection1.ExpectUpgradeCall(IndexedDBDatabaseMetadata::NO_VERSION);
+ idb_mojo_factory_.FlushForTesting();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ connection1.ValidateMetadata();
+ connection1.CreateDatabasePtr();
+
+ // Open connection 2. Should not open yet, as we are pending upgrade of
+ // database on connection 1.
+ TestDatabaseConnection connection2(url::Origin(GURL(kOrigin)),
+ base::UTF8ToUTF16(kDatabaseName), 1, 0);
+ connection2.QueueOpenRequestAndCreateCallbacks(&idb_mojo_factory_);
+ connection2.ExpectDatabaseSuccess(true);
+ idb_mojo_factory_.FlushForTesting();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION,
+ connection2.metadata().version);
+
+ // Create object store on first connection
+ TestTransaction txn(connection1.database(), 1);
+ txn.EnqueueCreateObjectStore(10, base::UTF8ToUTF16(kDatabaseName),
+ content::IndexedDBKeyPath(), false);
+ txn.EnqueueCommit();
+ txn.ExpectTxnComplete(connection1.connection_callbacks());
+ connection1.ExpectDatabaseSuccess(/* with_database */ false);
+ connection1.FlushDatabaseMessages();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+
+ connection2.ValidateMetadata();
+ connection2.CreateDatabasePtr();
+}
+
+TEST_F(IndexedDBDispatcherHostTest, PutWithInvalidBlob) {
+ const int64_t kObjectStoreId = 10;
+ const int64_t kUpgradeTxnId = 1;
+ // Open connection 1.
+ TestDatabaseConnection connection(url::Origin(GURL(kOrigin)),
+ base::UTF8ToUTF16(kDatabaseName), 1,
+ kUpgradeTxnId);
+ connection.QueueOpenRequestAndCreateCallbacks(&idb_mojo_factory_);
+ connection.ExpectUpgradeCall(IndexedDBDatabaseMetadata::NO_VERSION);
+ idb_mojo_factory_.FlushForTesting();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ connection.ValidateMetadata();
+ connection.CreateDatabasePtr();
+
+ // Create object store on first connection
+ TestTransaction txn(connection.database(), kUpgradeTxnId);
+ txn.EnqueueCreateObjectStore(kObjectStoreId, base::UTF8ToUTF16(kDatabaseName),
+ content::IndexedDBKeyPath(), false);
+
+ // Call Put with an invalid blob.
+ std::vector<::indexed_db::mojom::BlobInfoPtr> blobs;
+ blobs.push_back(::indexed_db::mojom::BlobInfo::New(
+ "fakeUUID", base::string16(), 100, nullptr));
+ auto callback_it = txn.EnqueuePut(
+ kObjectStoreId, Value::New("hello", std::move(blobs)),
+ content::IndexedDBKey(base::UTF8ToUTF16("hello")),
+ blink::kWebIDBPutModeAddOnly, std::vector<content::IndexedDBIndexKeys>());
+ txn.EnqueueCommit();
+
+ // Test we get right sequence.
+ {
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*txn.GetCallbacksObject(callback_it),
+ Error(blink::kWebIDBDatabaseExceptionUnknownError, _))
+ .Times(1);
+
+ EXPECT_CALL(
+ *connection.connection_callbacks(),
+ Abort(kUpgradeTxnId, blink::kWebIDBDatabaseExceptionUnknownError, _))
+ .Times(1);
+
+ EXPECT_CALL(*connection.open_callbacks(),
+ Error(blink::kWebIDBDatabaseExceptionAbortError, _))
+ .Times(1);
+ }
+ connection.FlushDatabaseMessages();
+ db_task_runner_->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace content
« no previous file with comments | « content/browser/indexed_db/indexed_db_dispatcher_host.cc ('k') | content/browser/indexed_db/indexed_db_transaction.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698