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/storage_module.h"
18
19 #include <bluetooth/log.h>
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22
23 #include <chrono>
24 #include <cstdio>
25 #include <filesystem>
26 #include <iomanip>
27 #include <optional>
28 #include <thread>
29
30 #include "common/bind.h"
31 #include "module.h"
32 #include "os/fake_timer/fake_timerfd.h"
33 #include "os/files.h"
34 #include "storage/config_cache.h"
35 #include "storage/config_keys.h"
36 #include "storage/device.h"
37 #include "storage/legacy_config_file.h"
38
39 namespace testing {
40
41 using bluetooth::TestModuleRegistry;
42 using bluetooth::hci::Address;
43 using bluetooth::os::fake_timer::fake_timerfd_advance;
44 using bluetooth::storage::ConfigCache;
45 using bluetooth::storage::Device;
46 using bluetooth::storage::LegacyConfigFile;
47 using bluetooth::storage::StorageModule;
48
49 static const std::chrono::milliseconds kTestConfigSaveDelay = std::chrono::milliseconds(100);
50 static const size_t kTestTempDevicesCapacity = 10;
51
52 class TestStorageModule : public StorageModule {
53 public:
TestStorageModule(std::string config_file_path,std::chrono::milliseconds config_save_delay,bool is_restricted_mode,bool is_single_user_mode)54 TestStorageModule(std::string config_file_path, std::chrono::milliseconds config_save_delay,
55 bool is_restricted_mode, bool is_single_user_mode)
56 : StorageModule(std::move(config_file_path), config_save_delay, kTestTempDevicesCapacity,
57 is_restricted_mode, is_single_user_mode) {}
58
GetMemoryOnlyConfigCachePublic()59 ConfigCache* GetMemoryOnlyConfigCachePublic() {
60 return StorageModule::GetMemoryOnlyConfigCache();
61 }
62
HasSectionPublic(const std::string & section) const63 bool HasSectionPublic(const std::string& section) const {
64 return StorageModule::HasSection(section);
65 }
HasPropertyPublic(const std::string & section,const std::string & property) const66 bool HasPropertyPublic(const std::string& section, const std::string& property) const {
67 return HasProperty(section, property);
68 }
69
GetPropertyPublic(const std::string & section,const std::string & property) const70 std::optional<std::string> GetPropertyPublic(const std::string& section,
71 const std::string& property) const {
72 return GetProperty(section, property);
73 }
SetPropertyPublic(std::string section,std::string property,std::string value)74 void SetPropertyPublic(std::string section, std::string property, std::string value) {
75 return SetProperty(section, property, value);
76 }
77
GetPersistentSectionsPublic() const78 std::vector<std::string> GetPersistentSectionsPublic() const { return GetPersistentSections(); }
79
RemovePropertyPublic(const std::string & section,const std::string & property)80 bool RemovePropertyPublic(const std::string& section, const std::string& property) {
81 return RemoveProperty(section, property);
82 }
83
ConvertEncryptOrDecryptKeyIfNeededPublic()84 void ConvertEncryptOrDecryptKeyIfNeededPublic() { return ConvertEncryptOrDecryptKeyIfNeeded(); }
85
RemoveSectionWithPropertyPublic(const std::string & property)86 void RemoveSectionWithPropertyPublic(const std::string& property) {
87 return RemoveSectionWithProperty(property);
88 }
89
RemoveSectionPublic(const std::string & section)90 void RemoveSectionPublic(const std::string& section) { return RemoveSection(section); }
91 };
92
93 class StorageModuleTest : public Test {
94 protected:
SetUp()95 void SetUp() override {
96 temp_dir_ = std::filesystem::temp_directory_path();
97 temp_config_ = temp_dir_ / "temp_config.txt";
98 DeleteConfigFiles();
99 ASSERT_FALSE(std::filesystem::exists(temp_config_));
100 }
101
TearDown()102 void TearDown() override {
103 test_registry_.StopAll();
104 DeleteConfigFiles();
105 }
106
DeleteConfigFiles()107 void DeleteConfigFiles() {
108 if (std::filesystem::exists(temp_config_)) {
109 ASSERT_TRUE(std::filesystem::remove(temp_config_));
110 }
111 }
112
FakeTimerAdvance(std::chrono::milliseconds time)113 void FakeTimerAdvance(std::chrono::milliseconds time) {
114 auto handler = test_registry_.GetTestModuleHandler(&StorageModule::Factory);
115 handler->Post(bluetooth::common::BindOnce(fake_timerfd_advance, time.count()));
116 }
117
WaitForReactorIdle(std::chrono::milliseconds time)118 bool WaitForReactorIdle(std::chrono::milliseconds time) {
119 bool stopped =
120 test_registry_.GetTestThread().GetReactor()->WaitForIdle(std::chrono::seconds(2));
121 if (!stopped) {
122 return false;
123 }
124 FakeTimerAdvance(time);
125 return test_registry_.GetTestThread().GetReactor()->WaitForIdle(std::chrono::seconds(2));
126 }
127
128 bluetooth::os::Handler* handler_;
129 TestModuleRegistry test_registry_;
130 std::filesystem::path temp_dir_;
131 std::filesystem::path temp_config_;
132 };
133
TEST_F(StorageModuleTest,empty_config_no_op_test)134 TEST_F(StorageModuleTest, empty_config_no_op_test) {
135 // Verify state before test
136 ASSERT_FALSE(std::filesystem::exists(temp_config_));
137
138 // Actual test
139 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
140 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
141 test_registry_.StopAll();
142
143 // Verify states after test
144 ASSERT_TRUE(std::filesystem::exists(temp_config_));
145
146 // Verify config after test
147 auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
148 ASSERT_TRUE(config);
149 ASSERT_TRUE(config->HasSection(StorageModule::kInfoSection));
150 }
151
152 static const std::string kReadTestConfig =
153 "[Info]\n"
154 "TimeCreated = 2020-05-20 01:20:56\n"
155 "\n"
156 "[Metrics]\n"
157 "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
158 "\n"
159 "[Adapter]\n"
160 "Address = 01:02:03:ab:cd:ef\n"
161 "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
162 "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
163 "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
164 "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
165 "ScanMode = 2\n"
166 "DiscoveryTimeout = 120\n"
167 "\n"
168 "[01:02:03:ab:cd:ea]\n"
169 "Name = hello world\n"
170 "LinkKey = fedcba0987654321fedcba0987654328\n"
171 "DevType = 1\n"
172 "\n";
173
TEST_F(StorageModuleTest,read_existing_config_test)174 TEST_F(StorageModuleTest, read_existing_config_test) {
175 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
176 // Actual test
177
178 // Set up
179 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
180 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
181
182 // Test
183 ASSERT_TRUE(storage->HasSectionPublic("Metrics"));
184 ASSERT_THAT(storage->GetPersistentSectionsPublic(), ElementsAre("01:02:03:ab:cd:ea"));
185 ASSERT_THAT(storage->GetPropertyPublic(StorageModule::kAdapterSection, BTIF_STORAGE_KEY_ADDRESS),
186 Optional(StrEq("01:02:03:ab:cd:ef")));
187
188 // Tear down
189 test_registry_.StopAll();
190
191 // Verify states after test
192 ASSERT_TRUE(std::filesystem::exists(temp_config_));
193
194 // Verify config after test
195 auto config = bluetooth::os::ReadSmallFile(temp_config_.string());
196 ASSERT_TRUE(config);
197 ASSERT_EQ(*config, kReadTestConfig);
198 }
199
TEST_F(StorageModuleTest,save_config_test)200 TEST_F(StorageModuleTest, save_config_test) {
201 // Prepare config file
202 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
203
204 // Set up
205 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
206 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
207
208 // Test
209 // Change a property
210 ASSERT_THAT(storage->GetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
211 Optional(StrEq("hello world")));
212 storage->SetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
213 ASSERT_THAT(storage->GetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
214 Optional(StrEq("foo")));
215 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
216
217 auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
218 ASSERT_TRUE(config);
219 ASSERT_THAT(config->GetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
220 Optional(StrEq("foo")));
221
222 // Remove a property
223 storage->RemovePropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME);
224 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
225 bluetooth::log::info("After waiting 2");
226 config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
227 ASSERT_TRUE(config);
228 ASSERT_FALSE(config->HasProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME));
229
230 // Remove a section
231 storage->RemoveSectionPublic("01:02:03:ab:cd:ea");
232 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
233 bluetooth::log::info("After waiting 3");
234 config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
235 ASSERT_TRUE(config);
236 ASSERT_FALSE(config->HasSection("01:02:03:ab:cd:ea"));
237
238 // Tear down
239 test_registry_.StopAll();
240
241 // Verify states after test
242 ASSERT_TRUE(std::filesystem::exists(temp_config_));
243 }
244
TEST_F(StorageModuleTest,get_bonded_devices_test)245 TEST_F(StorageModuleTest, get_bonded_devices_test) {
246 // Prepare config file
247 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
248
249 // Set up
250 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
251 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
252
253 ASSERT_EQ(storage->GetBondedDevices().size(), 1u);
254 auto address = Address::FromString("01:02:03:ab:cd:ea");
255 ASSERT_EQ(address, storage->GetBondedDevices()[0].GetAddress());
256
257 // Tear down
258 test_registry_.StopAll();
259 }
260
TEST_F(StorageModuleTest,unchanged_config_causes_no_write)261 TEST_F(StorageModuleTest, unchanged_config_causes_no_write) {
262 // Prepare config file
263 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
264
265 // Set up
266 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
267 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
268
269 ASSERT_EQ(storage->GetBondedDevices().size(), 1u);
270 auto address = Address::FromString("01:02:03:ab:cd:ea");
271 ASSERT_EQ(address, storage->GetBondedDevices()[0].GetAddress());
272
273 // Remove the file after it was read, so we can check if it was written with exists()
274 DeleteConfigFiles();
275
276 // Tear down
277 test_registry_.StopAll();
278
279 ASSERT_FALSE(std::filesystem::exists(temp_config_));
280 }
281
TEST_F(StorageModuleTest,changed_config_causes_a_write)282 TEST_F(StorageModuleTest, changed_config_causes_a_write) {
283 // Prepare config file
284 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
285
286 // Set up
287 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
288 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
289
290 // Remove the file after it was read, so we can check if it was written with exists()
291 DeleteConfigFiles();
292
293 // Change a property
294 storage->SetPropertyPublic("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
295
296 ASSERT_TRUE(WaitForReactorIdle(std::chrono::milliseconds(1)));
297
298 // Tear down
299 test_registry_.StopAll();
300
301 ASSERT_TRUE(std::filesystem::exists(temp_config_));
302 }
303
TEST_F(StorageModuleTest,no_config_causes_a_write)304 TEST_F(StorageModuleTest, no_config_causes_a_write) {
305 // Set up
306 auto* storage = new TestStorageModule(temp_config_.string(), kTestConfigSaveDelay, false, false);
307 test_registry_.InjectTestModule(&StorageModule::Factory, storage);
308
309 // Tear down
310 test_registry_.StopAll();
311
312 ASSERT_TRUE(std::filesystem::exists(temp_config_));
313 }
314
315 } // namespace testing
316