storage_monitor_chromeos_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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// chromeos::StorageMonitorCros unit tests. 6 7#include "chrome/browser/storage_monitor/storage_monitor_chromeos.h" 8 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/message_loop.h" 14#include "base/utf_string_conversions.h" 15#include "chrome/browser/storage_monitor/media_storage_util.h" 16#include "chrome/browser/storage_monitor/mock_removable_storage_observer.h" 17#include "chrome/browser/storage_monitor/removable_device_constants.h" 18#include "chrome/browser/storage_monitor/storage_info.h" 19#include "chrome/browser/storage_monitor/test_media_transfer_protocol_manager_linux.h" 20#include "chromeos/disks/mock_disk_mount_manager.h" 21#include "content/public/test/test_browser_thread.h" 22#include "testing/gtest/include/gtest/gtest.h" 23 24namespace chromeos { 25 26namespace { 27 28using content::BrowserThread; 29using disks::DiskMountManager; 30using testing::_; 31 32const char kDeviceNameWithManufacturerDetails[] = "110 KB (CompanyA, Z101)"; 33const char kDevice1[] = "/dev/d1"; 34const char kDevice1Name[] = "d1"; 35const char kDevice1NameWithSizeInfo[] = "110 KB d1"; 36const char kDevice2[] = "/dev/disk/d2"; 37const char kDevice2Name[] = "d2"; 38const char kDevice2NameWithSizeInfo[] = "207 KB d2"; 39const char kEmptyDeviceLabel[] = ""; 40const char kMountPointA[] = "mnt_a"; 41const char kMountPointB[] = "mnt_b"; 42const char kSDCardDeviceName1[] = "8.6 MB Amy_SD"; 43const char kSDCardDeviceName2[] = "8.6 MB SD Card"; 44const char kSDCardMountPoint1[] = "media/removable/Amy_SD"; 45const char kSDCardMountPoint2[] = "media/removable/SD Card"; 46const char kProductName[] = "Z101"; 47const char kUniqueId1[] = "FFFF-FFFF"; 48const char kUniqueId2[] = "FFFF-FF0F"; 49const char kVendorName[] = "CompanyA"; 50 51uint64 kDevice1SizeInBytes = 113048; 52uint64 kDevice2SizeInBytes = 212312; 53uint64 kSDCardSizeInBytes = 9000000; 54 55std::string GetDCIMDeviceId(const std::string& unique_id) { 56 return chrome::MediaStorageUtil::MakeDeviceId( 57 chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM, 58 chrome::kFSUniqueIdPrefix + unique_id); 59} 60 61// A test version of StorageMonitorCros that exposes protected methods to tests. 62class TestStorageMonitorCros : public StorageMonitorCros { 63 public: 64 TestStorageMonitorCros() {} 65 66 virtual ~TestStorageMonitorCros() {} 67 68 void Init() { 69 SetMediaTransferProtocolManagerForTest( 70 new chrome::TestMediaTransferProtocolManagerLinux()); 71 StorageMonitorCros::Init(); 72 } 73 74 virtual void OnMountEvent( 75 disks::DiskMountManager::MountEvent event, 76 MountError error_code, 77 const disks::DiskMountManager::MountPointInfo& mount_info) OVERRIDE { 78 StorageMonitorCros::OnMountEvent(event, error_code, mount_info); 79 } 80 81 virtual bool GetStorageInfoForPath( 82 const base::FilePath& path, 83 chrome::StorageInfo* device_info) const OVERRIDE { 84 return StorageMonitorCros::GetStorageInfoForPath(path, device_info); 85 } 86 virtual void EjectDevice( 87 const std::string& device_id, 88 base::Callback<void(EjectStatus)> callback) OVERRIDE { 89 StorageMonitorCros::EjectDevice(device_id, callback); 90 } 91 92 private: 93 DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros); 94}; 95 96// Wrapper class to test StorageMonitorCros. 97class StorageMonitorCrosTest : public testing::Test { 98 public: 99 StorageMonitorCrosTest(); 100 virtual ~StorageMonitorCrosTest(); 101 102 void EjectNotify(chrome::StorageMonitor::EjectStatus status); 103 104 protected: 105 // testing::Test: 106 virtual void SetUp() OVERRIDE; 107 virtual void TearDown() OVERRIDE; 108 109 void MountDevice(MountError error_code, 110 const DiskMountManager::MountPointInfo& mount_info, 111 const std::string& unique_id, 112 const std::string& device_label, 113 const std::string& vendor_name, 114 const std::string& product_name, 115 DeviceType device_type, 116 uint64 device_size_in_bytes); 117 118 void UnmountDevice(MountError error_code, 119 const DiskMountManager::MountPointInfo& mount_info); 120 121 uint64 GetDeviceStorageSize(const std::string& device_location); 122 123 // Create a directory named |dir| relative to the test directory. 124 // Set |with_dcim_dir| to true if the created directory will have a "DCIM" 125 // subdirectory. 126 // Returns the full path to the created directory on success, or an empty 127 // path on failure. 128 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir); 129 130 static void PostQuitToUIThread(); 131 static void WaitForFileThread(); 132 133 chrome::MockRemovableStorageObserver& observer() { 134 return *mock_storage_observer_; 135 } 136 137 MessageLoop ui_loop_; 138 139 scoped_ptr<TestStorageMonitorCros> monitor_; 140 141 // Owned by DiskMountManager. 142 disks::MockDiskMountManager* disk_mount_manager_mock_; 143 144 chrome::StorageMonitor::EjectStatus status_; 145 146 private: 147 content::TestBrowserThread ui_thread_; 148 content::TestBrowserThread file_thread_; 149 150 // Temporary directory for created test data. 151 base::ScopedTempDir scoped_temp_dir_; 152 153 // Objects that talks with StorageMonitorCros. 154 scoped_ptr<chrome::MockRemovableStorageObserver> mock_storage_observer_; 155 156 DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest); 157}; 158 159StorageMonitorCrosTest::StorageMonitorCrosTest() 160 : disk_mount_manager_mock_(NULL), 161 status_(chrome::StorageMonitor::EJECT_FAILURE), 162 ui_thread_(BrowserThread::UI, &ui_loop_), 163 file_thread_(BrowserThread::FILE) { 164} 165 166StorageMonitorCrosTest::~StorageMonitorCrosTest() { 167} 168 169void StorageMonitorCrosTest::SetUp() { 170 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 171 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); 172 file_thread_.Start(); 173 disk_mount_manager_mock_ = new disks::MockDiskMountManager(); 174 DiskMountManager::InitializeForTesting(disk_mount_manager_mock_); 175 disk_mount_manager_mock_->SetupDefaultReplies(); 176 177 mock_storage_observer_.reset(new chrome::MockRemovableStorageObserver); 178 179 // Initialize the test subject. 180 monitor_.reset(new TestStorageMonitorCros()); 181 monitor_->Init(); 182 monitor_->AddObserver(mock_storage_observer_.get()); 183} 184 185void StorageMonitorCrosTest::TearDown() { 186 monitor_->RemoveObserver(mock_storage_observer_.get()); 187 monitor_.reset(); 188 189 disk_mount_manager_mock_ = NULL; 190 DiskMountManager::Shutdown(); 191 WaitForFileThread(); 192} 193 194void 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); 207 } 208 monitor_->OnMountEvent(disks::DiskMountManager::MOUNTING, error_code, 209 mount_info); 210 WaitForFileThread(); 211} 212 213void StorageMonitorCrosTest::UnmountDevice( 214 MountError error_code, 215 const DiskMountManager::MountPointInfo& mount_info) { 216 monitor_->OnMountEvent(disks::DiskMountManager::UNMOUNTING, error_code, 217 mount_info); 218 if (error_code == MOUNT_ERROR_NONE) 219 disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info); 220 WaitForFileThread(); 221} 222 223uint64 StorageMonitorCrosTest::GetDeviceStorageSize( 224 const std::string& device_location) { 225 chrome::StorageInfo info; 226 if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info)) 227 return 0; 228 229 return info.total_size_in_bytes; 230} 231 232base::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); 237 if (with_dcim_dir) 238 path = path.Append(chrome::kDCIMDirectoryName); 239 if (!file_util::CreateDirectory(path)) 240 return base::FilePath(); 241 return return_path; 242} 243 244// static 245void StorageMonitorCrosTest::PostQuitToUIThread() { 246 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 247 MessageLoop::QuitClosure()); 248} 249 250// static 251void StorageMonitorCrosTest::WaitForFileThread() { 252 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 253 base::Bind(&PostQuitToUIThread)); 254 MessageLoop::current()->Run(); 255} 256 257void StorageMonitorCrosTest::EjectNotify( 258 chrome::StorageMonitor::EjectStatus status) { 259 status_ = status; 260} 261 262// Simple test case where we attach and detach a media device. 263TEST_F(StorageMonitorCrosTest, BasicAttachDetach) { 264 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 265 ASSERT_FALSE(mount_path1.empty()); 266 DiskMountManager::MountPointInfo mount_info(kDevice1, 267 mount_path1.value(), 268 MOUNT_TYPE_DEVICE, 269 disks::MOUNT_CONDITION_NONE); 270 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kDevice1Name, 271 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes); 272 EXPECT_EQ(1, observer().attach_calls()); 273 EXPECT_EQ(0, observer().detach_calls()); 274 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_attached().device_id); 275 EXPECT_EQ(ASCIIToUTF16(kDevice1NameWithSizeInfo), 276 observer().last_attached().name); 277 EXPECT_EQ(mount_path1.value(), observer().last_attached().location); 278 279 UnmountDevice(MOUNT_ERROR_NONE, mount_info); 280 EXPECT_EQ(1, observer().attach_calls()); 281 EXPECT_EQ(1, observer().detach_calls()); 282 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_detached().device_id); 283 284 base::FilePath mount_path2 = CreateMountPoint(kMountPointB, true); 285 ASSERT_FALSE(mount_path2.empty()); 286 DiskMountManager::MountPointInfo mount_info2(kDevice2, 287 mount_path2.value(), 288 MOUNT_TYPE_DEVICE, 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), observer().last_attached().device_id); 295 EXPECT_EQ(ASCIIToUTF16(kDevice2NameWithSizeInfo), 296 observer().last_attached().name); 297 EXPECT_EQ(mount_path2.value(), observer().last_attached().location); 298 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), observer().last_detached().device_id); 303} 304 305// Removable mass storage devices with no dcim folder are also recognized. 306TEST_F(StorageMonitorCrosTest, NoDCIM) { 307 testing::Sequence mock_sequence; 308 base::FilePath mount_path = CreateMountPoint(kMountPointA, false); 309 const std::string kUniqueId = "FFFF-FFFF"; 310 ASSERT_FALSE(mount_path.empty()); 311 DiskMountManager::MountPointInfo mount_info(kDevice1, 312 mount_path.value(), 313 MOUNT_TYPE_DEVICE, 314 disks::MOUNT_CONDITION_NONE); 315 const std::string device_id = chrome::MediaStorageUtil::MakeDeviceId( 316 chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM, 317 chrome::kFSUniqueIdPrefix + kUniqueId); 318 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name, 319 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes); 320 EXPECT_EQ(1, observer().attach_calls()); 321 EXPECT_EQ(0, observer().detach_calls()); 322 EXPECT_EQ(device_id, observer().last_attached().device_id); 323 EXPECT_EQ(ASCIIToUTF16(kDevice1NameWithSizeInfo), 324 observer().last_attached().name); 325 EXPECT_EQ(mount_path.value(), observer().last_attached().location); 326} 327 328// Non device mounts and mount errors are ignored. 329TEST_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()); 334 335 // Mount error. 336 DiskMountManager::MountPointInfo mount_info(kDevice1, 337 mount_path.value(), 338 MOUNT_TYPE_DEVICE, 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()); 344 345 // Not a device 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()); 351 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()); 359} 360 361TEST_F(StorageMonitorCrosTest, SDCardAttachDetach) { 362 base::FilePath mount_path1 = CreateMountPoint(kSDCardMountPoint1, true); 363 ASSERT_FALSE(mount_path1.empty()); 364 DiskMountManager::MountPointInfo mount_info1(kSDCardDeviceName1, 365 mount_path1.value(), 366 MOUNT_TYPE_DEVICE, 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), observer().last_attached().device_id); 373 EXPECT_EQ(ASCIIToUTF16(kSDCardDeviceName1), 374 observer().last_attached().name); 375 EXPECT_EQ(mount_path1.value(), observer().last_attached().location); 376 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), observer().last_detached().device_id); 381 382 base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true); 383 ASSERT_FALSE(mount_path2.empty()); 384 DiskMountManager::MountPointInfo mount_info2(kSDCardDeviceName2, 385 mount_path2.value(), 386 MOUNT_TYPE_DEVICE, 387 disks::MOUNT_CONDITION_NONE); 388 MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kSDCardDeviceName2, 389 kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes); 390 EXPECT_EQ(2, observer().attach_calls()); 391 EXPECT_EQ(1, observer().detach_calls()); 392 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2), observer().last_attached().device_id); 393 EXPECT_EQ(ASCIIToUTF16(kSDCardDeviceName2), 394 observer().last_attached().name); 395 EXPECT_EQ(mount_path2.value(), observer().last_attached().location); 396 397 UnmountDevice(MOUNT_ERROR_NONE, mount_info2); 398 EXPECT_EQ(2, observer().attach_calls()); 399 EXPECT_EQ(2, observer().detach_calls()); 400 EXPECT_EQ(GetDCIMDeviceId(kUniqueId2), observer().last_detached().device_id); 401} 402 403TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) { 404 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 405 ASSERT_FALSE(mount_path1.empty()); 406 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 407 mount_path1.value(), 408 MOUNT_TYPE_DEVICE, 409 disks::MOUNT_CONDITION_NONE); 410 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel, 411 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes); 412 EXPECT_EQ(1, observer().attach_calls()); 413 EXPECT_EQ(0, observer().detach_calls()); 414 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_attached().device_id); 415 EXPECT_EQ(ASCIIToUTF16(kDeviceNameWithManufacturerDetails), 416 observer().last_attached().name); 417 EXPECT_EQ(mount_path1.value(), observer().last_attached().location); 418 419 UnmountDevice(MOUNT_ERROR_NONE, mount_info); 420 EXPECT_EQ(1, observer().attach_calls()); 421 EXPECT_EQ(1, observer().detach_calls()); 422 EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_detached().device_id); 423} 424 425TEST_F(StorageMonitorCrosTest, GetStorageSize) { 426 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 427 ASSERT_FALSE(mount_path1.empty()); 428 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 429 mount_path1.value(), 430 MOUNT_TYPE_DEVICE, 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), observer().last_attached().device_id); 437 EXPECT_EQ(ASCIIToUTF16(kDeviceNameWithManufacturerDetails), 438 observer().last_attached().name); 439 EXPECT_EQ(mount_path1.value(), observer().last_attached().location); 440 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), observer().last_detached().device_id); 446} 447 448void UnmountFake(const std::string& location, 449 UnmountOptions options, 450 const DiskMountManager::UnmountPathCallback& cb) { 451 cb.Run(chromeos::MOUNT_ERROR_NONE); 452} 453 454TEST_F(StorageMonitorCrosTest, EjectTest) { 455 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 456 ASSERT_FALSE(mount_path1.empty()); 457 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 458 mount_path1.value(), 459 MOUNT_TYPE_DEVICE, 460 disks::MOUNT_CONDITION_NONE); 461 MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel, 462 kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes); 463 EXPECT_EQ(1, observer().attach_calls()); 464 EXPECT_EQ(0, observer().detach_calls()); 465 466 ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _)) 467 .WillByDefault(testing::Invoke(&UnmountFake)); 468 EXPECT_CALL(*disk_mount_manager_mock_, 469 UnmountPath(observer().last_attached().location, _, _)); 470 monitor_->EjectDevice(observer().last_attached().device_id, 471 base::Bind(&StorageMonitorCrosTest::EjectNotify, 472 base::Unretained(this))); 473 ui_loop_.RunUntilIdle(); 474 475 EXPECT_EQ(chrome::StorageMonitor::EJECT_OK, status_); 476} 477 478} // namespace 479 480} // namespace chromeos 481