blob: 023ab21113edb1d09f0c0729f30f22e68dace1da [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2020 The Chromium Authors
David Van Clevea4e3ee12020-01-21 20:07:012// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/sqlite_proto/key_value_data.h"
6
7#include "base/memory/scoped_refptr.h"
Sean Maher5b9af51f2022-11-21 15:32:478#include "base/task/single_thread_task_runner.h"
David Van Clevea4e3ee12020-01-21 20:07:019#include "base/test/task_environment.h"
David Van Clevea4e3ee12020-01-21 20:07:0110#include "components/sqlite_proto/table_manager.h"
11#include "components/sqlite_proto/test_proto.pb.h"
12#include "sql/database.h"
13#include "testing/gmock/include/gmock/gmock.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace sqlite_proto {
17
18namespace {
19
20template <typename T>
21class FakeKeyValueTable : public KeyValueTable<T> {
22 public:
23 FakeKeyValueTable() : sqlite_proto::KeyValueTable<T>("") {}
24 void GetAllData(std::map<std::string, T>* data_map,
25 sql::Database* db) const override {
26 *data_map = data_;
27 }
28 void UpdateData(const std::string& key,
29 const T& data,
30 sql::Database* db) override {
31 data_[key] = data;
32 }
33 void DeleteData(const std::vector<std::string>& keys,
34 sql::Database* db) override {
35 for (const auto& key : keys)
36 data_.erase(key);
37 }
38 void DeleteAllData(sql::Database* db) override { data_.clear(); }
39
40 std::map<std::string, T> data_;
41};
42
43class FakeTableManager : public TableManager {
44 public:
Sean Maher5b9af51f2022-11-21 15:32:4745 FakeTableManager()
46 : TableManager(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
David Van Clevea4e3ee12020-01-21 20:07:0147 void ScheduleDBTask(const base::Location& from_here,
48 base::OnceCallback<void(sql::Database*)> task) override {
49 GetTaskRunner()->PostTask(
50 from_here, base::BindOnce(&TableManager::ExecuteDBTaskOnDBSequence,
51 this, std::move(task)));
52 }
53 void ExecuteDBTaskOnDBSequence(
54 base::OnceCallback<void(sql::Database*)> task) override {
55 ASSERT_TRUE(GetTaskRunner()->RunsTasksInCurrentSequence());
56 std::move(task).Run(DB());
57 }
58
59 protected:
60 ~FakeTableManager() override = default;
61
62 // TableManager
David Van Cleveda471562021-07-15 01:14:5463 void CreateOrClearTablesIfNecessary() override {}
David Van Clevea4e3ee12020-01-21 20:07:0164 void LogDatabaseStats() override {}
65};
66
67MATCHER_P(EqualsProto,
68 message,
69 "Match a proto Message equal to the matcher's argument.") {
70 std::string expected_serialized, actual_serialized;
71 message.SerializeToString(&expected_serialized);
72 arg.SerializeToString(&actual_serialized);
73 return expected_serialized == actual_serialized;
74}
75
76struct TestProtoCompare {
77 bool operator()(const TestProto& lhs, const TestProto& rhs) {
78 return lhs.value() < rhs.value();
79 }
80};
81
82} // namespace
83
84class KeyValueDataTest : public ::testing::Test {
85 public:
86 KeyValueDataTest()
87 : manager_(base::MakeRefCounted<FakeTableManager>()),
Anton Bikineev1156b5f2021-05-15 22:35:3688 data_(manager_, &table_, absl::nullopt, base::TimeDelta()) {
David Van Clevea4e3ee12020-01-21 20:07:0189 // In these tests, we're using the current thread as the DB sequence.
90 data_.InitializeOnDBSequence();
91 }
92
93 ~KeyValueDataTest() override = default;
94
95 protected:
96 base::test::TaskEnvironment env_;
97 FakeKeyValueTable<TestProto> table_;
98 scoped_refptr<TableManager> manager_ =
99 base::MakeRefCounted<FakeTableManager>();
100 KeyValueData<TestProto, TestProtoCompare> data_;
101};
102
103TEST_F(KeyValueDataTest, GetWhenEmpty) {
104 TestProto result;
105 EXPECT_FALSE(data_.TryGetData("nonexistent_key", &result));
106}
107
108TEST_F(KeyValueDataTest, PutAndGet) {
109 TestProto first_entry, second_entry;
110 first_entry.set_value(1);
111 second_entry.set_value(1);
112
113 data_.UpdateData("a", first_entry);
114 data_.UpdateData("b", second_entry);
115
116 TestProto result;
117 ASSERT_TRUE(data_.TryGetData("a", &result));
118 EXPECT_THAT(result, EqualsProto(first_entry));
119
120 ASSERT_TRUE(data_.TryGetData("b", &result));
121 EXPECT_THAT(result, EqualsProto(second_entry));
122}
123
124// Test that deleting one entry:
125// - makes that entry inaccessible, but
126// - does not affect the remaining entry.
127TEST_F(KeyValueDataTest, Delete) {
128 TestProto first_entry, second_entry;
129 first_entry.set_value(1);
130 second_entry.set_value(1);
131
132 data_.UpdateData("a", first_entry);
133 data_.UpdateData("b", second_entry);
134
135 TestProto result;
136 data_.DeleteData(std::vector<std::string>{"b"});
137 EXPECT_FALSE(data_.TryGetData("b", &result));
138
139 ASSERT_TRUE(data_.TryGetData("a", &result));
140 EXPECT_THAT(result, EqualsProto(first_entry));
141}
142
143TEST_F(KeyValueDataTest, DeleteAll) {
144 TestProto first_entry, second_entry;
145 first_entry.set_value(1);
146 second_entry.set_value(1);
147
148 data_.UpdateData("a", first_entry);
149 data_.UpdateData("b", second_entry);
150
151 data_.DeleteAllData();
152
153 EXPECT_TRUE(data_.GetAllCached().empty());
154}
155
156TEST(KeyValueDataTestSize, CantAddToFullTable) {
157 FakeKeyValueTable<TestProto> table;
158 base::test::TaskEnvironment env;
159
160 auto manager = base::MakeRefCounted<FakeTableManager>();
161 KeyValueData<TestProto, TestProtoCompare> data(
162 manager, &table, /*max_num_entries=*/2,
163 /*flush_delay=*/base::TimeDelta());
164 // In these tests, we're using the current thread as the DB sequence.
165 data.InitializeOnDBSequence();
166
167 TestProto one_entry, two_entry, three_entry;
168 one_entry.set_value(1);
169 two_entry.set_value(2);
170 three_entry.set_value(3);
171
172 data.UpdateData("a", one_entry);
173 data.UpdateData("b", two_entry);
174 data.UpdateData("c", three_entry);
175
176 EXPECT_EQ(data.GetAllCached().size(), 2u);
177}
178
179// Test that building a KeyValueData on top of a backend table
180// with more than |max_num_entries| many entries leads to the table
181// being pruned down to a number of entries equal to the KeyValueData's
182// capacity.
183TEST(KeyValueDataTestSize, PrunesOverlargeTable) {
184 FakeKeyValueTable<TestProto> table;
185 base::test::TaskEnvironment env;
186
187 auto manager = base::MakeRefCounted<FakeTableManager>();
188
189 // Initialization: write a table of size 2 to |manager|'s backend.
190 {
191 KeyValueData<TestProto, TestProtoCompare> data(
Anton Bikineev1156b5f2021-05-15 22:35:36192 manager, &table, /*max_num_entries=*/absl::nullopt,
David Van Clevea4e3ee12020-01-21 20:07:01193 /*flush_delay=*/base::TimeDelta());
194 // In these tests, we're using the current thread as the DB sequence.
195 data.InitializeOnDBSequence();
196
197 TestProto one_entry, two_entry;
198 one_entry.set_value(1);
199 two_entry.set_value(2);
200
201 data.UpdateData("a", one_entry);
202 data.UpdateData("b", two_entry);
203
204 // Write changes through to the "disk."
205 env.RunUntilIdle();
206 }
207
208 {
209 KeyValueData<TestProto, TestProtoCompare> data(
210 manager, &table, /*max_num_entries=*/1,
211 /*flush_delay=*/base::TimeDelta());
212 // In these tests, we're using the current thread as the DB sequence.
213 data.InitializeOnDBSequence();
214
215 // A cache with size limit less than the size of the database
216 // should load items up to its capacity (evicting the rest).
217 EXPECT_EQ(data.GetAllCached().size(), 1u);
218 }
219
220 {
221 KeyValueData<TestProto, TestProtoCompare> data(
Anton Bikineev1156b5f2021-05-15 22:35:36222 manager, &table, /*max_num_entries=*/absl::nullopt,
David Van Clevea4e3ee12020-01-21 20:07:01223 /*flush_delay=*/base::TimeDelta());
224 // In these tests, we're using the current thread as the DB sequence.
225 data.InitializeOnDBSequence();
226
227 // The second, max_num_elements=1, cache should have pruned
228 // the database to a single element upon initialization.
229 EXPECT_EQ(data.GetAllCached().size(), 1u);
230 }
231}
232
233} // namespace sqlite_proto