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.
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/run_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/browser/browser_thread.h"
28 #include "content/public/test/test_browser_thread_bundle.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using base::ASCIIToUTF16;
32 using content::BrowserThread;
34 typedef std::vector<int> DeviceIndices;
36 // StorageMonitorWinTest -------------------------------------------------------
38 class StorageMonitorWinTest : public testing::Test {
40 StorageMonitorWinTest();
41 virtual ~StorageMonitorWinTest();
45 virtual void SetUp() OVERRIDE;
46 virtual void TearDown() OVERRIDE;
48 void PreAttachDevices();
50 // Runs all the pending tasks on UI thread, FILE thread and blocking thread.
53 void DoMassStorageDeviceAttachedTest(const DeviceIndices& device_indices);
54 void DoMassStorageDevicesDetachedTest(const DeviceIndices& device_indices);
56 // Injects a device attach or detach change (depending on the value of
57 // |test_attach|) and tests that the appropriate handler is called.
58 void DoMTPDeviceTest(const base::string16& pnp_device_id, bool test_attach);
60 // Gets the MTP details of the storage specified by the |storage_device_id|.
61 // On success, returns true and fills in |pnp_device_id| and
62 // |storage_object_id|.
63 bool GetMTPStorageInfo(const std::string& storage_device_id,
64 base::string16* pnp_device_id,
65 base::string16* storage_object_id);
67 TestStorageMonitorWin* monitor_;
69 // Weak pointer; owned by the device notifications class.
70 TestVolumeMountWatcherWin* volume_mount_watcher_;
72 MockRemovableStorageObserver observer_;
75 content::TestBrowserThreadBundle thread_bundle_;
77 DISALLOW_COPY_AND_ASSIGN(StorageMonitorWinTest);
80 StorageMonitorWinTest::StorageMonitorWinTest() {
83 StorageMonitorWinTest::~StorageMonitorWinTest() {
86 void StorageMonitorWinTest::SetUp() {
87 TestStorageMonitor::RemoveSingleton();
88 volume_mount_watcher_ = new TestVolumeMountWatcherWin;
89 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_,
90 new TestPortableDeviceWatcherWin);
91 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
92 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
93 DCHECK(browser_process);
94 browser_process->SetStorageMonitor(pass_monitor.Pass());
98 monitor_->AddObserver(&observer_);
101 void StorageMonitorWinTest::TearDown() {
103 monitor_->RemoveObserver(&observer_);
104 volume_mount_watcher_->ShutdownWorkerPool();
107 // Windows storage monitor must be destroyed on the same thread
109 TestStorageMonitor::RemoveSingleton();
112 void StorageMonitorWinTest::PreAttachDevices() {
113 TestStorageMonitor::RemoveSingleton();
115 volume_mount_watcher_ = new TestVolumeMountWatcherWin;
116 volume_mount_watcher_->SetAttachedDevicesFake();
118 int expect_attach_calls = 0;
119 std::vector<base::FilePath> initial_devices =
120 volume_mount_watcher_->GetAttachedDevicesCallback().Run();
121 for (std::vector<base::FilePath>::const_iterator it = initial_devices.begin();
122 it != initial_devices.end(); ++it) {
124 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable(*it, &removable));
126 expect_attach_calls++;
129 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_,
130 new TestPortableDeviceWatcherWin);
131 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
132 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
133 DCHECK(browser_process);
134 browser_process->SetStorageMonitor(pass_monitor.Pass());
136 monitor_->AddObserver(&observer_);
139 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
141 // This dance is because attachment bounces through a couple of
142 // closures, which need to be executed to finish the process.
144 volume_mount_watcher_->FlushWorkerPoolForTesting();
147 std::vector<base::FilePath> checked_devices =
148 volume_mount_watcher_->devices_checked();
149 sort(checked_devices.begin(), checked_devices.end());
150 EXPECT_EQ(initial_devices, checked_devices);
151 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
152 EXPECT_EQ(0, observer_.detach_calls());
155 void StorageMonitorWinTest::RunUntilIdle() {
156 volume_mount_watcher_->FlushWorkerPoolForTesting();
157 base::RunLoop().RunUntilIdle();
160 void StorageMonitorWinTest::DoMassStorageDeviceAttachedTest(
161 const DeviceIndices& device_indices) {
162 DEV_BROADCAST_VOLUME volume_broadcast;
163 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
164 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
165 volume_broadcast.dbcv_unitmask = 0x0;
166 volume_broadcast.dbcv_flags = 0x0;
168 int expect_attach_calls = observer_.attach_calls();
169 for (DeviceIndices::const_iterator it = device_indices.begin();
170 it != device_indices.end(); ++it) {
171 volume_broadcast.dbcv_unitmask |= 0x1 << *it;
173 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable(
174 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &removable));
176 expect_attach_calls++;
178 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
179 reinterpret_cast<DWORD>(&volume_broadcast));
182 volume_mount_watcher_->FlushWorkerPoolForTesting();
185 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
186 EXPECT_EQ(0, observer_.detach_calls());
189 void StorageMonitorWinTest::DoMassStorageDevicesDetachedTest(
190 const DeviceIndices& device_indices) {
191 DEV_BROADCAST_VOLUME volume_broadcast;
192 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
193 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
194 volume_broadcast.dbcv_unitmask = 0x0;
195 volume_broadcast.dbcv_flags = 0x0;
197 int pre_attach_calls = observer_.attach_calls();
198 int expect_detach_calls = 0;
199 for (DeviceIndices::const_iterator it = device_indices.begin();
200 it != device_indices.end(); ++it) {
201 volume_broadcast.dbcv_unitmask |= 0x1 << *it;
203 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(
204 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &info));
205 if (StorageInfo::IsRemovableDevice(info.device_id()))
206 ++expect_detach_calls;
208 monitor_->InjectDeviceChange(DBT_DEVICEREMOVECOMPLETE,
209 reinterpret_cast<DWORD>(&volume_broadcast));
211 EXPECT_EQ(pre_attach_calls, observer_.attach_calls());
212 EXPECT_EQ(expect_detach_calls, observer_.detach_calls());
215 void StorageMonitorWinTest::DoMTPDeviceTest(const base::string16& pnp_device_id,
217 GUID guidDevInterface = GUID_NULL;
218 HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface);
222 size_t device_id_size = pnp_device_id.size() * sizeof(base::char16);
223 size_t size = sizeof(DEV_BROADCAST_DEVICEINTERFACE) + device_id_size;
224 scoped_ptr_malloc<DEV_BROADCAST_DEVICEINTERFACE> dev_interface_broadcast(
225 static_cast<DEV_BROADCAST_DEVICEINTERFACE*>(malloc(size)));
226 DCHECK(dev_interface_broadcast.get());
227 ZeroMemory(dev_interface_broadcast.get(), size);
228 dev_interface_broadcast->dbcc_size = size;
229 dev_interface_broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
230 dev_interface_broadcast->dbcc_classguid = guidDevInterface;
231 memcpy(dev_interface_broadcast->dbcc_name, pnp_device_id.data(),
234 int expect_attach_calls = observer_.attach_calls();
235 int expect_detach_calls = observer_.detach_calls();
236 PortableDeviceWatcherWin::StorageObjectIDs storage_object_ids =
237 TestPortableDeviceWatcherWin::GetMTPStorageObjectIds(pnp_device_id);
238 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator it =
239 storage_object_ids.begin(); it != storage_object_ids.end(); ++it) {
240 std::string unique_id;
242 base::string16 location;
243 TestPortableDeviceWatcherWin::GetMTPStorageDetails(pnp_device_id, *it,
244 &location, &unique_id,
246 if (test_attach && !name.empty() && !unique_id.empty())
247 expect_attach_calls++;
248 else if (!name.empty() && !unique_id.empty())
249 expect_detach_calls++;
252 monitor_->InjectDeviceChange(
253 test_attach ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE,
254 reinterpret_cast<DWORD>(dev_interface_broadcast.get()));
257 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
258 EXPECT_EQ(expect_detach_calls, observer_.detach_calls());
261 bool StorageMonitorWinTest::GetMTPStorageInfo(
262 const std::string& storage_device_id,
263 base::string16* pnp_device_id,
264 base::string16* storage_object_id) {
265 return monitor_->GetMTPStorageInfoFromDeviceId(storage_device_id,
270 TEST_F(StorageMonitorWinTest, RandomMessage) {
271 monitor_->InjectDeviceChange(DBT_DEVICEQUERYREMOVE, NULL);
275 TEST_F(StorageMonitorWinTest, DevicesAttached) {
276 DeviceIndices device_indices;
277 device_indices.push_back(1); // B
278 device_indices.push_back(5); // F
279 device_indices.push_back(7); // H
280 device_indices.push_back(13); // N
281 DoMassStorageDeviceAttachedTest(device_indices);
284 EXPECT_TRUE(monitor_->volume_mount_watcher()->GetDeviceInfo(
285 base::FilePath(ASCIIToUTF16("F:\\")), &info));
286 EXPECT_EQ(ASCIIToUTF16("F:\\"), info.location());
287 EXPECT_EQ("dcim:\\\\?\\Volume{F0000000-0000-0000-0000-000000000000}\\",
289 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label());
291 EXPECT_FALSE(monitor_->GetStorageInfoForPath(
292 base::FilePath(ASCIIToUTF16("G:\\")), &info));
293 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
294 base::FilePath(ASCIIToUTF16("F:\\")), &info));
296 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
297 base::FilePath(ASCIIToUTF16("F:\\subdir")), &info1));
299 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
300 base::FilePath(ASCIIToUTF16("F:\\subdir\\sub")), &info2));
301 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label());
302 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info1.storage_label());
303 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info2.storage_label());
306 TEST_F(StorageMonitorWinTest, PathMountDevices) {
308 int init_storages = monitor_->GetAllAvailableStorages().size();
310 volume_mount_watcher_->AddDeviceForTesting(
311 base::FilePath(FILE_PATH_LITERAL("F:\\mount1")),
312 "dcim:mount1", L"mount1", 100);
313 volume_mount_watcher_->AddDeviceForTesting(
314 base::FilePath(FILE_PATH_LITERAL("F:\\mount1\\subdir")),
315 "dcim:mount1subdir", L"mount1subdir", 100);
316 volume_mount_watcher_->AddDeviceForTesting(
317 base::FilePath(FILE_PATH_LITERAL("F:\\mount2")),
318 "dcim:mount2", L"mount2", 100);
320 EXPECT_EQ(init_storages + 3, monitor_->GetAllAvailableStorages().size());
323 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
324 base::FilePath(ASCIIToUTF16("F:\\dir")), &info));
325 EXPECT_EQ(L"", info.name());
326 EXPECT_EQ(L"F:\\ Drive", info.storage_label());
327 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
328 base::FilePath(ASCIIToUTF16("F:\\mount1")), &info));
329 EXPECT_EQ(L"mount1", info.name());
330 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
331 base::FilePath(ASCIIToUTF16("F:\\mount1\\dir")), &info));
332 EXPECT_EQ(L"mount1", info.name());
333 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
334 base::FilePath(ASCIIToUTF16("F:\\mount2\\dir")), &info));
335 EXPECT_EQ(L"mount2", info.name());
336 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
337 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir")), &info));
338 EXPECT_EQ(L"mount1subdir", info.name());
339 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
340 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir")), &info));
341 EXPECT_EQ(L"mount1subdir", info.name());
342 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
343 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir\\dir")), &info));
344 EXPECT_EQ(L"mount1subdir", info.name());
347 TEST_F(StorageMonitorWinTest, DevicesAttachedHighBoundary) {
348 DeviceIndices device_indices;
349 device_indices.push_back(25);
351 DoMassStorageDeviceAttachedTest(device_indices);
354 TEST_F(StorageMonitorWinTest, DevicesAttachedLowBoundary) {
355 DeviceIndices device_indices;
356 device_indices.push_back(0);
358 DoMassStorageDeviceAttachedTest(device_indices);
361 TEST_F(StorageMonitorWinTest, DevicesAttachedAdjacentBits) {
362 DeviceIndices device_indices;
363 device_indices.push_back(0);
364 device_indices.push_back(1);
365 device_indices.push_back(2);
366 device_indices.push_back(3);
368 DoMassStorageDeviceAttachedTest(device_indices);
371 TEST_F(StorageMonitorWinTest, DevicesDetached) {
374 DeviceIndices device_indices;
375 device_indices.push_back(1);
376 device_indices.push_back(5);
377 device_indices.push_back(7);
378 device_indices.push_back(13);
380 DoMassStorageDevicesDetachedTest(device_indices);
383 TEST_F(StorageMonitorWinTest, DevicesDetachedHighBoundary) {
386 DeviceIndices device_indices;
387 device_indices.push_back(25);
389 DoMassStorageDevicesDetachedTest(device_indices);
392 TEST_F(StorageMonitorWinTest, DevicesDetachedLowBoundary) {
395 DeviceIndices device_indices;
396 device_indices.push_back(0);
398 DoMassStorageDevicesDetachedTest(device_indices);
401 TEST_F(StorageMonitorWinTest, DevicesDetachedAdjacentBits) {
404 DeviceIndices device_indices;
405 device_indices.push_back(0);
406 device_indices.push_back(1);
407 device_indices.push_back(2);
408 device_indices.push_back(3);
410 DoMassStorageDevicesDetachedTest(device_indices);
413 TEST_F(StorageMonitorWinTest, DuplicateAttachCheckSuppressed) {
414 // Make sure the original C: mount notification makes it all the
417 volume_mount_watcher_->FlushWorkerPoolForTesting();
420 volume_mount_watcher_->BlockDeviceCheckForTesting();
421 base::FilePath kAttachedDevicePath =
422 VolumeMountWatcherWin::DriveNumberToFilePath(8); // I:
424 DEV_BROADCAST_VOLUME volume_broadcast;
425 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
426 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
427 volume_broadcast.dbcv_flags = 0x0;
428 volume_broadcast.dbcv_unitmask = 0x100; // I: drive
429 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
430 reinterpret_cast<DWORD>(&volume_broadcast));
432 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
434 // Re-attach the same volume. We haven't released the mock device check
435 // event, so there'll be pending calls in the UI thread to finish the
436 // device check notification, blocking the duplicate device injection.
437 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
438 reinterpret_cast<DWORD>(&volume_broadcast));
440 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
441 volume_mount_watcher_->ReleaseDeviceCheck();
443 volume_mount_watcher_->ReleaseDeviceCheck();
445 // Now let all attach notifications finish running. We'll only get one
446 // finish-attach call.
447 volume_mount_watcher_->FlushWorkerPoolForTesting();
450 const std::vector<base::FilePath>& checked_devices =
451 volume_mount_watcher_->devices_checked();
452 ASSERT_EQ(1u, checked_devices.size());
453 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]);
455 // We'll receive a duplicate check now that the first check has fully cleared.
456 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
457 reinterpret_cast<DWORD>(&volume_broadcast));
458 volume_mount_watcher_->FlushWorkerPoolForTesting();
459 volume_mount_watcher_->ReleaseDeviceCheck();
462 ASSERT_EQ(2u, checked_devices.size());
463 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]);
464 EXPECT_EQ(kAttachedDevicePath, checked_devices[1]);
467 TEST_F(StorageMonitorWinTest, DeviceInfoForPath) {
470 StorageInfo device_info;
472 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"COM1:\\"),
475 // An unconnected removable device.
476 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"E:\\"),
479 // A connected removable device.
480 base::FilePath removable_device(L"F:\\");
481 EXPECT_TRUE(monitor_->GetStorageInfoForPath(removable_device, &device_info));
484 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(removable_device, &info));
485 EXPECT_TRUE(StorageInfo::IsRemovableDevice(info.device_id()));
486 EXPECT_EQ(info.device_id(), device_info.device_id());
487 EXPECT_EQ(info.name(), device_info.name());
488 EXPECT_EQ(info.location(), device_info.location());
489 EXPECT_EQ(1000000, info.total_size_in_bytes());
492 base::FilePath fixed_device(L"N:\\");
493 EXPECT_TRUE(monitor_->GetStorageInfoForPath(fixed_device, &device_info));
495 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(
496 fixed_device, &info));
497 EXPECT_FALSE(StorageInfo::IsRemovableDevice(info.device_id()));
498 EXPECT_EQ(info.device_id(), device_info.device_id());
499 EXPECT_EQ(info.name(), device_info.name());
500 EXPECT_EQ(info.location(), device_info.location());
503 // Test to verify basic MTP storage attach and detach notifications.
504 TEST_F(StorageMonitorWinTest, MTPDeviceBasicAttachDetach) {
505 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true);
506 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false);
509 // When a MTP storage device with invalid storage label and id is
510 // attached/detached, there should not be any device attach/detach
512 TEST_F(StorageMonitorWinTest, MTPDeviceWithInvalidInfo) {
513 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo,
515 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo,
519 // Attach a device with two data partitions. Verify that attach/detach
520 // notifications are sent out for each removable storage.
521 TEST_F(StorageMonitorWinTest, MTPDeviceWithMultipleStorageObjects) {
522 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages,
524 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages,
528 TEST_F(StorageMonitorWinTest, DriveNumberToFilePath) {
529 EXPECT_EQ(L"A:\\", VolumeMountWatcherWin::DriveNumberToFilePath(0).value());
530 EXPECT_EQ(L"Y:\\", VolumeMountWatcherWin::DriveNumberToFilePath(24).value());
531 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(-1).value());
532 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(199).value());
535 // Given a MTP storage persistent id, GetMTPStorageInfo() should fetch the
536 // device interface path and local storage object identifier.
537 TEST_F(StorageMonitorWinTest, GetMTPStorageInfoFromDeviceId) {
538 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true);
539 PortableDeviceWatcherWin::StorageObjects storage_objects =
540 TestPortableDeviceWatcherWin::GetDeviceStorageObjects(
541 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo);
542 for (PortableDeviceWatcherWin::StorageObjects::const_iterator it =
543 storage_objects.begin();
544 it != storage_objects.end(); ++it) {
545 base::string16 pnp_device_id;
546 base::string16 storage_object_id;
547 ASSERT_TRUE(GetMTPStorageInfo(it->object_persistent_id, &pnp_device_id,
548 &storage_object_id));
549 base::string16 expected(
550 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo);
551 EXPECT_EQ(expected, pnp_device_id);
552 EXPECT_EQ(it->object_persistent_id,
553 TestPortableDeviceWatcherWin::GetMTPStorageUniqueId(
554 pnp_device_id, storage_object_id));
556 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false);