• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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