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 #include <windows.h> 6 #include <dbt.h> 7 8 #include <string> 9 #include <vector> 10 11 #include "base/memory/ref_counted.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/synchronization/waitable_event.h" 16 #include "chrome/browser/storage_monitor/mock_removable_storage_observer.h" 17 #include "chrome/browser/storage_monitor/portable_device_watcher_win.h" 18 #include "chrome/browser/storage_monitor/removable_device_constants.h" 19 #include "chrome/browser/storage_monitor/storage_info.h" 20 #include "chrome/browser/storage_monitor/storage_monitor_win.h" 21 #include "chrome/browser/storage_monitor/test_portable_device_watcher_win.h" 22 #include "chrome/browser/storage_monitor/test_storage_monitor.h" 23 #include "chrome/browser/storage_monitor/test_storage_monitor_win.h" 24 #include "chrome/browser/storage_monitor/test_volume_mount_watcher_win.h" 25 #include "chrome/browser/storage_monitor/volume_mount_watcher_win.h" 26 #include "chrome/test/base/testing_browser_process.h" 27 #include "content/public/test/test_browser_thread.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 30 namespace chrome { 31 namespace test { 32 33 using content::BrowserThread; 34 35 typedef std::vector<int> DeviceIndices; 36 37 // StorageMonitorWinTest ------------------------------------------------------- 38 39 class StorageMonitorWinTest : public testing::Test { 40 public: 41 StorageMonitorWinTest(); 42 virtual ~StorageMonitorWinTest(); 43 44 protected: 45 // testing::Test: 46 virtual void SetUp() OVERRIDE; 47 virtual void TearDown() OVERRIDE; 48 49 void PreAttachDevices(); 50 51 // Runs all the pending tasks on UI thread, FILE thread and blocking thread. 52 void RunUntilIdle(); 53 54 void DoMassStorageDeviceAttachedTest(const DeviceIndices& device_indices); 55 void DoMassStorageDevicesDetachedTest(const DeviceIndices& device_indices); 56 57 // Injects a device attach or detach change (depending on the value of 58 // |test_attach|) and tests that the appropriate handler is called. 59 void DoMTPDeviceTest(const base::string16& pnp_device_id, bool test_attach); 60 61 // Gets the MTP details of the storage specified by the |storage_device_id|. 62 // On success, returns true and fills in |pnp_device_id| and 63 // |storage_object_id|. 64 bool GetMTPStorageInfo(const std::string& storage_device_id, 65 base::string16* pnp_device_id, 66 base::string16* storage_object_id); 67 68 TestStorageMonitorWin* monitor_; 69 70 // Weak pointer; owned by the device notifications class. 71 TestVolumeMountWatcherWin* volume_mount_watcher_; 72 73 MockRemovableStorageObserver observer_; 74 75 private: 76 base::MessageLoopForUI message_loop_; 77 content::TestBrowserThread ui_thread_; 78 content::TestBrowserThread file_thread_; 79 }; 80 81 StorageMonitorWinTest::StorageMonitorWinTest() 82 : ui_thread_(BrowserThread::UI, &message_loop_), 83 file_thread_(BrowserThread::FILE, &message_loop_) { 84 } 85 86 StorageMonitorWinTest::~StorageMonitorWinTest() { 87 } 88 89 void StorageMonitorWinTest::SetUp() { 90 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 91 test::TestStorageMonitor::RemoveSingleton(); 92 volume_mount_watcher_ = new TestVolumeMountWatcherWin; 93 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_, 94 new TestPortableDeviceWatcherWin); 95 scoped_ptr<StorageMonitor> pass_monitor(monitor_); 96 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal(); 97 DCHECK(browser_process); 98 browser_process->SetStorageMonitor(pass_monitor.Pass()); 99 100 monitor_->Init(); 101 RunUntilIdle(); 102 monitor_->AddObserver(&observer_); 103 } 104 105 void StorageMonitorWinTest::TearDown() { 106 RunUntilIdle(); 107 monitor_->RemoveObserver(&observer_); 108 volume_mount_watcher_->ShutdownWorkerPool(); 109 monitor_ = NULL; 110 111 // Windows storage monitor must be destroyed on the same thread 112 // as construction. 113 test::TestStorageMonitor::RemoveSingleton(); 114 } 115 116 void StorageMonitorWinTest::PreAttachDevices() { 117 test::TestStorageMonitor::RemoveSingleton(); 118 monitor_ = NULL; 119 volume_mount_watcher_ = new TestVolumeMountWatcherWin; 120 volume_mount_watcher_->SetAttachedDevicesFake(); 121 122 int expect_attach_calls = 0; 123 std::vector<base::FilePath> initial_devices = 124 volume_mount_watcher_->GetAttachedDevicesCallback().Run(); 125 for (std::vector<base::FilePath>::const_iterator it = initial_devices.begin(); 126 it != initial_devices.end(); ++it) { 127 bool removable; 128 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable(*it, &removable)); 129 if (removable) 130 expect_attach_calls++; 131 } 132 133 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_, 134 new TestPortableDeviceWatcherWin); 135 scoped_ptr<StorageMonitor> pass_monitor(monitor_); 136 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal(); 137 DCHECK(browser_process); 138 browser_process->SetStorageMonitor(pass_monitor.Pass()); 139 140 monitor_->AddObserver(&observer_); 141 monitor_->Init(); 142 143 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size()); 144 145 // This dance is because attachment bounces through a couple of 146 // closures, which need to be executed to finish the process. 147 RunUntilIdle(); 148 volume_mount_watcher_->FlushWorkerPoolForTesting(); 149 RunUntilIdle(); 150 151 std::vector<base::FilePath> checked_devices = 152 volume_mount_watcher_->devices_checked(); 153 sort(checked_devices.begin(), checked_devices.end()); 154 EXPECT_EQ(initial_devices, checked_devices); 155 EXPECT_EQ(expect_attach_calls, observer_.attach_calls()); 156 EXPECT_EQ(0, observer_.detach_calls()); 157 } 158 159 void StorageMonitorWinTest::RunUntilIdle() { 160 volume_mount_watcher_->FlushWorkerPoolForTesting(); 161 message_loop_.RunUntilIdle(); 162 } 163 164 void StorageMonitorWinTest::DoMassStorageDeviceAttachedTest( 165 const DeviceIndices& device_indices) { 166 DEV_BROADCAST_VOLUME volume_broadcast; 167 volume_broadcast.dbcv_size = sizeof(volume_broadcast); 168 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME; 169 volume_broadcast.dbcv_unitmask = 0x0; 170 volume_broadcast.dbcv_flags = 0x0; 171 172 int expect_attach_calls = observer_.attach_calls(); 173 for (DeviceIndices::const_iterator it = device_indices.begin(); 174 it != device_indices.end(); ++it) { 175 volume_broadcast.dbcv_unitmask |= 0x1 << *it; 176 bool removable; 177 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable( 178 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &removable)); 179 if (removable) 180 expect_attach_calls++; 181 } 182 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL, 183 reinterpret_cast<DWORD>(&volume_broadcast)); 184 185 RunUntilIdle(); 186 volume_mount_watcher_->FlushWorkerPoolForTesting(); 187 RunUntilIdle(); 188 189 EXPECT_EQ(expect_attach_calls, observer_.attach_calls()); 190 EXPECT_EQ(0, observer_.detach_calls()); 191 } 192 193 void StorageMonitorWinTest::DoMassStorageDevicesDetachedTest( 194 const DeviceIndices& device_indices) { 195 DEV_BROADCAST_VOLUME volume_broadcast; 196 volume_broadcast.dbcv_size = sizeof(volume_broadcast); 197 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME; 198 volume_broadcast.dbcv_unitmask = 0x0; 199 volume_broadcast.dbcv_flags = 0x0; 200 201 int pre_attach_calls = observer_.attach_calls(); 202 int expect_detach_calls = 0; 203 for (DeviceIndices::const_iterator it = device_indices.begin(); 204 it != device_indices.end(); ++it) { 205 volume_broadcast.dbcv_unitmask |= 0x1 << *it; 206 StorageInfo info; 207 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo( 208 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &info)); 209 if (StorageInfo::IsRemovableDevice(info.device_id())) 210 ++expect_detach_calls; 211 } 212 monitor_->InjectDeviceChange(DBT_DEVICEREMOVECOMPLETE, 213 reinterpret_cast<DWORD>(&volume_broadcast)); 214 RunUntilIdle(); 215 EXPECT_EQ(pre_attach_calls, observer_.attach_calls()); 216 EXPECT_EQ(expect_detach_calls, observer_.detach_calls()); 217 } 218 219 void StorageMonitorWinTest::DoMTPDeviceTest(const base::string16& pnp_device_id, 220 bool test_attach) { 221 GUID guidDevInterface = GUID_NULL; 222 HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface); 223 if (FAILED(hr)) 224 return; 225 226 size_t device_id_size = pnp_device_id.size() * sizeof(char16); 227 size_t size = sizeof(DEV_BROADCAST_DEVICEINTERFACE) + device_id_size; 228 scoped_ptr_malloc<DEV_BROADCAST_DEVICEINTERFACE> dev_interface_broadcast( 229 static_cast<DEV_BROADCAST_DEVICEINTERFACE*>(malloc(size))); 230 DCHECK(dev_interface_broadcast.get()); 231 ZeroMemory(dev_interface_broadcast.get(), size); 232 dev_interface_broadcast->dbcc_size = size; 233 dev_interface_broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 234 dev_interface_broadcast->dbcc_classguid = guidDevInterface; 235 memcpy(dev_interface_broadcast->dbcc_name, pnp_device_id.data(), 236 device_id_size); 237 238 int expect_attach_calls = observer_.attach_calls(); 239 int expect_detach_calls = observer_.detach_calls(); 240 PortableDeviceWatcherWin::StorageObjectIDs storage_object_ids = 241 TestPortableDeviceWatcherWin::GetMTPStorageObjectIds(pnp_device_id); 242 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator it = 243 storage_object_ids.begin(); it != storage_object_ids.end(); ++it) { 244 std::string unique_id; 245 base::string16 name; 246 base::string16 location; 247 TestPortableDeviceWatcherWin::GetMTPStorageDetails(pnp_device_id, *it, 248 &location, &unique_id, 249 &name); 250 if (test_attach && !name.empty() && !unique_id.empty()) 251 expect_attach_calls++; 252 else if (!name.empty() && !unique_id.empty()) 253 expect_detach_calls++; 254 } 255 256 monitor_->InjectDeviceChange( 257 test_attach ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, 258 reinterpret_cast<DWORD>(dev_interface_broadcast.get())); 259 260 RunUntilIdle(); 261 EXPECT_EQ(expect_attach_calls, observer_.attach_calls()); 262 EXPECT_EQ(expect_detach_calls, observer_.detach_calls()); 263 } 264 265 bool StorageMonitorWinTest::GetMTPStorageInfo( 266 const std::string& storage_device_id, 267 base::string16* pnp_device_id, 268 base::string16* storage_object_id) { 269 return monitor_->GetMTPStorageInfoFromDeviceId(storage_device_id, 270 pnp_device_id, 271 storage_object_id); 272 } 273 274 TEST_F(StorageMonitorWinTest, RandomMessage) { 275 monitor_->InjectDeviceChange(DBT_DEVICEQUERYREMOVE, NULL); 276 RunUntilIdle(); 277 } 278 279 TEST_F(StorageMonitorWinTest, DevicesAttached) { 280 DeviceIndices device_indices; 281 device_indices.push_back(1); // B 282 device_indices.push_back(5); // F 283 device_indices.push_back(7); // H 284 device_indices.push_back(13); // N 285 DoMassStorageDeviceAttachedTest(device_indices); 286 287 StorageInfo info; 288 EXPECT_TRUE(monitor_->volume_mount_watcher()->GetDeviceInfo( 289 base::FilePath(ASCIIToUTF16("F:\\")), &info)); 290 EXPECT_EQ(ASCIIToUTF16("F:\\"), info.location()); 291 EXPECT_EQ("dcim:\\\\?\\Volume{F0000000-0000-0000-0000-000000000000}\\", 292 info.device_id()); 293 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label()); 294 295 EXPECT_FALSE(monitor_->GetStorageInfoForPath( 296 base::FilePath(ASCIIToUTF16("G:\\")), &info)); 297 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 298 base::FilePath(ASCIIToUTF16("F:\\")), &info)); 299 StorageInfo info1; 300 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 301 base::FilePath(ASCIIToUTF16("F:\\subdir")), &info1)); 302 StorageInfo info2; 303 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 304 base::FilePath(ASCIIToUTF16("F:\\subdir\\sub")), &info2)); 305 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label()); 306 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info1.storage_label()); 307 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info2.storage_label()); 308 } 309 310 TEST_F(StorageMonitorWinTest, PathMountDevices) { 311 PreAttachDevices(); 312 int init_storages = monitor_->GetAllAvailableStorages().size(); 313 314 volume_mount_watcher_->AddDeviceForTesting( 315 base::FilePath(FILE_PATH_LITERAL("F:\\mount1")), 316 "dcim:mount1", L"mount1", 100); 317 volume_mount_watcher_->AddDeviceForTesting( 318 base::FilePath(FILE_PATH_LITERAL("F:\\mount1\\subdir")), 319 "dcim:mount1subdir", L"mount1subdir", 100); 320 volume_mount_watcher_->AddDeviceForTesting( 321 base::FilePath(FILE_PATH_LITERAL("F:\\mount2")), 322 "dcim:mount2", L"mount2", 100); 323 RunUntilIdle(); 324 EXPECT_EQ(init_storages + 3, monitor_->GetAllAvailableStorages().size()); 325 326 StorageInfo info; 327 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 328 base::FilePath(ASCIIToUTF16("F:\\dir")), &info)); 329 EXPECT_EQ(L"", info.name()); 330 EXPECT_EQ(L"F:\\ Drive", info.storage_label()); 331 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 332 base::FilePath(ASCIIToUTF16("F:\\mount1")), &info)); 333 EXPECT_EQ(L"mount1", info.name()); 334 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 335 base::FilePath(ASCIIToUTF16("F:\\mount1\\dir")), &info)); 336 EXPECT_EQ(L"mount1", info.name()); 337 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 338 base::FilePath(ASCIIToUTF16("F:\\mount2\\dir")), &info)); 339 EXPECT_EQ(L"mount2", info.name()); 340 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 341 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir")), &info)); 342 EXPECT_EQ(L"mount1subdir", info.name()); 343 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 344 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir")), &info)); 345 EXPECT_EQ(L"mount1subdir", info.name()); 346 EXPECT_TRUE(monitor_->GetStorageInfoForPath( 347 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir\\dir")), &info)); 348 EXPECT_EQ(L"mount1subdir", info.name()); 349 } 350 351 TEST_F(StorageMonitorWinTest, DevicesAttachedHighBoundary) { 352 DeviceIndices device_indices; 353 device_indices.push_back(25); 354 355 DoMassStorageDeviceAttachedTest(device_indices); 356 } 357 358 TEST_F(StorageMonitorWinTest, DevicesAttachedLowBoundary) { 359 DeviceIndices device_indices; 360 device_indices.push_back(0); 361 362 DoMassStorageDeviceAttachedTest(device_indices); 363 } 364 365 TEST_F(StorageMonitorWinTest, DevicesAttachedAdjacentBits) { 366 DeviceIndices device_indices; 367 device_indices.push_back(0); 368 device_indices.push_back(1); 369 device_indices.push_back(2); 370 device_indices.push_back(3); 371 372 DoMassStorageDeviceAttachedTest(device_indices); 373 } 374 375 TEST_F(StorageMonitorWinTest, DevicesDetached) { 376 PreAttachDevices(); 377 378 DeviceIndices device_indices; 379 device_indices.push_back(1); 380 device_indices.push_back(5); 381 device_indices.push_back(7); 382 device_indices.push_back(13); 383 384 DoMassStorageDevicesDetachedTest(device_indices); 385 } 386 387 TEST_F(StorageMonitorWinTest, DevicesDetachedHighBoundary) { 388 PreAttachDevices(); 389 390 DeviceIndices device_indices; 391 device_indices.push_back(25); 392 393 DoMassStorageDevicesDetachedTest(device_indices); 394 } 395 396 TEST_F(StorageMonitorWinTest, DevicesDetachedLowBoundary) { 397 PreAttachDevices(); 398 399 DeviceIndices device_indices; 400 device_indices.push_back(0); 401 402 DoMassStorageDevicesDetachedTest(device_indices); 403 } 404 405 TEST_F(StorageMonitorWinTest, DevicesDetachedAdjacentBits) { 406 PreAttachDevices(); 407 408 DeviceIndices device_indices; 409 device_indices.push_back(0); 410 device_indices.push_back(1); 411 device_indices.push_back(2); 412 device_indices.push_back(3); 413 414 DoMassStorageDevicesDetachedTest(device_indices); 415 } 416 417 TEST_F(StorageMonitorWinTest, DuplicateAttachCheckSuppressed) { 418 // Make sure the original C: mount notification makes it all the 419 // way through. 420 RunUntilIdle(); 421 volume_mount_watcher_->FlushWorkerPoolForTesting(); 422 RunUntilIdle(); 423 424 volume_mount_watcher_->BlockDeviceCheckForTesting(); 425 base::FilePath kAttachedDevicePath = 426 VolumeMountWatcherWin::DriveNumberToFilePath(8); // I: 427 428 DEV_BROADCAST_VOLUME volume_broadcast; 429 volume_broadcast.dbcv_size = sizeof(volume_broadcast); 430 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME; 431 volume_broadcast.dbcv_flags = 0x0; 432 volume_broadcast.dbcv_unitmask = 0x100; // I: drive 433 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL, 434 reinterpret_cast<DWORD>(&volume_broadcast)); 435 436 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size()); 437 438 // Re-attach the same volume. We haven't released the mock device check 439 // event, so there'll be pending calls in the UI thread to finish the 440 // device check notification, blocking the duplicate device injection. 441 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL, 442 reinterpret_cast<DWORD>(&volume_broadcast)); 443 444 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size()); 445 volume_mount_watcher_->ReleaseDeviceCheck(); 446 RunUntilIdle(); 447 volume_mount_watcher_->ReleaseDeviceCheck(); 448 449 // Now let all attach notifications finish running. We'll only get one 450 // finish-attach call. 451 volume_mount_watcher_->FlushWorkerPoolForTesting(); 452 RunUntilIdle(); 453 454 const std::vector<base::FilePath>& checked_devices = 455 volume_mount_watcher_->devices_checked(); 456 ASSERT_EQ(1u, checked_devices.size()); 457 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]); 458 459 // We'll receive a duplicate check now that the first check has fully cleared. 460 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL, 461 reinterpret_cast<DWORD>(&volume_broadcast)); 462 volume_mount_watcher_->FlushWorkerPoolForTesting(); 463 volume_mount_watcher_->ReleaseDeviceCheck(); 464 RunUntilIdle(); 465 466 ASSERT_EQ(2u, checked_devices.size()); 467 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]); 468 EXPECT_EQ(kAttachedDevicePath, checked_devices[1]); 469 } 470 471 TEST_F(StorageMonitorWinTest, DeviceInfoForPath) { 472 PreAttachDevices(); 473 474 StorageInfo device_info; 475 // An invalid path. 476 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"COM1:\\"), 477 &device_info)); 478 479 // An unconnected removable device. 480 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"E:\\"), 481 &device_info)); 482 483 // A connected removable device. 484 base::FilePath removable_device(L"F:\\"); 485 EXPECT_TRUE(monitor_->GetStorageInfoForPath(removable_device, &device_info)); 486 487 StorageInfo info; 488 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(removable_device, &info)); 489 EXPECT_TRUE(StorageInfo::IsRemovableDevice(info.device_id())); 490 EXPECT_EQ(info.device_id(), device_info.device_id()); 491 EXPECT_EQ(info.name(), device_info.name()); 492 EXPECT_EQ(info.location(), device_info.location()); 493 EXPECT_EQ(1000000, info.total_size_in_bytes()); 494 495 // A fixed device. 496 base::FilePath fixed_device(L"N:\\"); 497 EXPECT_TRUE(monitor_->GetStorageInfoForPath(fixed_device, &device_info)); 498 499 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo( 500 fixed_device, &info)); 501 EXPECT_FALSE(StorageInfo::IsRemovableDevice(info.device_id())); 502 EXPECT_EQ(info.device_id(), device_info.device_id()); 503 EXPECT_EQ(info.name(), device_info.name()); 504 EXPECT_EQ(info.location(), device_info.location()); 505 } 506 507 // Test to verify basic MTP storage attach and detach notifications. 508 TEST_F(StorageMonitorWinTest, MTPDeviceBasicAttachDetach) { 509 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true); 510 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false); 511 } 512 513 // When a MTP storage device with invalid storage label and id is 514 // attached/detached, there should not be any device attach/detach 515 // notifications. 516 TEST_F(StorageMonitorWinTest, MTPDeviceWithInvalidInfo) { 517 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo, 518 true); 519 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo, 520 false); 521 } 522 523 // Attach a device with two data partitions. Verify that attach/detach 524 // notifications are sent out for each removable storage. 525 TEST_F(StorageMonitorWinTest, MTPDeviceWithMultipleStorageObjects) { 526 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages, 527 true); 528 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages, 529 false); 530 } 531 532 TEST_F(StorageMonitorWinTest, DriveNumberToFilePath) { 533 EXPECT_EQ(L"A:\\", VolumeMountWatcherWin::DriveNumberToFilePath(0).value()); 534 EXPECT_EQ(L"Y:\\", VolumeMountWatcherWin::DriveNumberToFilePath(24).value()); 535 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(-1).value()); 536 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(199).value()); 537 } 538 539 // Given a MTP storage persistent id, GetMTPStorageInfo() should fetch the 540 // device interface path and local storage object identifier. 541 TEST_F(StorageMonitorWinTest, GetMTPStorageInfoFromDeviceId) { 542 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true); 543 PortableDeviceWatcherWin::StorageObjects storage_objects = 544 TestPortableDeviceWatcherWin::GetDeviceStorageObjects( 545 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo); 546 for (PortableDeviceWatcherWin::StorageObjects::const_iterator it = 547 storage_objects.begin(); 548 it != storage_objects.end(); ++it) { 549 base::string16 pnp_device_id; 550 base::string16 storage_object_id; 551 ASSERT_TRUE(GetMTPStorageInfo(it->object_persistent_id, &pnp_device_id, 552 &storage_object_id)); 553 base::string16 expected( 554 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo); 555 EXPECT_EQ(expected, pnp_device_id); 556 EXPECT_EQ(it->object_persistent_id, 557 TestPortableDeviceWatcherWin::GetMTPStorageUniqueId( 558 pnp_device_id, storage_object_id)); 559 } 560 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false); 561 } 562 563 } // namespace test 564 } // namespace chrome 565