1 /*
2 * Copyright (C) 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 bluetooth::storage {
40
41 using ::testing::ElementsAre;
42 using ::testing::StrEq;
43
44 using bluetooth::hci::Address;
45 using bluetooth::os::fake_timer::fake_timerfd_advance;
46 using bluetooth::storage::ConfigCache;
47 using bluetooth::storage::Device;
48 using bluetooth::storage::LegacyConfigFile;
49 using bluetooth::storage::StorageModule;
50
51 using namespace std::chrono_literals;
52
53 static const std::chrono::milliseconds kTestConfigSaveDelay = std::chrono::milliseconds(100);
54 static const size_t kTestTempDevicesCapacity = 10;
55
56 class StorageModuleTest : public testing::Test {
57 protected:
SetUp()58 void SetUp() override {
59 thread_ = new bluetooth::os::Thread("test_thread", bluetooth::os::Thread::Priority::NORMAL);
60 handler_ = new bluetooth::os::Handler(thread_);
61
62 temp_dir_ = std::filesystem::temp_directory_path();
63 temp_config_ = temp_dir_ / "temp_config.txt";
64
65 DeleteConfigFiles();
66 ASSERT_FALSE(std::filesystem::exists(temp_config_));
67
68 storage_ = new StorageModule(handler_, temp_config_.string(), kTestConfigSaveDelay,
69 kTestTempDevicesCapacity, false, false);
70 }
71
TearDown()72 void TearDown() override {
73 DeleteConfigFiles();
74 handler_->Clear();
75 handler_->WaitUntilStopped(200ms);
76 thread_->Stop();
77 delete storage_;
78 delete handler_;
79 delete thread_;
80 handler_ = nullptr;
81 thread_ = nullptr;
82 storage_ = nullptr;
83 }
84
DeleteConfigFiles()85 void DeleteConfigFiles() {
86 if (std::filesystem::exists(temp_config_)) {
87 ASSERT_TRUE(std::filesystem::remove(temp_config_));
88 }
89 }
90
SetProperty(std::string section,std::string property,std::string value)91 void SetProperty(std::string section, std::string property, std::string value) {
92 storage_->SetProperty(std::move(section), std::move(property), std::move(value));
93 }
94
GetProperty(const std::string & section,const std::string & property)95 std::optional<std::string> GetProperty(const std::string& section, const std::string& property) {
96 return storage_->GetProperty(section, property);
97 }
98
RemoveProperty(const std::string & section,const std::string & property)99 bool RemoveProperty(const std::string& section, const std::string& property) {
100 return storage_->RemoveProperty(section, property);
101 }
102
RemoveSection(const std::string & section)103 void RemoveSection(const std::string& section) { storage_->RemoveSection(section); }
HasSection(const std::string & section)104 bool HasSection(const std::string& section) { return storage_->HasSection(section); }
GetPersistentSections()105 std::vector<std::string> GetPersistentSections() { return storage_->GetPersistentSections(); }
106
FakeTimerAdvance(std::chrono::milliseconds time)107 void FakeTimerAdvance(std::chrono::milliseconds time) {
108 handler_->Post(bluetooth::common::BindOnce(fake_timerfd_advance, time.count()));
109 }
110
WaitForReactorIdle(std::chrono::milliseconds time)111 bool WaitForReactorIdle(std::chrono::milliseconds time) {
112 bool stopped = thread_->GetReactor()->WaitForIdle(200ms);
113 if (!stopped) {
114 return false;
115 }
116 FakeTimerAdvance(time);
117 return thread_->GetReactor()->WaitForIdle(200ms);
118 }
119
120 bluetooth::os::Thread* thread_;
121 bluetooth::os::Handler* handler_;
122 StorageModule* storage_;
123 std::filesystem::path temp_dir_;
124 std::filesystem::path temp_config_;
125 };
126
TEST_F(StorageModuleTest,empty_config_no_op_test)127 TEST_F(StorageModuleTest, empty_config_no_op_test) {
128 storage_->Start();
129 storage_->Stop();
130
131 // Verify states after test
132 ASSERT_TRUE(std::filesystem::exists(temp_config_));
133
134 // Verify config after test
135 auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
136 ASSERT_TRUE(config);
137 ASSERT_TRUE(config->HasSection(StorageModule::kInfoSection));
138 }
139
140 static const std::string kReadTestConfig =
141 "[Info]\n"
142 "TimeCreated = 2020-05-20 01:20:56\n"
143 "\n"
144 "[Metrics]\n"
145 "Salt256Bit = 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n"
146 "\n"
147 "[Adapter]\n"
148 "Address = 01:02:03:ab:cd:ef\n"
149 "LE_LOCAL_KEY_IRK = fedcba0987654321fedcba0987654321\n"
150 "LE_LOCAL_KEY_IR = fedcba0987654321fedcba0987654322\n"
151 "LE_LOCAL_KEY_DHK = fedcba0987654321fedcba0987654323\n"
152 "LE_LOCAL_KEY_ER = fedcba0987654321fedcba0987654324\n"
153 "ScanMode = 2\n"
154 "DiscoveryTimeout = 120\n"
155 "\n"
156 "[01:02:03:ab:cd:ea]\n"
157 "Name = hello world\n"
158 "LinkKey = fedcba0987654321fedcba0987654328\n"
159 "DevType = 1\n"
160 "\n";
161
TEST_F(StorageModuleTest,read_existing_config_test)162 TEST_F(StorageModuleTest, read_existing_config_test) {
163 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
164
165 // Set up
166 storage_->Start();
167
168 // Test
169 ASSERT_TRUE(HasSection("Metrics"));
170 ASSERT_THAT(GetPersistentSections(), ElementsAre("01:02:03:ab:cd:ea"));
171 ASSERT_THAT(GetProperty(StorageModule::kAdapterSection, BTIF_STORAGE_KEY_ADDRESS),
172 Optional(StrEq("01:02:03:ab:cd:ef")));
173
174 storage_->Stop();
175
176 // Verify states after test
177 ASSERT_TRUE(std::filesystem::exists(temp_config_));
178
179 // Verify config after test
180 auto config = bluetooth::os::ReadSmallFile(temp_config_.string());
181 ASSERT_TRUE(config);
182 ASSERT_EQ(*config, kReadTestConfig);
183 }
184
TEST_F(StorageModuleTest,save_config_test)185 TEST_F(StorageModuleTest, save_config_test) {
186 // Prepare config file
187 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
188
189 // Set up
190 storage_->Start();
191
192 // Test
193 // Change a property
194 ASSERT_THAT(GetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME),
195 Optional(StrEq("hello world")));
196 SetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
197 ASSERT_THAT(GetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME), Optional(StrEq("foo")));
198 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
199
200 auto config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
201 ASSERT_TRUE(config);
202 ASSERT_THAT(GetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME), Optional(StrEq("foo")));
203
204 // Remove a property
205 RemoveProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME);
206 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
207 bluetooth::log::info("After waiting 2");
208 config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
209 ASSERT_TRUE(config);
210 ASSERT_FALSE(config->HasProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME));
211
212 // Remove a section
213 RemoveSection("01:02:03:ab:cd:ea");
214 ASSERT_TRUE(WaitForReactorIdle(kTestConfigSaveDelay));
215 bluetooth::log::info("After waiting 3");
216 config = LegacyConfigFile::FromPath(temp_config_.string()).Read(kTestTempDevicesCapacity);
217 ASSERT_TRUE(config);
218 ASSERT_FALSE(config->HasSection("01:02:03:ab:cd:ea"));
219
220 // Tear down
221 storage_->Stop();
222
223 // Verify states after test
224 ASSERT_TRUE(std::filesystem::exists(temp_config_));
225 }
226
TEST_F(StorageModuleTest,get_bonded_devices_test)227 TEST_F(StorageModuleTest, get_bonded_devices_test) {
228 // Prepare config file
229 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
230
231 // Set up
232 storage_->Start();
233
234 ASSERT_EQ(storage_->GetBondedDevices().size(), 1u);
235 auto address = Address::FromString("01:02:03:ab:cd:ea");
236 ASSERT_EQ(address, storage_->GetBondedDevices()[0].GetAddress());
237
238 storage_->Stop();
239 }
240
TEST_F(StorageModuleTest,unchanged_config_causes_no_write)241 TEST_F(StorageModuleTest, unchanged_config_causes_no_write) {
242 // Prepare config file
243 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
244
245 // Set up
246 storage_->Start();
247
248 ASSERT_EQ(storage_->GetBondedDevices().size(), 1u);
249 auto address = Address::FromString("01:02:03:ab:cd:ea");
250 ASSERT_EQ(address, storage_->GetBondedDevices()[0].GetAddress());
251
252 // Remove the file after it was read, so we can check if it was written with exists()
253 DeleteConfigFiles();
254
255 // Tear down
256 storage_->Stop();
257
258 ASSERT_FALSE(std::filesystem::exists(temp_config_));
259 }
260
TEST_F(StorageModuleTest,changed_config_causes_a_write)261 TEST_F(StorageModuleTest, changed_config_causes_a_write) {
262 // Prepare config file
263 ASSERT_TRUE(bluetooth::os::WriteToFile(temp_config_.string(), kReadTestConfig));
264
265 // Set up
266 storage_->Start();
267
268 // Remove the file after it was read, so we can check if it was written with exists()
269 DeleteConfigFiles();
270
271 // Change a property
272 SetProperty("01:02:03:ab:cd:ea", BTIF_STORAGE_KEY_NAME, "foo");
273
274 ASSERT_TRUE(WaitForReactorIdle(std::chrono::milliseconds(1)));
275
276 // Tear down
277 storage_->Stop();
278
279 ASSERT_TRUE(std::filesystem::exists(temp_config_));
280 }
281
TEST_F(StorageModuleTest,no_config_causes_a_write)282 TEST_F(StorageModuleTest, no_config_causes_a_write) {
283 storage_->Start();
284 storage_->Stop();
285
286 ASSERT_TRUE(std::filesystem::exists(temp_config_));
287 }
288
289 } // namespace bluetooth::storage
290