storage_monitor_linux_unittest.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // StorageMonitorLinux unit tests.
  5. #include "components/storage_monitor/storage_monitor_linux.h"
  6. #include <mntent.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include <stdio.h>
  10. #include <memory>
  11. #include <string>
  12. #include "base/bind.h"
  13. #include "base/check.h"
  14. #include "base/files/file_util.h"
  15. #include "base/files/scoped_temp_dir.h"
  16. #include "base/run_loop.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "base/task/thread_pool/thread_pool_instance.h"
  19. #include "base/test/task_environment.h"
  20. #include "base/threading/thread_task_runner_handle.h"
  21. #include "components/storage_monitor/mock_removable_storage_observer.h"
  22. #include "components/storage_monitor/removable_device_constants.h"
  23. #include "components/storage_monitor/storage_info.h"
  24. #include "components/storage_monitor/storage_monitor.h"
  25. #include "components/storage_monitor/test_storage_monitor.h"
  26. #include "testing/gtest/include/gtest/gtest.h"
  27. namespace storage_monitor {
  28. namespace {
  29. const char kValidFS[] = "vfat";
  30. const char kInvalidFS[] = "invalidfs";
  31. const char kInvalidPath[] = "invalid path does not exist";
  32. const char kDeviceDCIM1[] = "d1";
  33. const char kDeviceDCIM2[] = "d2";
  34. const char kDeviceDCIM3[] = "d3";
  35. const char kDeviceNoDCIM[] = "d4";
  36. const char kDeviceFixed[] = "d5";
  37. const char kInvalidDevice[] = "invalid_device";
  38. const char kMountPointA[] = "mnt_a";
  39. const char kMountPointB[] = "mnt_b";
  40. const char kMountPointC[] = "mnt_c";
  41. struct TestDeviceData {
  42. const char* device_path;
  43. const char* unique_id;
  44. StorageInfo::Type type;
  45. uint64_t partition_size_in_bytes;
  46. };
  47. const TestDeviceData kTestDeviceData[] = {
  48. { kDeviceDCIM1, "UUID:FFF0-000F",
  49. StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 88788 },
  50. { kDeviceDCIM2, "VendorModelSerial:ComName:Model2010:8989",
  51. StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
  52. 8773 },
  53. { kDeviceDCIM3, "VendorModelSerial:::WEM319X792",
  54. StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 22837 },
  55. { kDeviceNoDCIM, "UUID:ABCD-1234",
  56. StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 512 },
  57. { kDeviceFixed, "UUID:743A-2349",
  58. StorageInfo::FIXED_MASS_STORAGE, 17282 },
  59. };
  60. std::unique_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
  61. const base::FilePath& mount_point) {
  62. bool device_found = false;
  63. size_t i = 0;
  64. for (; i < std::size(kTestDeviceData); i++) {
  65. if (device_path.value() == kTestDeviceData[i].device_path) {
  66. device_found = true;
  67. break;
  68. }
  69. }
  70. DCHECK(device_found);
  71. StorageInfo::Type type = kTestDeviceData[i].type;
  72. auto storage_info = std::make_unique<StorageInfo>(
  73. StorageInfo::MakeDeviceId(type, kTestDeviceData[i].unique_id),
  74. mount_point.value(), u"volume label", u"vendor name", u"model name",
  75. kTestDeviceData[i].partition_size_in_bytes);
  76. return storage_info;
  77. }
  78. uint64_t GetDevicePartitionSize(const std::string& device) {
  79. for (const auto& data : kTestDeviceData) {
  80. if (device == data.device_path)
  81. return data.partition_size_in_bytes;
  82. }
  83. return 0;
  84. }
  85. std::string GetDeviceId(const std::string& device) {
  86. for (const auto& data : kTestDeviceData) {
  87. if (device == data.device_path)
  88. return StorageInfo::MakeDeviceId(data.type, data.unique_id);
  89. }
  90. if (device == kInvalidDevice) {
  91. return StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE,
  92. kInvalidDevice);
  93. }
  94. return std::string();
  95. }
  96. class TestStorageMonitorLinux : public StorageMonitorLinux {
  97. public:
  98. explicit TestStorageMonitorLinux(const base::FilePath& path)
  99. : StorageMonitorLinux(path) {
  100. SetGetDeviceInfoCallbackForTest(base::BindRepeating(&GetDeviceInfo));
  101. }
  102. TestStorageMonitorLinux(const TestStorageMonitorLinux&) = delete;
  103. TestStorageMonitorLinux& operator=(const TestStorageMonitorLinux&) = delete;
  104. ~TestStorageMonitorLinux() override = default;
  105. void SetOnMtabUpdateCallback(base::OnceClosure on_mtab_update_callback) {
  106. EXPECT_FALSE(on_mtab_update_callback_);
  107. on_mtab_update_callback_ = std::move(on_mtab_update_callback);
  108. }
  109. private:
  110. void UpdateMtab(
  111. const MtabWatcherLinux::MountPointDeviceMap& new_mtab) override {
  112. StorageMonitorLinux::UpdateMtab(new_mtab);
  113. if (on_mtab_update_callback_)
  114. std::move(on_mtab_update_callback_).Run();
  115. }
  116. base::OnceClosure on_mtab_update_callback_;
  117. };
  118. class StorageMonitorLinuxTest : public testing::Test {
  119. public:
  120. struct MtabTestData {
  121. MtabTestData(const std::string& mount_device,
  122. const std::string& mount_point,
  123. const std::string& mount_type)
  124. : mount_device(mount_device),
  125. mount_point(mount_point),
  126. mount_type(mount_type) {
  127. }
  128. const std::string mount_device;
  129. const std::string mount_point;
  130. const std::string mount_type;
  131. };
  132. StorageMonitorLinuxTest() = default;
  133. StorageMonitorLinuxTest(const StorageMonitorLinuxTest&) = delete;
  134. StorageMonitorLinuxTest& operator=(const StorageMonitorLinuxTest&) = delete;
  135. ~StorageMonitorLinuxTest() override = default;
  136. protected:
  137. void SetUp() override {
  138. // Create and set up a temp dir with files for the test.
  139. ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
  140. base::FilePath test_dir =
  141. scoped_temp_dir_.GetPath().AppendASCII("test_etc");
  142. ASSERT_TRUE(base::CreateDirectory(test_dir));
  143. mtab_file_ = test_dir.AppendASCII("test_mtab");
  144. MtabTestData initial_test_data[] = {
  145. MtabTestData("dummydevice", "dummydir", kInvalidFS),
  146. };
  147. WriteToMtab(initial_test_data, std::size(initial_test_data),
  148. /*overwrite=*/true);
  149. monitor_ = std::make_unique<TestStorageMonitorLinux>(mtab_file_);
  150. mock_storage_observer_ = std::make_unique<MockRemovableStorageObserver>();
  151. monitor_->AddObserver(mock_storage_observer_.get());
  152. monitor_->Init();
  153. task_environment_.RunUntilIdle();
  154. }
  155. void TearDown() override {
  156. task_environment_.RunUntilIdle();
  157. monitor_->RemoveObserver(mock_storage_observer_.get());
  158. task_environment_.RunUntilIdle();
  159. // Linux storage monitor must be destroyed on the UI thread, so do it here.
  160. monitor_.reset();
  161. }
  162. // Append mtab entries from the |data| array of size |data_size| to the mtab
  163. // file, and run the message loop.
  164. void AppendToMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
  165. WriteToMtab(data, data_size, /*overwrite=*/false);
  166. WaitForMtabUpdate();
  167. }
  168. // Overwrite the mtab file with mtab entries from the |data| array of size
  169. // |data_size|, and run the message loop.
  170. void OverwriteMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
  171. WriteToMtab(data, data_size, /*overwrite=*/true);
  172. WaitForMtabUpdate();
  173. }
  174. // Simplied version of OverwriteMtabAndRunLoop() that just deletes all the
  175. // entries in the mtab file.
  176. void WriteEmptyMtabAndRunLoop() {
  177. OverwriteMtabAndRunLoop(/*data=*/nullptr, /*data_size=*/0);
  178. }
  179. // Create a directory named |dir| relative to the test directory.
  180. // It has a DCIM directory, so StorageMonitorLinux recognizes it as a media
  181. // directory.
  182. base::FilePath CreateMountPointWithDCIMDir(const std::string& dir) {
  183. return CreateMountPoint(dir, /*with_dcim_dir=*/true);
  184. }
  185. // Create a directory named |dir| relative to the test directory.
  186. // It does not have a DCIM directory, so StorageMonitorLinux does not
  187. // recognize it as a media directory.
  188. base::FilePath CreateMountPointWithoutDCIMDir(const std::string& dir) {
  189. return CreateMountPoint(dir, /*with_dcim_dir=*/false);
  190. }
  191. void RemoveDCIMDirFromMountPoint(const std::string& dir) {
  192. base::FilePath dcim =
  193. scoped_temp_dir_.GetPath().AppendASCII(dir).Append(kDCIMDirectoryName);
  194. base::DeleteFile(dcim);
  195. }
  196. MockRemovableStorageObserver& observer() {
  197. return *mock_storage_observer_;
  198. }
  199. StorageMonitor* notifier() {
  200. return monitor_.get();
  201. }
  202. uint64_t GetStorageSize(const base::FilePath& path) {
  203. StorageInfo info;
  204. if (!notifier()->GetStorageInfoForPath(path, &info))
  205. return 0;
  206. return info.total_size_in_bytes();
  207. }
  208. private:
  209. // Invoked after making an mtab update. Blocks (in an active RunLoop) until
  210. // the mtab changes are detected by the file watcher and side-effects of
  211. // UpdateMtab() propagate.
  212. void WaitForMtabUpdate() {
  213. base::RunLoop run_loop;
  214. monitor_->SetOnMtabUpdateCallback(run_loop.QuitClosure());
  215. // Wait until the UpdateMtab() notification comes in from the system
  216. // (cannot use RunUntilIdle right away as that would racily return early
  217. // per being idle until the system notification comes in).
  218. run_loop.Run();
  219. // UpdateMtab() causes asynchronous work on internal task runners, flush
  220. // everything to make sure `mock_storage_observer_` gets to observe the
  221. // change.
  222. task_environment_.RunUntilIdle();
  223. }
  224. // Create a directory named |dir| relative to the test directory.
  225. // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
  226. // subdirectory.
  227. // Returns the full path to the created directory on success, or an empty
  228. // path on failure.
  229. base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir) {
  230. base::FilePath return_path(scoped_temp_dir_.GetPath());
  231. return_path = return_path.AppendASCII(dir);
  232. base::FilePath path(return_path);
  233. if (with_dcim_dir)
  234. path = path.Append(kDCIMDirectoryName);
  235. if (!base::CreateDirectory(path))
  236. return base::FilePath();
  237. return return_path;
  238. }
  239. // Write the test mtab data to |mtab_file_|.
  240. // |data| is an array of mtab entries.
  241. // |data_size| is the array size of |data|.
  242. // |overwrite| specifies whether to overwrite |mtab_file_|.
  243. void WriteToMtab(const MtabTestData* data,
  244. size_t data_size,
  245. bool overwrite) {
  246. FILE* file = setmntent(mtab_file_.value().c_str(), overwrite ? "w" : "a");
  247. ASSERT_TRUE(file);
  248. // Due to the glibc *mntent() interface design, which is out of our
  249. // control, the mtnent struct has several char* fields, even though
  250. // addmntent() does not write to them in the calls below. To make the
  251. // compiler happy while avoiding making additional copies of strings,
  252. // we just const_cast() the strings' c_str()s.
  253. // Assuming addmntent() does not write to the char* fields, this is safe.
  254. // It is unlikely the platforms this test suite runs on will have an
  255. // addmntent() implementation that does change the char* fields. If that
  256. // was ever the case, the test suite will start crashing or failing.
  257. mntent entry;
  258. static const char kMountOpts[] = "rw";
  259. entry.mnt_opts = const_cast<char*>(kMountOpts);
  260. entry.mnt_freq = 0;
  261. entry.mnt_passno = 0;
  262. for (size_t i = 0; i < data_size; ++i) {
  263. entry.mnt_fsname = const_cast<char*>(data[i].mount_device.c_str());
  264. entry.mnt_dir = const_cast<char*>(data[i].mount_point.c_str());
  265. entry.mnt_type = const_cast<char*>(data[i].mount_type.c_str());
  266. ASSERT_EQ(0, addmntent(file, &entry));
  267. }
  268. ASSERT_EQ(1, endmntent(file));
  269. }
  270. base::test::TaskEnvironment task_environment_;
  271. std::unique_ptr<MockRemovableStorageObserver> mock_storage_observer_;
  272. // Temporary directory for created test data.
  273. base::ScopedTempDir scoped_temp_dir_;
  274. // Path to the test mtab file.
  275. base::FilePath mtab_file_;
  276. std::unique_ptr<TestStorageMonitorLinux> monitor_;
  277. };
  278. // TODO(https://crbug.com/1297464): This test is flaky.
  279. // Simple test case where we attach and detach a media device.
  280. TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) {
  281. base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA);
  282. ASSERT_FALSE(test_path.empty());
  283. MtabTestData test_data[] = {
  284. MtabTestData(kDeviceDCIM2, test_path.value(), kValidFS),
  285. MtabTestData(kDeviceFixed, kInvalidPath, kValidFS),
  286. };
  287. // Only |kDeviceDCIM2| should be attached, since |kDeviceFixed| has a bad
  288. // path.
  289. AppendToMtabAndRunLoop(test_data, std::size(test_data));
  290. EXPECT_EQ(1, observer().attach_calls());
  291. EXPECT_EQ(0, observer().detach_calls());
  292. EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_attached().device_id());
  293. EXPECT_EQ(test_path.value(), observer().last_attached().location());
  294. // |kDeviceDCIM2| should be detached here.
  295. WriteEmptyMtabAndRunLoop();
  296. EXPECT_EQ(1, observer().attach_calls());
  297. EXPECT_EQ(1, observer().detach_calls());
  298. EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_detached().device_id());
  299. }
  300. // Only removable devices are recognized.
  301. // This test is flaky, see https://crbug.com/1012211
  302. TEST_F(StorageMonitorLinuxTest, Removable) {
  303. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  304. ASSERT_FALSE(test_path_a.empty());
  305. MtabTestData test_data1[] = {
  306. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  307. };
  308. // |kDeviceDCIM1| should be attached as expected.
  309. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  310. EXPECT_EQ(1, observer().attach_calls());
  311. EXPECT_EQ(0, observer().detach_calls());
  312. EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_attached().device_id());
  313. EXPECT_EQ(test_path_a.value(), observer().last_attached().location());
  314. // This should do nothing, since |kDeviceFixed| is not removable.
  315. base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
  316. ASSERT_FALSE(test_path_b.empty());
  317. MtabTestData test_data2[] = {
  318. MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
  319. };
  320. AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
  321. EXPECT_EQ(1, observer().attach_calls());
  322. EXPECT_EQ(0, observer().detach_calls());
  323. // |kDeviceDCIM1| should be detached as expected.
  324. WriteEmptyMtabAndRunLoop();
  325. EXPECT_EQ(1, observer().attach_calls());
  326. EXPECT_EQ(1, observer().detach_calls());
  327. EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_detached().device_id());
  328. // |kDeviceNoDCIM| should be attached as expected.
  329. MtabTestData test_data3[] = {
  330. MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
  331. };
  332. AppendToMtabAndRunLoop(test_data3, std::size(test_data3));
  333. EXPECT_EQ(2, observer().attach_calls());
  334. EXPECT_EQ(1, observer().detach_calls());
  335. EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_attached().device_id());
  336. EXPECT_EQ(test_path_b.value(), observer().last_attached().location());
  337. // |kDeviceNoDCIM| should be detached as expected.
  338. WriteEmptyMtabAndRunLoop();
  339. EXPECT_EQ(2, observer().attach_calls());
  340. EXPECT_EQ(2, observer().detach_calls());
  341. EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_detached().device_id());
  342. }
  343. // More complicated test case with multiple devices on multiple mount points.
  344. TEST_F(StorageMonitorLinuxTest, SwapMountPoints) {
  345. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  346. base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
  347. ASSERT_FALSE(test_path_a.empty());
  348. ASSERT_FALSE(test_path_b.empty());
  349. // Attach two devices.
  350. // (*'d mounts are those StorageMonitor knows about.)
  351. // kDeviceDCIM1 -> kMountPointA *
  352. // kDeviceDCIM2 -> kMountPointB *
  353. MtabTestData test_data1[] = {
  354. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  355. MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
  356. };
  357. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  358. EXPECT_EQ(2, observer().attach_calls());
  359. EXPECT_EQ(0, observer().detach_calls());
  360. // Detach two devices from old mount points and attach the devices at new
  361. // mount points.
  362. // kDeviceDCIM1 -> kMountPointB *
  363. // kDeviceDCIM2 -> kMountPointA *
  364. MtabTestData test_data2[] = {
  365. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  366. MtabTestData(kDeviceDCIM2, test_path_a.value(), kValidFS),
  367. };
  368. OverwriteMtabAndRunLoop(test_data2, std::size(test_data2));
  369. EXPECT_EQ(4, observer().attach_calls());
  370. EXPECT_EQ(2, observer().detach_calls());
  371. // Detach all devices.
  372. WriteEmptyMtabAndRunLoop();
  373. EXPECT_EQ(4, observer().attach_calls());
  374. EXPECT_EQ(4, observer().detach_calls());
  375. }
  376. // More complicated test case with multiple devices on multiple mount points.
  377. TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) {
  378. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  379. base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
  380. ASSERT_FALSE(test_path_a.empty());
  381. ASSERT_FALSE(test_path_b.empty());
  382. // Attach two devices.
  383. // (*'d mounts are those StorageMonitor knows about.)
  384. // kDeviceDCIM1 -> kMountPointA *
  385. // kDeviceDCIM2 -> kMountPointB *
  386. MtabTestData test_data1[] = {
  387. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  388. MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
  389. };
  390. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  391. EXPECT_EQ(2, observer().attach_calls());
  392. EXPECT_EQ(0, observer().detach_calls());
  393. // Attach |kDeviceDCIM1| to |kMountPointB|.
  394. // |kDeviceDCIM2| is inaccessible, so it is detached. |kDeviceDCIM1| has been
  395. // attached at |kMountPointB|, but is still accessible from |kMountPointA|.
  396. // kDeviceDCIM1 -> kMountPointA *
  397. // kDeviceDCIM2 -> kMountPointB
  398. // kDeviceDCIM1 -> kMountPointB
  399. MtabTestData test_data2[] = {
  400. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  401. };
  402. AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
  403. EXPECT_EQ(2, observer().attach_calls());
  404. EXPECT_EQ(1, observer().detach_calls());
  405. // Detach |kDeviceDCIM1| from |kMountPointA|, causing a detach and attach
  406. // event.
  407. // kDeviceDCIM2 -> kMountPointB
  408. // kDeviceDCIM1 -> kMountPointB *
  409. MtabTestData test_data3[] = {
  410. MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
  411. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  412. };
  413. OverwriteMtabAndRunLoop(test_data3, std::size(test_data3));
  414. EXPECT_EQ(3, observer().attach_calls());
  415. EXPECT_EQ(2, observer().detach_calls());
  416. // Attach |kDeviceDCIM1| to |kMountPointA|.
  417. // kDeviceDCIM2 -> kMountPointB
  418. // kDeviceDCIM1 -> kMountPointB *
  419. // kDeviceDCIM1 -> kMountPointA
  420. MtabTestData test_data4[] = {
  421. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  422. };
  423. AppendToMtabAndRunLoop(test_data4, std::size(test_data4));
  424. EXPECT_EQ(3, observer().attach_calls());
  425. EXPECT_EQ(2, observer().detach_calls());
  426. // Detach |kDeviceDCIM1| from |kMountPointB|.
  427. // kDeviceDCIM1 -> kMountPointA *
  428. // kDeviceDCIM2 -> kMountPointB *
  429. OverwriteMtabAndRunLoop(test_data1, std::size(test_data1));
  430. EXPECT_EQ(5, observer().attach_calls());
  431. EXPECT_EQ(3, observer().detach_calls());
  432. // Detach all devices.
  433. WriteEmptyMtabAndRunLoop();
  434. EXPECT_EQ(5, observer().attach_calls());
  435. EXPECT_EQ(5, observer().detach_calls());
  436. }
  437. TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) {
  438. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  439. base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
  440. ASSERT_FALSE(test_path_a.empty());
  441. ASSERT_FALSE(test_path_b.empty());
  442. // Attach to one first.
  443. // (*'d mounts are those StorageMonitor knows about.)
  444. // kDeviceDCIM1 -> kMountPointA *
  445. MtabTestData test_data1[] = {
  446. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  447. };
  448. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  449. EXPECT_EQ(1, observer().attach_calls());
  450. EXPECT_EQ(0, observer().detach_calls());
  451. // Attach |kDeviceDCIM1| to |kMountPointB|.
  452. // kDeviceDCIM1 -> kMountPointA *
  453. // kDeviceDCIM1 -> kMountPointB
  454. MtabTestData test_data2[] = {
  455. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  456. };
  457. AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
  458. EXPECT_EQ(1, observer().attach_calls());
  459. EXPECT_EQ(0, observer().detach_calls());
  460. // Attach |kDeviceFixed| (a non-removable device) to |kMountPointA|.
  461. // kDeviceDCIM1 -> kMountPointA
  462. // kDeviceDCIM1 -> kMountPointB *
  463. // kDeviceFixed -> kMountPointA
  464. MtabTestData test_data3[] = {
  465. MtabTestData(kDeviceFixed, test_path_a.value(), kValidFS),
  466. };
  467. RemoveDCIMDirFromMountPoint(kMountPointA);
  468. AppendToMtabAndRunLoop(test_data3, std::size(test_data3));
  469. EXPECT_EQ(2, observer().attach_calls());
  470. EXPECT_EQ(1, observer().detach_calls());
  471. // Detach |kDeviceFixed|.
  472. // kDeviceDCIM1 -> kMountPointA
  473. // kDeviceDCIM1 -> kMountPointB *
  474. MtabTestData test_data4[] = {
  475. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  476. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  477. };
  478. CreateMountPointWithDCIMDir(kMountPointA);
  479. OverwriteMtabAndRunLoop(test_data4, std::size(test_data4));
  480. EXPECT_EQ(2, observer().attach_calls());
  481. EXPECT_EQ(1, observer().detach_calls());
  482. // Attach |kDeviceNoDCIM| (a non-DCIM device) to |kMountPointB|.
  483. // kDeviceDCIM1 -> kMountPointA *
  484. // kDeviceDCIM1 -> kMountPointB
  485. // kDeviceNoDCIM -> kMountPointB *
  486. MtabTestData test_data5[] = {
  487. MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
  488. };
  489. base::DeleteFile(test_path_b.Append(kDCIMDirectoryName));
  490. AppendToMtabAndRunLoop(test_data5, std::size(test_data5));
  491. EXPECT_EQ(4, observer().attach_calls());
  492. EXPECT_EQ(2, observer().detach_calls());
  493. // Detach |kDeviceNoDCIM|.
  494. // kDeviceDCIM1 -> kMountPointA *
  495. // kDeviceDCIM1 -> kMountPointB
  496. MtabTestData test_data6[] = {
  497. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  498. MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
  499. };
  500. CreateMountPointWithDCIMDir(kMountPointB);
  501. OverwriteMtabAndRunLoop(test_data6, std::size(test_data6));
  502. EXPECT_EQ(4, observer().attach_calls());
  503. EXPECT_EQ(3, observer().detach_calls());
  504. // Detach |kDeviceDCIM1| from |kMountPointB|.
  505. // kDeviceDCIM1 -> kMountPointA *
  506. OverwriteMtabAndRunLoop(test_data1, std::size(test_data1));
  507. EXPECT_EQ(4, observer().attach_calls());
  508. EXPECT_EQ(3, observer().detach_calls());
  509. // Detach all devices.
  510. WriteEmptyMtabAndRunLoop();
  511. EXPECT_EQ(4, observer().attach_calls());
  512. EXPECT_EQ(4, observer().detach_calls());
  513. }
  514. TEST_F(StorageMonitorLinuxTest, DeviceLookUp) {
  515. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  516. base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
  517. base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC);
  518. ASSERT_FALSE(test_path_a.empty());
  519. ASSERT_FALSE(test_path_b.empty());
  520. ASSERT_FALSE(test_path_c.empty());
  521. // Attach to one first.
  522. // (starred mounts are those StorageMonitor knows about.)
  523. // kDeviceDCIM1 -> kMountPointA *
  524. // kDeviceNoDCIM -> kMountPointB *
  525. // kDeviceFixed -> kMountPointC
  526. MtabTestData test_data1[] = {
  527. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  528. MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
  529. MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
  530. };
  531. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  532. EXPECT_EQ(2, observer().attach_calls());
  533. EXPECT_EQ(0, observer().detach_calls());
  534. StorageInfo device_info;
  535. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
  536. EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
  537. EXPECT_EQ(test_path_a.value(), device_info.location());
  538. EXPECT_EQ(88788ULL, device_info.total_size_in_bytes());
  539. EXPECT_EQ(u"volume label", device_info.storage_label());
  540. EXPECT_EQ(u"vendor name", device_info.vendor_name());
  541. EXPECT_EQ(u"model name", device_info.model_name());
  542. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
  543. EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), device_info.device_id());
  544. EXPECT_EQ(test_path_b.value(), device_info.location());
  545. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
  546. EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
  547. EXPECT_EQ(test_path_c.value(), device_info.location());
  548. // An invalid path.
  549. EXPECT_FALSE(notifier()->GetStorageInfoForPath(base::FilePath(kInvalidPath),
  550. &device_info));
  551. // Test filling in of the mount point.
  552. EXPECT_TRUE(
  553. notifier()->GetStorageInfoForPath(test_path_a.Append("some/other/path"),
  554. &device_info));
  555. EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
  556. EXPECT_EQ(test_path_a.value(), device_info.location());
  557. // One device attached at multiple points.
  558. // kDeviceDCIM1 -> kMountPointA *
  559. // kDeviceFixed -> kMountPointB
  560. // kDeviceFixed -> kMountPointC
  561. MtabTestData test_data2[] = {
  562. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  563. MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
  564. MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
  565. };
  566. AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
  567. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
  568. EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
  569. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
  570. EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
  571. EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
  572. EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
  573. EXPECT_EQ(2, observer().attach_calls());
  574. EXPECT_EQ(1, observer().detach_calls());
  575. }
  576. TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) {
  577. base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
  578. base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
  579. ASSERT_FALSE(test_path_a.empty());
  580. ASSERT_FALSE(test_path_b.empty());
  581. MtabTestData test_data1[] = {
  582. MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
  583. MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
  584. MtabTestData(kDeviceFixed, kInvalidPath, kInvalidFS),
  585. };
  586. AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
  587. EXPECT_EQ(2, observer().attach_calls());
  588. EXPECT_EQ(0, observer().detach_calls());
  589. EXPECT_EQ(GetDevicePartitionSize(kDeviceDCIM1),
  590. GetStorageSize(test_path_a));
  591. EXPECT_EQ(GetDevicePartitionSize(kDeviceNoDCIM),
  592. GetStorageSize(test_path_b));
  593. EXPECT_EQ(GetDevicePartitionSize(kInvalidPath),
  594. GetStorageSize(base::FilePath(kInvalidPath)));
  595. }
  596. } // namespace
  597. } // namespace storage_monitor