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