1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/media_device_salt/media_device_salt_database.h"
10 #include "base/containers/contains.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/functional/bind.h"
15 #include "base/test/bind.h"
16 #include "base/test/task_environment.h"
17 #include "base/time/clock.h"
18 #include "base/time/time.h"
19 #include "content/public/browser/storage_partition.h"
20 #include "sql/statement.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/abseil-cpp/absl/types/optional.h"
24 #include "third_party/blink/public/common/storage_key/storage_key.h"
26 namespace media_device_salt {
29 using StorageKeyMatcher =
30 ::content::StoragePartition::StorageKeyMatcherFunction;
31 using ::testing::ElementsAre;
32 using ::testing::IsEmpty;
33 using ::testing::Optional;
34 using ::testing::UnorderedElementsAre;
38 blink::StorageKey StorageKey1() {
39 return blink::StorageKey::CreateFromStringForTesting("https://example1.com");
42 blink::StorageKey StorageKey2() {
43 return blink::StorageKey::CreateFromStringForTesting("https://example2.com");
46 blink::StorageKey StorageKey3() {
47 return blink::StorageKey::CreateFromStringForTesting("https://example3.com");
52 // Test parameter indicates if the database should be in memory (true, for
53 // incognito mode) or persistent (false).
54 class MediaDeviceSaltDatabaseTest : public testing::TestWithParam<bool> {
56 MediaDeviceSaltDatabase& db() { return *db_; }
58 bool ShouldBeInMemory() const { return GetParam(); }
59 bool ShouldBePersistent() const { return !ShouldBeInMemory(); }
61 base::FilePath DbPath() {
62 return ShouldBeInMemory()
64 : temp_directory_.GetPath().AppendASCII("MediaDeviceSalts");
67 std::vector<std::pair<blink::StorageKey, std::string>> GetAllStoredEntries() {
68 std::vector<std::pair<blink::StorageKey, std::string>> entries;
69 sql::Statement statement(db().DatabaseForTesting().GetUniqueStatement(
70 "SELECT storage_key, salt FROM media_device_salts"));
71 while (statement.Step()) {
72 absl::optional<blink::StorageKey> key =
73 blink::StorageKey::Deserialize(statement.ColumnString(0));
74 CHECK(key.has_value());
75 entries.emplace_back(*key, statement.ColumnString(1));
80 StorageKeyMatcher CreateMatcherForKeys(std::vector<blink::StorageKey> keys) {
81 return base::BindLambdaForTesting([keys](const blink::StorageKey& key) {
82 return base::Contains(keys, key);
86 base::test::TaskEnvironment& task_environment() { return task_environment_; }
89 void SetUp() override {
90 if (ShouldBePersistent()) {
91 ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
93 db_.emplace(DbPath());
97 base::test::TaskEnvironment task_environment_{
98 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
99 base::ScopedTempDir temp_directory_;
100 absl::optional<MediaDeviceSaltDatabase> db_;
103 TEST_P(MediaDeviceSaltDatabaseTest, DatabasePathExists) {
104 // Issue an operation to ensure the dababase is open.
105 std::ignore = db().GetOrInsertSalt(StorageKey1());
106 ASSERT_EQ(base::PathExists(DbPath()), ShouldBePersistent());
109 TEST_P(MediaDeviceSaltDatabaseTest, NoStorageForOpaqueStorageKey) {
110 EXPECT_FALSE(db().GetOrInsertSalt(blink::StorageKey()).has_value());
113 TEST_P(MediaDeviceSaltDatabaseTest, InsertExplicitSalt) {
114 const std::string salt = CreateRandomSalt();
115 EXPECT_THAT(db().GetOrInsertSalt(StorageKey1(), salt), Optional(salt));
116 EXPECT_THAT(db().GetOrInsertSalt(StorageKey1()), Optional(salt));
117 EXPECT_THAT(GetAllStoredEntries(),
118 ElementsAre(std::pair(StorageKey1(), salt)));
121 TEST_P(MediaDeviceSaltDatabaseTest, InsertRandomSalts) {
122 absl::optional<std::string> salt1 = db().GetOrInsertSalt(StorageKey1());
123 absl::optional<std::string> salt2 = db().GetOrInsertSalt(StorageKey2());
124 ASSERT_TRUE(salt1.has_value());
125 ASSERT_TRUE(salt2.has_value());
126 EXPECT_NE(*salt1, *salt2);
127 EXPECT_THAT(GetAllStoredEntries(),
128 UnorderedElementsAre(std::pair(StorageKey1(), *salt1),
129 std::pair(StorageKey2(), *salt2)));
132 TEST_P(MediaDeviceSaltDatabaseTest, ReplaceSalt) {
133 EXPECT_TRUE(db().GetOrInsertSalt(StorageKey1(), "salt1").has_value());
134 EXPECT_TRUE(db().GetOrInsertSalt(StorageKey1(), "salt2").has_value());
135 EXPECT_THAT(db().GetOrInsertSalt(StorageKey1()),
136 Optional(std::string("salt1")));
137 // There should be only one entry in the database.
138 EXPECT_THAT(GetAllStoredEntries(),
139 UnorderedElementsAre(std::pair(StorageKey1(), "salt1")));
142 TEST_P(MediaDeviceSaltDatabaseTest, DeleteEntriesByTime) {
143 base::Time now = base::Time::Now();
144 db().GetOrInsertSalt(StorageKey1(), "salt1");
145 task_environment().AdvanceClock(base::Days(1000));
146 db().GetOrInsertSalt(StorageKey2(), "salt2");
147 task_environment().AdvanceClock(base::Days(1000));
148 db().GetOrInsertSalt(StorageKey3(), "salt3");
149 db().DeleteEntries(now + base::Days(500), now + base::Days(1500));
150 EXPECT_THAT(GetAllStoredEntries(),
151 UnorderedElementsAre(std::pair(StorageKey1(), "salt1"),
152 std::pair(StorageKey3(), "salt3")));
155 TEST_P(MediaDeviceSaltDatabaseTest, DeleteAllEntries) {
156 db().GetOrInsertSalt(StorageKey1(), "salt1");
157 task_environment().AdvanceClock(base::Seconds(1000));
158 db().GetOrInsertSalt(StorageKey2(), "salt2");
159 task_environment().AdvanceClock(base::Days(1000));
160 db().GetOrInsertSalt(StorageKey3(), "salt3");
161 EXPECT_EQ(GetAllStoredEntries().size(), 3u);
163 db().DeleteEntries(Time::Min(), Time::Max());
164 EXPECT_TRUE(GetAllStoredEntries().empty());
167 TEST_P(MediaDeviceSaltDatabaseTest, DeleteSingleStorageKeyUsingMatcher) {
168 db().GetOrInsertSalt(StorageKey1(), "salt1");
169 db().GetOrInsertSalt(StorageKey2(), "salt2");
170 db().GetOrInsertSalt(StorageKey3(), "salt3");
171 db().DeleteEntries(base::Time(), base::Time::Max(),
172 CreateMatcherForKeys({StorageKey1()}));
173 EXPECT_THAT(GetAllStoredEntries(),
174 UnorderedElementsAre(std::pair(StorageKey2(), "salt2"),
175 std::pair(StorageKey3(), "salt3")));
178 TEST_P(MediaDeviceSaltDatabaseTest, DeleteSomeStorageKeysUsingMatcher) {
179 db().GetOrInsertSalt(StorageKey1(), "salt1");
180 db().GetOrInsertSalt(StorageKey2(), "salt2");
181 db().GetOrInsertSalt(StorageKey3(), "salt3");
182 db().DeleteEntries(base::Time(), base::Time::Max(),
183 CreateMatcherForKeys({StorageKey1(), StorageKey3()}));
184 EXPECT_THAT(GetAllStoredEntries(),
185 ElementsAre(std::pair(StorageKey2(), "salt2")));
188 TEST_P(MediaDeviceSaltDatabaseTest, DeleteSingleStorageKey) {
189 db().GetOrInsertSalt(StorageKey1(), "salt1");
190 db().GetOrInsertSalt(StorageKey2(), "salt2");
191 db().GetOrInsertSalt(StorageKey3(), "salt3");
193 db().DeleteEntry(StorageKey1());
194 EXPECT_THAT(GetAllStoredEntries(),
195 UnorderedElementsAre(std::pair(StorageKey2(), "salt2"),
196 std::pair(StorageKey3(), "salt3")));
198 db().DeleteEntry(StorageKey3());
199 EXPECT_THAT(GetAllStoredEntries(),
200 ElementsAre(std::pair(StorageKey2(), "salt2")));
203 TEST_P(MediaDeviceSaltDatabaseTest, DatabaseErrors) {
204 db().ForceErrorForTesting();
205 EXPECT_FALSE(db().GetOrInsertSalt(StorageKey1()).has_value());
208 TEST_P(MediaDeviceSaltDatabaseTest, GetAllStorageKeys) {
209 db().GetOrInsertSalt(StorageKey1(), "salt1");
210 db().GetOrInsertSalt(StorageKey2(), "salt2");
211 db().GetOrInsertSalt(StorageKey3(), "salt3");
214 db().GetAllStorageKeys(),
215 UnorderedElementsAre(StorageKey1(), StorageKey2(), StorageKey3()));
217 db().DeleteEntry(StorageKey3());
218 EXPECT_THAT(db().GetAllStorageKeys(),
219 UnorderedElementsAre(StorageKey1(), StorageKey2()));
221 db().DeleteEntries(base::Time::Min(), base::Time::Max());
222 EXPECT_THAT(db().GetAllStorageKeys(), IsEmpty());
225 INSTANTIATE_TEST_SUITE_P(All, MediaDeviceSaltDatabaseTest, testing::Bool());
227 } // namespace media_device_salt