Commit d98a843a authored by Gang Wu's avatar Gang Wu Committed by Commit Bot

[Feed] Implement Journal Storage leveldb_proto

This CL adds persistence for journal data, and unittest for it.

Bug: 831633
Change-Id: I5d4d2821a595dc4be4d937ffd5d04ac4a79051c6
Reviewed-on: https://chromium-review.googlesource.com/1092411
Commit-Queue: Gang Wu <gangwu@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567412}
parent 5fcca7a5
...@@ -29,12 +29,36 @@ const char kStorageDatabaseFolder[] = "storage"; ...@@ -29,12 +29,36 @@ const char kStorageDatabaseFolder[] = "storage";
const size_t kDatabaseWriteBufferSizeBytes = 512 * 1024; const size_t kDatabaseWriteBufferSizeBytes = 512 * 1024;
const size_t kDatabaseWriteBufferSizeBytesForLowEndDevice = 128 * 1024; const size_t kDatabaseWriteBufferSizeBytesForLowEndDevice = 128 * 1024;
// Key prefix for content storage. // Key prefixes for content's storage key and journal's storage key. Because we
// put both content data and journal data into one storage, we need to add
// prefixes to their keys to distinguish between content keys and journal keys.
const char kContentStoragePrefix[] = "cs-"; const char kContentStoragePrefix[] = "cs-";
const char kJournalStoragePrefix[] = "js-";
// Formats key prefix for content data's key. // Formats content key to storage key by adding a prefix.
std::string FormatContentDatabaseKey(const std::string& key) { std::string FormatContentKeyToStorageKey(const std::string& content_key) {
return kContentStoragePrefix + key; return kContentStoragePrefix + content_key;
}
// Formats journal key to storage key by adding a prefix.
std::string FormatJournalKeyToStorageKey(const std::string& journal_key) {
return kJournalStoragePrefix + journal_key;
}
// Check if the |storage_key| is for journal data.
bool IsValidJournalKey(const std::string& storage_key) {
return base::StartsWith(storage_key, kJournalStoragePrefix,
base::CompareCase::SENSITIVE);
}
// Parse journal key from storage key. Return an empty string if |storage_key|
// is not recognized as journal key. ex. content's storage key.
std::string ParseJournalKey(const std::string& storage_key) {
if (!IsValidJournalKey(storage_key)) {
return std::string();
}
return storage_key.substr(strlen(kJournalStoragePrefix));
} }
bool DatabaseKeyFilter(const std::unordered_set<std::string>& key_set, bool DatabaseKeyFilter(const std::unordered_set<std::string>& key_set,
...@@ -85,79 +109,131 @@ bool FeedStorageDatabase::IsInitialized() const { ...@@ -85,79 +109,131 @@ bool FeedStorageDatabase::IsInitialized() const {
return INITIALIZED == database_status_; return INITIALIZED == database_status_;
} }
void FeedStorageDatabase::LoadContentEntries( void FeedStorageDatabase::LoadContent(const std::vector<std::string>& keys,
const std::vector<std::string>& keys, ContentLoadCallback callback) {
FeedContentStorageDatabaseCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unordered_set<std::string> key_set; std::unordered_set<std::string> key_set;
for (const auto& key : keys) { for (const auto& key : keys) {
key_set.insert(FormatContentDatabaseKey(key)); key_set.insert(FormatContentKeyToStorageKey(key));
} }
storage_database_->LoadEntriesWithFilter( storage_database_->LoadEntriesWithFilter(
base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)), base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
base::BindOnce(&FeedStorageDatabase::OnContentEntriesLoaded, base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void FeedStorageDatabase::LoadContentEntriesByPrefix( void FeedStorageDatabase::LoadContentByPrefix(const std::string& prefix,
const std::string& prefix, ContentLoadCallback callback) {
FeedContentStorageDatabaseCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string key_prefix = FormatContentDatabaseKey(prefix); std::string key_prefix = FormatContentKeyToStorageKey(prefix);
storage_database_->LoadEntriesWithFilter( storage_database_->LoadEntriesWithFilter(
base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)), base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
base::BindOnce(&FeedStorageDatabase::OnContentEntriesLoaded, base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void FeedStorageDatabase::SaveContentEntries( void FeedStorageDatabase::SaveContent(std::vector<KeyAndData> pairs,
std::vector<KeyAndData> entries, ConfirmationCallback callback) {
FeedStorageCommitCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto entries_to_save = std::make_unique<StorageEntryVector>(); auto contents_to_save = std::make_unique<StorageEntryVector>();
for (const auto& entry : entries) { for (auto entry : pairs) {
FeedStorageProto proto; FeedStorageProto proto;
proto.set_key(entry.first); proto.set_key(std::move(entry.first));
proto.set_content_data(entry.second); proto.set_content_data(std::move(entry.second));
entries_to_save->emplace_back(FormatContentDatabaseKey(entry.first), contents_to_save->emplace_back(FormatContentKeyToStorageKey(proto.key()),
std::move(proto)); std::move(proto));
} }
storage_database_->UpdateEntries( storage_database_->UpdateEntries(
std::move(entries_to_save), std::make_unique<std::vector<std::string>>(), std::move(contents_to_save), std::make_unique<std::vector<std::string>>(),
base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void FeedStorageDatabase::DeleteContentEntries( void FeedStorageDatabase::DeleteContent(
std::vector<std::string> keys_to_delete, const std::vector<std::string>& keys_to_delete,
FeedStorageCommitCallback callback) { ConfirmationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unordered_set<std::string> key_set; std::unordered_set<std::string> key_set;
for (const auto& key : keys_to_delete) { for (const auto& key : keys_to_delete) {
key_set.insert(FormatContentDatabaseKey(key)); key_set.insert(FormatContentKeyToStorageKey(key));
} }
storage_database_->LoadEntriesWithFilter( storage_database_->LoadEntriesWithFilter(
base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)), base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
base::BindOnce(&FeedStorageDatabase::OnContentDeletedEntriesLoaded, base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForDeleteContent,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void FeedStorageDatabase::DeleteContentEntriesByPrefix( void FeedStorageDatabase::DeleteContentByPrefix(
const std::string& prefix_to_delete, const std::string& prefix_to_delete,
FeedStorageCommitCallback callback) { ConfirmationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string key_prefix = FormatContentDatabaseKey(prefix_to_delete); std::string key_prefix = FormatContentKeyToStorageKey(prefix_to_delete);
storage_database_->LoadEntriesWithFilter( storage_database_->LoadEntriesWithFilter(
base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)), base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
base::BindOnce(&FeedStorageDatabase::OnContentDeletedEntriesLoaded, base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForDeleteContent,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FeedStorageDatabase::LoadJournal(const std::string& key,
JournalLoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
storage_database_->GetEntry(
FormatJournalKeyToStorageKey(key),
base::BindOnce(&FeedStorageDatabase::OnGetEntryForLoadJournal,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FeedStorageDatabase::LoadAllJournalKeys(JournalLoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
storage_database_->LoadKeys(
base::BindOnce(&FeedStorageDatabase::OnLoadKeysForLoadAllJournalKeys,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FeedStorageDatabase::AppendToJournal(const std::string& key,
std::vector<std::string> entries,
ConfirmationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
storage_database_->GetEntry(
FormatJournalKeyToStorageKey(key),
base::BindOnce(&FeedStorageDatabase::OnGetEntryAppendToJournal,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), key,
std::move(entries)));
}
void FeedStorageDatabase::CopyJournal(const std::string& from_key,
const std::string& to_key,
ConfirmationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
storage_database_->GetEntry(
FormatJournalKeyToStorageKey(from_key),
base::BindOnce(&FeedStorageDatabase::OnGetEntryForCopyJournal,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
to_key));
}
void FeedStorageDatabase::DeleteJournal(const std::string& key,
ConfirmationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto journals_to_delete = std::make_unique<std::vector<std::string>>();
journals_to_delete->push_back(FormatJournalKeyToStorageKey(key));
storage_database_->UpdateEntries(
std::make_unique<StorageEntryVector>(), std::move(journals_to_delete),
base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
...@@ -172,52 +248,145 @@ void FeedStorageDatabase::OnDatabaseInitialized(bool success) { ...@@ -172,52 +248,145 @@ void FeedStorageDatabase::OnDatabaseInitialized(bool success) {
} }
} }
void FeedStorageDatabase::OnContentEntriesLoaded( void FeedStorageDatabase::OnLoadEntriesForLoadContent(
FeedContentStorageDatabaseCallback callback, ContentLoadCallback callback,
bool success, bool success,
std::unique_ptr<std::vector<FeedStorageProto>> entries) { std::unique_ptr<std::vector<FeedStorageProto>> content) {
std::vector<KeyAndData> results; std::vector<KeyAndData> results;
if (!success || !entries) { if (!success || !content) {
DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed."; DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed.";
std::move(callback).Run(std::move(results)); std::move(callback).Run(std::move(results));
return; return;
} }
for (const auto& entry : *entries) { for (const auto& proto : *content) {
DCHECK(entry.has_key()); DCHECK(proto.has_key());
DCHECK(entry.has_content_data()); DCHECK(proto.has_content_data());
results.emplace_back(std::make_pair(entry.key(), entry.content_data())); results.emplace_back(proto.key(), proto.content_data());
} }
std::move(callback).Run(std::move(results)); std::move(callback).Run(std::move(results));
} }
void FeedStorageDatabase::OnContentDeletedEntriesLoaded( void FeedStorageDatabase::OnLoadEntriesForDeleteContent(
FeedStorageCommitCallback callback, ConfirmationCallback callback,
bool success, bool success,
std::unique_ptr<std::vector<FeedStorageProto>> entries) { std::unique_ptr<std::vector<FeedStorageProto>> content) {
auto entries_to_delete = std::make_unique<std::vector<std::string>>(); if (!success || !content) {
if (!success || !entries) {
DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed."; DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed.";
std::move(callback).Run(success); std::move(callback).Run(success);
return; return;
} }
for (const auto& entry : *entries) { auto contents_to_delete = std::make_unique<std::vector<std::string>>();
DCHECK(entry.has_content_data()); for (const auto& proto : *content) {
entries_to_delete->push_back(FormatContentDatabaseKey(entry.key())); DCHECK(proto.has_content_data());
contents_to_delete->push_back(FormatContentKeyToStorageKey(proto.key()));
}
storage_database_->UpdateEntries(
std::make_unique<StorageEntryVector>(), std::move(contents_to_delete),
base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FeedStorageDatabase::OnGetEntryForLoadJournal(
JournalLoadCallback callback,
bool success,
std::unique_ptr<FeedStorageProto> journal) {
std::vector<std::string> results;
if (!success || !journal) {
DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
std::move(callback).Run(std::move(results));
return;
}
for (int i = 0; i < journal->journal_data_size(); ++i) {
results.emplace_back(journal->journal_data(i));
}
std::move(callback).Run(std::move(results));
}
void FeedStorageDatabase::OnGetEntryAppendToJournal(
ConfirmationCallback callback,
std::string key,
std::vector<std::string> entries,
bool success,
std::unique_ptr<FeedStorageProto> journal) {
if (!success) {
DVLOG(1) << "FeedStorageDatabase load journal failed.";
std::move(callback).Run(success);
return;
}
if (journal == nullptr) {
// The journal does not exist, create a new one.
journal = std::make_unique<FeedStorageProto>();
journal->set_key(key);
}
DCHECK_EQ(journal->key(), key);
for (const std::string& entry : entries) {
journal->add_journal_data(entry);
} }
auto journals_to_save = std::make_unique<StorageEntryVector>();
journals_to_save->emplace_back(FormatJournalKeyToStorageKey(key),
std::move(*journal));
storage_database_->UpdateEntries( storage_database_->UpdateEntries(
std::make_unique<StorageEntryVector>(), std::move(entries_to_delete), std::move(journals_to_save), std::make_unique<std::vector<std::string>>(),
base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback))); weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void FeedStorageDatabase::OnStorageCommitted(FeedStorageCommitCallback callback, void FeedStorageDatabase::OnGetEntryForCopyJournal(
ConfirmationCallback callback,
std::string to_key,
bool success,
std::unique_ptr<FeedStorageProto> journal) {
if (!success || !journal) {
DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
std::move(callback).Run(success);
return;
}
journal->set_key(to_key);
auto journal_to_save = std::make_unique<StorageEntryVector>();
journal_to_save->emplace_back(FormatJournalKeyToStorageKey(to_key),
std::move(*journal));
storage_database_->UpdateEntries(
std::move(journal_to_save), std::make_unique<std::vector<std::string>>(),
base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FeedStorageDatabase::OnLoadKeysForLoadAllJournalKeys(
JournalLoadCallback callback,
bool success,
std::unique_ptr<std::vector<std::string>> keys) {
std::vector<std::string> results;
if (!success || !keys) {
DVLOG_IF(1, !success) << "FeedStorageDatabase load journal keys failed.";
std::move(callback).Run(std::move(results));
return;
}
// Filter out content keys, only keep journal keys.
for (const std::string& key : *keys) {
if (IsValidJournalKey(key))
results.emplace_back(ParseJournalKey(key));
}
std::move(callback).Run(std::move(results));
}
void FeedStorageDatabase::OnStorageCommitted(ConfirmationCallback callback,
bool success) { bool success) {
DVLOG_IF(1, !success) << "FeedStorageDatabase committed failed."; DVLOG_IF(1, !success) << "FeedStorageDatabase committed failed.";
std::move(callback).Run(success); std::move(callback).Run(success);
......
...@@ -27,11 +27,14 @@ class FeedStorageDatabase { ...@@ -27,11 +27,14 @@ class FeedStorageDatabase {
// Returns the storage data as a vector of key-value pairs when calling // Returns the storage data as a vector of key-value pairs when calling
// loading data. // loading data.
using FeedContentStorageDatabaseCallback = using ContentLoadCallback = base::OnceCallback<void(std::vector<KeyAndData>)>;
base::OnceCallback<void(std::vector<KeyAndData>)>;
// Returns the commit operations success or not. // Returns the journal data as a vector of strings when calling loading data.
using FeedStorageCommitCallback = base::OnceCallback<void(bool)>; using JournalLoadCallback =
base::OnceCallback<void(std::vector<std::string>)>;
// Returns whether the commit operation succeeded.
using ConfirmationCallback = base::OnceCallback<void(bool)>;
// Initializes the database with |database_folder|. // Initializes the database with |database_folder|.
FeedStorageDatabase( FeedStorageDatabase(
...@@ -52,36 +55,83 @@ class FeedStorageDatabase { ...@@ -52,36 +55,83 @@ class FeedStorageDatabase {
// failed. // failed.
bool IsInitialized() const; bool IsInitialized() const;
// content storage. // Loads the content data for the |keys| and passes them to |callback|.
void LoadContentEntries(const std::vector<std::string>& keys, void LoadContent(const std::vector<std::string>& keys,
FeedContentStorageDatabaseCallback callback); ContentLoadCallback callback);
void LoadContentEntriesByPrefix(const std::string& prefix,
FeedContentStorageDatabaseCallback callback); // Loads the content data whose key matches |prefix|, and passes them to
void SaveContentEntries(std::vector<KeyAndData> entries, // |callback|.
FeedStorageCommitCallback callback); void LoadContentByPrefix(const std::string& prefix,
void DeleteContentEntries(std::vector<std::string> keys_to_delete, ContentLoadCallback callback);
FeedStorageCommitCallback callback);
void DeleteContentEntriesByPrefix(const std::string& prefix_to_delete, // Inserts or updates the content data |pairs|, |callback| will be called when
FeedStorageCommitCallback callback); // the data are saved or if there is an error. The fields in |pairs| will be
// std::move.
void SaveContent(std::vector<KeyAndData> pairs,
ConfirmationCallback callback);
// Deletes the content data for |keys_to_delete|, |callback| will be called
// when the data are deleted or if there is an error.
void DeleteContent(const std::vector<std::string>& keys_to_delete,
ConfirmationCallback callback);
// Deletes the content data whose key matches |prefix_to_delete|, |callback|
// will be called when the content are deleted or if there is an error.
void DeleteContentByPrefix(const std::string& prefix_to_delete,
ConfirmationCallback callback);
// Loads the journal data for the |key| and passes it to |callback|.
void LoadJournal(const std::string& key, JournalLoadCallback callback);
// Loads all journal keys in the storage, and passes them to |callback|.
void LoadAllJournalKeys(JournalLoadCallback callback);
// Appends |entries| to a journal whose key is |key|, if there the journal do
// not exist, create one. |callback| will be called when the data are saved or
// if there is an error.
void AppendToJournal(const std::string& key,
std::vector<std::string> entries,
ConfirmationCallback callback);
// Creates a new journal with name |to_key|, and copys all data from the
// journal with |from_key| to it. |callback| will be called when the data are
// saved or if there is an error.
void CopyJournal(const std::string& from_key,
const std::string& to_key,
ConfirmationCallback callback);
// Deletes the journal with |key|, |callback| will be called when the journal
// is deleted or if there is an error.
void DeleteJournal(const std::string& key, ConfirmationCallback callback);
private: private:
// Initialization // Callback methods given to |storage_database_| for async responses.
void OnDatabaseInitialized(bool success); void OnDatabaseInitialized(bool success);
void OnLoadEntriesForLoadContent(
// Loading ContentLoadCallback callback,
void OnContentEntriesLoaded(
FeedContentStorageDatabaseCallback callback,
bool success, bool success,
std::unique_ptr<std::vector<FeedStorageProto>> entries); std::unique_ptr<std::vector<FeedStorageProto>> content);
void OnLoadEntriesForDeleteContent(
// Deleting ConfirmationCallback callback,
void OnContentDeletedEntriesLoaded(
FeedStorageCommitCallback callback,
bool success, bool success,
std::unique_ptr<std::vector<FeedStorageProto>> entries); std::unique_ptr<std::vector<FeedStorageProto>> content);
void OnGetEntryForLoadJournal(JournalLoadCallback callback,
// Commit callback bool success,
void OnStorageCommitted(FeedStorageCommitCallback callback, bool success); std::unique_ptr<FeedStorageProto> journal);
void OnGetEntryAppendToJournal(ConfirmationCallback callback,
std::string key,
std::vector<std::string> entries,
bool success,
std::unique_ptr<FeedStorageProto> journal);
void OnGetEntryForCopyJournal(ConfirmationCallback callback,
std::string to_key,
bool success,
std::unique_ptr<FeedStorageProto> journal);
void OnLoadKeysForLoadAllJournalKeys(
JournalLoadCallback callback,
bool success,
std::unique_ptr<std::vector<std::string>> keys);
void OnStorageCommitted(ConfirmationCallback callback, bool success);
State database_status_; State database_status_;
......
...@@ -28,6 +28,15 @@ const std::string kContentKey3 = "ContentKey3"; ...@@ -28,6 +28,15 @@ const std::string kContentKey3 = "ContentKey3";
const std::string kContentData1 = "Content Data1"; const std::string kContentData1 = "Content Data1";
const std::string kContentData2 = "Content Data2"; const std::string kContentData2 = "Content Data2";
const std::string kContentData3 = "Content Data3"; const std::string kContentData3 = "Content Data3";
const std::string kJournalKey1 = "JournalKey1";
const std::string kJournalKey2 = "JournalKey2";
const std::string kJournalKey3 = "JournalKey3";
const std::string kJournalData1 = "Journal Data1";
const std::string kJournalData2 = "Journal Data2";
const std::string kJournalData3 = "Journal Data3";
const std::string kJournalData4 = "Journal Data4";
const std::string kJournalData5 = "Journal Data5";
const std::string kJournalData6 = "Journal Data6";
} // namespace } // namespace
class FeedStorageDatabaseTest : public testing::Test { class FeedStorageDatabaseTest : public testing::Test {
...@@ -61,6 +70,16 @@ class FeedStorageDatabaseTest : public testing::Test { ...@@ -61,6 +70,16 @@ class FeedStorageDatabaseTest : public testing::Test {
storage_db_storage_["cs-" + key] = storage_proto; storage_db_storage_["cs-" + key] = storage_proto;
} }
void InjectJournalStorageProto(const std::string& key,
const std::vector<std::string>& entries) {
FeedStorageProto storage_proto;
storage_proto.set_key(key);
for (const std::string& entry : entries) {
storage_proto.add_journal_data(entry);
}
storage_db_storage_["js-" + key] = storage_proto;
}
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); } void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
FakeDB<FeedStorageProto>* storage_db() { return storage_db_; } FakeDB<FeedStorageProto>* storage_db() { return storage_db_; }
...@@ -69,6 +88,7 @@ class FeedStorageDatabaseTest : public testing::Test { ...@@ -69,6 +88,7 @@ class FeedStorageDatabaseTest : public testing::Test {
MOCK_METHOD1(OnContentEntriesReceived, MOCK_METHOD1(OnContentEntriesReceived,
void(std::vector<std::pair<std::string, std::string>>)); void(std::vector<std::pair<std::string, std::string>>));
MOCK_METHOD1(OnJournalEntryReceived, void(std::vector<std::string>));
MOCK_METHOD1(OnStorageCommitted, void(bool)); MOCK_METHOD1(OnStorageCommitted, void(bool));
private: private:
...@@ -97,7 +117,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentAfterInitSuccess) { ...@@ -97,7 +117,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentAfterInitSuccess) {
CreateDatabase(/*init_database=*/true); CreateDatabase(/*init_database=*/true);
EXPECT_CALL(*this, OnContentEntriesReceived(_)); EXPECT_CALL(*this, OnContentEntriesReceived(_));
db()->LoadContentEntries( db()->LoadContent(
{kContentKey1}, {kContentKey1},
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
...@@ -110,6 +130,10 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { ...@@ -110,6 +130,10 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) {
// Store |kContentKey1| and |kContentKey2|. // Store |kContentKey1| and |kContentKey2|.
InjectContentStorageProto(kContentKey1, kContentData1); InjectContentStorageProto(kContentKey1, kContentData1);
InjectContentStorageProto(kContentKey2, kContentData2); InjectContentStorageProto(kContentKey2, kContentData2);
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
InjectJournalStorageProto(kJournalKey3, {kJournalData6});
// Try to Load |kContentKey2| and |kContentKey3|, only |kContentKey2| should // Try to Load |kContentKey2| and |kContentKey3|, only |kContentKey2| should
// return. // return.
...@@ -119,7 +143,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { ...@@ -119,7 +143,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) {
EXPECT_EQ(results[0].first, kContentKey2); EXPECT_EQ(results[0].first, kContentKey2);
EXPECT_EQ(results[0].second, kContentData2); EXPECT_EQ(results[0].second, kContentData2);
}); });
db()->LoadContentEntries( db()->LoadContent(
{kContentKey2, kContentKey3}, {kContentKey2, kContentKey3},
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
...@@ -129,9 +153,14 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { ...@@ -129,9 +153,14 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) {
TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) {
CreateDatabase(/*init_database=*/true); CreateDatabase(/*init_database=*/true);
// Store |kContentKey1| and |kContentKey2|. // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
// |kJournalKey3|.
InjectContentStorageProto(kContentKey1, kContentData1); InjectContentStorageProto(kContentKey1, kContentData1);
InjectContentStorageProto(kContentKey2, kContentData2); InjectContentStorageProto(kContentKey2, kContentData2);
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
InjectJournalStorageProto(kJournalKey3, {kJournalData6});
// Try to Load "ContentKey", both |kContentKey1| and |kContentKey2| should // Try to Load "ContentKey", both |kContentKey1| and |kContentKey2| should
// return. // return.
...@@ -143,7 +172,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { ...@@ -143,7 +172,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) {
EXPECT_EQ(results[1].first, kContentKey2); EXPECT_EQ(results[1].first, kContentKey2);
EXPECT_EQ(results[1].second, kContentData2); EXPECT_EQ(results[1].second, kContentData2);
}); });
db()->LoadContentEntriesByPrefix( db()->LoadContentByPrefix(
kContentKeyPrefix, kContentKeyPrefix,
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
...@@ -153,13 +182,13 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { ...@@ -153,13 +182,13 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) {
TEST_F(FeedStorageDatabaseTest, SaveContent) { TEST_F(FeedStorageDatabaseTest, SaveContent) {
CreateDatabase(/*init_database=*/true); CreateDatabase(/*init_database=*/true);
// Save |kContentKey1| and |kContentKey2|. // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
// |kJournalKey3|.
std::vector<std::pair<std::string, std::string>> entries; std::vector<std::pair<std::string, std::string>> entries;
entries.push_back(std::make_pair(kContentKey1, kContentData1)); entries.push_back(std::make_pair(kContentKey1, kContentData1));
entries.push_back(std::make_pair(kContentKey2, kContentData2)); entries.push_back(std::make_pair(kContentKey2, kContentData2));
EXPECT_CALL(*this, OnStorageCommitted(true)); EXPECT_CALL(*this, OnStorageCommitted(true));
db()->SaveContentEntries( db()->SaveContent(std::move(entries),
std::move(entries),
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this))); base::Unretained(this)));
storage_db()->UpdateCallback(true); storage_db()->UpdateCallback(true);
...@@ -173,7 +202,7 @@ TEST_F(FeedStorageDatabaseTest, SaveContent) { ...@@ -173,7 +202,7 @@ TEST_F(FeedStorageDatabaseTest, SaveContent) {
EXPECT_EQ(results[1].first, kContentKey2); EXPECT_EQ(results[1].first, kContentKey2);
EXPECT_EQ(results[1].second, kContentData2); EXPECT_EQ(results[1].second, kContentData2);
}); });
db()->LoadContentEntries( db()->LoadContent(
{kContentKey1, kContentKey2}, {kContentKey1, kContentKey2},
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
...@@ -192,7 +221,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) { ...@@ -192,7 +221,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) {
keys.push_back(kContentKey2); keys.push_back(kContentKey2);
keys.push_back(kContentKey3); keys.push_back(kContentKey3);
EXPECT_CALL(*this, OnStorageCommitted(true)); EXPECT_CALL(*this, OnStorageCommitted(true));
db()->DeleteContentEntries( db()->DeleteContent(
std::move(keys), std::move(keys),
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this))); base::Unretained(this)));
...@@ -206,7 +235,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) { ...@@ -206,7 +235,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) {
EXPECT_EQ(results[0].first, kContentKey1); EXPECT_EQ(results[0].first, kContentKey1);
EXPECT_EQ(results[0].second, kContentData1); EXPECT_EQ(results[0].second, kContentData1);
}); });
db()->LoadContentEntries( db()->LoadContent(
{kContentKey1, kContentKey2}, {kContentKey1, kContentKey2},
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
...@@ -222,7 +251,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) { ...@@ -222,7 +251,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) {
// Delete |kContentKey1| and |kContentKey2| // Delete |kContentKey1| and |kContentKey2|
EXPECT_CALL(*this, OnStorageCommitted(true)); EXPECT_CALL(*this, OnStorageCommitted(true));
db()->DeleteContentEntriesByPrefix( db()->DeleteContentByPrefix(
kContentKeyPrefix, kContentKeyPrefix,
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this))); base::Unretained(this)));
...@@ -234,11 +263,228 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) { ...@@ -234,11 +263,228 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) {
.WillOnce([](std::vector<std::pair<std::string, std::string>> results) { .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
EXPECT_EQ(results.size(), 0U); EXPECT_EQ(results.size(), 0U);
}); });
db()->LoadContentEntries( db()->LoadContent(
{kContentKey1, kContentKey2}, {kContentKey1, kContentKey2},
base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
base::Unretained(this))); base::Unretained(this)));
storage_db()->LoadCallback(true); storage_db()->LoadCallback(true);
} }
TEST_F(FeedStorageDatabaseTest, LoadJournalEntry) {
CreateDatabase(/*init_database=*/true);
// Store |kJournalKey1|.
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
// Try to Load |kJournalKey1|.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 3U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
EXPECT_EQ(results[2], kJournalData3);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
}
TEST_F(FeedStorageDatabaseTest, LoadNonExistingJournalEntry) {
CreateDatabase(/*init_database=*/true);
// Try to Load |kJournalKey1|.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 0U);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
}
TEST_F(FeedStorageDatabaseTest, LoadAllJournalKeys) {
CreateDatabase(/*init_database=*/true);
// Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
// |kJournalKey3|.
InjectContentStorageProto(kContentKey1, kContentData1);
InjectContentStorageProto(kContentKey2, kContentData2);
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
InjectJournalStorageProto(kJournalKey3, {kJournalData6});
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 3U);
EXPECT_EQ(results[0], kJournalKey1);
EXPECT_EQ(results[1], kJournalKey2);
EXPECT_EQ(results[2], kJournalKey3);
});
db()->LoadAllJournalKeys(
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->LoadKeysCallback(true);
}
TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalExists) {
CreateDatabase(/*init_database=*/true);
// Save |kContentKey1|
EXPECT_CALL(*this, OnStorageCommitted(true));
db()->AppendToJournal(
kJournalKey1, {kJournalData1, kJournalData2},
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this)));
storage_db()->GetCallback(true);
storage_db()->UpdateCallback(true);
// Make sure they're there.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 2U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
Mock::VerifyAndClearExpectations(this);
// Append more for |kContentKey1|
EXPECT_CALL(*this, OnStorageCommitted(true));
db()->AppendToJournal(
kJournalKey1, {kJournalData3, kJournalData4, kJournalData5},
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this)));
storage_db()->GetCallback(true);
storage_db()->UpdateCallback(true);
// Check new instances are there.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 5U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
EXPECT_EQ(results[2], kJournalData3);
EXPECT_EQ(results[3], kJournalData4);
EXPECT_EQ(results[4], kJournalData5);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
}
TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalMissing) {
CreateDatabase(/*init_database=*/true);
// Append data for |kContentKey1|
EXPECT_CALL(*this, OnStorageCommitted(true));
db()->AppendToJournal(
kJournalKey1, {kJournalData1, kJournalData2, kJournalData3},
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this)));
storage_db()->GetCallback(true);
storage_db()->UpdateCallback(true);
// Check new data are there.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 3U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
EXPECT_EQ(results[2], kJournalData3);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
}
TEST_F(FeedStorageDatabaseTest, CopyJournal) {
CreateDatabase(/*init_database=*/true);
// Save |kContentKey1|.
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
// Copy |kContentKey1| to |kContentKey2|.
EXPECT_CALL(*this, OnStorageCommitted(true));
db()->CopyJournal(kJournalKey1, kJournalKey2,
base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this)));
storage_db()->GetCallback(true);
storage_db()->UpdateCallback(true);
// Check new journal is there.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 3U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
EXPECT_EQ(results[2], kJournalData3);
});
db()->LoadJournal(
kJournalKey2,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
Mock::VerifyAndClearExpectations(this);
// Check first journal is still there.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 3U);
EXPECT_EQ(results[0], kJournalData1);
EXPECT_EQ(results[1], kJournalData2);
EXPECT_EQ(results[2], kJournalData3);
});
db()->LoadJournal(
kJournalKey1,
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->GetCallback(true);
}
TEST_F(FeedStorageDatabaseTest, DeleteJournal) {
CreateDatabase(/*init_database=*/true);
// Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
InjectJournalStorageProto(kJournalKey1,
{kJournalData1, kJournalData2, kJournalData3});
InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
InjectJournalStorageProto(kJournalKey3, {kJournalData6});
// Delete |kJournalKey2|.
EXPECT_CALL(*this, OnStorageCommitted(true));
db()->DeleteJournal(
kJournalKey2, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
base::Unretained(this)));
storage_db()->UpdateCallback(true);
// Make sure |kJournalKey2| got deleted.
EXPECT_CALL(*this, OnJournalEntryReceived(_))
.WillOnce([](std::vector<std::string> results) {
ASSERT_EQ(results.size(), 2U);
EXPECT_EQ(results[0], kJournalKey1);
EXPECT_EQ(results[1], kJournalKey3);
});
db()->LoadAllJournalKeys(
base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
base::Unretained(this)));
storage_db()->LoadKeysCallback(true);
}
} // namespace feed } // namespace feed
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