// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // StorageMonitorLinux unit tests. #include "components/storage_monitor/storage_monitor_linux.h" #include #include #include #include #include #include #include "base/bind.h" #include "base/check.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "components/storage_monitor/mock_removable_storage_observer.h" #include "components/storage_monitor/removable_device_constants.h" #include "components/storage_monitor/storage_info.h" #include "components/storage_monitor/storage_monitor.h" #include "components/storage_monitor/test_storage_monitor.h" #include "testing/gtest/include/gtest/gtest.h" namespace storage_monitor { namespace { const char kValidFS[] = "vfat"; const char kInvalidFS[] = "invalidfs"; const char kInvalidPath[] = "invalid path does not exist"; const char kDeviceDCIM1[] = "d1"; const char kDeviceDCIM2[] = "d2"; const char kDeviceDCIM3[] = "d3"; const char kDeviceNoDCIM[] = "d4"; const char kDeviceFixed[] = "d5"; const char kInvalidDevice[] = "invalid_device"; const char kMountPointA[] = "mnt_a"; const char kMountPointB[] = "mnt_b"; const char kMountPointC[] = "mnt_c"; struct TestDeviceData { const char* device_path; const char* unique_id; StorageInfo::Type type; uint64_t partition_size_in_bytes; }; const TestDeviceData kTestDeviceData[] = { { kDeviceDCIM1, "UUID:FFF0-000F", StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 88788 }, { kDeviceDCIM2, "VendorModelSerial:ComName:Model2010:8989", StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 8773 }, { kDeviceDCIM3, "VendorModelSerial:::WEM319X792", StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 22837 }, { kDeviceNoDCIM, "UUID:ABCD-1234", StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 512 }, { kDeviceFixed, "UUID:743A-2349", StorageInfo::FIXED_MASS_STORAGE, 17282 }, }; std::unique_ptr GetDeviceInfo(const base::FilePath& device_path, const base::FilePath& mount_point) { bool device_found = false; size_t i = 0; for (; i < std::size(kTestDeviceData); i++) { if (device_path.value() == kTestDeviceData[i].device_path) { device_found = true; break; } } DCHECK(device_found); StorageInfo::Type type = kTestDeviceData[i].type; auto storage_info = std::make_unique( StorageInfo::MakeDeviceId(type, kTestDeviceData[i].unique_id), mount_point.value(), u"volume label", u"vendor name", u"model name", kTestDeviceData[i].partition_size_in_bytes); return storage_info; } uint64_t GetDevicePartitionSize(const std::string& device) { for (const auto& data : kTestDeviceData) { if (device == data.device_path) return data.partition_size_in_bytes; } return 0; } std::string GetDeviceId(const std::string& device) { for (const auto& data : kTestDeviceData) { if (device == data.device_path) return StorageInfo::MakeDeviceId(data.type, data.unique_id); } if (device == kInvalidDevice) { return StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE, kInvalidDevice); } return std::string(); } class TestStorageMonitorLinux : public StorageMonitorLinux { public: explicit TestStorageMonitorLinux(const base::FilePath& path) : StorageMonitorLinux(path) { SetGetDeviceInfoCallbackForTest(base::BindRepeating(&GetDeviceInfo)); } TestStorageMonitorLinux(const TestStorageMonitorLinux&) = delete; TestStorageMonitorLinux& operator=(const TestStorageMonitorLinux&) = delete; ~TestStorageMonitorLinux() override = default; void SetOnMtabUpdateCallback(base::OnceClosure on_mtab_update_callback) { EXPECT_FALSE(on_mtab_update_callback_); on_mtab_update_callback_ = std::move(on_mtab_update_callback); } private: void UpdateMtab( const MtabWatcherLinux::MountPointDeviceMap& new_mtab) override { StorageMonitorLinux::UpdateMtab(new_mtab); if (on_mtab_update_callback_) std::move(on_mtab_update_callback_).Run(); } base::OnceClosure on_mtab_update_callback_; }; class StorageMonitorLinuxTest : public testing::Test { public: struct MtabTestData { MtabTestData(const std::string& mount_device, const std::string& mount_point, const std::string& mount_type) : mount_device(mount_device), mount_point(mount_point), mount_type(mount_type) { } const std::string mount_device; const std::string mount_point; const std::string mount_type; }; StorageMonitorLinuxTest() = default; StorageMonitorLinuxTest(const StorageMonitorLinuxTest&) = delete; StorageMonitorLinuxTest& operator=(const StorageMonitorLinuxTest&) = delete; ~StorageMonitorLinuxTest() override = default; protected: void SetUp() override { // Create and set up a temp dir with files for the test. ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); base::FilePath test_dir = scoped_temp_dir_.GetPath().AppendASCII("test_etc"); ASSERT_TRUE(base::CreateDirectory(test_dir)); mtab_file_ = test_dir.AppendASCII("test_mtab"); MtabTestData initial_test_data[] = { MtabTestData("dummydevice", "dummydir", kInvalidFS), }; WriteToMtab(initial_test_data, std::size(initial_test_data), /*overwrite=*/true); monitor_ = std::make_unique(mtab_file_); mock_storage_observer_ = std::make_unique(); monitor_->AddObserver(mock_storage_observer_.get()); monitor_->Init(); task_environment_.RunUntilIdle(); } void TearDown() override { task_environment_.RunUntilIdle(); monitor_->RemoveObserver(mock_storage_observer_.get()); task_environment_.RunUntilIdle(); // Linux storage monitor must be destroyed on the UI thread, so do it here. monitor_.reset(); } // Append mtab entries from the |data| array of size |data_size| to the mtab // file, and run the message loop. void AppendToMtabAndRunLoop(const MtabTestData* data, size_t data_size) { WriteToMtab(data, data_size, /*overwrite=*/false); WaitForMtabUpdate(); } // Overwrite the mtab file with mtab entries from the |data| array of size // |data_size|, and run the message loop. void OverwriteMtabAndRunLoop(const MtabTestData* data, size_t data_size) { WriteToMtab(data, data_size, /*overwrite=*/true); WaitForMtabUpdate(); } // Simplied version of OverwriteMtabAndRunLoop() that just deletes all the // entries in the mtab file. void WriteEmptyMtabAndRunLoop() { OverwriteMtabAndRunLoop(/*data=*/nullptr, /*data_size=*/0); } // Create a directory named |dir| relative to the test directory. // It has a DCIM directory, so StorageMonitorLinux recognizes it as a media // directory. base::FilePath CreateMountPointWithDCIMDir(const std::string& dir) { return CreateMountPoint(dir, /*with_dcim_dir=*/true); } // Create a directory named |dir| relative to the test directory. // It does not have a DCIM directory, so StorageMonitorLinux does not // recognize it as a media directory. base::FilePath CreateMountPointWithoutDCIMDir(const std::string& dir) { return CreateMountPoint(dir, /*with_dcim_dir=*/false); } void RemoveDCIMDirFromMountPoint(const std::string& dir) { base::FilePath dcim = scoped_temp_dir_.GetPath().AppendASCII(dir).Append(kDCIMDirectoryName); base::DeleteFile(dcim); } MockRemovableStorageObserver& observer() { return *mock_storage_observer_; } StorageMonitor* notifier() { return monitor_.get(); } uint64_t GetStorageSize(const base::FilePath& path) { StorageInfo info; if (!notifier()->GetStorageInfoForPath(path, &info)) return 0; return info.total_size_in_bytes(); } private: // Invoked after making an mtab update. Blocks (in an active RunLoop) until // the mtab changes are detected by the file watcher and side-effects of // UpdateMtab() propagate. void WaitForMtabUpdate() { base::RunLoop run_loop; monitor_->SetOnMtabUpdateCallback(run_loop.QuitClosure()); // Wait until the UpdateMtab() notification comes in from the system // (cannot use RunUntilIdle right away as that would racily return early // per being idle until the system notification comes in). run_loop.Run(); // UpdateMtab() causes asynchronous work on internal task runners, flush // everything to make sure `mock_storage_observer_` gets to observe the // change. task_environment_.RunUntilIdle(); } // Create a directory named |dir| relative to the test directory. // Set |with_dcim_dir| to true if the created directory will have a "DCIM" // subdirectory. // Returns the full path to the created directory on success, or an empty // path on failure. base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir) { base::FilePath return_path(scoped_temp_dir_.GetPath()); return_path = return_path.AppendASCII(dir); base::FilePath path(return_path); if (with_dcim_dir) path = path.Append(kDCIMDirectoryName); if (!base::CreateDirectory(path)) return base::FilePath(); return return_path; } // Write the test mtab data to |mtab_file_|. // |data| is an array of mtab entries. // |data_size| is the array size of |data|. // |overwrite| specifies whether to overwrite |mtab_file_|. void WriteToMtab(const MtabTestData* data, size_t data_size, bool overwrite) { FILE* file = setmntent(mtab_file_.value().c_str(), overwrite ? "w" : "a"); ASSERT_TRUE(file); // Due to the glibc *mntent() interface design, which is out of our // control, the mtnent struct has several char* fields, even though // addmntent() does not write to them in the calls below. To make the // compiler happy while avoiding making additional copies of strings, // we just const_cast() the strings' c_str()s. // Assuming addmntent() does not write to the char* fields, this is safe. // It is unlikely the platforms this test suite runs on will have an // addmntent() implementation that does change the char* fields. If that // was ever the case, the test suite will start crashing or failing. mntent entry; static const char kMountOpts[] = "rw"; entry.mnt_opts = const_cast(kMountOpts); entry.mnt_freq = 0; entry.mnt_passno = 0; for (size_t i = 0; i < data_size; ++i) { entry.mnt_fsname = const_cast(data[i].mount_device.c_str()); entry.mnt_dir = const_cast(data[i].mount_point.c_str()); entry.mnt_type = const_cast(data[i].mount_type.c_str()); ASSERT_EQ(0, addmntent(file, &entry)); } ASSERT_EQ(1, endmntent(file)); } base::test::TaskEnvironment task_environment_; std::unique_ptr mock_storage_observer_; // Temporary directory for created test data. base::ScopedTempDir scoped_temp_dir_; // Path to the test mtab file. base::FilePath mtab_file_; std::unique_ptr monitor_; }; // TODO(https://crbug.com/1297464): This test is flaky. // Simple test case where we attach and detach a media device. TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) { base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA); ASSERT_FALSE(test_path.empty()); MtabTestData test_data[] = { MtabTestData(kDeviceDCIM2, test_path.value(), kValidFS), MtabTestData(kDeviceFixed, kInvalidPath, kValidFS), }; // Only |kDeviceDCIM2| should be attached, since |kDeviceFixed| has a bad // path. AppendToMtabAndRunLoop(test_data, std::size(test_data)); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_attached().device_id()); EXPECT_EQ(test_path.value(), observer().last_attached().location()); // |kDeviceDCIM2| should be detached here. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_detached().device_id()); } // Only removable devices are recognized. // This test is flaky, see https://crbug.com/1012211 TEST_F(StorageMonitorLinuxTest, Removable) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); ASSERT_FALSE(test_path_a.empty()); MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), }; // |kDeviceDCIM1| should be attached as expected. AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_attached().device_id()); EXPECT_EQ(test_path_a.value(), observer().last_attached().location()); // This should do nothing, since |kDeviceFixed| is not removable. base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB); ASSERT_FALSE(test_path_b.empty()); MtabTestData test_data2[] = { MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data2, std::size(test_data2)); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); // |kDeviceDCIM1| should be detached as expected. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_detached().device_id()); // |kDeviceNoDCIM| should be attached as expected. MtabTestData test_data3[] = { MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data3, std::size(test_data3)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_attached().device_id()); EXPECT_EQ(test_path_b.value(), observer().last_attached().location()); // |kDeviceNoDCIM| should be detached as expected. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(2, observer().detach_calls()); EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_detached().device_id()); } // More complicated test case with multiple devices on multiple mount points. TEST_F(StorageMonitorLinuxTest, SwapMountPoints) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB); ASSERT_FALSE(test_path_a.empty()); ASSERT_FALSE(test_path_b.empty()); // Attach two devices. // (*'d mounts are those StorageMonitor knows about.) // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM2 -> kMountPointB * MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); // Detach two devices from old mount points and attach the devices at new // mount points. // kDeviceDCIM1 -> kMountPointB * // kDeviceDCIM2 -> kMountPointA * MtabTestData test_data2[] = { MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), MtabTestData(kDeviceDCIM2, test_path_a.value(), kValidFS), }; OverwriteMtabAndRunLoop(test_data2, std::size(test_data2)); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(2, observer().detach_calls()); // Detach all devices. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(4, observer().detach_calls()); } // More complicated test case with multiple devices on multiple mount points. TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB); ASSERT_FALSE(test_path_a.empty()); ASSERT_FALSE(test_path_b.empty()); // Attach two devices. // (*'d mounts are those StorageMonitor knows about.) // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM2 -> kMountPointB * MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); // Attach |kDeviceDCIM1| to |kMountPointB|. // |kDeviceDCIM2| is inaccessible, so it is detached. |kDeviceDCIM1| has been // attached at |kMountPointB|, but is still accessible from |kMountPointA|. // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM2 -> kMountPointB // kDeviceDCIM1 -> kMountPointB MtabTestData test_data2[] = { MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data2, std::size(test_data2)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); // Detach |kDeviceDCIM1| from |kMountPointA|, causing a detach and attach // event. // kDeviceDCIM2 -> kMountPointB // kDeviceDCIM1 -> kMountPointB * MtabTestData test_data3[] = { MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS), MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), }; OverwriteMtabAndRunLoop(test_data3, std::size(test_data3)); EXPECT_EQ(3, observer().attach_calls()); EXPECT_EQ(2, observer().detach_calls()); // Attach |kDeviceDCIM1| to |kMountPointA|. // kDeviceDCIM2 -> kMountPointB // kDeviceDCIM1 -> kMountPointB * // kDeviceDCIM1 -> kMountPointA MtabTestData test_data4[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data4, std::size(test_data4)); EXPECT_EQ(3, observer().attach_calls()); EXPECT_EQ(2, observer().detach_calls()); // Detach |kDeviceDCIM1| from |kMountPointB|. // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM2 -> kMountPointB * OverwriteMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(5, observer().attach_calls()); EXPECT_EQ(3, observer().detach_calls()); // Detach all devices. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(5, observer().attach_calls()); EXPECT_EQ(5, observer().detach_calls()); } TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB); ASSERT_FALSE(test_path_a.empty()); ASSERT_FALSE(test_path_b.empty()); // Attach to one first. // (*'d mounts are those StorageMonitor knows about.) // kDeviceDCIM1 -> kMountPointA * MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); // Attach |kDeviceDCIM1| to |kMountPointB|. // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM1 -> kMountPointB MtabTestData test_data2[] = { MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data2, std::size(test_data2)); EXPECT_EQ(1, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); // Attach |kDeviceFixed| (a non-removable device) to |kMountPointA|. // kDeviceDCIM1 -> kMountPointA // kDeviceDCIM1 -> kMountPointB * // kDeviceFixed -> kMountPointA MtabTestData test_data3[] = { MtabTestData(kDeviceFixed, test_path_a.value(), kValidFS), }; RemoveDCIMDirFromMountPoint(kMountPointA); AppendToMtabAndRunLoop(test_data3, std::size(test_data3)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); // Detach |kDeviceFixed|. // kDeviceDCIM1 -> kMountPointA // kDeviceDCIM1 -> kMountPointB * MtabTestData test_data4[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), }; CreateMountPointWithDCIMDir(kMountPointA); OverwriteMtabAndRunLoop(test_data4, std::size(test_data4)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); // Attach |kDeviceNoDCIM| (a non-DCIM device) to |kMountPointB|. // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM1 -> kMountPointB // kDeviceNoDCIM -> kMountPointB * MtabTestData test_data5[] = { MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS), }; base::DeleteFile(test_path_b.Append(kDCIMDirectoryName)); AppendToMtabAndRunLoop(test_data5, std::size(test_data5)); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(2, observer().detach_calls()); // Detach |kDeviceNoDCIM|. // kDeviceDCIM1 -> kMountPointA * // kDeviceDCIM1 -> kMountPointB MtabTestData test_data6[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS), }; CreateMountPointWithDCIMDir(kMountPointB); OverwriteMtabAndRunLoop(test_data6, std::size(test_data6)); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(3, observer().detach_calls()); // Detach |kDeviceDCIM1| from |kMountPointB|. // kDeviceDCIM1 -> kMountPointA * OverwriteMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(3, observer().detach_calls()); // Detach all devices. WriteEmptyMtabAndRunLoop(); EXPECT_EQ(4, observer().attach_calls()); EXPECT_EQ(4, observer().detach_calls()); } TEST_F(StorageMonitorLinuxTest, DeviceLookUp) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB); base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC); ASSERT_FALSE(test_path_a.empty()); ASSERT_FALSE(test_path_b.empty()); ASSERT_FALSE(test_path_c.empty()); // Attach to one first. // (starred mounts are those StorageMonitor knows about.) // kDeviceDCIM1 -> kMountPointA * // kDeviceNoDCIM -> kMountPointB * // kDeviceFixed -> kMountPointC MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS), MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); StorageInfo device_info; EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id()); EXPECT_EQ(test_path_a.value(), device_info.location()); EXPECT_EQ(88788ULL, device_info.total_size_in_bytes()); EXPECT_EQ(u"volume label", device_info.storage_label()); EXPECT_EQ(u"vendor name", device_info.vendor_name()); EXPECT_EQ(u"model name", device_info.model_name()); EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), device_info.device_id()); EXPECT_EQ(test_path_b.value(), device_info.location()); EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id()); EXPECT_EQ(test_path_c.value(), device_info.location()); // An invalid path. EXPECT_FALSE(notifier()->GetStorageInfoForPath(base::FilePath(kInvalidPath), &device_info)); // Test filling in of the mount point. EXPECT_TRUE( notifier()->GetStorageInfoForPath(test_path_a.Append("some/other/path"), &device_info)); EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id()); EXPECT_EQ(test_path_a.value(), device_info.location()); // One device attached at multiple points. // kDeviceDCIM1 -> kMountPointA * // kDeviceFixed -> kMountPointB // kDeviceFixed -> kMountPointC MtabTestData test_data2[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS), MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS), }; AppendToMtabAndRunLoop(test_data2, std::size(test_data2)); EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id()); EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id()); EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info)); EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id()); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(1, observer().detach_calls()); } TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) { base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA); base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB); ASSERT_FALSE(test_path_a.empty()); ASSERT_FALSE(test_path_b.empty()); MtabTestData test_data1[] = { MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS), MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS), MtabTestData(kDeviceFixed, kInvalidPath, kInvalidFS), }; AppendToMtabAndRunLoop(test_data1, std::size(test_data1)); EXPECT_EQ(2, observer().attach_calls()); EXPECT_EQ(0, observer().detach_calls()); EXPECT_EQ(GetDevicePartitionSize(kDeviceDCIM1), GetStorageSize(test_path_a)); EXPECT_EQ(GetDevicePartitionSize(kDeviceNoDCIM), GetStorageSize(test_path_b)); EXPECT_EQ(GetDevicePartitionSize(kInvalidPath), GetStorageSize(base::FilePath(kInvalidPath))); } } // namespace } // namespace storage_monitor