Commit 0f84f4f9 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Remove FakeLevelDBDatabase[ErrorOnWrite]

This gets rid of fake implementations of the LevelDBDatabase mojom
interface and refactors all dependent tests to use a real
in-memory LevelDBDatabaseImpl instead.

Local/SessionStorageContextMojo are updated to use a LevelDBDatabaseImpl
directly, and other DOM storage classes can by updated similarly in
follow-up CLs. This is a step in incrementally deleting the
LevelDBDatabase mojom interface and migrating DOM storage implementation
to consume a DomStorageDatabase directly.

Bug: 1000959
Change-Id: Ieaa8e46a6ebc0f41dc85b3c60423552c88cb0c6a
Tbr: sky@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1837145
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#704376}
parent 2185050d
...@@ -185,6 +185,8 @@ DomStorageDatabase::DomStorageDatabase( ...@@ -185,6 +185,8 @@ DomStorageDatabase::DomStorageDatabase(
DomStorageDatabase::~DomStorageDatabase() { DomStorageDatabase::~DomStorageDatabase() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this); this);
if (destruction_callback_)
std::move(destruction_callback_).Run();
} }
// static // static
...@@ -337,6 +339,8 @@ DomStorageDatabase::Status DomStorageDatabase::Commit( ...@@ -337,6 +339,8 @@ DomStorageDatabase::Status DomStorageDatabase::Commit(
leveldb::WriteBatch* batch) const { leveldb::WriteBatch* batch) const {
if (!db_) if (!db_)
return Status::IOError(kInvalidDatabaseMessage); return Status::IOError(kInvalidDatabaseMessage);
if (fail_commits_for_testing_)
return Status::IOError("Simulated I/O Error");
return db_->Write(leveldb::WriteOptions(), batch); return db_->Write(leveldb::WriteOptions(), batch);
} }
......
...@@ -146,6 +146,12 @@ class DomStorageDatabase : private base::trace_event::MemoryDumpProvider { ...@@ -146,6 +146,12 @@ class DomStorageDatabase : private base::trace_event::MemoryDumpProvider {
// usable; in such cases, all future operations will return an IOError status. // usable; in such cases, all future operations will return an IOError status.
Status RewriteDB(); Status RewriteDB();
void SetDestructionCallbackForTesting(base::OnceClosure callback) {
destruction_callback_ = std::move(callback);
}
void MakeAllCommitsFailForTesting() { fail_commits_for_testing_ = true; }
private: private:
friend class base::SequenceBound<DomStorageDatabase>; friend class base::SequenceBound<DomStorageDatabase>;
...@@ -194,6 +200,13 @@ class DomStorageDatabase : private base::trace_event::MemoryDumpProvider { ...@@ -194,6 +200,13 @@ class DomStorageDatabase : private base::trace_event::MemoryDumpProvider {
memory_dump_id_; memory_dump_id_;
std::unique_ptr<leveldb::DB> db_; std::unique_ptr<leveldb::DB> db_;
// Causes all calls to |Commit()| to fail with an IOError for simulated
// disk failures in testing.
bool fail_commits_for_testing_ = false;
// Callback to run on destruction in tests.
base::OnceClosure destruction_callback_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(DomStorageDatabase); DISALLOW_COPY_AND_ASSIGN(DomStorageDatabase);
......
...@@ -17,6 +17,7 @@ include_rules = [ ...@@ -17,6 +17,7 @@ include_rules = [
"+components/services/leveldb", "+components/services/leveldb",
"+components/services/quarantine/test_support.h", "+components/services/quarantine/test_support.h",
"+components/services/quarantine/quarantine.h", "+components/services/quarantine/quarantine.h",
"+components/services/storage",
"+components/session_manager/core", "+components/session_manager/core",
"+components/leveldb_proto/public", "+components/leveldb_proto/public",
"+components/link_header_util", "+components/link_header_util",
......
...@@ -435,17 +435,14 @@ void DOMStorageContextWrapper::OpenSessionStorage( ...@@ -435,17 +435,14 @@ void DOMStorageContextWrapper::OpenSessionStorage(
std::move(receiver))); std::move(receiver)));
} }
void DOMStorageContextWrapper::SetLocalStorageDatabaseFactoryForTesting( void DOMStorageContextWrapper::SetLocalStorageDatabaseOpenCallbackForTesting(
base::RepeatingCallback<std::unique_ptr<leveldb::mojom::LevelDBDatabase>()> LocalStorageDatabaseOpenCallback callback) {
factory) {
// base::Unretained is safe here, because the mojo_state_ won't be deleted
// until a ShutdownAndDelete task has been ran on the mojo_task_runner_, and
// as soon as that task is posted, mojo_state_ is set to null, preventing
// further tasks from being queued.
mojo_task_runner_->PostTask( mojo_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&LocalStorageContextMojo::SetDatabaseFactoryForTesting, base::BindOnce(
base::Unretained(mojo_state_), std::move(factory))); &LocalStorageContextMojo::SetDatabaseOpenCallbackForTesting,
base::Unretained(mojo_state_),
base::BindOnce(std::move(callback), mojo_state_)));
} }
scoped_refptr<SessionStorageNamespaceImpl> scoped_refptr<SessionStorageNamespaceImpl>
......
...@@ -104,9 +104,10 @@ class CONTENT_EXPORT DOMStorageContextWrapper ...@@ -104,9 +104,10 @@ class CONTENT_EXPORT DOMStorageContextWrapper
mojo::ReportBadMessageCallback bad_message_callback, mojo::ReportBadMessageCallback bad_message_callback,
mojo::PendingReceiver<blink::mojom::SessionStorageNamespace> receiver); mojo::PendingReceiver<blink::mojom::SessionStorageNamespace> receiver);
void SetLocalStorageDatabaseFactoryForTesting( using LocalStorageDatabaseOpenCallback =
base::RepeatingCallback< base::OnceCallback<void(LocalStorageContextMojo*)>;
std::unique_ptr<leveldb::mojom::LevelDBDatabase>()>); void SetLocalStorageDatabaseOpenCallbackForTesting(
LocalStorageDatabaseOpenCallback callback);
SessionStorageContextMojo* mojo_session_state() { SessionStorageContextMojo* mojo_session_state() {
return mojo_session_state_; return mojo_session_state_;
......
...@@ -675,6 +675,11 @@ std::vector<uint8_t> LocalStorageContextMojo::MigrateString( ...@@ -675,6 +675,11 @@ std::vector<uint8_t> LocalStorageContextMojo::MigrateString(
return result; return result;
} }
void LocalStorageContextMojo::SetDatabaseOpenCallbackForTesting(
base::OnceClosure callback) {
RunWhenConnected(std::move(callback));
}
LocalStorageContextMojo::~LocalStorageContextMojo() { LocalStorageContextMojo::~LocalStorageContextMojo() {
DCHECK_EQ(connection_state_, CONNECTION_SHUTDOWN); DCHECK_EQ(connection_state_, CONNECTION_SHUTDOWN);
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
...@@ -702,13 +707,6 @@ void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { ...@@ -702,13 +707,6 @@ void LocalStorageContextMojo::RunWhenConnected(base::OnceClosure callback) {
void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) { void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) {
DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
if (database_factory_for_testing_) {
in_memory_ = true;
database_ = database_factory_for_testing_.Run();
OnDatabaseOpened(leveldb::mojom::DatabaseError::OK);
return;
}
if (!directory_.empty() && directory_.IsAbsolute() && !in_memory_only) { if (!directory_.empty() && directory_.IsAbsolute() && !in_memory_only) {
// We were given a subdirectory to write to, so use a disk-backed database. // We were given a subdirectory to write to, so use a disk-backed database.
leveldb_env::Options options; leveldb_env::Options options;
......
...@@ -15,9 +15,12 @@ ...@@ -15,9 +15,12 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/memory_dump_provider.h" #include "base/trace_event/memory_dump_provider.h"
#include "components/services/leveldb/leveldb_database_impl.h"
#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "components/services/leveldb/public/mojom/leveldb.mojom.h"
#include "components/services/storage/dom_storage/dom_storage_database.h"
#include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/browser/dom_storage/dom_storage_task_runner.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -95,12 +98,6 @@ class CONTENT_EXPORT LocalStorageContextMojo ...@@ -95,12 +98,6 @@ class CONTENT_EXPORT LocalStorageContextMojo
// Clears unused storage areas, when thresholds are reached. // Clears unused storage areas, when thresholds are reached.
void PurgeUnusedAreasIfNeeded(); void PurgeUnusedAreasIfNeeded();
using DatabaseFactory = base::RepeatingCallback<
std::unique_ptr<leveldb::mojom::LevelDBDatabase>()>;
void SetDatabaseFactoryForTesting(DatabaseFactory factory) {
database_factory_for_testing_ = std::move(factory);
}
// base::trace_event::MemoryDumpProvider implementation. // base::trace_event::MemoryDumpProvider implementation.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override; base::trace_event::ProcessMemoryDump* pmd) override;
...@@ -112,6 +109,17 @@ class CONTENT_EXPORT LocalStorageContextMojo ...@@ -112,6 +109,17 @@ class CONTENT_EXPORT LocalStorageContextMojo
return task_runner_; return task_runner_;
} }
// Access the underlying DomStorageDatabase. May be null if the database is
// not yet open.
const base::SequenceBound<storage::DomStorageDatabase>&
GetDatabaseForTesting() const {
return database_->database();
}
// Wait for the database to be opened, or for opening to fail. If the database
// is already opened, |callback| is invoked immediately.
void SetDatabaseOpenCallbackForTesting(base::OnceClosure callback);
private: private:
friend class DOMStorageBrowserTest; friend class DOMStorageBrowserTest;
...@@ -183,7 +191,7 @@ class CONTENT_EXPORT LocalStorageContextMojo ...@@ -183,7 +191,7 @@ class CONTENT_EXPORT LocalStorageContextMojo
base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_; base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_;
std::unique_ptr<leveldb::mojom::LevelDBDatabase> database_; std::unique_ptr<leveldb::LevelDBDatabaseImpl> database_;
bool tried_to_recreate_during_open_ = false; bool tried_to_recreate_during_open_ = false;
bool in_memory_ = false; bool in_memory_ = false;
...@@ -205,8 +213,6 @@ class CONTENT_EXPORT LocalStorageContextMojo ...@@ -205,8 +213,6 @@ class CONTENT_EXPORT LocalStorageContextMojo
// Name of an extra histogram to log open results to, if not null. // Name of an extra histogram to log open results to, if not null.
const char* open_result_histogram_ = nullptr; const char* open_result_histogram_ = nullptr;
DatabaseFactory database_factory_for_testing_;
base::WeakPtrFactory<LocalStorageContextMojo> weak_ptr_factory_{this}; base::WeakPtrFactory<LocalStorageContextMojo> weak_ptr_factory_{this};
}; };
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "content/browser/dom_storage/session_storage_metadata.h" #include "content/browser/dom_storage/session_storage_metadata.h"
#include "content/browser/dom_storage/test/storage_area_test_util.h" #include "content/browser/dom_storage/test/storage_area_test_util.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "content/test/fake_leveldb_database.h"
#include "content/test/gmock_util.h" #include "content/test/gmock_util.h"
#include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/associated_remote.h"
......
...@@ -535,6 +535,11 @@ void SessionStorageContextMojo::FlushAreaForTesting( ...@@ -535,6 +535,11 @@ void SessionStorageContextMojo::FlushAreaForTesting(
it->second->FlushOriginForTesting(origin); it->second->FlushOriginForTesting(origin);
} }
void SessionStorageContextMojo::SetDatabaseOpenCallbackForTesting(
base::OnceClosure callback) {
RunWhenConnected(std::move(callback));
}
scoped_refptr<SessionStorageMetadata::MapData> scoped_refptr<SessionStorageMetadata::MapData>
SessionStorageContextMojo::RegisterNewAreaMap( SessionStorageContextMojo::RegisterNewAreaMap(
SessionStorageMetadata::NamespaceEntry namespace_entry, SessionStorageMetadata::NamespaceEntry namespace_entry,
...@@ -698,13 +703,6 @@ void SessionStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { ...@@ -698,13 +703,6 @@ void SessionStorageContextMojo::RunWhenConnected(base::OnceClosure callback) {
void SessionStorageContextMojo::InitiateConnection(bool in_memory_only) { void SessionStorageContextMojo::InitiateConnection(bool in_memory_only) {
DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
if (database_factory_for_testing_) {
database_ = database_factory_for_testing_.Run();
in_memory_ = true;
OnDatabaseOpened(leveldb::mojom::DatabaseError::OK);
return;
}
if (backing_mode_ != BackingMode::kNoDisk && !in_memory_only && if (backing_mode_ != BackingMode::kNoDisk && !in_memory_only &&
!partition_directory_.empty()) { !partition_directory_.empty()) {
// We were given a subdirectory to write to, so use a disk backed database. // We were given a subdirectory to write to, so use a disk backed database.
......
...@@ -18,8 +18,11 @@ ...@@ -18,8 +18,11 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/threading/sequence_bound.h"
#include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/memory_dump_provider.h" #include "base/trace_event/memory_dump_provider.h"
#include "components/services/leveldb/leveldb_database_impl.h"
#include "components/services/storage/dom_storage/dom_storage_database.h"
#include "content/browser/dom_storage/session_storage_data_map.h" #include "content/browser/dom_storage/session_storage_data_map.h"
#include "content/browser/dom_storage/session_storage_metadata.h" #include "content/browser/dom_storage/session_storage_metadata.h"
#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" #include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h"
...@@ -132,12 +135,6 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -132,12 +135,6 @@ class CONTENT_EXPORT SessionStorageContextMojo
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override; base::trace_event::ProcessMemoryDump* pmd) override;
using DatabaseFactory = base::RepeatingCallback<
std::unique_ptr<leveldb::mojom::LevelDBDatabase>()>;
void SetDatabaseFactoryForTesting(DatabaseFactory factory) {
database_factory_for_testing_ = std::move(factory);
}
void PretendToConnectForTesting(); void PretendToConnectForTesting();
leveldb::mojom::LevelDBDatabase* DatabaseForTesting() { leveldb::mojom::LevelDBDatabase* DatabaseForTesting() {
...@@ -147,6 +144,17 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -147,6 +144,17 @@ class CONTENT_EXPORT SessionStorageContextMojo
void FlushAreaForTesting(const std::string& namespace_id, void FlushAreaForTesting(const std::string& namespace_id,
const url::Origin& origin); const url::Origin& origin);
// Access the underlying DomStorageDatabase. May be null if the database is
// not yet open.
const base::SequenceBound<storage::DomStorageDatabase>&
GetDatabaseForTesting() const {
return database_->database();
}
// Wait for the database to be opened, or for opening to fail. If the database
// is already opened, |callback| is invoked immediately.
void SetDatabaseOpenCallbackForTesting(base::OnceClosure callback);
private: private:
friend class DOMStorageBrowserTest; friend class DOMStorageBrowserTest;
FRIEND_TEST_ALL_PREFIXES(SessionStorageContextMojoTest, FRIEND_TEST_ALL_PREFIXES(SessionStorageContextMojoTest,
...@@ -250,7 +258,7 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -250,7 +258,7 @@ class CONTENT_EXPORT SessionStorageContextMojo
base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_; base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_;
std::unique_ptr<leveldb::mojom::LevelDBDatabase> database_; std::unique_ptr<leveldb::LevelDBDatabaseImpl> database_;
bool in_memory_ = false; bool in_memory_ = false;
bool tried_to_recreate_during_open_ = false; bool tried_to_recreate_during_open_ = false;
...@@ -281,8 +289,6 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -281,8 +289,6 @@ class CONTENT_EXPORT SessionStorageContextMojo
// Name of an extra histogram to log open results to, if not null. // Name of an extra histogram to log open results to, if not null.
const char* open_result_histogram_ = nullptr; const char* open_result_histogram_ = nullptr;
DatabaseFactory database_factory_for_testing_;
base::WeakPtrFactory<SessionStorageContextMojo> weak_ptr_factory_{this}; base::WeakPtrFactory<SessionStorageContextMojo> weak_ptr_factory_{this};
}; };
......
...@@ -8,12 +8,16 @@ ...@@ -8,12 +8,16 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/span.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "components/services/leveldb/leveldb_database_impl.h"
#include "components/services/leveldb/public/cpp/util.h" #include "components/services/leveldb/public/cpp/util.h"
#include "components/services/storage/dom_storage/dom_storage_database.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "content/test/fake_leveldb_database.h"
#include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
...@@ -26,6 +30,10 @@ namespace { ...@@ -26,6 +30,10 @@ namespace {
using leveldb::StdStringToUint8Vector; using leveldb::StdStringToUint8Vector;
using leveldb::Uint8VectorToStdString; using leveldb::Uint8VectorToStdString;
base::span<const uint8_t> MakeBytes(base::StringPiece str) {
return base::as_bytes(base::make_span(str));
}
class MockListener : public SessionStorageDataMap::Listener { class MockListener : public SessionStorageDataMap::Listener {
public: public:
MockListener() {} MockListener() {}
...@@ -79,23 +87,58 @@ class GetAllCallback : public blink::mojom::StorageAreaGetAllCallback { ...@@ -79,23 +87,58 @@ class GetAllCallback : public blink::mojom::StorageAreaGetAllCallback {
class SessionStorageDataMapTest : public testing::Test { class SessionStorageDataMapTest : public testing::Test {
public: public:
SessionStorageDataMapTest() SessionStorageDataMapTest()
: test_origin_(url::Origin::Create(GURL("http://host1.com:1"))), : test_origin_(url::Origin::Create(GURL("http://host1.com:1"))) {
database_(&mock_data_) { base::RunLoop loop;
// Should show up in first map. database_ = leveldb::LevelDBDatabaseImpl::OpenInMemory(
mock_data_[StdStringToUint8Vector("map-1-key1")] = base::nullopt, "SessionStorageDataMapTest",
StdStringToUint8Vector("data1"); base::CreateSequencedTaskRunner({base::MayBlock(), base::ThreadPool()}),
// Dummy data to verify we don't delete everything. base::BindLambdaForTesting([&](leveldb::mojom::DatabaseError error) {
mock_data_[StdStringToUint8Vector("map-3-key1")] = ASSERT_EQ(leveldb::mojom::DatabaseError::OK, error);
StdStringToUint8Vector("data3"); loop.Quit();
}));
loop.Run();
database_->database().PostTaskWithThisObject(
FROM_HERE, base::BindOnce([](const storage::DomStorageDatabase& db) {
// Should show up in first map.
leveldb::Status status =
db.Put(MakeBytes("map-1-key1"), MakeBytes("data1"));
ASSERT_TRUE(status.ok());
// Dummy data to verify we don't delete everything.
status = db.Put(MakeBytes("map-3-key1"), MakeBytes("data3"));
ASSERT_TRUE(status.ok());
}));
}
~SessionStorageDataMapTest() override = default;
std::map<std::string, std::string> GetDatabaseContents() {
std::vector<storage::DomStorageDatabase::KeyValuePair> entries;
base::RunLoop loop;
database_->database().PostTaskWithThisObject(
FROM_HERE,
base::BindLambdaForTesting([&](const storage::DomStorageDatabase& db) {
leveldb::Status status = db.GetPrefixed({}, &entries);
ASSERT_TRUE(status.ok());
loop.Quit();
}));
loop.Run();
std::map<std::string, std::string> contents;
for (auto& entry : entries) {
contents.emplace(std::string(entry.key.begin(), entry.key.end()),
std::string(entry.value.begin(), entry.value.end()));
}
return contents;
} }
~SessionStorageDataMapTest() override {}
protected: protected:
BrowserTaskEnvironment task_environment_; BrowserTaskEnvironment task_environment_;
testing::StrictMock<MockListener> listener_; testing::StrictMock<MockListener> listener_;
url::Origin test_origin_; url::Origin test_origin_;
std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_; std::unique_ptr<leveldb::LevelDBDatabaseImpl> database_;
FakeLevelDBDatabase database_;
}; };
} // namespace } // namespace
...@@ -110,7 +153,7 @@ TEST_F(SessionStorageDataMapTest, BasicEmptyCreation) { ...@@ -110,7 +153,7 @@ TEST_F(SessionStorageDataMapTest, BasicEmptyCreation) {
&listener_, &listener_,
base::MakeRefCounted<SessionStorageMetadata::MapData>(1, base::MakeRefCounted<SessionStorageMetadata::MapData>(1,
test_origin_), test_origin_),
&database_); database_.get());
bool success; bool success;
std::vector<blink::mojom::KeyValuePtr> data; std::vector<blink::mojom::KeyValuePtr> data;
...@@ -131,7 +174,7 @@ TEST_F(SessionStorageDataMapTest, BasicEmptyCreation) { ...@@ -131,7 +174,7 @@ TEST_F(SessionStorageDataMapTest, BasicEmptyCreation) {
// Test data is not cleared on deletion. // Test data is not cleared on deletion.
map = nullptr; map = nullptr;
EXPECT_EQ(2u, mock_data_.size()); EXPECT_EQ(2u, GetDatabaseContents().size());
} }
TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) { TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) {
...@@ -142,7 +185,7 @@ TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) { ...@@ -142,7 +185,7 @@ TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) {
scoped_refptr<SessionStorageDataMap> map = SessionStorageDataMap::CreateEmpty( scoped_refptr<SessionStorageDataMap> map = SessionStorageDataMap::CreateEmpty(
&listener_, &listener_,
base::MakeRefCounted<SessionStorageMetadata::MapData>(1, test_origin_), base::MakeRefCounted<SessionStorageMetadata::MapData>(1, test_origin_),
&database_); database_.get());
bool success; bool success;
std::vector<blink::mojom::KeyValuePtr> data; std::vector<blink::mojom::KeyValuePtr> data;
...@@ -161,7 +204,7 @@ TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) { ...@@ -161,7 +204,7 @@ TEST_F(SessionStorageDataMapTest, ExplicitlyEmpty) {
// Test data is not cleared on deletion. // Test data is not cleared on deletion.
map = nullptr; map = nullptr;
EXPECT_EQ(2u, mock_data_.size()); EXPECT_EQ(2u, GetDatabaseContents().size());
} }
TEST_F(SessionStorageDataMapTest, Clone) { TEST_F(SessionStorageDataMapTest, Clone) {
...@@ -174,7 +217,7 @@ TEST_F(SessionStorageDataMapTest, Clone) { ...@@ -174,7 +217,7 @@ TEST_F(SessionStorageDataMapTest, Clone) {
&listener_, &listener_,
base::MakeRefCounted<SessionStorageMetadata::MapData>(1, base::MakeRefCounted<SessionStorageMetadata::MapData>(1,
test_origin_), test_origin_),
&database_); database_.get());
EXPECT_CALL(listener_, EXPECT_CALL(listener_,
OnDataMapCreation(StdStringToUint8Vector("2"), testing::_)) OnDataMapCreation(StdStringToUint8Vector("2"), testing::_))
...@@ -205,9 +248,7 @@ TEST_F(SessionStorageDataMapTest, Clone) { ...@@ -205,9 +248,7 @@ TEST_F(SessionStorageDataMapTest, Clone) {
EXPECT_EQ(StdStringToUint8Vector("data1"), data[0]->value); EXPECT_EQ(StdStringToUint8Vector("data1"), data[0]->value);
// Test that the data was copied. // Test that the data was copied.
EXPECT_EQ(StdStringToUint8Vector("data1"), EXPECT_EQ("data1", GetDatabaseContents()["map-2-key1"]);
mock_data_[StdStringToUint8Vector("map-2-key1")]);
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1")))
.Times(1); .Times(1);
EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("2"))) EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("2")))
...@@ -216,7 +257,7 @@ TEST_F(SessionStorageDataMapTest, Clone) { ...@@ -216,7 +257,7 @@ TEST_F(SessionStorageDataMapTest, Clone) {
// Test data is not cleared on deletion. // Test data is not cleared on deletion.
map1 = nullptr; map1 = nullptr;
map2 = nullptr; map2 = nullptr;
EXPECT_EQ(3u, mock_data_.size()); EXPECT_EQ(3u, GetDatabaseContents().size());
} }
} // namespace content } // namespace content
// Copyright 2018 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/dom_storage/test/fake_leveldb_database_error_on_write.h"
#include "base/callback.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace content {
namespace test {
FakeLevelDBDatabaseErrorOnWrite::FakeLevelDBDatabaseErrorOnWrite(
std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
: FakeLevelDBDatabase(mock_data) {}
FakeLevelDBDatabaseErrorOnWrite::~FakeLevelDBDatabaseErrorOnWrite() = default;
void FakeLevelDBDatabaseErrorOnWrite::Write(
std::vector<leveldb::mojom::BatchedOperationPtr> operations,
WriteCallback callback) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
leveldb::mojom::DatabaseError::IO_ERROR));
}
} // namespace test
} // namespace content
// Copyright 2018 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.
#ifndef CONTENT_BROWSER_DOM_STORAGE_TEST_FAKE_LEVELDB_DATABASE_ERROR_ON_WRITE_H_
#define CONTENT_BROWSER_DOM_STORAGE_TEST_FAKE_LEVELDB_DATABASE_ERROR_ON_WRITE_H_
#include <stdint.h>
#include <map>
#include <vector>
#include "content/test/fake_leveldb_database.h"
namespace content {
namespace test {
// Reports an error on every |Write| call.
class FakeLevelDBDatabaseErrorOnWrite : public FakeLevelDBDatabase {
public:
explicit FakeLevelDBDatabaseErrorOnWrite(
std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data);
~FakeLevelDBDatabaseErrorOnWrite() override;
void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations,
WriteCallback callback) override;
};
} // namespace test
} // namespace content
#endif // CONTENT_BROWSER_DOM_STORAGE_TEST_FAKE_LEVELDB_DATABASE_ERROR_ON_WRITE_H_
...@@ -257,8 +257,6 @@ jumbo_static_library("test_support") { ...@@ -257,8 +257,6 @@ jumbo_static_library("test_support") {
"dwrite_font_fake_sender_win.h", "dwrite_font_fake_sender_win.h",
"fake_compositor_dependencies.cc", "fake_compositor_dependencies.cc",
"fake_compositor_dependencies.h", "fake_compositor_dependencies.h",
"fake_leveldb_database.cc",
"fake_leveldb_database.h",
"fake_mojo_message_dispatch_context.h", "fake_mojo_message_dispatch_context.h",
"fake_network.cc", "fake_network.cc",
"fake_network.h", "fake_network.h",
...@@ -1610,8 +1608,6 @@ test("content_unittests") { ...@@ -1610,8 +1608,6 @@ test("content_unittests") {
"../browser/dom_storage/session_storage_metadata_unittest.cc", "../browser/dom_storage/session_storage_metadata_unittest.cc",
"../browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc", "../browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc",
"../browser/dom_storage/storage_area_impl_unittest.cc", "../browser/dom_storage/storage_area_impl_unittest.cc",
"../browser/dom_storage/test/fake_leveldb_database_error_on_write.cc",
"../browser/dom_storage/test/fake_leveldb_database_error_on_write.h",
"../browser/dom_storage/test/storage_area_test_util.cc", "../browser/dom_storage/test/storage_area_test_util.cc",
"../browser/dom_storage/test/storage_area_test_util.h", "../browser/dom_storage/test/storage_area_test_util.h",
"../browser/download/download_manager_impl_unittest.cc", "../browser/download/download_manager_impl_unittest.cc",
......
// Copyright 2016 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/test/fake_leveldb_database.h"
#include <iterator>
#include <utility>
#include "base/bind.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
namespace content {
namespace {
using leveldb::mojom::BatchedOperation;
using leveldb::mojom::BatchedOperationPtr;
leveldb::mojom::KeyValuePtr CreateKeyValue(std::vector<uint8_t> key,
std::vector<uint8_t> value) {
leveldb::mojom::KeyValuePtr result = leveldb::mojom::KeyValue::New();
result->key = std::move(key);
result->value = std::move(value);
return result;
}
base::StringPiece AsStringPiece(const std::vector<uint8_t>& data) {
return base::StringPiece(reinterpret_cast<const char*>(data.data()),
data.size());
}
bool StartsWith(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& prefix) {
return base::StartsWith(AsStringPiece(key), AsStringPiece(prefix),
base::CompareCase::SENSITIVE);
}
std::vector<uint8_t> successor(std::vector<uint8_t> data) {
for (unsigned i = data.size(); i > 0; --i) {
if (data[i - 1] < 255) {
data[i - 1]++;
return data;
}
}
NOTREACHED();
return data;
}
} // namespace
FakeLevelDBDatabase::FakeLevelDBDatabase(
std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data)
: mock_data_(*mock_data) {}
FakeLevelDBDatabase::~FakeLevelDBDatabase() {
if (destruction_callback_)
std::move(destruction_callback_).Run();
}
void FakeLevelDBDatabase::Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
PutCallback callback) {
mock_data_[key] = value;
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Delete(const std::vector<uint8_t>& key,
DeleteCallback callback) {
mock_data_.erase(key);
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::DeletePrefixed(const std::vector<uint8_t>& key_prefix,
DeletePrefixedCallback callback) {
mock_data_.erase(mock_data_.lower_bound(key_prefix),
mock_data_.lower_bound(successor(key_prefix)));
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::RewriteDB(RewriteDBCallback callback) {
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Write(
std::vector<leveldb::mojom::BatchedOperationPtr> operations,
WriteCallback callback) {
// Replace prefix delete and prefix copy with operations first, and then
// execute all operations. This models how the leveldb WriteBatch works.
for (size_t i = 0; i < operations.size(); ++i) {
const auto& op = operations[i];
switch (op->type) {
case leveldb::mojom::BatchOperationType::PUT_KEY:
break;
case leveldb::mojom::BatchOperationType::DELETE_KEY:
break;
case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY: {
std::vector<leveldb::mojom::BatchedOperationPtr> changes;
for (auto map_it = mock_data_.lower_bound(op->key);
map_it != mock_data_.lower_bound(successor(op->key)); ++map_it) {
BatchedOperationPtr item = BatchedOperation::New();
item->type = leveldb::mojom::BatchOperationType::DELETE_KEY;
item->key = map_it->first;
changes.push_back(std::move(item));
}
size_t diff = changes.size();
operations.insert(operations.begin() + i,
std::make_move_iterator(changes.begin()),
std::make_move_iterator(changes.end()));
i += diff;
continue;
}
case leveldb::mojom::BatchOperationType::COPY_PREFIXED_KEY: {
DCHECK(op->value);
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
copy_changes = CopyPrefixedHelper(op->key, *op->value);
std::vector<leveldb::mojom::BatchedOperationPtr> changes;
for (auto& change : copy_changes) {
BatchedOperationPtr item = BatchedOperation::New();
item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
item->key = std::move(change.first);
item->value = std::move(change.second);
changes.push_back(std::move(item));
}
size_t diff = changes.size();
operations.insert(operations.begin() + i,
std::make_move_iterator(changes.begin()),
std::make_move_iterator(changes.end()));
i += diff;
continue;
}
}
}
for (const auto& op : operations) {
switch (op->type) {
case leveldb::mojom::BatchOperationType::PUT_KEY:
DCHECK(op->value);
mock_data_[op->key] = *op->value;
break;
case leveldb::mojom::BatchOperationType::DELETE_KEY:
mock_data_.erase(op->key);
break;
case leveldb::mojom::BatchOperationType::DELETE_PREFIXED_KEY:
break;
case leveldb::mojom::BatchOperationType::COPY_PREFIXED_KEY:
break;
}
}
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
void FakeLevelDBDatabase::Get(const std::vector<uint8_t>& key,
GetCallback callback) {
if (mock_data_.find(key) != mock_data_.end()) {
std::move(callback).Run(leveldb::mojom::DatabaseError::OK, mock_data_[key]);
} else {
std::move(callback).Run(leveldb::mojom::DatabaseError::NOT_FOUND,
std::vector<uint8_t>());
}
}
void FakeLevelDBDatabase::GetPrefixed(const std::vector<uint8_t>& key_prefix,
GetPrefixedCallback callback) {
std::vector<leveldb::mojom::KeyValuePtr> data;
for (const auto& row : mock_data_) {
if (StartsWith(row.first, key_prefix)) {
data.push_back(CreateKeyValue(row.first, row.second));
}
}
std::move(callback).Run(leveldb::mojom::DatabaseError::OK, std::move(data));
}
void FakeLevelDBDatabase::GetMany(
std::vector<leveldb::mojom::GetManyRequestPtr> keys_or_prefixes,
GetManyCallback callback) {
std::vector<leveldb::mojom::GetManyResultPtr> data;
for (const auto& request : keys_or_prefixes) {
leveldb::mojom::GetManyResultPtr result =
leveldb::mojom::GetManyResult::New();
leveldb::Status status;
if (request->is_key()) {
const auto& key = request->get_key();
if (mock_data_.find(key) != mock_data_.end())
result->set_key_value(mock_data_[key]);
else
result->set_status(leveldb::mojom::DatabaseError::NOT_FOUND);
} else {
const auto& key_prefix = request->get_key_prefix();
std::vector<leveldb::mojom::KeyValuePtr> values;
for (const auto& row : mock_data_) {
if (StartsWith(row.first, key_prefix))
values.push_back(CreateKeyValue(row.first, row.second));
}
result->set_key_prefix_values(std::move(values));
}
data.push_back(std::move(result));
}
std::move(callback).Run(std::move(data));
}
void FakeLevelDBDatabase::CopyPrefixed(
const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix,
CopyPrefixedCallback callback) {
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> changes =
CopyPrefixedHelper(source_key_prefix, destination_key_prefix);
mock_data_.insert(changes.begin(), changes.end());
std::move(callback).Run(leveldb::mojom::DatabaseError::OK);
}
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
FakeLevelDBDatabase::CopyPrefixedHelper(
const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix) {
size_t source_key_prefix_size = source_key_prefix.size();
size_t destination_key_prefix_size = destination_key_prefix.size();
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
write_batch;
for (auto map_it = mock_data_.lower_bound(source_key_prefix);
map_it != mock_data_.lower_bound(successor(source_key_prefix));
++map_it) {
size_t excess_key = map_it->first.size() - source_key_prefix_size;
std::vector<uint8_t> new_key(destination_key_prefix_size + excess_key);
std::copy(destination_key_prefix.begin(), destination_key_prefix.end(),
new_key.begin());
std::copy(map_it->first.begin() + source_key_prefix_size,
map_it->first.end(),
new_key.begin() + destination_key_prefix_size);
write_batch.emplace_back(std::move(new_key), map_it->second);
}
return write_batch;
}
} // namespace content
// Copyright 2016 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.
#ifndef CONTENT_TEST_FAKE_LEVELDB_DATABASE_H_
#define CONTENT_TEST_FAKE_LEVELDB_DATABASE_H_
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "components/services/leveldb/public/mojom/leveldb.mojom.h"
namespace content {
// Simple implementation of the leveldb::mojom::LevelDBDatabase interface that
// is backed by a std::map.
class FakeLevelDBDatabase : public leveldb::mojom::LevelDBDatabase {
public:
// |mock_data| must not be null and must outlive this MockLevelDBDatabase
// instance. All callbacks are called synchronously.
explicit FakeLevelDBDatabase(
std::map<std::vector<uint8_t>, std::vector<uint8_t>>* mock_data);
~FakeLevelDBDatabase() override;
void SetDestructionCallback(base::OnceClosure callback) {
destruction_callback_ = std::move(callback);
}
// LevelDBDatabase:
void Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
PutCallback callback) override;
void Delete(const std::vector<uint8_t>& key,
DeleteCallback callback) override;
void DeletePrefixed(const std::vector<uint8_t>& key_prefix,
DeletePrefixedCallback callback) override;
void RewriteDB(RewriteDBCallback callback) override;
void Write(std::vector<leveldb::mojom::BatchedOperationPtr> operations,
WriteCallback callback) override;
void Get(const std::vector<uint8_t>& key, GetCallback callback) override;
void GetPrefixed(const std::vector<uint8_t>& key_prefix,
GetPrefixedCallback callback) override;
void GetMany(std::vector<leveldb::mojom::GetManyRequestPtr> keys_or_prefixes,
GetManyCallback callback) override;
void CopyPrefixed(const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix,
CopyPrefixedCallback callback) override;
private:
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>
CopyPrefixedHelper(const std::vector<uint8_t>& source_key_prefix,
const std::vector<uint8_t>& destination_key_prefix);
std::map<std::vector<uint8_t>, std::vector<uint8_t>>& mock_data_;
base::OnceClosure destruction_callback_;
};
} // namespace content
#endif // CONTENT_TEST_FAKE_LEVELDB_DATABASE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment