storage_monitor_chromeos_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/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" 25 26namespace chromeos { 27 28namespace { 29 30using content::BrowserThread; 31using disks::DiskMountManager; 32using testing::_; 33 34const char kDevice1[] = "/dev/d1"; 35const char kDevice1Name[] = "d1"; 36const char kDevice2[] = "/dev/disk/d2"; 37const char kDevice2Name[] = "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 StorageInfo::MakeDeviceId( 56 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 57 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 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(const base::FilePath& path, 81 StorageInfo* device_info) const OVERRIDE { 82 return StorageMonitorCros::GetStorageInfoForPath(path, device_info); 83 } 84 virtual void EjectDevice( 85 const std::string& device_id, 86 base::Callback<void(EjectStatus)> callback) OVERRIDE { 87 StorageMonitorCros::EjectDevice(device_id, callback); 88 } 89 90 private: 91 DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros); 92}; 93 94// Wrapper class to test StorageMonitorCros. 95class StorageMonitorCrosTest : public testing::Test { 96 public: 97 StorageMonitorCrosTest(); 98 virtual ~StorageMonitorCrosTest(); 99 100 void EjectNotify(StorageMonitor::EjectStatus status); 101 102 protected: 103 // testing::Test: 104 virtual void SetUp() OVERRIDE; 105 virtual void TearDown() OVERRIDE; 106 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); 115 116 void UnmountDevice(MountError error_code, 117 const DiskMountManager::MountPointInfo& mount_info); 118 119 uint64 GetDeviceStorageSize(const std::string& device_location); 120 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" 123 // subdirectory. 124 // Returns the full path to the created directory on success, or an empty 125 // path on failure. 126 base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir); 127 128 static void PostQuitToUIThread(); 129 static void WaitForFileThread(); 130 131 MockRemovableStorageObserver& observer() { 132 return *mock_storage_observer_; 133 } 134 135 TestStorageMonitorCros* monitor_; 136 137 // Owned by DiskMountManager. 138 disks::MockDiskMountManager* disk_mount_manager_mock_; 139 140 StorageMonitor::EjectStatus status_; 141 142 private: 143 content::TestBrowserThreadBundle thread_bundle_; 144 145 // Temporary directory for created test data. 146 base::ScopedTempDir scoped_temp_dir_; 147 148 // Objects that talks with StorageMonitorCros. 149 scoped_ptr<MockRemovableStorageObserver> mock_storage_observer_; 150 151 DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest); 152}; 153 154StorageMonitorCrosTest::StorageMonitorCrosTest() 155 : monitor_(NULL), 156 disk_mount_manager_mock_(NULL), 157 status_(StorageMonitor::EJECT_FAILURE), 158 thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD) { 159} 160 161StorageMonitorCrosTest::~StorageMonitorCrosTest() { 162} 163 164void 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(); 170 171 mock_storage_observer_.reset(new MockRemovableStorageObserver); 172 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()); 180 181 monitor_->Init(); 182 monitor_->AddObserver(mock_storage_observer_.get()); 183} 184 185void StorageMonitorCrosTest::TearDown() { 186 monitor_->RemoveObserver(mock_storage_observer_.get()); 187 monitor_ = NULL; 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 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(kDCIMDirectoryName); 239 if (!base::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 base::MessageLoop::QuitClosure()); 248} 249 250// static 251void StorageMonitorCrosTest::WaitForFileThread() { 252 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 253 base::Bind(&PostQuitToUIThread)); 254 base::MessageLoop::current()->Run(); 255} 256 257void StorageMonitorCrosTest::EjectNotify(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 = 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(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 base::RunLoop().RunUntilIdle(); 478 479 EXPECT_EQ(StorageMonitor::EJECT_OK, status_); 480} 481 482} // namespace 483 484} // namespace chromeos 485