diff --git a/components/feed/core/feed_storage_database.cc b/components/feed/core/feed_storage_database.cc index 889abe9cfac995..fa3fcc868bbc7f 100644 --- a/components/feed/core/feed_storage_database.cc +++ b/components/feed/core/feed_storage_database.cc @@ -29,12 +29,36 @@ const char kStorageDatabaseFolder[] = "storage"; const size_t kDatabaseWriteBufferSizeBytes = 512 * 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 kJournalStoragePrefix[] = "js-"; -// Formats key prefix for content data's key. -std::string FormatContentDatabaseKey(const std::string& key) { - return kContentStoragePrefix + key; +// Formats content key to storage key by adding a prefix. +std::string FormatContentKeyToStorageKey(const std::string& content_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& key_set, @@ -85,79 +109,131 @@ bool FeedStorageDatabase::IsInitialized() const { return INITIALIZED == database_status_; } -void FeedStorageDatabase::LoadContentEntries( - const std::vector& keys, - FeedContentStorageDatabaseCallback callback) { +void FeedStorageDatabase::LoadContent(const std::vector& keys, + ContentLoadCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::unordered_set key_set; for (const auto& key : keys) { - key_set.insert(FormatContentDatabaseKey(key)); + key_set.insert(FormatContentKeyToStorageKey(key)); } storage_database_->LoadEntriesWithFilter( base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)), - base::BindOnce(&FeedStorageDatabase::OnContentEntriesLoaded, + base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void FeedStorageDatabase::LoadContentEntriesByPrefix( - const std::string& prefix, - FeedContentStorageDatabaseCallback callback) { +void FeedStorageDatabase::LoadContentByPrefix(const std::string& prefix, + ContentLoadCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::string key_prefix = FormatContentDatabaseKey(prefix); + std::string key_prefix = FormatContentKeyToStorageKey(prefix); storage_database_->LoadEntriesWithFilter( base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)), - base::BindOnce(&FeedStorageDatabase::OnContentEntriesLoaded, + base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void FeedStorageDatabase::SaveContentEntries( - std::vector entries, - FeedStorageCommitCallback callback) { +void FeedStorageDatabase::SaveContent(std::vector pairs, + ConfirmationCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - auto entries_to_save = std::make_unique(); - for (const auto& entry : entries) { + auto contents_to_save = std::make_unique(); + for (auto entry : pairs) { FeedStorageProto proto; - proto.set_key(entry.first); - proto.set_content_data(entry.second); - entries_to_save->emplace_back(FormatContentDatabaseKey(entry.first), - std::move(proto)); + proto.set_key(std::move(entry.first)); + proto.set_content_data(std::move(entry.second)); + contents_to_save->emplace_back(FormatContentKeyToStorageKey(proto.key()), + std::move(proto)); } storage_database_->UpdateEntries( - std::move(entries_to_save), std::make_unique>(), + std::move(contents_to_save), std::make_unique>(), base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void FeedStorageDatabase::DeleteContentEntries( - std::vector keys_to_delete, - FeedStorageCommitCallback callback) { +void FeedStorageDatabase::DeleteContent( + const std::vector& keys_to_delete, + ConfirmationCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::unordered_set key_set; for (const auto& key : keys_to_delete) { - key_set.insert(FormatContentDatabaseKey(key)); + key_set.insert(FormatContentKeyToStorageKey(key)); } storage_database_->LoadEntriesWithFilter( base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)), - base::BindOnce(&FeedStorageDatabase::OnContentDeletedEntriesLoaded, + base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForDeleteContent, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void FeedStorageDatabase::DeleteContentEntriesByPrefix( +void FeedStorageDatabase::DeleteContentByPrefix( const std::string& prefix_to_delete, - FeedStorageCommitCallback callback) { + ConfirmationCallback callback) { 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( 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 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>(); + journals_to_delete->push_back(FormatJournalKeyToStorageKey(key)); + + storage_database_->UpdateEntries( + std::make_unique(), std::move(journals_to_delete), + base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } @@ -172,52 +248,145 @@ void FeedStorageDatabase::OnDatabaseInitialized(bool success) { } } -void FeedStorageDatabase::OnContentEntriesLoaded( - FeedContentStorageDatabaseCallback callback, +void FeedStorageDatabase::OnLoadEntriesForLoadContent( + ContentLoadCallback callback, bool success, - std::unique_ptr> entries) { + std::unique_ptr> content) { std::vector results; - if (!success || !entries) { + if (!success || !content) { DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed."; std::move(callback).Run(std::move(results)); return; } - for (const auto& entry : *entries) { - DCHECK(entry.has_key()); - DCHECK(entry.has_content_data()); + for (const auto& proto : *content) { + DCHECK(proto.has_key()); + 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)); } -void FeedStorageDatabase::OnContentDeletedEntriesLoaded( - FeedStorageCommitCallback callback, +void FeedStorageDatabase::OnLoadEntriesForDeleteContent( + ConfirmationCallback callback, bool success, - std::unique_ptr> entries) { - auto entries_to_delete = std::make_unique>(); - - if (!success || !entries) { + std::unique_ptr> content) { + if (!success || !content) { DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed."; std::move(callback).Run(success); return; } - for (const auto& entry : *entries) { - DCHECK(entry.has_content_data()); - entries_to_delete->push_back(FormatContentDatabaseKey(entry.key())); + auto contents_to_delete = std::make_unique>(); + for (const auto& proto : *content) { + DCHECK(proto.has_content_data()); + contents_to_delete->push_back(FormatContentKeyToStorageKey(proto.key())); + } + + storage_database_->UpdateEntries( + std::make_unique(), 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 journal) { + std::vector 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 entries, + bool success, + std::unique_ptr 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(); + 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(); + journals_to_save->emplace_back(FormatJournalKeyToStorageKey(key), + std::move(*journal)); storage_database_->UpdateEntries( - std::make_unique(), std::move(entries_to_delete), + std::move(journals_to_save), std::make_unique>(), base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, 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 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(); + journal_to_save->emplace_back(FormatJournalKeyToStorageKey(to_key), + std::move(*journal)); + + storage_database_->UpdateEntries( + std::move(journal_to_save), std::make_unique>(), + base::BindOnce(&FeedStorageDatabase::OnStorageCommitted, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void FeedStorageDatabase::OnLoadKeysForLoadAllJournalKeys( + JournalLoadCallback callback, + bool success, + std::unique_ptr> keys) { + std::vector 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) { DVLOG_IF(1, !success) << "FeedStorageDatabase committed failed."; std::move(callback).Run(success); diff --git a/components/feed/core/feed_storage_database.h b/components/feed/core/feed_storage_database.h index 3525eadbc68180..2a2ce7032ebac4 100644 --- a/components/feed/core/feed_storage_database.h +++ b/components/feed/core/feed_storage_database.h @@ -27,11 +27,14 @@ class FeedStorageDatabase { // Returns the storage data as a vector of key-value pairs when calling // loading data. - using FeedContentStorageDatabaseCallback = - base::OnceCallback)>; + using ContentLoadCallback = base::OnceCallback)>; - // Returns the commit operations success or not. - using FeedStorageCommitCallback = base::OnceCallback; + // Returns the journal data as a vector of strings when calling loading data. + using JournalLoadCallback = + base::OnceCallback)>; + + // Returns whether the commit operation succeeded. + using ConfirmationCallback = base::OnceCallback; // Initializes the database with |database_folder|. FeedStorageDatabase( @@ -52,36 +55,83 @@ class FeedStorageDatabase { // failed. bool IsInitialized() const; - // content storage. - void LoadContentEntries(const std::vector& keys, - FeedContentStorageDatabaseCallback callback); - void LoadContentEntriesByPrefix(const std::string& prefix, - FeedContentStorageDatabaseCallback callback); - void SaveContentEntries(std::vector entries, - FeedStorageCommitCallback callback); - void DeleteContentEntries(std::vector keys_to_delete, - FeedStorageCommitCallback callback); - void DeleteContentEntriesByPrefix(const std::string& prefix_to_delete, - FeedStorageCommitCallback callback); + // Loads the content data for the |keys| and passes them to |callback|. + void LoadContent(const std::vector& keys, + ContentLoadCallback callback); + + // Loads the content data whose key matches |prefix|, and passes them to + // |callback|. + void LoadContentByPrefix(const std::string& prefix, + ContentLoadCallback callback); + + // Inserts or updates the content data |pairs|, |callback| will be called when + // the data are saved or if there is an error. The fields in |pairs| will be + // std::move. + void SaveContent(std::vector 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& 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 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: - // Initialization + // Callback methods given to |storage_database_| for async responses. void OnDatabaseInitialized(bool success); - - // Loading - void OnContentEntriesLoaded( - FeedContentStorageDatabaseCallback callback, + void OnLoadEntriesForLoadContent( + ContentLoadCallback callback, bool success, - std::unique_ptr> entries); - - // Deleting - void OnContentDeletedEntriesLoaded( - FeedStorageCommitCallback callback, + std::unique_ptr> content); + void OnLoadEntriesForDeleteContent( + ConfirmationCallback callback, bool success, - std::unique_ptr> entries); - - // Commit callback - void OnStorageCommitted(FeedStorageCommitCallback callback, bool success); + std::unique_ptr> content); + void OnGetEntryForLoadJournal(JournalLoadCallback callback, + bool success, + std::unique_ptr journal); + void OnGetEntryAppendToJournal(ConfirmationCallback callback, + std::string key, + std::vector entries, + bool success, + std::unique_ptr journal); + void OnGetEntryForCopyJournal(ConfirmationCallback callback, + std::string to_key, + bool success, + std::unique_ptr journal); + void OnLoadKeysForLoadAllJournalKeys( + JournalLoadCallback callback, + bool success, + std::unique_ptr> keys); + void OnStorageCommitted(ConfirmationCallback callback, bool success); State database_status_; diff --git a/components/feed/core/feed_storage_database_unittest.cc b/components/feed/core/feed_storage_database_unittest.cc index a084e8f8606860..64c38c9042aa01 100644 --- a/components/feed/core/feed_storage_database_unittest.cc +++ b/components/feed/core/feed_storage_database_unittest.cc @@ -28,6 +28,15 @@ const std::string kContentKey3 = "ContentKey3"; const std::string kContentData1 = "Content Data1"; const std::string kContentData2 = "Content Data2"; 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 class FeedStorageDatabaseTest : public testing::Test { @@ -61,6 +70,16 @@ class FeedStorageDatabaseTest : public testing::Test { storage_db_storage_["cs-" + key] = storage_proto; } + void InjectJournalStorageProto(const std::string& key, + const std::vector& 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(); } FakeDB* storage_db() { return storage_db_; } @@ -69,6 +88,7 @@ class FeedStorageDatabaseTest : public testing::Test { MOCK_METHOD1(OnContentEntriesReceived, void(std::vector>)); + MOCK_METHOD1(OnJournalEntryReceived, void(std::vector)); MOCK_METHOD1(OnStorageCommitted, void(bool)); private: @@ -97,7 +117,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentAfterInitSuccess) { CreateDatabase(/*init_database=*/true); EXPECT_CALL(*this, OnContentEntriesReceived(_)); - db()->LoadContentEntries( + db()->LoadContent( {kContentKey1}, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); @@ -110,6 +130,10 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { // Store |kContentKey1| and |kContentKey2|. InjectContentStorageProto(kContentKey1, kContentData1); InjectContentStorageProto(kContentKey2, kContentData2); + InjectJournalStorageProto(kJournalKey1, + {kJournalData1, kJournalData2, kJournalData3}); + InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5}); + InjectJournalStorageProto(kJournalKey3, {kJournalData6}); // Try to Load |kContentKey2| and |kContentKey3|, only |kContentKey2| should // return. @@ -119,7 +143,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { EXPECT_EQ(results[0].first, kContentKey2); EXPECT_EQ(results[0].second, kContentData2); }); - db()->LoadContentEntries( + db()->LoadContent( {kContentKey2, kContentKey3}, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); @@ -129,9 +153,14 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) { TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { CreateDatabase(/*init_database=*/true); - // Store |kContentKey1| and |kContentKey2|. + // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|, + // |kJournalKey3|. InjectContentStorageProto(kContentKey1, kContentData1); InjectContentStorageProto(kContentKey2, kContentData2); + InjectJournalStorageProto(kJournalKey1, + {kJournalData1, kJournalData2, kJournalData3}); + InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5}); + InjectJournalStorageProto(kJournalKey3, {kJournalData6}); // Try to Load "ContentKey", both |kContentKey1| and |kContentKey2| should // return. @@ -143,7 +172,7 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { EXPECT_EQ(results[1].first, kContentKey2); EXPECT_EQ(results[1].second, kContentData2); }); - db()->LoadContentEntriesByPrefix( + db()->LoadContentByPrefix( kContentKeyPrefix, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); @@ -153,15 +182,15 @@ TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) { TEST_F(FeedStorageDatabaseTest, SaveContent) { CreateDatabase(/*init_database=*/true); - // Save |kContentKey1| and |kContentKey2|. + // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|, + // |kJournalKey3|. std::vector> entries; entries.push_back(std::make_pair(kContentKey1, kContentData1)); entries.push_back(std::make_pair(kContentKey2, kContentData2)); EXPECT_CALL(*this, OnStorageCommitted(true)); - db()->SaveContentEntries( - std::move(entries), - base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, - base::Unretained(this))); + db()->SaveContent(std::move(entries), + base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, + base::Unretained(this))); storage_db()->UpdateCallback(true); // Make sure they're there. @@ -173,7 +202,7 @@ TEST_F(FeedStorageDatabaseTest, SaveContent) { EXPECT_EQ(results[1].first, kContentKey2); EXPECT_EQ(results[1].second, kContentData2); }); - db()->LoadContentEntries( + db()->LoadContent( {kContentKey1, kContentKey2}, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); @@ -192,7 +221,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) { keys.push_back(kContentKey2); keys.push_back(kContentKey3); EXPECT_CALL(*this, OnStorageCommitted(true)); - db()->DeleteContentEntries( + db()->DeleteContent( std::move(keys), base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this))); @@ -206,7 +235,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContent) { EXPECT_EQ(results[0].first, kContentKey1); EXPECT_EQ(results[0].second, kContentData1); }); - db()->LoadContentEntries( + db()->LoadContent( {kContentKey1, kContentKey2}, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); @@ -222,7 +251,7 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) { // Delete |kContentKey1| and |kContentKey2| EXPECT_CALL(*this, OnStorageCommitted(true)); - db()->DeleteContentEntriesByPrefix( + db()->DeleteContentByPrefix( kContentKeyPrefix, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this))); @@ -234,11 +263,228 @@ TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) { .WillOnce([](std::vector> results) { EXPECT_EQ(results.size(), 0U); }); - db()->LoadContentEntries( + db()->LoadContent( {kContentKey1, kContentKey2}, base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived, base::Unretained(this))); 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 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 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 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 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 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 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 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 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 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