blob: d92d4391397f4f560145c69107b5df1a11ced118 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_LEVELDB_PROTO_TESTING_FAKE_DB_H_
#define COMPONENTS_LEVELDB_PROTO_TESTING_FAKE_DB_H_
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "components/leveldb_proto/internal/proto_database_impl.h"
#include "components/leveldb_proto/public/proto_database.h"
#include "components/leveldb_proto/public/shared_proto_database_client_list.h"
namespace leveldb_proto::test {
template <typename P, typename T = P>
class FakeDB : public ProtoDatabaseImpl<P, T> {
using Callback = base::OnceCallback<void(bool)>;
public:
using EntryMap = std::map<std::string, P>;
explicit FakeDB(EntryMap* db);
// ProtoDatabase implementation.
void Init(Callbacks::InitStatusCallback callback) override;
void Init(const leveldb_env::Options& unique_db_options,
Callbacks::InitStatusCallback callback) override;
void UpdateEntries(std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove,
Callbacks::UpdateCallback callback) override;
void UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename Util::Internal<T>::KeyEntryVector>
entries_to_save,
const KeyFilter& filter,
Callbacks::UpdateCallback callback) override;
void LoadEntries(
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const KeyFilter& key_filter,
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadKeysAndEntries(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesWithFilter(
const KeyFilter& filter,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesWithFilter(
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesWhile(
const std::string& start,
const leveldb_proto::KeyIteratorController& controller,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeys(Callbacks::LoadKeysCallback callback) override;
void GetEntry(const std::string& key,
typename Callbacks::Internal<T>::GetCallback callback) override;
void Destroy(Callbacks::DestroyCallback callback) override;
base::FilePath& GetDirectory();
void InitCallback(bool success);
void InitStatusCallback(Enums::InitStatus status);
void LoadCallback(bool success);
void LoadKeysCallback(bool success);
void GetCallback(bool success);
void UpdateCallback(bool success);
void DestroyCallback(bool success);
static base::FilePath DirectoryForTestDB();
// These methods allow enqueueing the results for upcoming Get* or Update*
// calls in advance. When a Get* or Update* call is issued, if there is a
// queued result available, the receiving FakeDB instance will immediately
// post an async task to complete that call with the next queued result.
void QueueGetResult(bool result) { queued_get_results_.push(result); }
void QueueUpdateResult(bool result) { queued_update_results_.push(result); }
private:
void InvokingInvalidCallback(const std::string& callback_name);
static void RunLoadCallback(
typename Callbacks::Internal<T>::LoadCallback callback,
std::unique_ptr<typename std::vector<T>> entries,
bool success);
static void RunLoadKeysAndEntriesCallback(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback,
std::unique_ptr<typename std::map<std::string, T>> entries,
bool success);
static void RunLoadKeysCallback(
typename Callbacks::LoadKeysCallback callback,
std::unique_ptr<std::vector<std::string>> keys,
bool success);
static void RunGetCallback(
typename Callbacks::Internal<T>::GetCallback callback,
std::unique_ptr<T> entry,
bool success);
base::FilePath dir_;
raw_ptr<EntryMap> db_;
Callback init_callback_;
Callbacks::InitStatusCallback init_status_callback_;
Callback load_callback_;
Callback load_keys_callback_;
Callback get_callback_;
Callback update_callback_;
Callback destroy_callback_;
std::queue<bool> queued_get_results_;
std::queue<bool> queued_update_results_;
};
namespace {
template <typename P, typename T>
void DataToProtoWrap(T* data, P* proto) {
if constexpr (std::is_base_of_v<google::protobuf::MessageLite, T>) {
proto->Swap(data);
} else {
DataToProto(data, proto);
}
}
template <typename P, typename T>
void ProtoToDataWrap(const P& proto, T* data) {
if constexpr (std::is_base_of_v<google::protobuf::MessageLite, T>) {
*data = proto;
} else {
P copy = proto;
ProtoToData(&copy, data);
}
}
} // namespace
template <typename P, typename T>
FakeDB<P, T>::FakeDB(EntryMap* db)
: ProtoDatabaseImpl<P, T>(
ProtoDbType::TEST_DATABASE0,
base::FilePath(FILE_PATH_LITERAL("db_dir")),
base::MakeRefCounted<base::TestSimpleTaskRunner>()) {
db_ = db;
}
template <typename P, typename T>
void FakeDB<P, T>::Init(Callbacks::InitStatusCallback callback) {
dir_ = base::FilePath(FILE_PATH_LITERAL("db_dir"));
init_status_callback_ = std::move(callback);
}
template <typename P, typename T>
void FakeDB<P, T>::Init(const leveldb_env::Options& unique_db_options,
Callbacks::InitStatusCallback callback) {
Init(std::move(callback));
}
template <typename P, typename T>
void FakeDB<P, T>::UpdateEntries(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove,
Callbacks::UpdateCallback callback) {
for (auto& pair : *entries_to_save)
DataToProtoWrap(&pair.second, &(*db_)[pair.first]);
for (const auto& key : *keys_to_remove)
db_->erase(key);
update_callback_ = std::move(callback);
if (!queued_update_results_.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&FakeDB<P, T>::UpdateCallback, base::Unretained(this),
queued_update_results_.front()));
queued_update_results_.pop();
}
}
template <typename P, typename T>
void FakeDB<P, T>::UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename Util::Internal<T>::KeyEntryVector> entries_to_save,
const KeyFilter& delete_key_filter,
Callbacks::UpdateCallback callback) {
auto it = db_->begin();
while (it != db_->end()) {
if (!delete_key_filter.is_null() && delete_key_filter.Run(it->first))
db_->erase(it++);
else
++it;
}
for (auto& pair : *entries_to_save)
DataToProtoWrap(&pair.second, &(*db_)[pair.first]);
update_callback_ = std::move(callback);
if (!queued_update_results_.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&FakeDB<P, T>::UpdateCallback, base::Unretained(this),
queued_update_results_.front()));
queued_update_results_.pop();
}
}
template <typename P, typename T>
void FakeDB<P, T>::LoadEntries(
typename Callbacks::Internal<T>::LoadCallback callback) {
LoadEntriesWithFilter(KeyFilter(), std::move(callback));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadEntriesWithFilter(
const KeyFilter& key_filter,
typename Callbacks::Internal<T>::LoadCallback callback) {
LoadEntriesWithFilter(key_filter, leveldb::ReadOptions(), std::string(),
std::move(callback));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadEntriesWithFilter(
const KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadCallback callback) {
std::unique_ptr<std::vector<T>> entries(new std::vector<T>());
for (const auto& pair : *db_) {
if (key_filter.is_null() || key_filter.Run(pair.first)) {
if (pair.first.compare(0, target_prefix.length(), target_prefix) == 0) {
entries->emplace_back(T());
ProtoToDataWrap<P, T>(pair.second, &entries->back());
}
}
}
load_callback_ =
base::BindOnce(RunLoadCallback, std::move(callback), std::move(entries));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysAndEntries(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(KeyFilter(), std::move(callback));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysAndEntriesWithFilter(
const KeyFilter& key_filter,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(key_filter, leveldb::ReadOptions(),
std::string(), std::move(callback));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysAndEntriesWithFilter(
const KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
auto keys_entries = std::make_unique<std::map<std::string, T>>();
for (const auto& pair : *db_) {
if (key_filter.is_null() || key_filter.Run(pair.first)) {
if (pair.first.compare(0, target_prefix.length(), target_prefix) == 0)
ProtoToDataWrap<P, T>(pair.second, &(*keys_entries)[pair.first]);
}
}
load_callback_ = base::BindOnce(RunLoadKeysAndEntriesCallback,
std::move(callback), std::move(keys_entries));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
auto keys_entries = std::make_unique<std::map<std::string, T>>();
for (const auto& pair : *db_) {
if (pair.first >= start && pair.first <= end)
ProtoToDataWrap<P, T>(pair.second, &(*keys_entries)[pair.first]);
}
load_callback_ = base::BindOnce(RunLoadKeysAndEntriesCallback,
std::move(callback), std::move(keys_entries));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysAndEntriesWhile(
const std::string& start,
const leveldb_proto::KeyIteratorController& controller,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
auto keys_entries = std::make_unique<std::map<std::string, T>>();
for (const auto& pair : *db_) {
if (pair.first < start)
continue;
const Enums::KeyIteratorAction action = controller.Run(pair.first);
if (action == Enums::kLoadAndContinue || action == Enums::kLoadAndStop) {
ProtoToDataWrap<P, T>(pair.second, &(*keys_entries)[pair.first]);
}
if (action == Enums::kSkipAndStop || action == Enums::kLoadAndStop)
break;
}
load_callback_ = base::BindOnce(RunLoadKeysAndEntriesCallback,
std::move(callback), std::move(keys_entries));
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeys(Callbacks::LoadKeysCallback callback) {
std::unique_ptr<std::vector<std::string>> keys(
new std::vector<std::string>());
for (const auto& pair : *db_)
keys->push_back(pair.first);
load_keys_callback_ =
base::BindOnce(RunLoadKeysCallback, std::move(callback), std::move(keys));
}
template <typename P, typename T>
void FakeDB<P, T>::GetEntry(
const std::string& key,
typename Callbacks::Internal<T>::GetCallback callback) {
std::unique_ptr<T> entry;
auto it = db_->find(key);
if (it != db_->end()) {
entry.reset(new T());
ProtoToDataWrap<P, T>(it->second, entry.get());
}
get_callback_ =
base::BindOnce(RunGetCallback, std::move(callback), std::move(entry));
if (!queued_get_results_.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&FakeDB<P, T>::GetCallback, base::Unretained(this),
queued_get_results_.front()));
queued_get_results_.pop();
}
}
template <typename P, typename T>
void FakeDB<P, T>::Destroy(Callbacks::DestroyCallback callback) {
db_->clear();
destroy_callback_ = std::move(callback);
}
template <typename P, typename T>
base::FilePath& FakeDB<P, T>::GetDirectory() {
return dir_;
}
template <typename P, typename T>
void FakeDB<P, T>::InitCallback(bool success) {
if (!init_callback_)
InvokingInvalidCallback("InitCallback");
std::move(init_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::InitStatusCallback(Enums::InitStatus status) {
if (!init_status_callback_)
InvokingInvalidCallback("InitCallback");
std::move(init_status_callback_).Run(status);
}
template <typename P, typename T>
void FakeDB<P, T>::LoadCallback(bool success) {
if (!load_callback_)
InvokingInvalidCallback("LoadCallback");
std::move(load_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::LoadKeysCallback(bool success) {
if (!load_keys_callback_)
InvokingInvalidCallback("LoadKeysCallback");
std::move(load_keys_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::GetCallback(bool success) {
if (get_callback_.is_null())
InvokingInvalidCallback("GetCallback");
std::move(get_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::UpdateCallback(bool success) {
if (!update_callback_)
InvokingInvalidCallback("UpdateCallback");
std::move(update_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::DestroyCallback(bool success) {
if (!destroy_callback_)
InvokingInvalidCallback("DestroyCallback");
std::move(destroy_callback_).Run(success);
}
template <typename P, typename T>
void FakeDB<P, T>::InvokingInvalidCallback(const std::string& callback_name) {
std::string present_callbacks;
if (init_callback_)
present_callbacks += " InitCallback";
if (init_status_callback_)
present_callbacks += " InitStatusCallback";
if (load_callback_)
present_callbacks += " LoadCallback";
if (load_keys_callback_)
present_callbacks += " LoadKeysCallback";
if (get_callback_)
present_callbacks += " GetCallback";
if (update_callback_)
present_callbacks += " UpdateCallback";
if (destroy_callback_)
present_callbacks += " DestroyCallback";
NOTREACHED() << "Test tried to invoke FakeDB " << callback_name
<< ", but this callback is not present. Did you mean to invoke "
"one of the present callbacks: ("
<< present_callbacks << ")?";
}
// static
template <typename P, typename T>
void FakeDB<P, T>::RunLoadCallback(
typename Callbacks::Internal<T>::LoadCallback callback,
std::unique_ptr<typename std::vector<T>> entries,
bool success) {
std::move(callback).Run(success, std::move(entries));
}
// static
template <typename P, typename T>
void FakeDB<P, T>::RunLoadKeysAndEntriesCallback(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback,
std::unique_ptr<typename std::map<std::string, T>> keys_entries,
bool success) {
std::move(callback).Run(success, std::move(keys_entries));
}
// static
template <typename P, typename T>
void FakeDB<P, T>::RunLoadKeysCallback(
Callbacks::LoadKeysCallback callback,
std::unique_ptr<std::vector<std::string>> keys,
bool success) {
std::move(callback).Run(success, std::move(keys));
}
// static
template <typename P, typename T>
void FakeDB<P, T>::RunGetCallback(
typename Callbacks::Internal<T>::GetCallback callback,
std::unique_ptr<T> entry,
bool success) {
std::move(callback).Run(success, std::move(entry));
}
// static
template <typename P, typename T>
base::FilePath FakeDB<P, T>::DirectoryForTestDB() {
return base::FilePath(FILE_PATH_LITERAL("/fake/path"));
}
} // namespace leveldb_proto::test
#endif // COMPONENTS_LEVELDB_PROTO_TESTING_FAKE_DB_H_