1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // StorageMonitorLinux unit tests.
7 #include "components/storage_monitor/storage_monitor_linux.h"
17 #include "base/check.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_temp_dir.h"
20 #include "base/functional/bind.h"
21 #include "base/run_loop.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/thread_pool/thread_pool_instance.h"
24 #include "base/test/task_environment.h"
25 #include "components/storage_monitor/mock_removable_storage_observer.h"
26 #include "components/storage_monitor/removable_device_constants.h"
27 #include "components/storage_monitor/storage_info.h"
28 #include "components/storage_monitor/storage_monitor.h"
29 #include "components/storage_monitor/test_storage_monitor.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 namespace storage_monitor {
36 const char kValidFS[] = "vfat";
37 const char kInvalidFS[] = "invalidfs";
39 const char kInvalidPath[] = "invalid path does not exist";
41 const char kDeviceDCIM1[] = "d1";
42 const char kDeviceDCIM2[] = "d2";
43 const char kDeviceDCIM3[] = "d3";
44 const char kDeviceNoDCIM[] = "d4";
45 const char kDeviceFixed[] = "d5";
47 const char kInvalidDevice[] = "invalid_device";
49 const char kMountPointA[] = "mnt_a";
50 const char kMountPointB[] = "mnt_b";
51 const char kMountPointC[] = "mnt_c";
53 struct TestDeviceData {
54 const char* device_path;
55 const char* unique_id;
56 StorageInfo::Type type;
57 uint64_t partition_size_in_bytes;
60 const TestDeviceData kTestDeviceData[] = {
61 { kDeviceDCIM1, "UUID:FFF0-000F",
62 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 88788 },
63 { kDeviceDCIM2, "VendorModelSerial:ComName:Model2010:8989",
64 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
66 { kDeviceDCIM3, "VendorModelSerial:::WEM319X792",
67 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 22837 },
68 { kDeviceNoDCIM, "UUID:ABCD-1234",
69 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 512 },
70 { kDeviceFixed, "UUID:743A-2349",
71 StorageInfo::FIXED_MASS_STORAGE, 17282 },
74 std::unique_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
75 const base::FilePath& mount_point) {
76 bool device_found = false;
78 for (; i < std::size(kTestDeviceData); i++) {
79 if (device_path.value() == kTestDeviceData[i].device_path) {
86 StorageInfo::Type type = kTestDeviceData[i].type;
87 auto storage_info = std::make_unique<StorageInfo>(
88 StorageInfo::MakeDeviceId(type, kTestDeviceData[i].unique_id),
89 mount_point.value(), u"volume label", u"vendor name", u"model name",
90 kTestDeviceData[i].partition_size_in_bytes);
94 uint64_t GetDevicePartitionSize(const std::string& device) {
95 for (const auto& data : kTestDeviceData) {
96 if (device == data.device_path)
97 return data.partition_size_in_bytes;
102 std::string GetDeviceId(const std::string& device) {
103 for (const auto& data : kTestDeviceData) {
104 if (device == data.device_path)
105 return StorageInfo::MakeDeviceId(data.type, data.unique_id);
107 if (device == kInvalidDevice) {
108 return StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE,
111 return std::string();
114 class TestStorageMonitorLinux : public StorageMonitorLinux {
116 explicit TestStorageMonitorLinux(const base::FilePath& path)
117 : StorageMonitorLinux(path) {
118 SetGetDeviceInfoCallbackForTest(base::BindRepeating(&GetDeviceInfo));
121 TestStorageMonitorLinux(const TestStorageMonitorLinux&) = delete;
122 TestStorageMonitorLinux& operator=(const TestStorageMonitorLinux&) = delete;
124 ~TestStorageMonitorLinux() override = default;
126 void SetOnMtabUpdateCallback(base::OnceClosure on_mtab_update_callback) {
127 EXPECT_FALSE(on_mtab_update_callback_);
128 on_mtab_update_callback_ = std::move(on_mtab_update_callback);
133 const MtabWatcherLinux::MountPointDeviceMap& new_mtab) override {
134 StorageMonitorLinux::UpdateMtab(new_mtab);
135 if (on_mtab_update_callback_)
136 std::move(on_mtab_update_callback_).Run();
139 base::OnceClosure on_mtab_update_callback_;
142 class StorageMonitorLinuxTest : public testing::Test {
144 struct MtabTestData {
145 MtabTestData(const std::string& mount_device,
146 const std::string& mount_point,
147 const std::string& mount_type)
148 : mount_device(mount_device),
149 mount_point(mount_point),
150 mount_type(mount_type) {
153 const std::string mount_device;
154 const std::string mount_point;
155 const std::string mount_type;
158 StorageMonitorLinuxTest() = default;
160 StorageMonitorLinuxTest(const StorageMonitorLinuxTest&) = delete;
161 StorageMonitorLinuxTest& operator=(const StorageMonitorLinuxTest&) = delete;
163 ~StorageMonitorLinuxTest() override = default;
166 void SetUp() override {
167 // Create and set up a temp dir with files for the test.
168 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
169 base::FilePath test_dir =
170 scoped_temp_dir_.GetPath().AppendASCII("test_etc");
171 ASSERT_TRUE(base::CreateDirectory(test_dir));
172 mtab_file_ = test_dir.AppendASCII("test_mtab");
173 MtabTestData initial_test_data[] = {
174 MtabTestData("dummydevice", "dummydir", kInvalidFS),
176 WriteToMtab(initial_test_data, std::size(initial_test_data),
179 monitor_ = std::make_unique<TestStorageMonitorLinux>(mtab_file_);
180 mock_storage_observer_ = std::make_unique<MockRemovableStorageObserver>();
181 monitor_->AddObserver(mock_storage_observer_.get());
184 task_environment_.RunUntilIdle();
187 void TearDown() override {
188 task_environment_.RunUntilIdle();
189 monitor_->RemoveObserver(mock_storage_observer_.get());
190 task_environment_.RunUntilIdle();
192 // Linux storage monitor must be destroyed on the UI thread, so do it here.
196 // Append mtab entries from the |data| array of size |data_size| to the mtab
197 // file, and run the message loop.
198 void AppendToMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
199 WriteToMtab(data, data_size, /*overwrite=*/false);
203 // Overwrite the mtab file with mtab entries from the |data| array of size
204 // |data_size|, and run the message loop.
205 void OverwriteMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
206 WriteToMtab(data, data_size, /*overwrite=*/true);
210 // Simplied version of OverwriteMtabAndRunLoop() that just deletes all the
211 // entries in the mtab file.
212 void WriteEmptyMtabAndRunLoop() {
213 OverwriteMtabAndRunLoop(/*data=*/nullptr, /*data_size=*/0);
216 // Create a directory named |dir| relative to the test directory.
217 // It has a DCIM directory, so StorageMonitorLinux recognizes it as a media
219 base::FilePath CreateMountPointWithDCIMDir(const std::string& dir) {
220 return CreateMountPoint(dir, /*with_dcim_dir=*/true);
223 // Create a directory named |dir| relative to the test directory.
224 // It does not have a DCIM directory, so StorageMonitorLinux does not
225 // recognize it as a media directory.
226 base::FilePath CreateMountPointWithoutDCIMDir(const std::string& dir) {
227 return CreateMountPoint(dir, /*with_dcim_dir=*/false);
230 void RemoveDCIMDirFromMountPoint(const std::string& dir) {
231 base::FilePath dcim =
232 scoped_temp_dir_.GetPath().AppendASCII(dir).Append(kDCIMDirectoryName);
233 base::DeleteFile(dcim);
236 MockRemovableStorageObserver& observer() {
237 return *mock_storage_observer_;
240 StorageMonitor* notifier() {
241 return monitor_.get();
244 uint64_t GetStorageSize(const base::FilePath& path) {
246 if (!notifier()->GetStorageInfoForPath(path, &info))
249 return info.total_size_in_bytes();
253 // Invoked after making an mtab update. Blocks (in an active RunLoop) until
254 // the mtab changes are detected by the file watcher and side-effects of
255 // UpdateMtab() propagate.
256 void WaitForMtabUpdate() {
257 base::RunLoop run_loop;
258 monitor_->SetOnMtabUpdateCallback(run_loop.QuitClosure());
259 // Wait until the UpdateMtab() notification comes in from the system
260 // (cannot use RunUntilIdle right away as that would racily return early
261 // per being idle until the system notification comes in).
263 // UpdateMtab() causes asynchronous work on internal task runners, flush
264 // everything to make sure `mock_storage_observer_` gets to observe the
266 task_environment_.RunUntilIdle();
269 // Create a directory named |dir| relative to the test directory.
270 // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
272 // Returns the full path to the created directory on success, or an empty
274 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir) {
275 base::FilePath return_path(scoped_temp_dir_.GetPath());
276 return_path = return_path.AppendASCII(dir);
277 base::FilePath path(return_path);
279 path = path.Append(kDCIMDirectoryName);
280 if (!base::CreateDirectory(path))
281 return base::FilePath();
285 // Write the test mtab data to |mtab_file_|.
286 // |data| is an array of mtab entries.
287 // |data_size| is the array size of |data|.
288 // |overwrite| specifies whether to overwrite |mtab_file_|.
289 void WriteToMtab(const MtabTestData* data,
292 FILE* file = setmntent(mtab_file_.value().c_str(), overwrite ? "w" : "a");
295 // Due to the glibc *mntent() interface design, which is out of our
296 // control, the mtnent struct has several char* fields, even though
297 // addmntent() does not write to them in the calls below. To make the
298 // compiler happy while avoiding making additional copies of strings,
299 // we just const_cast() the strings' c_str()s.
300 // Assuming addmntent() does not write to the char* fields, this is safe.
301 // It is unlikely the platforms this test suite runs on will have an
302 // addmntent() implementation that does change the char* fields. If that
303 // was ever the case, the test suite will start crashing or failing.
305 static const char kMountOpts[] = "rw";
306 entry.mnt_opts = const_cast<char*>(kMountOpts);
308 entry.mnt_passno = 0;
309 for (size_t i = 0; i < data_size; ++i) {
310 entry.mnt_fsname = const_cast<char*>(data[i].mount_device.c_str());
311 entry.mnt_dir = const_cast<char*>(data[i].mount_point.c_str());
312 entry.mnt_type = const_cast<char*>(data[i].mount_type.c_str());
313 ASSERT_EQ(0, addmntent(file, &entry));
315 ASSERT_EQ(1, endmntent(file));
318 base::test::TaskEnvironment task_environment_;
320 std::unique_ptr<MockRemovableStorageObserver> mock_storage_observer_;
322 // Temporary directory for created test data.
323 base::ScopedTempDir scoped_temp_dir_;
324 // Path to the test mtab file.
325 base::FilePath mtab_file_;
327 std::unique_ptr<TestStorageMonitorLinux> monitor_;
330 // TODO(https://crbug.com/1297464): This test is flaky.
331 // Simple test case where we attach and detach a media device.
332 TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) {
333 base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA);
334 ASSERT_FALSE(test_path.empty());
335 MtabTestData test_data[] = {
336 MtabTestData(kDeviceDCIM2, test_path.value(), kValidFS),
337 MtabTestData(kDeviceFixed, kInvalidPath, kValidFS),
339 // Only |kDeviceDCIM2| should be attached, since |kDeviceFixed| has a bad
341 AppendToMtabAndRunLoop(test_data, std::size(test_data));
343 EXPECT_EQ(1, observer().attach_calls());
344 EXPECT_EQ(0, observer().detach_calls());
345 EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_attached().device_id());
346 EXPECT_EQ(test_path.value(), observer().last_attached().location());
348 // |kDeviceDCIM2| should be detached here.
349 WriteEmptyMtabAndRunLoop();
350 EXPECT_EQ(1, observer().attach_calls());
351 EXPECT_EQ(1, observer().detach_calls());
352 EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_detached().device_id());
355 // Only removable devices are recognized.
356 // This test is flaky, see https://crbug.com/1012211
357 TEST_F(StorageMonitorLinuxTest, Removable) {
358 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
359 ASSERT_FALSE(test_path_a.empty());
360 MtabTestData test_data1[] = {
361 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
363 // |kDeviceDCIM1| should be attached as expected.
364 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
366 EXPECT_EQ(1, observer().attach_calls());
367 EXPECT_EQ(0, observer().detach_calls());
368 EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_attached().device_id());
369 EXPECT_EQ(test_path_a.value(), observer().last_attached().location());
371 // This should do nothing, since |kDeviceFixed| is not removable.
372 base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
373 ASSERT_FALSE(test_path_b.empty());
374 MtabTestData test_data2[] = {
375 MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
377 AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
378 EXPECT_EQ(1, observer().attach_calls());
379 EXPECT_EQ(0, observer().detach_calls());
381 // |kDeviceDCIM1| should be detached as expected.
382 WriteEmptyMtabAndRunLoop();
383 EXPECT_EQ(1, observer().attach_calls());
384 EXPECT_EQ(1, observer().detach_calls());
385 EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_detached().device_id());
387 // |kDeviceNoDCIM| should be attached as expected.
388 MtabTestData test_data3[] = {
389 MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
391 AppendToMtabAndRunLoop(test_data3, std::size(test_data3));
392 EXPECT_EQ(2, observer().attach_calls());
393 EXPECT_EQ(1, observer().detach_calls());
394 EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_attached().device_id());
395 EXPECT_EQ(test_path_b.value(), observer().last_attached().location());
397 // |kDeviceNoDCIM| should be detached as expected.
398 WriteEmptyMtabAndRunLoop();
399 EXPECT_EQ(2, observer().attach_calls());
400 EXPECT_EQ(2, observer().detach_calls());
401 EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_detached().device_id());
404 // More complicated test case with multiple devices on multiple mount points.
405 TEST_F(StorageMonitorLinuxTest, SwapMountPoints) {
406 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
407 base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
408 ASSERT_FALSE(test_path_a.empty());
409 ASSERT_FALSE(test_path_b.empty());
411 // Attach two devices.
412 // (*'d mounts are those StorageMonitor knows about.)
413 // kDeviceDCIM1 -> kMountPointA *
414 // kDeviceDCIM2 -> kMountPointB *
415 MtabTestData test_data1[] = {
416 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
417 MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
419 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
420 EXPECT_EQ(2, observer().attach_calls());
421 EXPECT_EQ(0, observer().detach_calls());
423 // Detach two devices from old mount points and attach the devices at new
425 // kDeviceDCIM1 -> kMountPointB *
426 // kDeviceDCIM2 -> kMountPointA *
427 MtabTestData test_data2[] = {
428 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
429 MtabTestData(kDeviceDCIM2, test_path_a.value(), kValidFS),
431 OverwriteMtabAndRunLoop(test_data2, std::size(test_data2));
432 EXPECT_EQ(4, observer().attach_calls());
433 EXPECT_EQ(2, observer().detach_calls());
435 // Detach all devices.
436 WriteEmptyMtabAndRunLoop();
437 EXPECT_EQ(4, observer().attach_calls());
438 EXPECT_EQ(4, observer().detach_calls());
441 // More complicated test case with multiple devices on multiple mount points.
442 TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) {
443 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
444 base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
445 ASSERT_FALSE(test_path_a.empty());
446 ASSERT_FALSE(test_path_b.empty());
448 // Attach two devices.
449 // (*'d mounts are those StorageMonitor knows about.)
450 // kDeviceDCIM1 -> kMountPointA *
451 // kDeviceDCIM2 -> kMountPointB *
452 MtabTestData test_data1[] = {
453 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
454 MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
456 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
457 EXPECT_EQ(2, observer().attach_calls());
458 EXPECT_EQ(0, observer().detach_calls());
460 // Attach |kDeviceDCIM1| to |kMountPointB|.
461 // |kDeviceDCIM2| is inaccessible, so it is detached. |kDeviceDCIM1| has been
462 // attached at |kMountPointB|, but is still accessible from |kMountPointA|.
463 // kDeviceDCIM1 -> kMountPointA *
464 // kDeviceDCIM2 -> kMountPointB
465 // kDeviceDCIM1 -> kMountPointB
466 MtabTestData test_data2[] = {
467 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
469 AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
470 EXPECT_EQ(2, observer().attach_calls());
471 EXPECT_EQ(1, observer().detach_calls());
473 // Detach |kDeviceDCIM1| from |kMountPointA|, causing a detach and attach
475 // kDeviceDCIM2 -> kMountPointB
476 // kDeviceDCIM1 -> kMountPointB *
477 MtabTestData test_data3[] = {
478 MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
479 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
481 OverwriteMtabAndRunLoop(test_data3, std::size(test_data3));
482 EXPECT_EQ(3, observer().attach_calls());
483 EXPECT_EQ(2, observer().detach_calls());
485 // Attach |kDeviceDCIM1| to |kMountPointA|.
486 // kDeviceDCIM2 -> kMountPointB
487 // kDeviceDCIM1 -> kMountPointB *
488 // kDeviceDCIM1 -> kMountPointA
489 MtabTestData test_data4[] = {
490 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
492 AppendToMtabAndRunLoop(test_data4, std::size(test_data4));
493 EXPECT_EQ(3, observer().attach_calls());
494 EXPECT_EQ(2, observer().detach_calls());
496 // Detach |kDeviceDCIM1| from |kMountPointB|.
497 // kDeviceDCIM1 -> kMountPointA *
498 // kDeviceDCIM2 -> kMountPointB *
499 OverwriteMtabAndRunLoop(test_data1, std::size(test_data1));
500 EXPECT_EQ(5, observer().attach_calls());
501 EXPECT_EQ(3, observer().detach_calls());
503 // Detach all devices.
504 WriteEmptyMtabAndRunLoop();
505 EXPECT_EQ(5, observer().attach_calls());
506 EXPECT_EQ(5, observer().detach_calls());
509 TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) {
510 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
511 base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
512 ASSERT_FALSE(test_path_a.empty());
513 ASSERT_FALSE(test_path_b.empty());
515 // Attach to one first.
516 // (*'d mounts are those StorageMonitor knows about.)
517 // kDeviceDCIM1 -> kMountPointA *
518 MtabTestData test_data1[] = {
519 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
521 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
522 EXPECT_EQ(1, observer().attach_calls());
523 EXPECT_EQ(0, observer().detach_calls());
525 // Attach |kDeviceDCIM1| to |kMountPointB|.
526 // kDeviceDCIM1 -> kMountPointA *
527 // kDeviceDCIM1 -> kMountPointB
528 MtabTestData test_data2[] = {
529 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
531 AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
532 EXPECT_EQ(1, observer().attach_calls());
533 EXPECT_EQ(0, observer().detach_calls());
535 // Attach |kDeviceFixed| (a non-removable device) to |kMountPointA|.
536 // kDeviceDCIM1 -> kMountPointA
537 // kDeviceDCIM1 -> kMountPointB *
538 // kDeviceFixed -> kMountPointA
539 MtabTestData test_data3[] = {
540 MtabTestData(kDeviceFixed, test_path_a.value(), kValidFS),
542 RemoveDCIMDirFromMountPoint(kMountPointA);
543 AppendToMtabAndRunLoop(test_data3, std::size(test_data3));
544 EXPECT_EQ(2, observer().attach_calls());
545 EXPECT_EQ(1, observer().detach_calls());
547 // Detach |kDeviceFixed|.
548 // kDeviceDCIM1 -> kMountPointA
549 // kDeviceDCIM1 -> kMountPointB *
550 MtabTestData test_data4[] = {
551 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
552 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
554 CreateMountPointWithDCIMDir(kMountPointA);
555 OverwriteMtabAndRunLoop(test_data4, std::size(test_data4));
556 EXPECT_EQ(2, observer().attach_calls());
557 EXPECT_EQ(1, observer().detach_calls());
559 // Attach |kDeviceNoDCIM| (a non-DCIM device) to |kMountPointB|.
560 // kDeviceDCIM1 -> kMountPointA *
561 // kDeviceDCIM1 -> kMountPointB
562 // kDeviceNoDCIM -> kMountPointB *
563 MtabTestData test_data5[] = {
564 MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
566 base::DeleteFile(test_path_b.Append(kDCIMDirectoryName));
567 AppendToMtabAndRunLoop(test_data5, std::size(test_data5));
568 EXPECT_EQ(4, observer().attach_calls());
569 EXPECT_EQ(2, observer().detach_calls());
571 // Detach |kDeviceNoDCIM|.
572 // kDeviceDCIM1 -> kMountPointA *
573 // kDeviceDCIM1 -> kMountPointB
574 MtabTestData test_data6[] = {
575 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
576 MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
578 CreateMountPointWithDCIMDir(kMountPointB);
579 OverwriteMtabAndRunLoop(test_data6, std::size(test_data6));
580 EXPECT_EQ(4, observer().attach_calls());
581 EXPECT_EQ(3, observer().detach_calls());
583 // Detach |kDeviceDCIM1| from |kMountPointB|.
584 // kDeviceDCIM1 -> kMountPointA *
585 OverwriteMtabAndRunLoop(test_data1, std::size(test_data1));
586 EXPECT_EQ(4, observer().attach_calls());
587 EXPECT_EQ(3, observer().detach_calls());
589 // Detach all devices.
590 WriteEmptyMtabAndRunLoop();
591 EXPECT_EQ(4, observer().attach_calls());
592 EXPECT_EQ(4, observer().detach_calls());
595 TEST_F(StorageMonitorLinuxTest, DeviceLookUp) {
596 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
597 base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
598 base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC);
599 ASSERT_FALSE(test_path_a.empty());
600 ASSERT_FALSE(test_path_b.empty());
601 ASSERT_FALSE(test_path_c.empty());
603 // Attach to one first.
604 // (starred mounts are those StorageMonitor knows about.)
605 // kDeviceDCIM1 -> kMountPointA *
606 // kDeviceNoDCIM -> kMountPointB *
607 // kDeviceFixed -> kMountPointC
608 MtabTestData test_data1[] = {
609 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
610 MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
611 MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
613 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
614 EXPECT_EQ(2, observer().attach_calls());
615 EXPECT_EQ(0, observer().detach_calls());
617 StorageInfo device_info;
618 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
619 EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
620 EXPECT_EQ(test_path_a.value(), device_info.location());
621 EXPECT_EQ(88788ULL, device_info.total_size_in_bytes());
622 EXPECT_EQ(u"volume label", device_info.storage_label());
623 EXPECT_EQ(u"vendor name", device_info.vendor_name());
624 EXPECT_EQ(u"model name", device_info.model_name());
626 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
627 EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), device_info.device_id());
628 EXPECT_EQ(test_path_b.value(), device_info.location());
630 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
631 EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
632 EXPECT_EQ(test_path_c.value(), device_info.location());
635 EXPECT_FALSE(notifier()->GetStorageInfoForPath(base::FilePath(kInvalidPath),
638 // Test filling in of the mount point.
640 notifier()->GetStorageInfoForPath(test_path_a.Append("some/other/path"),
642 EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
643 EXPECT_EQ(test_path_a.value(), device_info.location());
645 // One device attached at multiple points.
646 // kDeviceDCIM1 -> kMountPointA *
647 // kDeviceFixed -> kMountPointB
648 // kDeviceFixed -> kMountPointC
649 MtabTestData test_data2[] = {
650 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
651 MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
652 MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
654 AppendToMtabAndRunLoop(test_data2, std::size(test_data2));
656 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
657 EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
659 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
660 EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
662 EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
663 EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
665 EXPECT_EQ(2, observer().attach_calls());
666 EXPECT_EQ(1, observer().detach_calls());
669 TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) {
670 base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
671 base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
672 ASSERT_FALSE(test_path_a.empty());
673 ASSERT_FALSE(test_path_b.empty());
675 MtabTestData test_data1[] = {
676 MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
677 MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
678 MtabTestData(kDeviceFixed, kInvalidPath, kInvalidFS),
680 AppendToMtabAndRunLoop(test_data1, std::size(test_data1));
681 EXPECT_EQ(2, observer().attach_calls());
682 EXPECT_EQ(0, observer().detach_calls());
684 EXPECT_EQ(GetDevicePartitionSize(kDeviceDCIM1),
685 GetStorageSize(test_path_a));
686 EXPECT_EQ(GetDevicePartitionSize(kDeviceNoDCIM),
687 GetStorageSize(test_path_b));
688 EXPECT_EQ(GetDevicePartitionSize(kInvalidPath),
689 GetStorageSize(base::FilePath(kInvalidPath)));
694 } // namespace storage_monitor