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