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.
5 // chromeos::StorageMonitorCros unit tests.
7 #include "chrome/browser/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 "chrome/browser/storage_monitor/mock_removable_storage_observer.h"
16 #include "chrome/browser/storage_monitor/removable_device_constants.h"
17 #include "chrome/browser/storage_monitor/storage_info.h"
18 #include "chrome/browser/storage_monitor/test_media_transfer_protocol_manager_linux.h"
19 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
20 #include "chrome/test/base/testing_browser_process.h"
21 #include "chromeos/disks/mock_disk_mount_manager.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/test/test_browser_thread_bundle.h"
24 #include "testing/gtest/include/gtest/gtest.h"
30 using content::BrowserThread;
31 using disks::DiskMountManager;
34 const char kDevice1[] = "/dev/d1";
35 const char kDevice1Name[] = "d1";
36 const char kDevice2[] = "/dev/disk/d2";
37 const char kDevice2Name[] = "d2";
38 const char kEmptyDeviceLabel[] = "";
39 const char kMountPointA[] = "mnt_a";
40 const char kMountPointB[] = "mnt_b";
41 const char kSDCardDeviceName1[] = "8.6 MB Amy_SD";
42 const char kSDCardDeviceName2[] = "8.6 MB SD Card";
43 const char kSDCardMountPoint1[] = "media/removable/Amy_SD";
44 const char kSDCardMountPoint2[] = "media/removable/SD Card";
45 const char kProductName[] = "Z101";
46 const char kUniqueId1[] = "FFFF-FFFF";
47 const char kUniqueId2[] = "FFFF-FF0F";
48 const char kVendorName[] = "CompanyA";
50 uint64 kDevice1SizeInBytes = 113048;
51 uint64 kDevice2SizeInBytes = 212312;
52 uint64 kSDCardSizeInBytes = 9000000;
54 std::string GetDCIMDeviceId(const std::string& unique_id) {
55 return StorageInfo::MakeDeviceId(
56 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
57 kFSUniqueIdPrefix + unique_id);
60 // A test version of StorageMonitorCros that exposes protected methods to tests.
61 class TestStorageMonitorCros : public StorageMonitorCros {
63 TestStorageMonitorCros() {}
65 virtual ~TestStorageMonitorCros() {}
67 virtual void Init() OVERRIDE {
68 SetMediaTransferProtocolManagerForTest(
69 new TestMediaTransferProtocolManagerLinux());
70 StorageMonitorCros::Init();
73 virtual void OnMountEvent(
74 disks::DiskMountManager::MountEvent event,
75 MountError error_code,
76 const disks::DiskMountManager::MountPointInfo& mount_info) OVERRIDE {
77 StorageMonitorCros::OnMountEvent(event, error_code, mount_info);
80 virtual bool GetStorageInfoForPath(const base::FilePath& path,
81 StorageInfo* device_info) const OVERRIDE {
82 return StorageMonitorCros::GetStorageInfoForPath(path, device_info);
84 virtual void EjectDevice(
85 const std::string& device_id,
86 base::Callback<void(EjectStatus)> callback) OVERRIDE {
87 StorageMonitorCros::EjectDevice(device_id, callback);
91 DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros);
94 // Wrapper class to test StorageMonitorCros.
95 class StorageMonitorCrosTest : public testing::Test {
97 StorageMonitorCrosTest();
98 virtual ~StorageMonitorCrosTest();
100 void EjectNotify(StorageMonitor::EjectStatus status);
104 virtual void SetUp() OVERRIDE;
105 virtual void TearDown() OVERRIDE;
107 void MountDevice(MountError error_code,
108 const DiskMountManager::MountPointInfo& mount_info,
109 const std::string& unique_id,
110 const std::string& device_label,
111 const std::string& vendor_name,
112 const std::string& product_name,
113 DeviceType device_type,
114 uint64 device_size_in_bytes);
116 void UnmountDevice(MountError error_code,
117 const DiskMountManager::MountPointInfo& mount_info);
119 uint64 GetDeviceStorageSize(const std::string& device_location);
121 // Create a directory named |dir| relative to the test directory.
122 // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
124 // Returns the full path to the created directory on success, or an empty
126 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir);
128 static void PostQuitToUIThread();
129 static void WaitForFileThread();
131 MockRemovableStorageObserver& observer() {
132 return *mock_storage_observer_;
135 TestStorageMonitorCros* monitor_;
137 // Owned by DiskMountManager.
138 disks::MockDiskMountManager* disk_mount_manager_mock_;
140 StorageMonitor::EjectStatus status_;
143 content::TestBrowserThreadBundle thread_bundle_;
145 // Temporary directory for created test data.
146 base::ScopedTempDir scoped_temp_dir_;
148 // Objects that talks with StorageMonitorCros.
149 scoped_ptr<MockRemovableStorageObserver> mock_storage_observer_;
151 DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest);
154 StorageMonitorCrosTest::StorageMonitorCrosTest()
156 disk_mount_manager_mock_(NULL),
157 status_(StorageMonitor::EJECT_FAILURE),
158 thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD) {
161 StorageMonitorCrosTest::~StorageMonitorCrosTest() {
164 void StorageMonitorCrosTest::SetUp() {
165 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
166 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
167 disk_mount_manager_mock_ = new disks::MockDiskMountManager();
168 DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
169 disk_mount_manager_mock_->SetupDefaultReplies();
171 mock_storage_observer_.reset(new MockRemovableStorageObserver);
173 // Initialize the test subject.
174 TestStorageMonitor::RemoveSingleton();
175 monitor_ = new TestStorageMonitorCros();
176 scoped_ptr<StorageMonitor> pass_monitor(monitor_);
177 TestingBrowserProcess* browser_process = TestingBrowserProcess::GetGlobal();
178 DCHECK(browser_process);
179 browser_process->SetStorageMonitor(pass_monitor.Pass());
182 monitor_->AddObserver(mock_storage_observer_.get());
185 void StorageMonitorCrosTest::TearDown() {
186 monitor_->RemoveObserver(mock_storage_observer_.get());
189 disk_mount_manager_mock_ = NULL;
190 DiskMountManager::Shutdown();
194 void StorageMonitorCrosTest::MountDevice(
195 MountError error_code,
196 const DiskMountManager::MountPointInfo& mount_info,
197 const std::string& unique_id,
198 const std::string& device_label,
199 const std::string& vendor_name,
200 const std::string& product_name,
201 DeviceType device_type,
202 uint64 device_size_in_bytes) {
203 if (error_code == MOUNT_ERROR_NONE) {
204 disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
205 mount_info, unique_id, device_label, vendor_name, product_name,
206 device_type, device_size_in_bytes);
208 monitor_->OnMountEvent(disks::DiskMountManager::MOUNTING, error_code,
213 void StorageMonitorCrosTest::UnmountDevice(
214 MountError error_code,
215 const DiskMountManager::MountPointInfo& mount_info) {
216 monitor_->OnMountEvent(disks::DiskMountManager::UNMOUNTING, error_code,
218 if (error_code == MOUNT_ERROR_NONE)
219 disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info);
223 uint64 StorageMonitorCrosTest::GetDeviceStorageSize(
224 const std::string& device_location) {
226 if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info))
229 return info.total_size_in_bytes();
232 base::FilePath StorageMonitorCrosTest::CreateMountPoint(
233 const std::string& dir, bool with_dcim_dir) {
234 base::FilePath return_path(scoped_temp_dir_.path());
235 return_path = return_path.AppendASCII(dir);
236 base::FilePath path(return_path);
238 path = path.Append(kDCIMDirectoryName);
239 if (!base::CreateDirectory(path))
240 return base::FilePath();
245 void StorageMonitorCrosTest::PostQuitToUIThread() {
246 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
247 base::MessageLoop::QuitClosure());
251 void StorageMonitorCrosTest::WaitForFileThread() {
252 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
253 base::Bind(&PostQuitToUIThread));
254 base::MessageLoop::current()->Run();
257 void StorageMonitorCrosTest::EjectNotify(StorageMonitor::EjectStatus status) {
261 // Simple test case where we attach and detach a media device.
262 TEST_F(StorageMonitorCrosTest, BasicAttachDetach) {
263 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
264 ASSERT_FALSE(mount_path1.empty());
265 DiskMountManager::MountPointInfo mount_info(kDevice1,
268 disks::MOUNT_CONDITION_NONE);
269 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kDevice1Name,
270 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
271 EXPECT_EQ(1, observer().attach_calls());
272 EXPECT_EQ(0, observer().detach_calls());
273 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
274 observer().last_attached().device_id());
275 EXPECT_EQ(base::string16(), observer().last_attached().name());
276 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
278 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
279 EXPECT_EQ(1, observer().attach_calls());
280 EXPECT_EQ(1, observer().detach_calls());
281 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
282 observer().last_detached().device_id());
284 base::FilePath mount_path2 = CreateMountPoint(kMountPointB, true);
285 ASSERT_FALSE(mount_path2.empty());
286 DiskMountManager::MountPointInfo mount_info2(kDevice2,
289 disks::MOUNT_CONDITION_NONE);
290 MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kDevice2Name,
291 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice2SizeInBytes);
292 EXPECT_EQ(2, observer().attach_calls());
293 EXPECT_EQ(1, observer().detach_calls());
294 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
295 observer().last_attached().device_id());
296 EXPECT_EQ(base::string16(), observer().last_attached().name());
297 EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
299 UnmountDevice(MOUNT_ERROR_NONE, mount_info2);
300 EXPECT_EQ(2, observer().attach_calls());
301 EXPECT_EQ(2, observer().detach_calls());
302 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
303 observer().last_detached().device_id());
306 // Removable mass storage devices with no dcim folder are also recognized.
307 TEST_F(StorageMonitorCrosTest, NoDCIM) {
308 testing::Sequence mock_sequence;
309 base::FilePath mount_path = CreateMountPoint(kMountPointA, false);
310 const std::string kUniqueId = "FFFF-FFFF";
311 ASSERT_FALSE(mount_path.empty());
312 DiskMountManager::MountPointInfo mount_info(kDevice1,
315 disks::MOUNT_CONDITION_NONE);
316 const std::string device_id = StorageInfo::MakeDeviceId(
317 StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
318 kFSUniqueIdPrefix + kUniqueId);
319 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
320 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
321 EXPECT_EQ(1, observer().attach_calls());
322 EXPECT_EQ(0, observer().detach_calls());
323 EXPECT_EQ(device_id, observer().last_attached().device_id());
324 EXPECT_EQ(base::string16(), observer().last_attached().name());
325 EXPECT_EQ(mount_path.value(), observer().last_attached().location());
328 // Non device mounts and mount errors are ignored.
329 TEST_F(StorageMonitorCrosTest, Ignore) {
330 testing::Sequence mock_sequence;
331 base::FilePath mount_path = CreateMountPoint(kMountPointA, true);
332 const std::string kUniqueId = "FFFF-FFFF";
333 ASSERT_FALSE(mount_path.empty());
336 DiskMountManager::MountPointInfo mount_info(kDevice1,
339 disks::MOUNT_CONDITION_NONE);
340 MountDevice(MOUNT_ERROR_UNKNOWN, mount_info, kUniqueId, kDevice1Name,
341 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
342 EXPECT_EQ(0, observer().attach_calls());
343 EXPECT_EQ(0, observer().detach_calls());
346 mount_info.mount_type = MOUNT_TYPE_ARCHIVE;
347 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
348 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
349 EXPECT_EQ(0, observer().attach_calls());
350 EXPECT_EQ(0, observer().detach_calls());
352 // Unsupported file system.
353 mount_info.mount_type = MOUNT_TYPE_DEVICE;
354 mount_info.mount_condition = disks::MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
355 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
356 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
357 EXPECT_EQ(0, observer().attach_calls());
358 EXPECT_EQ(0, observer().detach_calls());
361 TEST_F(StorageMonitorCrosTest, SDCardAttachDetach) {
362 base::FilePath mount_path1 = CreateMountPoint(kSDCardMountPoint1, true);
363 ASSERT_FALSE(mount_path1.empty());
364 DiskMountManager::MountPointInfo mount_info1(kSDCardDeviceName1,
367 disks::MOUNT_CONDITION_NONE);
368 MountDevice(MOUNT_ERROR_NONE, mount_info1, kUniqueId2, kSDCardDeviceName1,
369 kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes);
370 EXPECT_EQ(1, observer().attach_calls());
371 EXPECT_EQ(0, observer().detach_calls());
372 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
373 observer().last_attached().device_id());
374 EXPECT_EQ(base::string16(), observer().last_attached().name());
375 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
377 UnmountDevice(MOUNT_ERROR_NONE, mount_info1);
378 EXPECT_EQ(1, observer().attach_calls());
379 EXPECT_EQ(1, observer().detach_calls());
380 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
381 observer().last_detached().device_id());
383 base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true);
384 ASSERT_FALSE(mount_path2.empty());
385 DiskMountManager::MountPointInfo mount_info2(kSDCardDeviceName2,
388 disks::MOUNT_CONDITION_NONE);
389 MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kSDCardDeviceName2,
390 kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes);
391 EXPECT_EQ(2, observer().attach_calls());
392 EXPECT_EQ(1, observer().detach_calls());
393 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
394 observer().last_attached().device_id());
395 EXPECT_EQ(base::string16(), observer().last_attached().name());
396 EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
398 UnmountDevice(MOUNT_ERROR_NONE, mount_info2);
399 EXPECT_EQ(2, observer().attach_calls());
400 EXPECT_EQ(2, observer().detach_calls());
401 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
402 observer().last_detached().device_id());
405 TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) {
406 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
407 ASSERT_FALSE(mount_path1.empty());
408 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
411 disks::MOUNT_CONDITION_NONE);
412 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
413 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
414 EXPECT_EQ(1, observer().attach_calls());
415 EXPECT_EQ(0, observer().detach_calls());
416 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
417 observer().last_attached().device_id());
418 EXPECT_EQ(base::string16(), observer().last_attached().name());
419 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
421 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
422 EXPECT_EQ(1, observer().attach_calls());
423 EXPECT_EQ(1, observer().detach_calls());
424 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
425 observer().last_detached().device_id());
428 TEST_F(StorageMonitorCrosTest, GetStorageSize) {
429 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
430 ASSERT_FALSE(mount_path1.empty());
431 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
434 disks::MOUNT_CONDITION_NONE);
435 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
436 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
437 EXPECT_EQ(1, observer().attach_calls());
438 EXPECT_EQ(0, observer().detach_calls());
439 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
440 observer().last_attached().device_id());
441 EXPECT_EQ(base::string16(), observer().last_attached().name());
442 EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
444 EXPECT_EQ(kDevice1SizeInBytes, GetDeviceStorageSize(mount_path1.value()));
445 UnmountDevice(MOUNT_ERROR_NONE, mount_info);
446 EXPECT_EQ(1, observer().attach_calls());
447 EXPECT_EQ(1, observer().detach_calls());
448 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
449 observer().last_detached().device_id());
452 void UnmountFake(const std::string& location,
453 UnmountOptions options,
454 const DiskMountManager::UnmountPathCallback& cb) {
455 cb.Run(chromeos::MOUNT_ERROR_NONE);
458 TEST_F(StorageMonitorCrosTest, EjectTest) {
459 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
460 ASSERT_FALSE(mount_path1.empty());
461 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
464 disks::MOUNT_CONDITION_NONE);
465 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
466 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
467 EXPECT_EQ(1, observer().attach_calls());
468 EXPECT_EQ(0, observer().detach_calls());
470 ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _))
471 .WillByDefault(testing::Invoke(&UnmountFake));
472 EXPECT_CALL(*disk_mount_manager_mock_,
473 UnmountPath(observer().last_attached().location(), _, _));
474 monitor_->EjectDevice(observer().last_attached().device_id(),
475 base::Bind(&StorageMonitorCrosTest::EjectNotify,
476 base::Unretained(this)));
477 base::RunLoop().RunUntilIdle();
479 EXPECT_EQ(StorageMonitor::EJECT_OK, status_);
484 } // namespace chromeos