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