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.
5 // chromeos::StorageMonitorCros unit tests.
7 #include "components/storage_monitor/storage_monitor_chromeos.h"
9 #include "base/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chromeos/disks/mock_disk_mount_manager.h"
16 #include "components/storage_monitor/mock_removable_storage_observer.h"
17 #include "components/storage_monitor/removable_device_constants.h"
18 #include "components/storage_monitor/storage_info.h"
19 #include "components/storage_monitor/test_media_transfer_protocol_manager_linux.h"
20 #include "components/storage_monitor/test_storage_monitor.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 using content::BrowserThread;
30 using disks::DiskMountManager;
33 const char kDevice1[] = "/dev/d1";
34 const char kDevice1Name[] = "d1";
35 const char kDevice2[] = "/dev/disk/d2";
36 const char kDevice2Name[] = "d2";
37 const char kEmptyDeviceLabel[] = "";
38 const char kMountPointA[] = "mnt_a";
39 const char kMountPointB[] = "mnt_b";
40 const char kSDCardDeviceName1[] = "8.6 MB Amy_SD";
41 const char kSDCardDeviceName2[] = "8.6 MB SD Card";
42 const char kSDCardMountPoint1[] = "media/removable/Amy_SD";
43 const char kSDCardMountPoint2[] = "media/removable/SD Card";
44 const char kProductName[] = "Z101";
45 const char kUniqueId1[] = "FFFF-FFFF";
46 const char kUniqueId2[] = "FFFF-FF0F";
47 const char kVendorName[] = "CompanyA";
49 uint64 kDevice1SizeInBytes = 113048;
50 uint64 kDevice2SizeInBytes = 212312;
51 uint64 kSDCardSizeInBytes = 9000000;
53 std::string GetDCIMDeviceId(const std::string& unique_id) {
54 return StorageInfo::MakeDeviceId(
55 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
56 kFSUniqueIdPrefix + unique_id);
59 // A test version of StorageMonitorCros that exposes protected methods to tests.
60 class TestStorageMonitorCros : public StorageMonitorCros {
62 TestStorageMonitorCros() {}
64 virtual ~TestStorageMonitorCros() {}
66 virtual void Init() OVERRIDE {
67 SetMediaTransferProtocolManagerForTest(
68 new TestMediaTransferProtocolManagerLinux());
69 StorageMonitorCros::Init();
72 virtual void OnMountEvent(
73 disks::DiskMountManager::MountEvent event,
74 MountError error_code,
75 const disks::DiskMountManager::MountPointInfo& mount_info) OVERRIDE {
76 StorageMonitorCros::OnMountEvent(event, error_code, mount_info);
79 virtual bool GetStorageInfoForPath(const base::FilePath& path,
80 StorageInfo* device_info) const OVERRIDE {
81 return StorageMonitorCros::GetStorageInfoForPath(path, device_info);
83 virtual void EjectDevice(
84 const std::string& device_id,
85 base::Callback<void(EjectStatus)> callback) OVERRIDE {
86 StorageMonitorCros::EjectDevice(device_id, callback);
90 DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros);
93 // Wrapper class to test StorageMonitorCros.
94 class StorageMonitorCrosTest : public testing::Test {
96 StorageMonitorCrosTest();
97 virtual ~StorageMonitorCrosTest();
99 void EjectNotify(StorageMonitor::EjectStatus status);
103 virtual void SetUp() OVERRIDE;
104 virtual void TearDown() OVERRIDE;
106 void MountDevice(MountError error_code,
107 const DiskMountManager::MountPointInfo& mount_info,
108 const std::string& unique_id,
109 const std::string& device_label,
110 const std::string& vendor_name,
111 const std::string& product_name,
112 DeviceType device_type,
113 uint64 device_size_in_bytes);
115 void UnmountDevice(MountError error_code,
116 const DiskMountManager::MountPointInfo& mount_info);
118 uint64 GetDeviceStorageSize(const std::string& device_location);
120 // Create a directory named |dir| relative to the test directory.
121 // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
123 // Returns the full path to the created directory on success, or an empty
125 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir);
127 static void PostQuitToUIThread();
128 static void WaitForFileThread();
130 MockRemovableStorageObserver& observer() {
131 return *mock_storage_observer_;
134 TestStorageMonitorCros* monitor_;
136 // Owned by DiskMountManager.
137 disks::MockDiskMountManager* disk_mount_manager_mock_;
139 StorageMonitor::EjectStatus status_;
142 content::TestBrowserThreadBundle thread_bundle_;
144 // Temporary directory for created test data.
145 base::ScopedTempDir scoped_temp_dir_;
147 // Objects that talks with StorageMonitorCros.
148 scoped_ptr<MockRemovableStorageObserver> mock_storage_observer_;
150 DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest);
153 StorageMonitorCrosTest::StorageMonitorCrosTest()
155 disk_mount_manager_mock_(NULL),
156 status_(StorageMonitor::EJECT_FAILURE),
157 thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD) {
160 StorageMonitorCrosTest::~StorageMonitorCrosTest() {
163 void StorageMonitorCrosTest::SetUp() {
164 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
165 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
166 disk_mount_manager_mock_ = new disks::MockDiskMountManager();
167 DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
168 disk_mount_manager_mock_->SetupDefaultReplies();
170 mock_storage_observer_.reset(new MockRemovableStorageObserver);
172 // Initialize the test subject.
173 TestStorageMonitor::Destroy();
174 monitor_ = new TestStorageMonitorCros();
175 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
176 StorageMonitor::SetStorageMonitorForTesting(pass_monitor.Pass());
179 monitor_->AddObserver(mock_storage_observer_.get());
182 void StorageMonitorCrosTest::TearDown() {
183 monitor_->RemoveObserver(mock_storage_observer_.get());
186 disk_mount_manager_mock_ = NULL;
187 DiskMountManager::Shutdown();
191 void StorageMonitorCrosTest::MountDevice(
192 MountError error_code,
193 const DiskMountManager::MountPointInfo& mount_info,
194 const std::string& unique_id,
195 const std::string& device_label,
196 const std::string& vendor_name,
197 const std::string& product_name,
198 DeviceType device_type,
199 uint64 device_size_in_bytes) {
200 if (error_code == MOUNT_ERROR_NONE) {
201 disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
202 mount_info, unique_id, device_label, vendor_name, product_name,
203 device_type, device_size_in_bytes);
205 monitor_->OnMountEvent(disks::DiskMountManager::MOUNTING, error_code,
210 void StorageMonitorCrosTest::UnmountDevice(
211 MountError error_code,
212 const DiskMountManager::MountPointInfo& mount_info) {
213 monitor_->OnMountEvent(disks::DiskMountManager::UNMOUNTING, error_code,
215 if (error_code == MOUNT_ERROR_NONE)
216 disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info);
220 uint64 StorageMonitorCrosTest::GetDeviceStorageSize(
221 const std::string& device_location) {
223 if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info))
226 return info.total_size_in_bytes();
229 base::FilePath StorageMonitorCrosTest::CreateMountPoint(
230 const std::string& dir, bool with_dcim_dir) {
231 base::FilePath return_path(scoped_temp_dir_.path());
232 return_path = return_path.AppendASCII(dir);
233 base::FilePath path(return_path);
235 path = path.Append(kDCIMDirectoryName);
236 if (!base::CreateDirectory(path))
237 return base::FilePath();
242 void StorageMonitorCrosTest::PostQuitToUIThread() {
243 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
244 base::MessageLoop::QuitClosure());
248 void StorageMonitorCrosTest::WaitForFileThread() {
249 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
250 base::Bind(&PostQuitToUIThread));
251 base::MessageLoop::current()->Run();
254 void StorageMonitorCrosTest::EjectNotify(StorageMonitor::EjectStatus status) {
258 // Simple test case where we attach and detach a media device.
259 TEST_F(StorageMonitorCrosTest, BasicAttachDetach) {
260 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
261 ASSERT_FALSE(mount_path1.empty());
262 DiskMountManager::MountPointInfo mount_info(kDevice1,
265 disks::MOUNT_CONDITION_NONE);
266 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kDevice1Name,
267 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
268 EXPECT_EQ(1, observer().attach_calls());
269 EXPECT_EQ(0, observer().detach_calls());
270 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
271 observer().last_attached().device_id());
272 EXPECT_EQ(base::string16(), observer().last_attached().name());
273 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
275 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
276 EXPECT_EQ(1, observer().attach_calls());
277 EXPECT_EQ(1, observer().detach_calls());
278 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
279 observer().last_detached().device_id());
281 base::FilePath mount_path2 = CreateMountPoint(kMountPointB, true);
282 ASSERT_FALSE(mount_path2.empty());
283 DiskMountManager::MountPointInfo mount_info2(kDevice2,
286 disks::MOUNT_CONDITION_NONE);
287 MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kDevice2Name,
288 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice2SizeInBytes);
289 EXPECT_EQ(2, observer().attach_calls());
290 EXPECT_EQ(1, observer().detach_calls());
291 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
292 observer().last_attached().device_id());
293 EXPECT_EQ(base::string16(), observer().last_attached().name());
294 EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
296 UnmountDevice(MOUNT_ERROR_NONE, mount_info2);
297 EXPECT_EQ(2, observer().attach_calls());
298 EXPECT_EQ(2, observer().detach_calls());
299 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
300 observer().last_detached().device_id());
303 // Removable mass storage devices with no dcim folder are also recognized.
304 TEST_F(StorageMonitorCrosTest, NoDCIM) {
305 testing::Sequence mock_sequence;
306 base::FilePath mount_path = CreateMountPoint(kMountPointA, false);
307 const std::string kUniqueId = "FFFF-FFFF";
308 ASSERT_FALSE(mount_path.empty());
309 DiskMountManager::MountPointInfo mount_info(kDevice1,
312 disks::MOUNT_CONDITION_NONE);
313 const std::string device_id = StorageInfo::MakeDeviceId(
314 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
315 kFSUniqueIdPrefix + kUniqueId);
316 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
317 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
318 EXPECT_EQ(1, observer().attach_calls());
319 EXPECT_EQ(0, observer().detach_calls());
320 EXPECT_EQ(device_id, observer().last_attached().device_id());
321 EXPECT_EQ(base::string16(), observer().last_attached().name());
322 EXPECT_EQ(mount_path.value(), observer().last_attached().location());
325 // Non device mounts and mount errors are ignored.
326 TEST_F(StorageMonitorCrosTest, Ignore) {
327 testing::Sequence mock_sequence;
328 base::FilePath mount_path = CreateMountPoint(kMountPointA, true);
329 const std::string kUniqueId = "FFFF-FFFF";
330 ASSERT_FALSE(mount_path.empty());
333 DiskMountManager::MountPointInfo mount_info(kDevice1,
336 disks::MOUNT_CONDITION_NONE);
337 MountDevice(MOUNT_ERROR_UNKNOWN, mount_info, kUniqueId, kDevice1Name,
338 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
339 EXPECT_EQ(0, observer().attach_calls());
340 EXPECT_EQ(0, observer().detach_calls());
343 mount_info.mount_type = MOUNT_TYPE_ARCHIVE;
344 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
345 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
346 EXPECT_EQ(0, observer().attach_calls());
347 EXPECT_EQ(0, observer().detach_calls());
349 // Unsupported file system.
350 mount_info.mount_type = MOUNT_TYPE_DEVICE;
351 mount_info.mount_condition = disks::MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
352 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
353 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
354 EXPECT_EQ(0, observer().attach_calls());
355 EXPECT_EQ(0, observer().detach_calls());
358 TEST_F(StorageMonitorCrosTest, SDCardAttachDetach) {
359 base::FilePath mount_path1 = CreateMountPoint(kSDCardMountPoint1, true);
360 ASSERT_FALSE(mount_path1.empty());
361 DiskMountManager::MountPointInfo mount_info1(kSDCardDeviceName1,
364 disks::MOUNT_CONDITION_NONE);
365 MountDevice(MOUNT_ERROR_NONE, mount_info1, kUniqueId2, kSDCardDeviceName1,
366 kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes);
367 EXPECT_EQ(1, observer().attach_calls());
368 EXPECT_EQ(0, observer().detach_calls());
369 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
370 observer().last_attached().device_id());
371 EXPECT_EQ(base::string16(), observer().last_attached().name());
372 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
374 UnmountDevice(MOUNT_ERROR_NONE, mount_info1);
375 EXPECT_EQ(1, observer().attach_calls());
376 EXPECT_EQ(1, observer().detach_calls());
377 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
378 observer().last_detached().device_id());
380 base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true);
381 ASSERT_FALSE(mount_path2.empty());
382 DiskMountManager::MountPointInfo mount_info2(kSDCardDeviceName2,
385 disks::MOUNT_CONDITION_NONE);
386 MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kSDCardDeviceName2,
387 kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes);
388 EXPECT_EQ(2, observer().attach_calls());
389 EXPECT_EQ(1, observer().detach_calls());
390 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
391 observer().last_attached().device_id());
392 EXPECT_EQ(base::string16(), observer().last_attached().name());
393 EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
395 UnmountDevice(MOUNT_ERROR_NONE, mount_info2);
396 EXPECT_EQ(2, observer().attach_calls());
397 EXPECT_EQ(2, observer().detach_calls());
398 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
399 observer().last_detached().device_id());
402 TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) {
403 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
404 ASSERT_FALSE(mount_path1.empty());
405 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
408 disks::MOUNT_CONDITION_NONE);
409 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
410 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
411 EXPECT_EQ(1, observer().attach_calls());
412 EXPECT_EQ(0, observer().detach_calls());
413 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
414 observer().last_attached().device_id());
415 EXPECT_EQ(base::string16(), observer().last_attached().name());
416 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
418 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
419 EXPECT_EQ(1, observer().attach_calls());
420 EXPECT_EQ(1, observer().detach_calls());
421 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
422 observer().last_detached().device_id());
425 TEST_F(StorageMonitorCrosTest, GetStorageSize) {
426 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
427 ASSERT_FALSE(mount_path1.empty());
428 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
431 disks::MOUNT_CONDITION_NONE);
432 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
433 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
434 EXPECT_EQ(1, observer().attach_calls());
435 EXPECT_EQ(0, observer().detach_calls());
436 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
437 observer().last_attached().device_id());
438 EXPECT_EQ(base::string16(), observer().last_attached().name());
439 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
441 EXPECT_EQ(kDevice1SizeInBytes, GetDeviceStorageSize(mount_path1.value()));
442 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
443 EXPECT_EQ(1, observer().attach_calls());
444 EXPECT_EQ(1, observer().detach_calls());
445 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
446 observer().last_detached().device_id());
449 void UnmountFake(const std::string& location,
450 UnmountOptions options,
451 const DiskMountManager::UnmountPathCallback& cb) {
452 cb.Run(chromeos::MOUNT_ERROR_NONE);
455 TEST_F(StorageMonitorCrosTest, EjectTest) {
456 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
457 ASSERT_FALSE(mount_path1.empty());
458 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
461 disks::MOUNT_CONDITION_NONE);
462 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
463 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
464 EXPECT_EQ(1, observer().attach_calls());
465 EXPECT_EQ(0, observer().detach_calls());
467 ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _))
468 .WillByDefault(testing::Invoke(&UnmountFake));
469 EXPECT_CALL(*disk_mount_manager_mock_,
470 UnmountPath(observer().last_attached().location(), _, _));
471 monitor_->EjectDevice(observer().last_attached().device_id(),
472 base::Bind(&StorageMonitorCrosTest::EjectNotify,
473 base::Unretained(this)));
474 base::RunLoop().RunUntilIdle();
476 EXPECT_EQ(StorageMonitor::EJECT_OK, status_);
481 } // namespace chromeos