storage_monitor_chromeos_unittest.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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/message_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 "chromeos/disks/mock_disk_mount_manager.h" 20#include "content/public/test/test_browser_thread.h" 21#include "testing/gtest/include/gtest/gtest.h" 22 23namespace chromeos { 24 25namespace { 26 27using content::BrowserThread; 28using disks::DiskMountManager; 29using testing::_; 30 31const char kDeviceNameWithManufacturerDetails[] = "110 KB (CompanyA, Z101)"; 32const char kDevice1[] = "/dev/d1"; 33const char kDevice1Name[] = "d1"; 34const char kDevice1NameWithSizeInfo[] = "110 KB d1"; 35const char kDevice2[] = "/dev/disk/d2"; 36const char kDevice2Name[] = "d2"; 37const char kDevice2NameWithSizeInfo[] = "207 KB d2"; 38const char kEmptyDeviceLabel[] = ""; 39const char kMountPointA[] = "mnt_a"; 40const char kMountPointB[] = "mnt_b"; 41const char kSDCardDeviceName1[] = "8.6 MB Amy_SD"; 42const char kSDCardDeviceName2[] = "8.6 MB SD Card"; 43const char kSDCardMountPoint1[] = "media/removable/Amy_SD"; 44const char kSDCardMountPoint2[] = "media/removable/SD Card"; 45const char kProductName[] = "Z101"; 46const char kUniqueId1[] = "FFFF-FFFF"; 47const char kUniqueId2[] = "FFFF-FF0F"; 48const char kVendorName[] = "CompanyA"; 49 50uint64 kDevice1SizeInBytes = 113048; 51uint64 kDevice2SizeInBytes = 212312; 52uint64 kSDCardSizeInBytes = 9000000; 53 54std::string GetDCIMDeviceId(const std::string& unique_id) { 55 return chrome::StorageInfo::MakeDeviceId( 56 chrome::StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 57 chrome::kFSUniqueIdPrefix + unique_id); 58} 59 60// A test version of StorageMonitorCros that exposes protected methods to tests. 61class TestStorageMonitorCros : public StorageMonitorCros { 62 public: 63 TestStorageMonitorCros() {} 64 65 virtual ~TestStorageMonitorCros() {} 66 67 virtual void Init() OVERRIDE { 68 SetMediaTransferProtocolManagerForTest( 69 new chrome::TestMediaTransferProtocolManagerLinux()); 70 StorageMonitorCros::Init(); 71 } 72 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); 78 } 79 80 virtual bool GetStorageInfoForPath( 81 const base::FilePath& path, 82 chrome::StorageInfo* device_info) const OVERRIDE { 83 return StorageMonitorCros::GetStorageInfoForPath(path, device_info); 84 } 85 virtual void EjectDevice( 86 const std::string& device_id, 87 base::Callback<void(EjectStatus)> callback) OVERRIDE { 88 StorageMonitorCros::EjectDevice(device_id, callback); 89 } 90 91 private: 92 DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros); 93}; 94 95// Wrapper class to test StorageMonitorCros. 96class StorageMonitorCrosTest : public testing::Test { 97 public: 98 StorageMonitorCrosTest(); 99 virtual ~StorageMonitorCrosTest(); 100 101 void EjectNotify(chrome::StorageMonitor::EjectStatus status); 102 103 protected: 104 // testing::Test: 105 virtual void SetUp() OVERRIDE; 106 virtual void TearDown() OVERRIDE; 107 108 void MountDevice(MountError error_code, 109 const DiskMountManager::MountPointInfo& mount_info, 110 const std::string& unique_id, 111 const std::string& device_label, 112 const std::string& vendor_name, 113 const std::string& product_name, 114 DeviceType device_type, 115 uint64 device_size_in_bytes); 116 117 void UnmountDevice(MountError error_code, 118 const DiskMountManager::MountPointInfo& mount_info); 119 120 uint64 GetDeviceStorageSize(const std::string& device_location); 121 122 // Create a directory named |dir| relative to the test directory. 123 // Set |with_dcim_dir| to true if the created directory will have a "DCIM" 124 // subdirectory. 125 // Returns the full path to the created directory on success, or an empty 126 // path on failure. 127 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir); 128 129 static void PostQuitToUIThread(); 130 static void WaitForFileThread(); 131 132 chrome::MockRemovableStorageObserver& observer() { 133 return *mock_storage_observer_; 134 } 135 136 base::MessageLoop ui_loop_; 137 138 scoped_ptr<TestStorageMonitorCros> monitor_; 139 140 // Owned by DiskMountManager. 141 disks::MockDiskMountManager* disk_mount_manager_mock_; 142 143 chrome::StorageMonitor::EjectStatus status_; 144 145 private: 146 content::TestBrowserThread ui_thread_; 147 content::TestBrowserThread file_thread_; 148 149 // Temporary directory for created test data. 150 base::ScopedTempDir scoped_temp_dir_; 151 152 // Objects that talks with StorageMonitorCros. 153 scoped_ptr<chrome::MockRemovableStorageObserver> mock_storage_observer_; 154 155 DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest); 156}; 157 158StorageMonitorCrosTest::StorageMonitorCrosTest() 159 : disk_mount_manager_mock_(NULL), 160 status_(chrome::StorageMonitor::EJECT_FAILURE), 161 ui_thread_(BrowserThread::UI, &ui_loop_), 162 file_thread_(BrowserThread::FILE) { 163} 164 165StorageMonitorCrosTest::~StorageMonitorCrosTest() { 166} 167 168void StorageMonitorCrosTest::SetUp() { 169 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 170 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); 171 file_thread_.Start(); 172 disk_mount_manager_mock_ = new disks::MockDiskMountManager(); 173 DiskMountManager::InitializeForTesting(disk_mount_manager_mock_); 174 disk_mount_manager_mock_->SetupDefaultReplies(); 175 176 mock_storage_observer_.reset(new chrome::MockRemovableStorageObserver); 177 178 // Initialize the test subject. 179 monitor_.reset(new TestStorageMonitorCros()); 180 monitor_->Init(); 181 monitor_->AddObserver(mock_storage_observer_.get()); 182} 183 184void StorageMonitorCrosTest::TearDown() { 185 monitor_->RemoveObserver(mock_storage_observer_.get()); 186 monitor_.reset(); 187 188 disk_mount_manager_mock_ = NULL; 189 DiskMountManager::Shutdown(); 190 WaitForFileThread(); 191} 192 193void StorageMonitorCrosTest::MountDevice( 194 MountError error_code, 195 const DiskMountManager::MountPointInfo& mount_info, 196 const std::string& unique_id, 197 const std::string& device_label, 198 const std::string& vendor_name, 199 const std::string& product_name, 200 DeviceType device_type, 201 uint64 device_size_in_bytes) { 202 if (error_code == MOUNT_ERROR_NONE) { 203 disk_mount_manager_mock_->CreateDiskEntryForMountDevice( 204 mount_info, unique_id, device_label, vendor_name, product_name, 205 device_type, device_size_in_bytes); 206 } 207 monitor_->OnMountEvent(disks::DiskMountManager::MOUNTING, error_code, 208 mount_info); 209 WaitForFileThread(); 210} 211 212void StorageMonitorCrosTest::UnmountDevice( 213 MountError error_code, 214 const DiskMountManager::MountPointInfo& mount_info) { 215 monitor_->OnMountEvent(disks::DiskMountManager::UNMOUNTING, error_code, 216 mount_info); 217 if (error_code == MOUNT_ERROR_NONE) 218 disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info); 219 WaitForFileThread(); 220} 221 222uint64 StorageMonitorCrosTest::GetDeviceStorageSize( 223 const std::string& device_location) { 224 chrome::StorageInfo info; 225 if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info)) 226 return 0; 227 228 return info.total_size_in_bytes(); 229} 230 231base::FilePath StorageMonitorCrosTest::CreateMountPoint( 232 const std::string& dir, bool with_dcim_dir) { 233 base::FilePath return_path(scoped_temp_dir_.path()); 234 return_path = return_path.AppendASCII(dir); 235 base::FilePath path(return_path); 236 if (with_dcim_dir) 237 path = path.Append(chrome::kDCIMDirectoryName); 238 if (!file_util::CreateDirectory(path)) 239 return base::FilePath(); 240 return return_path; 241} 242 243// static 244void StorageMonitorCrosTest::PostQuitToUIThread() { 245 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 246 base::MessageLoop::QuitClosure()); 247} 248 249// static 250void StorageMonitorCrosTest::WaitForFileThread() { 251 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 252 base::Bind(&PostQuitToUIThread)); 253 base::MessageLoop::current()->Run(); 254} 255 256void StorageMonitorCrosTest::EjectNotify( 257 chrome::StorageMonitor::EjectStatus status) { 258 status_ = status; 259} 260 261// Simple test case where we attach and detach a media device. 262TEST_F(StorageMonitorCrosTest, BasicAttachDetach) { 263 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 264 ASSERT_FALSE(mount_path1.empty()); 265 DiskMountManager::MountPointInfo mount_info(kDevice1, 266 mount_path1.value(), 267 MOUNT_TYPE_DEVICE, 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(string16(), observer().last_attached().name()); 276 EXPECT_EQ(mount_path1.value(), observer().last_attached().location()); 277 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()); 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), 295 observer().last_attached().device_id()); 296 EXPECT_EQ(string16(), 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), 303 observer().last_detached().device_id()); 304} 305 306// Removable mass storage devices with no dcim folder are also recognized. 307TEST_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, 313 mount_path.value(), 314 MOUNT_TYPE_DEVICE, 315 disks::MOUNT_CONDITION_NONE); 316 const std::string device_id = chrome::StorageInfo::MakeDeviceId( 317 chrome::StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 318 chrome::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(string16(), 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), 373 observer().last_attached().device_id()); 374 EXPECT_EQ(string16(), 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), 381 observer().last_detached().device_id()); 382 383 base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true); 384 ASSERT_FALSE(mount_path2.empty()); 385 DiskMountManager::MountPointInfo mount_info2(kSDCardDeviceName2, 386 mount_path2.value(), 387 MOUNT_TYPE_DEVICE, 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(string16(), observer().last_attached().name()); 396 EXPECT_EQ(mount_path2.value(), observer().last_attached().location()); 397 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()); 403} 404 405TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) { 406 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 407 ASSERT_FALSE(mount_path1.empty()); 408 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 409 mount_path1.value(), 410 MOUNT_TYPE_DEVICE, 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(string16(), observer().last_attached().name()); 419 EXPECT_EQ(mount_path1.value(), observer().last_attached().location()); 420 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()); 426} 427 428TEST_F(StorageMonitorCrosTest, GetStorageSize) { 429 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 430 ASSERT_FALSE(mount_path1.empty()); 431 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 432 mount_path1.value(), 433 MOUNT_TYPE_DEVICE, 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(string16(), observer().last_attached().name()); 442 EXPECT_EQ(mount_path1.value(), observer().last_attached().location()); 443 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()); 450} 451 452void UnmountFake(const std::string& location, 453 UnmountOptions options, 454 const DiskMountManager::UnmountPathCallback& cb) { 455 cb.Run(chromeos::MOUNT_ERROR_NONE); 456} 457 458TEST_F(StorageMonitorCrosTest, EjectTest) { 459 base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true); 460 ASSERT_FALSE(mount_path1.empty()); 461 DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel, 462 mount_path1.value(), 463 MOUNT_TYPE_DEVICE, 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()); 469 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 ui_loop_.RunUntilIdle(); 478 479 EXPECT_EQ(chrome::StorageMonitor::EJECT_OK, status_); 480} 481 482} // namespace 483 484} // namespace chromeos 485