1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "storage/config_cache.h"
18
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21
22 #include <cstdio>
23
24 #include "hci/enum_helper.h"
25 #include "storage/device.h"
26
27 namespace testing {
28
29 namespace {
GetTestAddress(int i)30 std::string GetTestAddress(int i) {
31 std::string res = "00:00:00:00:00:00";
32 res.reserve(res.size() + 1);
33 std::snprintf(res.data(), res.capacity(), "AA:BB:CC:DD:EE:%02d", i);
34 return res;
35 }
36 } // namespace
37
38 using bluetooth::storage::ConfigCache;
39 using bluetooth::storage::Device;
40 using SectionAndPropertyValue = bluetooth::storage::ConfigCache::SectionAndPropertyValue;
41
TEST(ConfigCacheTest,simple_set_get_test)42 TEST(ConfigCacheTest, simple_set_get_test) {
43 ConfigCache config(100, Device::kLinkKeyProperties);
44 config.SetProperty("A", "B", "C");
45 auto value = config.GetProperty("A", "B");
46 ASSERT_TRUE(value);
47 ASSERT_EQ(*value, "C");
48 }
49
TEST(ConfigCacheTest,empty_values_test)50 TEST(ConfigCacheTest, empty_values_test) {
51 ConfigCache config(100, Device::kLinkKeyProperties);
52 ASSERT_DEATH({ config.SetProperty("", "B", "C"); }, "Empty section name not allowed");
53 ASSERT_DEATH({ config.SetProperty("A", "", "C"); }, "Empty property name not allowed");
54 // empty value is allowed
55 config.SetProperty("A", "B", "");
56 auto value = config.GetProperty("A", "B");
57 ASSERT_TRUE(value);
58 ASSERT_EQ(*value, "");
59 }
60
TEST(ConfigCacheTest,insert_boundary_device_with_linkkey_test)61 TEST(ConfigCacheTest, insert_boundary_device_with_linkkey_test) {
62 ConfigCache config(2, Device::kLinkKeyProperties);
63 config.SetProperty("A", "B", "C");
64 config.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
65 config.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
66 config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
67 ASSERT_TRUE(config.GetProperty("CC:DD:EE:FF:00:10", "Name"));
68 }
69
TEST(ConfigCacheTest,comparison_test)70 TEST(ConfigCacheTest, comparison_test) {
71 ConfigCache config_1(2, Device::kLinkKeyProperties);
72 config_1.SetProperty("A", "B", "C");
73 config_1.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
74 config_1.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
75 config_1.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
76 ConfigCache config_2(2, Device::kLinkKeyProperties);
77 config_2.SetProperty("A", "B", "C");
78 config_2.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
79 config_2.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
80 config_2.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
81 ASSERT_EQ(config_1, config_2);
82 // Config with different temp device order should not be equal
83 ASSERT_TRUE(config_2.GetProperty("CC:DD:EE:FF:00:10", "Name"));
84 ASSERT_NE(config_1, config_2);
85 ASSERT_TRUE(config_1.GetProperty("CC:DD:EE:FF:00:10", "Name"));
86 ASSERT_EQ(config_1, config_2);
87 // Config with different persistent device order should not be equal
88 config_1.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
89 config_2.RemoveSection("CC:DD:EE:FF:00:11");
90 config_2.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
91 config_2.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
92 ASSERT_NE(config_1, config_2);
93 // Config with different capacity should not be equal
94 ConfigCache config_3(3, Device::kLinkKeyProperties);
95 config_3.SetProperty("A", "B", "C");
96 config_3.SetProperty("CC:DD:EE:FF:00:10", "Name", "Hello");
97 config_3.SetProperty("CC:DD:EE:FF:00:09", "Name", "Hello 2");
98 config_3.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
99 config_3.SetProperty("CC:DD:EE:FF:00:12", "LinkKey", "AABBAABBCCDDEE");
100 ASSERT_NE(config_1, config_3);
101 // Empty config should not be equal to non-empty ones
102 ConfigCache config_4(2, Device::kLinkKeyProperties);
103 ASSERT_NE(config_1, config_4);
104 // Empty configs should be equal
105 ConfigCache config_5(2, Device::kLinkKeyProperties);
106 ASSERT_EQ(config_4, config_5);
107 // Empty configs with different capacity should not be equal
108 ConfigCache config_6(3, Device::kLinkKeyProperties);
109 ASSERT_NE(config_4, config_6);
110 }
111
TEST(ConfigCacheTest,empty_string_test)112 TEST(ConfigCacheTest, empty_string_test) {
113 ConfigCache config(100, Device::kLinkKeyProperties);
114 config.SetProperty("A", "B", "");
115 auto value = config.GetProperty("A", "B");
116 ASSERT_TRUE(value);
117 ASSERT_EQ(*value, "");
118 }
119
TEST(ConfigCacheTest,mac_address_set_get_test)120 TEST(ConfigCacheTest, mac_address_set_get_test) {
121 ConfigCache config(100, Device::kLinkKeyProperties);
122 config.SetProperty("A", "B", "C");
123 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
124 auto value = config.GetProperty("A", "B");
125 ASSERT_TRUE(value);
126 ASSERT_EQ(*value, "C");
127 value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
128 ASSERT_TRUE(value);
129 ASSERT_EQ(*value, "C");
130 ASSERT_FALSE(config.GetProperty("A", "BC"));
131 ASSERT_FALSE(config.GetProperty("ABC", "B"));
132 }
133
TEST(ConfigCacheTest,has_section_and_property_test)134 TEST(ConfigCacheTest, has_section_and_property_test) {
135 ConfigCache config(100, Device::kLinkKeyProperties);
136 config.SetProperty("A", "B", "C");
137 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
138 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
139 ASSERT_TRUE(config.HasSection("A"));
140 ASSERT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
141 ASSERT_TRUE(config.HasProperty("A", "B"));
142 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
143 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
144 auto value = config.GetProperty("AA:BB:CC:DD:EE:FF", "C");
145 ASSERT_TRUE(value);
146 ASSERT_EQ(*value, "D");
147 value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
148 ASSERT_TRUE(value);
149 ASSERT_EQ(*value, "C");
150 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "E");
151 value = config.GetProperty("AA:BB:CC:DD:EE:FF", "B");
152 ASSERT_TRUE(value);
153 ASSERT_THAT(value, Optional(StrEq("E")));
154 ASSERT_FALSE(config.HasSection("Ab"));
155 ASSERT_FALSE(config.HasSection("AA:11:CC:DD:EE:FF"));
156 ASSERT_FALSE(config.HasProperty("A", "bB"));
157 ASSERT_FALSE(config.HasProperty("AA:BB:11:DD:EE:FF", "B"));
158 }
159
TEST(ConfigCacheTest,remove_section_test)160 TEST(ConfigCacheTest, remove_section_test) {
161 ConfigCache config(100, Device::kLinkKeyProperties);
162 config.SetProperty("A", "B", "C");
163 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
164 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
165 ASSERT_TRUE(config.HasSection("A"));
166 ASSERT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
167 ASSERT_TRUE(config.HasProperty("A", "B"));
168 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
169 ASSERT_TRUE(config.RemoveSection("AA:BB:CC:DD:EE:FF"));
170 ASSERT_TRUE(config.RemoveSection("A"));
171 ASSERT_FALSE(config.HasProperty("A", "B"));
172 ASSERT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
173 }
174
TEST(ConfigCacheTest,remove_property_test)175 TEST(ConfigCacheTest, remove_property_test) {
176 ConfigCache config(100, Device::kLinkKeyProperties);
177 config.SetProperty("A", "B", "C");
178 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
179 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
180 ASSERT_TRUE(config.HasSection("A"));
181 ASSERT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
182 ASSERT_TRUE(config.HasProperty("A", "B"));
183 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
184 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "C"));
185 ASSERT_TRUE(config.RemoveProperty("AA:BB:CC:DD:EE:FF", "B"));
186 ASSERT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
187 ASSERT_FALSE(config.GetProperty("AA:BB:CC:DD:EE:FF", "B"));
188 }
189
TEST(ConfigCacheTest,remove_all_properties_from_section_test)190 TEST(ConfigCacheTest, remove_all_properties_from_section_test) {
191 ConfigCache config(100, Device::kLinkKeyProperties);
192 config.SetProperty("A", "B", "C");
193 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
194 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
195 ASSERT_TRUE(config.HasSection("A"));
196 ASSERT_TRUE(config.HasSection("AA:BB:CC:DD:EE:FF"));
197 ASSERT_TRUE(config.HasProperty("A", "B"));
198 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
199 ASSERT_TRUE(config.HasProperty("AA:BB:CC:DD:EE:FF", "C"));
200 ASSERT_TRUE(config.RemoveSection("AA:BB:CC:DD:EE:FF"));
201 ASSERT_FALSE(config.HasSection("AA:BB:CC:DD:EE:FF"));
202 ASSERT_FALSE(config.HasProperty("AA:BB:CC:DD:EE:FF", "B"));
203 ASSERT_FALSE(config.GetProperty("AA:BB:CC:DD:EE:FF", "C"));
204 }
205
TEST(ConfigCacheTest,get_persistent_devices_test)206 TEST(ConfigCacheTest, get_persistent_devices_test) {
207 ConfigCache config(100, Device::kLinkKeyProperties);
208 config.SetProperty("A", "B", "C");
209 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
210 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
211 config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
212 ASSERT_TRUE(config.HasProperty("CC:DD:EE:FF:00:11", "LinkKey"));
213 ASSERT_THAT(config.GetPersistentSections(), ElementsAre("CC:DD:EE:FF:00:11"));
214 config.SetProperty("AA:BB:CC:DD:EE:FF", "LinkKey", "DEERDEERDEER");
215 ASSERT_THAT(config.GetPersistentSections(), ElementsAre("CC:DD:EE:FF:00:11", "AA:BB:CC:DD:EE:FF"));
216 ASSERT_TRUE(config.RemoveProperty("CC:DD:EE:FF:00:11", "LinkKey"));
217 ASSERT_THAT(config.GetPersistentSections(), ElementsAre("AA:BB:CC:DD:EE:FF"));
218 }
219
TEST(ConfigCacheTest,appoaching_temporary_config_limit_test)220 TEST(ConfigCacheTest, appoaching_temporary_config_limit_test) {
221 ConfigCache config(2, Device::kLinkKeyProperties);
222 for (int i = 0; i < 10; ++i) {
223 config.SetProperty(GetTestAddress(i), "Name", "Hello" + std::to_string(i));
224 if (i % 2 == 0) {
225 config.SetProperty(GetTestAddress(i), "LinkKey", "Key" + std::to_string(i));
226 }
227 }
228 for (int i = 0; i < 10; ++i) {
229 if (i % 2 == 0) {
230 ASSERT_TRUE(config.HasSection(GetTestAddress(i)));
231 ASSERT_TRUE(config.HasProperty(GetTestAddress(i), "LinkKey"));
232 ASSERT_THAT(config.GetProperty(GetTestAddress(i), "Name"), Optional(StrEq("Hello" + std::to_string(i))));
233 } else if (i >= 7) {
234 ASSERT_TRUE(config.HasSection(GetTestAddress(i)));
235 ASSERT_THAT(config.GetProperty(GetTestAddress(i), "Name"), Optional(StrEq("Hello" + std::to_string(i))));
236 } else {
237 ASSERT_FALSE(config.HasSection(GetTestAddress(i)));
238 }
239 }
240 ASSERT_THAT(
241 config.GetPersistentSections(),
242 ElementsAre(GetTestAddress(0), GetTestAddress(2), GetTestAddress(4), GetTestAddress(6), GetTestAddress(8)));
243 }
244
TEST(ConfigCacheTest,remove_section_with_property_test)245 TEST(ConfigCacheTest, remove_section_with_property_test) {
246 ConfigCache config(100, Device::kLinkKeyProperties);
247 config.SetProperty("A", "B", "C");
248 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
249 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
250 config.SetProperty("CC:DD:EE:FF:00:11", "B", "AABBAABBCCDDEE");
251 config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
252 config.RemoveSectionWithProperty("B");
253 ASSERT_FALSE(config.HasSection("A"));
254 ASSERT_FALSE(config.HasSection("AA:BB:CC:DD:EE:FF"));
255 ASSERT_FALSE(config.HasSection("CC:DD:EE:FF:00:11"));
256 }
257
TEST(ConfigCacheTest,persistent_config_changed_callback_test)258 TEST(ConfigCacheTest, persistent_config_changed_callback_test) {
259 ConfigCache config(100, Device::kLinkKeyProperties);
260 int num_change = 0;
261 config.SetPersistentConfigChangedCallback([&num_change] { num_change++; });
262 config.SetProperty("A", "B", "C");
263 ASSERT_EQ(num_change, 1);
264 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
265 ASSERT_EQ(num_change, 1);
266 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
267 ASSERT_EQ(num_change, 1);
268 config.SetProperty("CC:DD:EE:FF:00:11", "B", "AABBAABBCCDDEE");
269 ASSERT_EQ(num_change, 1);
270 config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
271 ASSERT_EQ(num_change, 2);
272 config.RemoveProperty("CC:DD:EE:FF:00:11", "LinkKey");
273 ASSERT_EQ(num_change, 3);
274 config.RemoveSectionWithProperty("B");
275 ASSERT_EQ(num_change, 4);
276 }
277
TEST(ConfigCacheTest,fix_device_type_inconsistency_test)278 TEST(ConfigCacheTest, fix_device_type_inconsistency_test) {
279 ConfigCache config(100, Device::kLinkKeyProperties);
280 config.SetProperty("A", "B", "C");
281 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
282 config.SetProperty("AA:BB:CC:DD:EE:FF", "C", "D");
283 ASSERT_TRUE(config.FixDeviceTypeInconsistencies());
284 ASSERT_THAT(
285 config.GetProperty("AA:BB:CC:DD:EE:FF", "DevType"),
286 Optional(StrEq(std::to_string(bluetooth::hci::DeviceType::BR_EDR))));
287 config.SetProperty("CC:DD:EE:FF:00:11", "B", "AABBAABBCCDDEE");
288 config.SetProperty("CC:DD:EE:FF:00:11", "DevType", std::to_string(bluetooth::hci::DeviceType::BR_EDR));
289 config.SetProperty("CC:DD:EE:FF:00:11", "LinkKey", "AABBAABBCCDDEE");
290 ASSERT_FALSE(config.FixDeviceTypeInconsistencies());
291 ASSERT_THAT(
292 config.GetProperty("CC:DD:EE:FF:00:11", "DevType"),
293 Optional(StrEq(std::to_string(bluetooth::hci::DeviceType::BR_EDR))));
294 config.SetProperty("CC:DD:EE:FF:00:11", "LE_KEY_PENC", "AABBAABBCCDDEE");
295 ASSERT_TRUE(config.FixDeviceTypeInconsistencies());
296 ASSERT_THAT(
297 config.GetProperty("CC:DD:EE:FF:00:11", "DevType"),
298 Optional(StrEq(std::to_string(bluetooth::hci::DeviceType::DUAL))));
299 config.RemoveProperty("CC:DD:EE:FF:00:11", "LinkKey");
300 ASSERT_TRUE(config.FixDeviceTypeInconsistencies());
301 ASSERT_THAT(
302 config.GetProperty("CC:DD:EE:FF:00:11", "DevType"),
303 Optional(StrEq(std::to_string(bluetooth::hci::DeviceType::LE))));
304 }
305
TEST(ConfigCacheTest,test_get_section_with_property)306 TEST(ConfigCacheTest, test_get_section_with_property) {
307 ConfigCache config(100, Device::kLinkKeyProperties);
308 config.SetProperty("A", "B", "C");
309 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
310 config.SetProperty("AA:BB:CC:DD:EE:EF", "C", "D");
311 ASSERT_THAT(
312 config.GetSectionNamesWithProperty("B"),
313 ElementsAre(
314 SectionAndPropertyValue{.section = "A", .property = "C"},
315 SectionAndPropertyValue{.section = "AA:BB:CC:DD:EE:FF", .property = "C"}));
316 }
317
TEST(ConfigCacheTest,test_get_sections_matching_at_least_one_property)318 TEST(ConfigCacheTest, test_get_sections_matching_at_least_one_property) {
319 ConfigCache config(100, Device::kLinkKeyProperties);
320 config.SetProperty("A", "B", "C");
321 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
322 config.SetProperty("AA:BB:CC:DD:EE:EF", "C", "D");
323 ASSERT_TRUE(config.HasAtLeastOneMatchingPropertiesInSection("AA:BB:CC:DD:EE:FF", {"B", "C", "D"}));
324 ASSERT_TRUE(config.HasAtLeastOneMatchingPropertiesInSection("A", {"B", "C", "D"}));
325 ASSERT_FALSE(config.HasAtLeastOneMatchingPropertiesInSection("AA:BB:CC:DD:EE:FF", {"BC", "D"}));
326 }
327
TEST(ConfigCacheTest,test_empty_persistent_properties)328 TEST(ConfigCacheTest, test_empty_persistent_properties) {
329 ConfigCache config(100, {});
330 config.SetProperty("A", "B", "C");
331 config.SetProperty("AA:BB:CC:DD:EE:FF", "B", "C");
332 config.SetProperty("AA:BB:CC:DD:EE:EF", "C", "D");
333 config.SetProperty("AA:BB:CC:DD:EE:EF", "LinkKey", "D");
334 ASSERT_TRUE(config.HasAtLeastOneMatchingPropertiesInSection("AA:BB:CC:DD:EE:FF", {"B", "C", "D"}));
335 ASSERT_TRUE(config.HasAtLeastOneMatchingPropertiesInSection("A", {"B", "C", "D"}));
336 ASSERT_FALSE(config.HasAtLeastOneMatchingPropertiesInSection("AA:BB:CC:DD:EE:FF", {"BC", "D"}));
337 ASSERT_THAT(config.GetPersistentSections(), ElementsAre());
338 }
339
340 } // namespace testing