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 content::BrowserThread;
33 typedef std::vector<int> DeviceIndices;
35 // StorageMonitorWinTest -------------------------------------------------------
37 class StorageMonitorWinTest : public testing::Test {
39 StorageMonitorWinTest();
40 virtual ~StorageMonitorWinTest();
44 virtual void SetUp() OVERRIDE;
45 virtual void TearDown() OVERRIDE;
47 void PreAttachDevices();
49 // Runs all the pending tasks on UI thread, FILE thread and blocking thread.
52 void DoMassStorageDeviceAttachedTest(const DeviceIndices& device_indices);
53 void DoMassStorageDevicesDetachedTest(const DeviceIndices& device_indices);
55 // Injects a device attach or detach change (depending on the value of
56 // |test_attach|) and tests that the appropriate handler is called.
57 void DoMTPDeviceTest(const base::string16& pnp_device_id, bool test_attach);
59 // Gets the MTP details of the storage specified by the |storage_device_id|.
60 // On success, returns true and fills in |pnp_device_id| and
61 // |storage_object_id|.
62 bool GetMTPStorageInfo(const std::string& storage_device_id,
63 base::string16* pnp_device_id,
64 base::string16* storage_object_id);
66 TestStorageMonitorWin* monitor_;
68 // Weak pointer; owned by the device notifications class.
69 TestVolumeMountWatcherWin* volume_mount_watcher_;
71 MockRemovableStorageObserver observer_;
74 content::TestBrowserThreadBundle thread_bundle_;
76 DISALLOW_COPY_AND_ASSIGN(StorageMonitorWinTest);
79 StorageMonitorWinTest::StorageMonitorWinTest() {
82 StorageMonitorWinTest::~StorageMonitorWinTest() {
85 void StorageMonitorWinTest::SetUp() {
86 TestStorageMonitor::RemoveSingleton();
87 volume_mount_watcher_ = new TestVolumeMountWatcherWin;
88 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_,
89 new TestPortableDeviceWatcherWin);
90 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
91 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
92 DCHECK(browser_process);
93 browser_process->SetStorageMonitor(pass_monitor.Pass());
97 monitor_->AddObserver(&observer_);
100 void StorageMonitorWinTest::TearDown() {
102 monitor_->RemoveObserver(&observer_);
103 volume_mount_watcher_->ShutdownWorkerPool();
106 // Windows storage monitor must be destroyed on the same thread
108 TestStorageMonitor::RemoveSingleton();
111 void StorageMonitorWinTest::PreAttachDevices() {
112 TestStorageMonitor::RemoveSingleton();
114 volume_mount_watcher_ = new TestVolumeMountWatcherWin;
115 volume_mount_watcher_->SetAttachedDevicesFake();
117 int expect_attach_calls = 0;
118 std::vector<base::FilePath> initial_devices =
119 volume_mount_watcher_->GetAttachedDevicesCallback().Run();
120 for (std::vector<base::FilePath>::const_iterator it = initial_devices.begin();
121 it != initial_devices.end(); ++it) {
123 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable(*it, &removable));
125 expect_attach_calls++;
128 monitor_ = new TestStorageMonitorWin(volume_mount_watcher_,
129 new TestPortableDeviceWatcherWin);
130 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
131 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
132 DCHECK(browser_process);
133 browser_process->SetStorageMonitor(pass_monitor.Pass());
135 monitor_->AddObserver(&observer_);
138 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
140 // This dance is because attachment bounces through a couple of
141 // closures, which need to be executed to finish the process.
143 volume_mount_watcher_->FlushWorkerPoolForTesting();
146 std::vector<base::FilePath> checked_devices =
147 volume_mount_watcher_->devices_checked();
148 sort(checked_devices.begin(), checked_devices.end());
149 EXPECT_EQ(initial_devices, checked_devices);
150 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
151 EXPECT_EQ(0, observer_.detach_calls());
154 void StorageMonitorWinTest::RunUntilIdle() {
155 volume_mount_watcher_->FlushWorkerPoolForTesting();
156 base::RunLoop().RunUntilIdle();
159 void StorageMonitorWinTest::DoMassStorageDeviceAttachedTest(
160 const DeviceIndices& device_indices) {
161 DEV_BROADCAST_VOLUME volume_broadcast;
162 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
163 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
164 volume_broadcast.dbcv_unitmask = 0x0;
165 volume_broadcast.dbcv_flags = 0x0;
167 int expect_attach_calls = observer_.attach_calls();
168 for (DeviceIndices::const_iterator it = device_indices.begin();
169 it != device_indices.end(); ++it) {
170 volume_broadcast.dbcv_unitmask |= 0x1 << *it;
172 ASSERT_TRUE(volume_mount_watcher_->GetDeviceRemovable(
173 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &removable));
175 expect_attach_calls++;
177 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
178 reinterpret_cast<DWORD>(&volume_broadcast));
181 volume_mount_watcher_->FlushWorkerPoolForTesting();
184 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
185 EXPECT_EQ(0, observer_.detach_calls());
188 void StorageMonitorWinTest::DoMassStorageDevicesDetachedTest(
189 const DeviceIndices& device_indices) {
190 DEV_BROADCAST_VOLUME volume_broadcast;
191 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
192 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
193 volume_broadcast.dbcv_unitmask = 0x0;
194 volume_broadcast.dbcv_flags = 0x0;
196 int pre_attach_calls = observer_.attach_calls();
197 int expect_detach_calls = 0;
198 for (DeviceIndices::const_iterator it = device_indices.begin();
199 it != device_indices.end(); ++it) {
200 volume_broadcast.dbcv_unitmask |= 0x1 << *it;
202 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(
203 VolumeMountWatcherWin::DriveNumberToFilePath(*it), &info));
204 if (StorageInfo::IsRemovableDevice(info.device_id()))
205 ++expect_detach_calls;
207 monitor_->InjectDeviceChange(DBT_DEVICEREMOVECOMPLETE,
208 reinterpret_cast<DWORD>(&volume_broadcast));
210 EXPECT_EQ(pre_attach_calls, observer_.attach_calls());
211 EXPECT_EQ(expect_detach_calls, observer_.detach_calls());
214 void StorageMonitorWinTest::DoMTPDeviceTest(const base::string16& pnp_device_id,
216 GUID guidDevInterface = GUID_NULL;
217 HRESULT hr = CLSIDFromString(kWPDDevInterfaceGUID, &guidDevInterface);
221 size_t device_id_size = pnp_device_id.size() * sizeof(char16);
222 size_t size = sizeof(DEV_BROADCAST_DEVICEINTERFACE) + device_id_size;
223 scoped_ptr_malloc<DEV_BROADCAST_DEVICEINTERFACE> dev_interface_broadcast(
224 static_cast<DEV_BROADCAST_DEVICEINTERFACE*>(malloc(size)));
225 DCHECK(dev_interface_broadcast.get());
226 ZeroMemory(dev_interface_broadcast.get(), size);
227 dev_interface_broadcast->dbcc_size = size;
228 dev_interface_broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
229 dev_interface_broadcast->dbcc_classguid = guidDevInterface;
230 memcpy(dev_interface_broadcast->dbcc_name, pnp_device_id.data(),
233 int expect_attach_calls = observer_.attach_calls();
234 int expect_detach_calls = observer_.detach_calls();
235 PortableDeviceWatcherWin::StorageObjectIDs storage_object_ids =
236 TestPortableDeviceWatcherWin::GetMTPStorageObjectIds(pnp_device_id);
237 for (PortableDeviceWatcherWin::StorageObjectIDs::const_iterator it =
238 storage_object_ids.begin(); it != storage_object_ids.end(); ++it) {
239 std::string unique_id;
241 base::string16 location;
242 TestPortableDeviceWatcherWin::GetMTPStorageDetails(pnp_device_id, *it,
243 &location, &unique_id,
245 if (test_attach && !name.empty() && !unique_id.empty())
246 expect_attach_calls++;
247 else if (!name.empty() && !unique_id.empty())
248 expect_detach_calls++;
251 monitor_->InjectDeviceChange(
252 test_attach ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE,
253 reinterpret_cast<DWORD>(dev_interface_broadcast.get()));
256 EXPECT_EQ(expect_attach_calls, observer_.attach_calls());
257 EXPECT_EQ(expect_detach_calls, observer_.detach_calls());
260 bool StorageMonitorWinTest::GetMTPStorageInfo(
261 const std::string& storage_device_id,
262 base::string16* pnp_device_id,
263 base::string16* storage_object_id) {
264 return monitor_->GetMTPStorageInfoFromDeviceId(storage_device_id,
269 TEST_F(StorageMonitorWinTest, RandomMessage) {
270 monitor_->InjectDeviceChange(DBT_DEVICEQUERYREMOVE, NULL);
274 TEST_F(StorageMonitorWinTest, DevicesAttached) {
275 DeviceIndices device_indices;
276 device_indices.push_back(1); // B
277 device_indices.push_back(5); // F
278 device_indices.push_back(7); // H
279 device_indices.push_back(13); // N
280 DoMassStorageDeviceAttachedTest(device_indices);
283 EXPECT_TRUE(monitor_->volume_mount_watcher()->GetDeviceInfo(
284 base::FilePath(ASCIIToUTF16("F:\\")), &info));
285 EXPECT_EQ(ASCIIToUTF16("F:\\"), info.location());
286 EXPECT_EQ("dcim:\\\\?\\Volume{F0000000-0000-0000-0000-000000000000}\\",
288 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label());
290 EXPECT_FALSE(monitor_->GetStorageInfoForPath(
291 base::FilePath(ASCIIToUTF16("G:\\")), &info));
292 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
293 base::FilePath(ASCIIToUTF16("F:\\")), &info));
295 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
296 base::FilePath(ASCIIToUTF16("F:\\subdir")), &info1));
298 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
299 base::FilePath(ASCIIToUTF16("F:\\subdir\\sub")), &info2));
300 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info.storage_label());
301 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info1.storage_label());
302 EXPECT_EQ(ASCIIToUTF16("F:\\ Drive"), info2.storage_label());
305 TEST_F(StorageMonitorWinTest, PathMountDevices) {
307 int init_storages = monitor_->GetAllAvailableStorages().size();
309 volume_mount_watcher_->AddDeviceForTesting(
310 base::FilePath(FILE_PATH_LITERAL("F:\\mount1")),
311 "dcim:mount1", L"mount1", 100);
312 volume_mount_watcher_->AddDeviceForTesting(
313 base::FilePath(FILE_PATH_LITERAL("F:\\mount1\\subdir")),
314 "dcim:mount1subdir", L"mount1subdir", 100);
315 volume_mount_watcher_->AddDeviceForTesting(
316 base::FilePath(FILE_PATH_LITERAL("F:\\mount2")),
317 "dcim:mount2", L"mount2", 100);
319 EXPECT_EQ(init_storages + 3, monitor_->GetAllAvailableStorages().size());
322 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
323 base::FilePath(ASCIIToUTF16("F:\\dir")), &info));
324 EXPECT_EQ(L"", info.name());
325 EXPECT_EQ(L"F:\\ Drive", info.storage_label());
326 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
327 base::FilePath(ASCIIToUTF16("F:\\mount1")), &info));
328 EXPECT_EQ(L"mount1", info.name());
329 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
330 base::FilePath(ASCIIToUTF16("F:\\mount1\\dir")), &info));
331 EXPECT_EQ(L"mount1", info.name());
332 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
333 base::FilePath(ASCIIToUTF16("F:\\mount2\\dir")), &info));
334 EXPECT_EQ(L"mount2", info.name());
335 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
336 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir")), &info));
337 EXPECT_EQ(L"mount1subdir", info.name());
338 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
339 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir")), &info));
340 EXPECT_EQ(L"mount1subdir", info.name());
341 EXPECT_TRUE(monitor_->GetStorageInfoForPath(
342 base::FilePath(ASCIIToUTF16("F:\\mount1\\subdir\\dir\\dir")), &info));
343 EXPECT_EQ(L"mount1subdir", info.name());
346 TEST_F(StorageMonitorWinTest, DevicesAttachedHighBoundary) {
347 DeviceIndices device_indices;
348 device_indices.push_back(25);
350 DoMassStorageDeviceAttachedTest(device_indices);
353 TEST_F(StorageMonitorWinTest, DevicesAttachedLowBoundary) {
354 DeviceIndices device_indices;
355 device_indices.push_back(0);
357 DoMassStorageDeviceAttachedTest(device_indices);
360 TEST_F(StorageMonitorWinTest, DevicesAttachedAdjacentBits) {
361 DeviceIndices device_indices;
362 device_indices.push_back(0);
363 device_indices.push_back(1);
364 device_indices.push_back(2);
365 device_indices.push_back(3);
367 DoMassStorageDeviceAttachedTest(device_indices);
370 TEST_F(StorageMonitorWinTest, DevicesDetached) {
373 DeviceIndices device_indices;
374 device_indices.push_back(1);
375 device_indices.push_back(5);
376 device_indices.push_back(7);
377 device_indices.push_back(13);
379 DoMassStorageDevicesDetachedTest(device_indices);
382 TEST_F(StorageMonitorWinTest, DevicesDetachedHighBoundary) {
385 DeviceIndices device_indices;
386 device_indices.push_back(25);
388 DoMassStorageDevicesDetachedTest(device_indices);
391 TEST_F(StorageMonitorWinTest, DevicesDetachedLowBoundary) {
394 DeviceIndices device_indices;
395 device_indices.push_back(0);
397 DoMassStorageDevicesDetachedTest(device_indices);
400 TEST_F(StorageMonitorWinTest, DevicesDetachedAdjacentBits) {
403 DeviceIndices device_indices;
404 device_indices.push_back(0);
405 device_indices.push_back(1);
406 device_indices.push_back(2);
407 device_indices.push_back(3);
409 DoMassStorageDevicesDetachedTest(device_indices);
412 TEST_F(StorageMonitorWinTest, DuplicateAttachCheckSuppressed) {
413 // Make sure the original C: mount notification makes it all the
416 volume_mount_watcher_->FlushWorkerPoolForTesting();
419 volume_mount_watcher_->BlockDeviceCheckForTesting();
420 base::FilePath kAttachedDevicePath =
421 VolumeMountWatcherWin::DriveNumberToFilePath(8); // I:
423 DEV_BROADCAST_VOLUME volume_broadcast;
424 volume_broadcast.dbcv_size = sizeof(volume_broadcast);
425 volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
426 volume_broadcast.dbcv_flags = 0x0;
427 volume_broadcast.dbcv_unitmask = 0x100; // I: drive
428 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
429 reinterpret_cast<DWORD>(&volume_broadcast));
431 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
433 // Re-attach the same volume. We haven't released the mock device check
434 // event, so there'll be pending calls in the UI thread to finish the
435 // device check notification, blocking the duplicate device injection.
436 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
437 reinterpret_cast<DWORD>(&volume_broadcast));
439 EXPECT_EQ(0u, volume_mount_watcher_->devices_checked().size());
440 volume_mount_watcher_->ReleaseDeviceCheck();
442 volume_mount_watcher_->ReleaseDeviceCheck();
444 // Now let all attach notifications finish running. We'll only get one
445 // finish-attach call.
446 volume_mount_watcher_->FlushWorkerPoolForTesting();
449 const std::vector<base::FilePath>& checked_devices =
450 volume_mount_watcher_->devices_checked();
451 ASSERT_EQ(1u, checked_devices.size());
452 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]);
454 // We'll receive a duplicate check now that the first check has fully cleared.
455 monitor_->InjectDeviceChange(DBT_DEVICEARRIVAL,
456 reinterpret_cast<DWORD>(&volume_broadcast));
457 volume_mount_watcher_->FlushWorkerPoolForTesting();
458 volume_mount_watcher_->ReleaseDeviceCheck();
461 ASSERT_EQ(2u, checked_devices.size());
462 EXPECT_EQ(kAttachedDevicePath, checked_devices[0]);
463 EXPECT_EQ(kAttachedDevicePath, checked_devices[1]);
466 TEST_F(StorageMonitorWinTest, DeviceInfoForPath) {
469 StorageInfo device_info;
471 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"COM1:\\"),
474 // An unconnected removable device.
475 EXPECT_FALSE(monitor_->GetStorageInfoForPath(base::FilePath(L"E:\\"),
478 // A connected removable device.
479 base::FilePath removable_device(L"F:\\");
480 EXPECT_TRUE(monitor_->GetStorageInfoForPath(removable_device, &device_info));
483 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(removable_device, &info));
484 EXPECT_TRUE(StorageInfo::IsRemovableDevice(info.device_id()));
485 EXPECT_EQ(info.device_id(), device_info.device_id());
486 EXPECT_EQ(info.name(), device_info.name());
487 EXPECT_EQ(info.location(), device_info.location());
488 EXPECT_EQ(1000000, info.total_size_in_bytes());
491 base::FilePath fixed_device(L"N:\\");
492 EXPECT_TRUE(monitor_->GetStorageInfoForPath(fixed_device, &device_info));
494 ASSERT_TRUE(volume_mount_watcher_->GetDeviceInfo(
495 fixed_device, &info));
496 EXPECT_FALSE(StorageInfo::IsRemovableDevice(info.device_id()));
497 EXPECT_EQ(info.device_id(), device_info.device_id());
498 EXPECT_EQ(info.name(), device_info.name());
499 EXPECT_EQ(info.location(), device_info.location());
502 // Test to verify basic MTP storage attach and detach notifications.
503 TEST_F(StorageMonitorWinTest, MTPDeviceBasicAttachDetach) {
504 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true);
505 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false);
508 // When a MTP storage device with invalid storage label and id is
509 // attached/detached, there should not be any device attach/detach
511 TEST_F(StorageMonitorWinTest, MTPDeviceWithInvalidInfo) {
512 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo,
514 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithInvalidInfo,
518 // Attach a device with two data partitions. Verify that attach/detach
519 // notifications are sent out for each removable storage.
520 TEST_F(StorageMonitorWinTest, MTPDeviceWithMultipleStorageObjects) {
521 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages,
523 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithMultipleStorages,
527 TEST_F(StorageMonitorWinTest, DriveNumberToFilePath) {
528 EXPECT_EQ(L"A:\\", VolumeMountWatcherWin::DriveNumberToFilePath(0).value());
529 EXPECT_EQ(L"Y:\\", VolumeMountWatcherWin::DriveNumberToFilePath(24).value());
530 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(-1).value());
531 EXPECT_EQ(L"", VolumeMountWatcherWin::DriveNumberToFilePath(199).value());
534 // Given a MTP storage persistent id, GetMTPStorageInfo() should fetch the
535 // device interface path and local storage object identifier.
536 TEST_F(StorageMonitorWinTest, GetMTPStorageInfoFromDeviceId) {
537 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, true);
538 PortableDeviceWatcherWin::StorageObjects storage_objects =
539 TestPortableDeviceWatcherWin::GetDeviceStorageObjects(
540 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo);
541 for (PortableDeviceWatcherWin::StorageObjects::const_iterator it =
542 storage_objects.begin();
543 it != storage_objects.end(); ++it) {
544 base::string16 pnp_device_id;
545 base::string16 storage_object_id;
546 ASSERT_TRUE(GetMTPStorageInfo(it->object_persistent_id, &pnp_device_id,
547 &storage_object_id));
548 base::string16 expected(
549 TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo);
550 EXPECT_EQ(expected, pnp_device_id);
551 EXPECT_EQ(it->object_persistent_id,
552 TestPortableDeviceWatcherWin::GetMTPStorageUniqueId(
553 pnp_device_id, storage_object_id));
555 DoMTPDeviceTest(TestPortableDeviceWatcherWin::kMTPDeviceWithValidInfo, false);