blob: 88085735afad3225867914ef04f2ba0ba27e4d10 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/user_annotations/user_annotations_database.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/protobuf_matchers.h"
#include "base/test/task_environment.h"
#include "components/os_crypt/async/browser/test_utils.h"
#include "components/user_annotations/user_annotations_features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace user_annotations {
using ::base::test::EqualsProto;
using ::optimization_guide::proto::UserAnnotationsEntry;
using ::testing::UnorderedElementsAre;
UserAnnotationsEntry CreateUserAnnotationsEntry(const std::string& key,
const std::string& value) {
UserAnnotationsEntry entry;
entry.set_key(key);
entry.set_value(value);
return entry;
}
class UserAnnotationsDatabaseTest : public testing::Test {
public:
void SetUp() override {
CHECK(temp_dir_.CreateUniqueTempDir());
os_crypt_ = os_crypt_async::GetTestOSCryptAsyncForTesting(
/*is_sync_for_unittests=*/true);
CreateDatabase();
}
void TearDown() override {
database_.reset();
CHECK(temp_dir_.Delete());
}
void CreateDatabase() {
base::RunLoop run_loop;
on_database_created_closure_ = run_loop.QuitClosure();
encryptor_ready_subscription_ = os_crypt_->GetInstance(
base::BindOnce(&UserAnnotationsDatabaseTest::CreateDatabaseOnCryptReady,
base::Unretained(this)));
run_loop.Run();
}
protected:
void CreateDatabaseOnCryptReady(os_crypt_async::Encryptor encryptor,
bool success) {
ASSERT_TRUE(success);
database_ = std::make_unique<UserAnnotationsDatabase>(temp_dir_.GetPath(),
std::move(encryptor));
std::move(on_database_created_closure_).Run();
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::ScopedTempDir temp_dir_;
std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
base::CallbackListSubscription encryptor_ready_subscription_;
std::unique_ptr<UserAnnotationsDatabase> database_;
base::OnceClosure on_database_created_closure_;
};
TEST_F(UserAnnotationsDatabaseTest, StoreAndRetrieve) {
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
std::vector<UserAnnotationsEntry> entries;
entries.push_back(CreateUserAnnotationsEntry("foo", "foo_value"));
entries.push_back(CreateUserAnnotationsEntry("bar", "bar_value"));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries(entries, /*deleted_entry_ids=*/{}));
entries[0].set_entry_id(1);
entries[1].set_entry_id(2);
EXPECT_THAT(
*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(entries[0]), EqualsProto(entries[1])));
// Reopen the database, and it should have the entries.
database_.reset();
CreateDatabase();
EXPECT_THAT(
*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(entries[0]), EqualsProto(entries[1])));
}
TEST_F(UserAnnotationsDatabaseTest, EntriesNewAndChanged) {
std::vector<UserAnnotationsEntry> entries;
entries.push_back(CreateUserAnnotationsEntry("foo", "foo_value"));
entries.push_back(CreateUserAnnotationsEntry("bar", "bar_value"));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries(entries, /*deleted_entry_ids=*/{}));
entries[0].set_entry_id(1);
entries[1].set_entry_id(2);
EXPECT_EQ(2U, database_->RetrieveAllEntries()->size());
// Add new entry, and change foo.
auto bazEntry = (CreateUserAnnotationsEntry("baz", "baz_value"));
entries[0].set_value("new_foo_value");
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({entries[0], bazEntry},
/*deleted_entry_ids=*/{}));
bazEntry.set_entry_id(3);
EXPECT_THAT(
*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(entries[0]), EqualsProto(entries[1]),
EqualsProto(bazEntry)));
}
TEST_F(UserAnnotationsDatabaseTest, EntriesChangedAndDeleted) {
std::vector<UserAnnotationsEntry> entries;
entries.push_back(CreateUserAnnotationsEntry("foo", "foo_value"));
entries.push_back(CreateUserAnnotationsEntry("bar", "bar_value"));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries(entries, /*deleted_entry_ids=*/{}));
entries[0].set_entry_id(1);
entries[1].set_entry_id(2);
EXPECT_EQ(2U, database_->RetrieveAllEntries()->size());
// Change foo, and delete bar.
entries[0].set_value("new_foo_value");
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({entries[0]}, /*deleted_entry_ids=*/{2}));
EXPECT_THAT(*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(entries[0])));
// Delete foo.
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({}, /*deleted_entry_ids=*/{1}));
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, RemoveEntry) {
std::vector<UserAnnotationsEntry> entries;
entries.push_back(CreateUserAnnotationsEntry("foo", "foo_value"));
entries.push_back(CreateUserAnnotationsEntry("bar", "bar_value"));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries(entries, /*deleted_entry_ids=*/{}));
entries[0].set_entry_id(1);
entries[1].set_entry_id(2);
auto db_entries = *database_->RetrieveAllEntries();
EXPECT_EQ(2U, db_entries.size());
EXPECT_TRUE(database_->RemoveEntry(db_entries[0].entry_id()));
EXPECT_TRUE(database_->RemoveEntry(db_entries[1].entry_id()));
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, RemoveAllEntries) {
std::vector<UserAnnotationsEntry> entries;
entries.push_back(CreateUserAnnotationsEntry("foo", "foo_value"));
entries.push_back(CreateUserAnnotationsEntry("bar", "bar_value"));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries(entries, /*deleted_entry_ids=*/{}));
EXPECT_TRUE(database_->RemoveAllEntries());
EXPECT_TRUE(database_->RemoveAllEntries());
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, RemoveAllAnnotationsInRange) {
auto foo_entry = CreateUserAnnotationsEntry("foo", "foo_value");
auto bar_entry = CreateUserAnnotationsEntry("bar", "bar_value");
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({foo_entry}, /*deleted_entry_ids=*/{}));
task_environment_.FastForwardBy(base::Hours(1));
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({bar_entry}, /*deleted_entry_ids=*/{}));
EXPECT_EQ(2u, database_->RetrieveAllEntries()->size());
// Delete all.
database_->RemoveAnnotationsInRange(base::Time::Min(), base::Time::Max());
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, RemoveAnnotationsInRange) {
auto foo_entry = CreateUserAnnotationsEntry("foo", "foo_value");
auto bar_entry = CreateUserAnnotationsEntry("bar", "bar_value");
auto foo_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({foo_entry}, /*deleted_entry_ids=*/{}));
task_environment_.FastForwardBy(base::Hours(1));
auto bar_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({bar_entry}, /*deleted_entry_ids=*/{}));
EXPECT_EQ(2u, database_->RetrieveAllEntries()->size());
// Delete foo.
database_->RemoveAnnotationsInRange(foo_create_time - base::Seconds(1),
foo_create_time + base::Seconds(1));
bar_entry.set_entry_id(2);
EXPECT_THAT(*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(bar_entry)));
// Delete bar.
database_->RemoveAnnotationsInRange(bar_create_time - base::Seconds(1),
bar_create_time + base::Seconds(1));
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, RemoveAnnotationsInRangeBackward) {
auto foo_entry = CreateUserAnnotationsEntry("foo", "foo_value");
auto bar_entry = CreateUserAnnotationsEntry("bar", "bar_value");
auto foo_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({foo_entry}, /*deleted_entry_ids=*/{}));
task_environment_.FastForwardBy(base::Hours(1));
auto bar_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({bar_entry}, /*deleted_entry_ids=*/{}));
EXPECT_EQ(2u, database_->RetrieveAllEntries()->size());
// Delete bar.
database_->RemoveAnnotationsInRange(bar_create_time - base::Seconds(1),
bar_create_time + base::Seconds(1));
foo_entry.set_entry_id(1);
EXPECT_THAT(*database_->RetrieveAllEntries(),
UnorderedElementsAre(EqualsProto(foo_entry)));
// Delete foo.
database_->RemoveAnnotationsInRange(foo_create_time - base::Seconds(1),
foo_create_time + base::Seconds(1));
EXPECT_TRUE(database_->RetrieveAllEntries()->empty());
}
TEST_F(UserAnnotationsDatabaseTest, GetCountOfValuesContainedBetween) {
auto foo_entry = CreateUserAnnotationsEntry("foo", "foo_value");
auto bar_entry = CreateUserAnnotationsEntry("bar", "bar_value");
auto foo_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({foo_entry}, /*deleted_entry_ids=*/{}));
task_environment_.FastForwardBy(base::Hours(1));
auto bar_create_time = base::Time::Now();
EXPECT_EQ(UserAnnotationsExecutionResult::kSuccess,
database_->UpdateEntries({bar_entry}, /*deleted_entry_ids=*/{}));
EXPECT_EQ(2u, database_->RetrieveAllEntries()->size());
// One entry: bar.
int count = database_->GetCountOfValuesContainedBetween(
bar_create_time - base::Seconds(1), bar_create_time + base::Seconds(1));
EXPECT_EQ(1, count);
// One entry: foo.
count = database_->GetCountOfValuesContainedBetween(
foo_create_time - base::Seconds(1), foo_create_time + base::Seconds(1));
EXPECT_EQ(1, count);
// All.
count = database_->GetCountOfValuesContainedBetween(base::Time::Min(),
base::Time::Max());
EXPECT_EQ(2, count);
}
} // namespace user_annotations