blob: fd38a9049045161f9654185bab8f6c9ba2092239 [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"
Jan Keitel12a0f972025-04-19 07:44:129#include "base/test/protobuf_matchers.h"
David Van Clevea4e3ee12020-01-21 20:07:0110#include "base/test/task_environment.h"
David Van Clevea4e3ee12020-01-21 20:07:0111#include "components/sqlite_proto/table_manager.h"
12#include "components/sqlite_proto/test_proto.pb.h"
13#include "sql/database.h"
14#include "testing/gmock/include/gmock/gmock.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace sqlite_proto {
18
19namespace {
20
Jan Keitel12a0f972025-04-19 07:44:1221using base::test::EqualsProto;
22
David Van Clevea4e3ee12020-01-21 20:07:0123template <typename T>
24class FakeKeyValueTable : public KeyValueTable<T> {
25 public:
26 FakeKeyValueTable() : sqlite_proto::KeyValueTable<T>("") {}
27 void GetAllData(std::map<std::string, T>* data_map,
28 sql::Database* db) const override {
29 *data_map = data_;
30 }
31 void UpdateData(const std::string& key,
32 const T& data,
33 sql::Database* db) override {
34 data_[key] = data;
35 }
36 void DeleteData(const std::vector<std::string>& keys,
37 sql::Database* db) override {
38 for (const auto& key : keys)
39 data_.erase(key);
40 }
41 void DeleteAllData(sql::Database* db) override { data_.clear(); }
42
43 std::map<std::string, T> data_;
44};
45
46class FakeTableManager : public TableManager {
47 public:
Sean Maher5b9af51f2022-11-21 15:32:4748 FakeTableManager()
49 : TableManager(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
David Van Clevea4e3ee12020-01-21 20:07:0150 void ScheduleDBTask(const base::Location& from_here,
51 base::OnceCallback<void(sql::Database*)> task) override {
52 GetTaskRunner()->PostTask(
53 from_here, base::BindOnce(&TableManager::ExecuteDBTaskOnDBSequence,
54 this, std::move(task)));
55 }
56 void ExecuteDBTaskOnDBSequence(
57 base::OnceCallback<void(sql::Database*)> task) override {
58 ASSERT_TRUE(GetTaskRunner()->RunsTasksInCurrentSequence());
59 std::move(task).Run(DB());
60 }
61
62 protected:
63 ~FakeTableManager() override = default;
64
65 // TableManager
David Van Cleveda471562021-07-15 01:14:5466 void CreateOrClearTablesIfNecessary() override {}
David Van Clevea4e3ee12020-01-21 20:07:0167 void LogDatabaseStats() override {}
68};
69
David Van Clevea4e3ee12020-01-21 20:07:0170struct TestProtoCompare {
71 bool operator()(const TestProto& lhs, const TestProto& rhs) {
72 return lhs.value() < rhs.value();
73 }
74};
75
76} // namespace
77
78class KeyValueDataTest : public ::testing::Test {
79 public:
80 KeyValueDataTest()
81 : manager_(base::MakeRefCounted<FakeTableManager>()),
Arthur Sonzognic571efb2024-01-26 20:26:1882 data_(manager_, &table_, std::nullopt, base::TimeDelta()) {
David Van Clevea4e3ee12020-01-21 20:07:0183 // In these tests, we're using the current thread as the DB sequence.
84 data_.InitializeOnDBSequence();
85 }
86
87 ~KeyValueDataTest() override = default;
88
89 protected:
90 base::test::TaskEnvironment env_;
91 FakeKeyValueTable<TestProto> table_;
92 scoped_refptr<TableManager> manager_ =
93 base::MakeRefCounted<FakeTableManager>();
94 KeyValueData<TestProto, TestProtoCompare> data_;
95};
96
97TEST_F(KeyValueDataTest, GetWhenEmpty) {
98 TestProto result;
99 EXPECT_FALSE(data_.TryGetData("nonexistent_key", &result));
100}
101
102TEST_F(KeyValueDataTest, PutAndGet) {
103 TestProto first_entry, second_entry;
104 first_entry.set_value(1);
105 second_entry.set_value(1);
106
107 data_.UpdateData("a", first_entry);
108 data_.UpdateData("b", second_entry);
109
110 TestProto result;
111 ASSERT_TRUE(data_.TryGetData("a", &result));
112 EXPECT_THAT(result, EqualsProto(first_entry));
113
114 ASSERT_TRUE(data_.TryGetData("b", &result));
115 EXPECT_THAT(result, EqualsProto(second_entry));
116}
117
118// Test that deleting one entry:
119// - makes that entry inaccessible, but
120// - does not affect the remaining entry.
121TEST_F(KeyValueDataTest, Delete) {
122 TestProto first_entry, second_entry;
123 first_entry.set_value(1);
124 second_entry.set_value(1);
125
126 data_.UpdateData("a", first_entry);
127 data_.UpdateData("b", second_entry);
128
129 TestProto result;
130 data_.DeleteData(std::vector<std::string>{"b"});
131 EXPECT_FALSE(data_.TryGetData("b", &result));
132
133 ASSERT_TRUE(data_.TryGetData("a", &result));
134 EXPECT_THAT(result, EqualsProto(first_entry));
135}
136
137TEST_F(KeyValueDataTest, DeleteAll) {
138 TestProto first_entry, second_entry;
139 first_entry.set_value(1);
140 second_entry.set_value(1);
141
142 data_.UpdateData("a", first_entry);
143 data_.UpdateData("b", second_entry);
144
145 data_.DeleteAllData();
146
147 EXPECT_TRUE(data_.GetAllCached().empty());
148}
149
150TEST(KeyValueDataTestSize, CantAddToFullTable) {
151 FakeKeyValueTable<TestProto> table;
152 base::test::TaskEnvironment env;
153
154 auto manager = base::MakeRefCounted<FakeTableManager>();
155 KeyValueData<TestProto, TestProtoCompare> data(
156 manager, &table, /*max_num_entries=*/2,
157 /*flush_delay=*/base::TimeDelta());
158 // In these tests, we're using the current thread as the DB sequence.
159 data.InitializeOnDBSequence();
160
161 TestProto one_entry, two_entry, three_entry;
162 one_entry.set_value(1);
163 two_entry.set_value(2);
164 three_entry.set_value(3);
165
166 data.UpdateData("a", one_entry);
167 data.UpdateData("b", two_entry);
168 data.UpdateData("c", three_entry);
169
170 EXPECT_EQ(data.GetAllCached().size(), 2u);
171}
172
173// Test that building a KeyValueData on top of a backend table
174// with more than |max_num_entries| many entries leads to the table
175// being pruned down to a number of entries equal to the KeyValueData's
176// capacity.
177TEST(KeyValueDataTestSize, PrunesOverlargeTable) {
178 FakeKeyValueTable<TestProto> table;
179 base::test::TaskEnvironment env;
180
181 auto manager = base::MakeRefCounted<FakeTableManager>();
182
183 // Initialization: write a table of size 2 to |manager|'s backend.
184 {
185 KeyValueData<TestProto, TestProtoCompare> data(
Arthur Sonzognic571efb2024-01-26 20:26:18186 manager, &table, /*max_num_entries=*/std::nullopt,
David Van Clevea4e3ee12020-01-21 20:07:01187 /*flush_delay=*/base::TimeDelta());
188 // In these tests, we're using the current thread as the DB sequence.
189 data.InitializeOnDBSequence();
190
191 TestProto one_entry, two_entry;
192 one_entry.set_value(1);
193 two_entry.set_value(2);
194
195 data.UpdateData("a", one_entry);
196 data.UpdateData("b", two_entry);
197
198 // Write changes through to the "disk."
199 env.RunUntilIdle();
200 }
201
202 {
203 KeyValueData<TestProto, TestProtoCompare> data(
204 manager, &table, /*max_num_entries=*/1,
205 /*flush_delay=*/base::TimeDelta());
206 // In these tests, we're using the current thread as the DB sequence.
207 data.InitializeOnDBSequence();
208
209 // A cache with size limit less than the size of the database
210 // should load items up to its capacity (evicting the rest).
211 EXPECT_EQ(data.GetAllCached().size(), 1u);
212 }
213
214 {
215 KeyValueData<TestProto, TestProtoCompare> data(
Arthur Sonzognic571efb2024-01-26 20:26:18216 manager, &table, /*max_num_entries=*/std::nullopt,
David Van Clevea4e3ee12020-01-21 20:07:01217 /*flush_delay=*/base::TimeDelta());
218 // In these tests, we're using the current thread as the DB sequence.
219 data.InitializeOnDBSequence();
220
221 // The second, max_num_elements=1, cache should have pruned
222 // the database to a single element upon initialization.
223 EXPECT_EQ(data.GetAllCached().size(), 1u);
224 }
225}
226
227} // namespace sqlite_proto