disk_mount_manager_unittest.cc revision 010d83a9304c5a91596085d917d248abff47903a
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#include "base/bind.h" 6#include "base/message_loop/message_loop.h" 7#include "chromeos/dbus/fake_cros_disks_client.h" 8#include "chromeos/dbus/fake_dbus_thread_manager.h" 9#include "chromeos/disks/disk_mount_manager.h" 10#include "testing/gmock/include/gmock/gmock.h" 11#include "testing/gtest/include/gtest/gtest.h" 12 13using chromeos::disks::DiskMountManager; 14using chromeos::CrosDisksClient; 15using chromeos::DBusThreadManager; 16using chromeos::FakeCrosDisksClient; 17using chromeos::FakeDBusThreadManager; 18using testing::_; 19using testing::Field; 20using testing::InSequence; 21 22namespace { 23 24// Holds information needed to create a DiskMountManager::Disk instance. 25struct TestDiskInfo { 26 const char* source_path; 27 const char* mount_path; 28 const char* system_path; 29 const char* file_path; 30 const char* device_label; 31 const char* drive_label; 32 const char* vendor_id; 33 const char* vendor_name; 34 const char* product_id; 35 const char* product_name; 36 const char* fs_uuid; 37 const char* system_path_prefix; 38 chromeos::DeviceType device_type; 39 uint64 size_in_bytes; 40 bool is_parent; 41 bool is_read_only; 42 bool has_media; 43 bool on_boot_device; 44 bool is_hidden; 45}; 46 47// Holds information to create a DiskMOuntManager::MountPointInfo instance. 48struct TestMountPointInfo { 49 const char* source_path; 50 const char* mount_path; 51 chromeos::MountType mount_type; 52 chromeos::disks::MountCondition mount_condition; 53}; 54 55// List of disks held in DiskMountManager at the begining of the test. 56const TestDiskInfo kTestDisks[] = { 57 { 58 "/device/source_path", 59 "/device/mount_path", 60 "/device/prefix/system_path", 61 "/device/file_path", 62 "/device/device_label", 63 "/device/drive_label", 64 "/device/vendor_id", 65 "/device/vendor_name", 66 "/device/product_id", 67 "/device/product_name", 68 "/device/fs_uuid", 69 "/device/prefix", 70 chromeos::DEVICE_TYPE_USB, 71 1073741824, // size in bytes 72 false, // is parent 73 false, // is read only 74 true, // has media 75 false, // is on boot device 76 false // is hidden 77 }, 78}; 79 80// List of mount points held in DiskMountManager at the begining of the test. 81const TestMountPointInfo kTestMountPoints[] = { 82 { 83 "/archive/source_path", 84 "/archive/mount_path", 85 chromeos::MOUNT_TYPE_ARCHIVE, 86 chromeos::disks::MOUNT_CONDITION_NONE 87 }, 88 { 89 "/device/source_path", 90 "/device/mount_path", 91 chromeos::MOUNT_TYPE_DEVICE, 92 chromeos::disks::MOUNT_CONDITION_NONE 93 }, 94}; 95 96// Mocks DiskMountManager observer. 97class MockDiskMountManagerObserver : public DiskMountManager::Observer { 98 public: 99 virtual ~MockDiskMountManagerObserver() {} 100 101 MOCK_METHOD2(OnDiskEvent, void(DiskMountManager::DiskEvent event, 102 const DiskMountManager::Disk* disk)); 103 MOCK_METHOD2(OnDeviceEvent, void(DiskMountManager::DeviceEvent event, 104 const std::string& device_path)); 105 MOCK_METHOD3(OnMountEvent, 106 void(DiskMountManager::MountEvent event, 107 chromeos::MountError error_code, 108 const DiskMountManager::MountPointInfo& mount_point)); 109 MOCK_METHOD3(OnFormatEvent, 110 void(DiskMountManager::FormatEvent event, 111 chromeos::FormatError error_code, 112 const std::string& device_path)); 113}; 114 115class DiskMountManagerTest : public testing::Test { 116 public: 117 DiskMountManagerTest() {} 118 virtual ~DiskMountManagerTest() {} 119 120 // Sets up test dbus tread manager and disks mount manager. 121 // Initializes disk mount manager disks and mount points. 122 // Adds a test observer to the disk mount manager. 123 virtual void SetUp() { 124 FakeDBusThreadManager* fake_thread_manager = new FakeDBusThreadManager(); 125 fake_cros_disks_client_ = new FakeCrosDisksClient; 126 fake_thread_manager->SetCrosDisksClient( 127 scoped_ptr<CrosDisksClient>(fake_cros_disks_client_)); 128 129 DBusThreadManager::InitializeForTesting(fake_thread_manager); 130 131 DiskMountManager::Initialize(); 132 133 InitDisksAndMountPoints(); 134 135 DiskMountManager::GetInstance()->AddObserver(&observer_); 136 } 137 138 // Shuts down dbus thread manager and disk moutn manager used in the test. 139 virtual void TearDown() { 140 DiskMountManager::GetInstance()->RemoveObserver(&observer_); 141 DiskMountManager::Shutdown(); 142 DBusThreadManager::Shutdown(); 143 } 144 145 protected: 146 // Checks if disk mount manager contains a mount point with specified moutn 147 // path. 148 bool HasMountPoint(const std::string& mount_path) { 149 const DiskMountManager::MountPointMap& mount_points = 150 DiskMountManager::GetInstance()->mount_points(); 151 return mount_points.find(mount_path) != mount_points.end(); 152 } 153 154 private: 155 // Adds a new disk to the disk mount manager. 156 void AddTestDisk(const TestDiskInfo& disk) { 157 EXPECT_TRUE(DiskMountManager::GetInstance()->AddDiskForTest( 158 new DiskMountManager::Disk(disk.source_path, 159 disk.mount_path, 160 disk.system_path, 161 disk.file_path, 162 disk.device_label, 163 disk.drive_label, 164 disk.vendor_id, 165 disk.vendor_name, 166 disk.product_id, 167 disk.product_name, 168 disk.fs_uuid, 169 disk.system_path_prefix, 170 disk.device_type, 171 disk.size_in_bytes, 172 disk.is_parent, 173 disk.is_read_only, 174 disk.has_media, 175 disk.on_boot_device, 176 disk.is_hidden))); 177 } 178 179 // Adds a new mount point to the disk mount manager. 180 // If the moutn point is a device mount point, disk with its source path 181 // should already be added to the disk mount manager. 182 void AddTestMountPoint(const TestMountPointInfo& mount_point) { 183 EXPECT_TRUE(DiskMountManager::GetInstance()->AddMountPointForTest( 184 DiskMountManager::MountPointInfo(mount_point.source_path, 185 mount_point.mount_path, 186 mount_point.mount_type, 187 mount_point.mount_condition))); 188 } 189 190 // Adds disks and mount points to disk mount manager. 191 void InitDisksAndMountPoints() { 192 // Disks should be added first (when adding device mount points it is 193 // expected that the corresponding disk is already added). 194 for (size_t i = 0; i < arraysize(kTestDisks); i++) 195 AddTestDisk(kTestDisks[i]); 196 197 for (size_t i = 0; i < arraysize(kTestMountPoints); i++) 198 AddTestMountPoint(kTestMountPoints[i]); 199 } 200 201 protected: 202 chromeos::FakeCrosDisksClient* fake_cros_disks_client_; 203 MockDiskMountManagerObserver observer_; 204 base::MessageLoopForUI message_loop_; 205}; 206 207// Tests that the observer gets notified on attempt to format non existent mount 208// point. 209TEST_F(DiskMountManagerTest, Format_NotMounted) { 210 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 211 chromeos::FORMAT_ERROR_UNKNOWN, 212 "/mount/non_existent")) 213 .Times(1); 214 DiskMountManager::GetInstance()->FormatMountedDevice("/mount/non_existent"); 215} 216 217// Tests that it is not possible to format archive mount point. 218TEST_F(DiskMountManagerTest, Format_Archive) { 219 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 220 chromeos::FORMAT_ERROR_UNKNOWN, 221 "/archive/source_path")) 222 .Times(1); 223 224 DiskMountManager::GetInstance()->FormatMountedDevice("/archive/mount_path"); 225} 226 227// Tests that format fails if the device cannot be unmounted. 228TEST_F(DiskMountManagerTest, Format_FailToUnmount) { 229 // Before formatting mounted device, the device should be unmounted. 230 // In this test unmount will fail, and there should be no attempt to 231 // format the device. 232 233 // Set up expectations for observer mock. 234 // Observer should be notified that unmount attempt fails and format task 235 // failed to start. 236 { 237 InSequence s; 238 239 EXPECT_CALL(observer_, 240 OnMountEvent(DiskMountManager::UNMOUNTING, 241 chromeos::MOUNT_ERROR_INTERNAL, 242 Field(&DiskMountManager::MountPointInfo::mount_path, 243 "/device/mount_path"))) 244 .Times(1); 245 246 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 247 chromeos::FORMAT_ERROR_UNKNOWN, 248 "/device/source_path")) 249 .Times(1); 250 } 251 252 fake_cros_disks_client_->MakeUnmountFail(); 253 // Start test. 254 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 255 256 // Cros disks will respond asynchronoulsy, so let's drain the message loop. 257 message_loop_.RunUntilIdle(); 258 259 EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); 260 EXPECT_EQ("/device/mount_path", 261 fake_cros_disks_client_->last_unmount_device_path()); 262 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 263 fake_cros_disks_client_->last_unmount_options()); 264 EXPECT_EQ(0, fake_cros_disks_client_->format_call_count()); 265 266 // The device mount should still be here. 267 EXPECT_TRUE(HasMountPoint("/device/mount_path")); 268} 269 270// Tests that observer is notified when cros disks fails to start format 271// process. 272TEST_F(DiskMountManagerTest, Format_FormatFailsToStart) { 273 // Before formatting mounted device, the device should be unmounted. 274 // In this test, unmount will succeed, but call to Format method will 275 // fail. 276 277 // Set up expectations for observer mock. 278 // Observer should be notified that the device was unmounted and format task 279 // failed to start. 280 { 281 InSequence s; 282 283 EXPECT_CALL(observer_, 284 OnMountEvent(DiskMountManager::UNMOUNTING, 285 chromeos::MOUNT_ERROR_NONE, 286 Field(&DiskMountManager::MountPointInfo::mount_path, 287 "/device/mount_path"))) 288 .Times(1); 289 290 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 291 chromeos::FORMAT_ERROR_UNKNOWN, 292 "/device/source_path")) 293 .Times(1); 294 } 295 296 fake_cros_disks_client_->MakeFormatFail(); 297 // Start the test. 298 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 299 300 // Cros disks will respond asynchronoulsy, so let's drain the message loop. 301 message_loop_.RunUntilIdle(); 302 303 EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); 304 EXPECT_EQ("/device/mount_path", 305 fake_cros_disks_client_->last_unmount_device_path()); 306 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 307 fake_cros_disks_client_->last_unmount_options()); 308 EXPECT_EQ(1, fake_cros_disks_client_->format_call_count()); 309 EXPECT_EQ("/device/source_path", 310 fake_cros_disks_client_->last_format_device_path()); 311 EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem()); 312 313 // The device mount should be gone. 314 EXPECT_FALSE(HasMountPoint("/device/mount_path")); 315} 316 317// Tests the case where there are two format requests for the same device. 318TEST_F(DiskMountManagerTest, Format_ConcurrentFormatCalls) { 319 // Only the first format request should be processed (the second unmount 320 // request fails because the device is already unmounted at that point). 321 // CrosDisksClient will report that the format process for the first request 322 // is successfully started. 323 324 // Set up expectations for observer mock. 325 // The observer should get a FORMAT_STARTED event for one format request and a 326 // FORMAT_COMPLETED with an error code for the other format request. The 327 // formatting will be started only for the first request. 328 // There should be only one UNMOUNTING event. The result of the second one 329 // should not be reported as the mount point will go away after the first 330 // request. 331 // 332 // Note that in this test the format completion signal will not be simulated, 333 // so the observer should not get FORMAT_COMPLETED signal. 334 { 335 InSequence s; 336 337 EXPECT_CALL(observer_, 338 OnMountEvent(DiskMountManager::UNMOUNTING, 339 chromeos::MOUNT_ERROR_NONE, 340 Field(&DiskMountManager::MountPointInfo::mount_path, 341 "/device/mount_path"))) 342 .Times(1); 343 344 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 345 chromeos::FORMAT_ERROR_UNKNOWN, 346 "/device/source_path")) 347 .Times(1); 348 349 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED, 350 chromeos::FORMAT_ERROR_NONE, 351 "/device/source_path")) 352 .Times(1); 353 } 354 355 fake_cros_disks_client_->set_unmount_listener( 356 base::Bind(&FakeCrosDisksClient::MakeUnmountFail, 357 base::Unretained(fake_cros_disks_client_))); 358 // Start the test. 359 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 360 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 361 362 // Cros disks will respond asynchronoulsy, so let's drain the message loop. 363 message_loop_.RunUntilIdle(); 364 365 EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count()); 366 EXPECT_EQ("/device/mount_path", 367 fake_cros_disks_client_->last_unmount_device_path()); 368 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 369 fake_cros_disks_client_->last_unmount_options()); 370 EXPECT_EQ(1, fake_cros_disks_client_->format_call_count()); 371 EXPECT_EQ("/device/source_path", 372 fake_cros_disks_client_->last_format_device_path()); 373 EXPECT_EQ("vfat", 374 fake_cros_disks_client_->last_format_filesystem()); 375 376 // The device mount should be gone. 377 EXPECT_FALSE(HasMountPoint("/device/mount_path")); 378} 379 380// Tests the case when the format process actually starts and fails. 381TEST_F(DiskMountManagerTest, Format_FormatFails) { 382 // Both unmount and format device cals are successful in this test. 383 384 // Set up expectations for observer mock. 385 // The observer should get notified that the device was unmounted and that 386 // formatting has started. 387 // After the formatting starts, the test will simulate failing 388 // FORMAT_COMPLETED signal, so the observer should also be notified the 389 // formatting has failed (FORMAT_COMPLETED event). 390 { 391 InSequence s; 392 393 EXPECT_CALL(observer_, 394 OnMountEvent(DiskMountManager::UNMOUNTING, 395 chromeos::MOUNT_ERROR_NONE, 396 Field(&DiskMountManager::MountPointInfo::mount_path, 397 "/device/mount_path"))) 398 .Times(1); 399 400 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED, 401 chromeos::FORMAT_ERROR_NONE, 402 "/device/source_path")) 403 .Times(1); 404 405 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 406 chromeos::FORMAT_ERROR_UNKNOWN, 407 "/device/source_path")) 408 .Times(1); 409 } 410 411 // Start the test. 412 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 413 414 // Wait for Unmount and Format calls to end. 415 message_loop_.RunUntilIdle(); 416 417 EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); 418 EXPECT_EQ("/device/mount_path", 419 fake_cros_disks_client_->last_unmount_device_path()); 420 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 421 fake_cros_disks_client_->last_unmount_options()); 422 EXPECT_EQ(1, fake_cros_disks_client_->format_call_count()); 423 EXPECT_EQ("/device/source_path", 424 fake_cros_disks_client_->last_format_device_path()); 425 EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem()); 426 427 // The device should be unmounted by now. 428 EXPECT_FALSE(HasMountPoint("/device/mount_path")); 429 430 // Send failing FORMAT_COMPLETED signal. 431 // The failure is marked by ! in fromt of the path (but this should change 432 // soon). 433 fake_cros_disks_client_->SendFormatCompletedEvent( 434 chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path"); 435} 436 437// Tests the case when formatting completes successfully. 438TEST_F(DiskMountManagerTest, Format_FormatSuccess) { 439 // Set up cros disks client mocks. 440 // Both unmount and format device cals are successful in this test. 441 442 // Set up expectations for observer mock. 443 // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED 444 // events (all of them without an error set). 445 { 446 InSequence s; 447 448 EXPECT_CALL(observer_, 449 OnMountEvent(DiskMountManager::UNMOUNTING, 450 chromeos::MOUNT_ERROR_NONE, 451 Field(&DiskMountManager::MountPointInfo::mount_path, 452 "/device/mount_path"))) 453 .Times(1); 454 455 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED, 456 chromeos::FORMAT_ERROR_NONE, 457 "/device/source_path")) 458 .Times(1); 459 460 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 461 chromeos::FORMAT_ERROR_NONE, 462 "/device/source_path")) 463 .Times(1); 464 } 465 466 // Start the test. 467 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 468 469 // Wait for Unmount and Format calls to end. 470 message_loop_.RunUntilIdle(); 471 472 EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); 473 EXPECT_EQ("/device/mount_path", 474 fake_cros_disks_client_->last_unmount_device_path()); 475 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 476 fake_cros_disks_client_->last_unmount_options()); 477 EXPECT_EQ(1, fake_cros_disks_client_->format_call_count()); 478 EXPECT_EQ("/device/source_path", 479 fake_cros_disks_client_->last_format_device_path()); 480 EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem()); 481 482 // The device should be unmounted by now. 483 EXPECT_FALSE(HasMountPoint("/device/mount_path")); 484 485 // Simulate cros_disks reporting success. 486 fake_cros_disks_client_->SendFormatCompletedEvent( 487 chromeos::FORMAT_ERROR_NONE, "/device/source_path"); 488} 489 490// Tests that it's possible to format the device twice in a row (this may not be 491// true if the list of pending formats is not properly cleared). 492TEST_F(DiskMountManagerTest, Format_ConsecutiveFormatCalls) { 493 // All unmount and format device cals are successful in this test. 494 // Each of the should be made twice (once for each formatting task). 495 496 // Set up expectations for observer mock. 497 // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED 498 // events (all of them without an error set) twice (once for each formatting 499 // task). 500 // Also, there should be a MOUNTING event when the device remounting is 501 // simulated. 502 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED, 503 chromeos::FORMAT_ERROR_NONE, 504 "/device/source_path")) 505 .Times(2); 506 507 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED, 508 chromeos::FORMAT_ERROR_NONE, 509 "/device/source_path")) 510 .Times(2); 511 512 EXPECT_CALL(observer_, 513 OnMountEvent(DiskMountManager::UNMOUNTING, 514 chromeos::MOUNT_ERROR_NONE, 515 Field(&DiskMountManager::MountPointInfo::mount_path, 516 "/device/mount_path"))) 517 .Times(2); 518 519 EXPECT_CALL(observer_, 520 OnMountEvent(DiskMountManager::MOUNTING, 521 chromeos::MOUNT_ERROR_NONE, 522 Field(&DiskMountManager::MountPointInfo::mount_path, 523 "/device/mount_path"))) 524 .Times(1); 525 526 // Start the test. 527 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 528 529 // Wait for Unmount and Format calls to end. 530 message_loop_.RunUntilIdle(); 531 532 EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); 533 EXPECT_EQ("/device/mount_path", 534 fake_cros_disks_client_->last_unmount_device_path()); 535 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 536 fake_cros_disks_client_->last_unmount_options()); 537 EXPECT_EQ(1, fake_cros_disks_client_->format_call_count()); 538 EXPECT_EQ("/device/source_path", 539 fake_cros_disks_client_->last_format_device_path()); 540 EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem()); 541 542 // The device should be unmounted by now. 543 EXPECT_FALSE(HasMountPoint("/device/mount_path")); 544 545 // Simulate cros_disks reporting success. 546 fake_cros_disks_client_->SendFormatCompletedEvent( 547 chromeos::FORMAT_ERROR_NONE, "/device/source_path"); 548 549 // Simulate the device remounting. 550 fake_cros_disks_client_->SendMountCompletedEvent( 551 chromeos::MOUNT_ERROR_NONE, 552 "/device/source_path", 553 chromeos::MOUNT_TYPE_DEVICE, 554 "/device/mount_path"); 555 556 EXPECT_TRUE(HasMountPoint("/device/mount_path")); 557 558 // Try formatting again. 559 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path"); 560 561 // Wait for Unmount and Format calls to end. 562 message_loop_.RunUntilIdle(); 563 564 EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count()); 565 EXPECT_EQ("/device/mount_path", 566 fake_cros_disks_client_->last_unmount_device_path()); 567 EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, 568 fake_cros_disks_client_->last_unmount_options()); 569 EXPECT_EQ(2, fake_cros_disks_client_->format_call_count()); 570 EXPECT_EQ("/device/source_path", 571 fake_cros_disks_client_->last_format_device_path()); 572 EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem()); 573 574 // Simulate cros_disks reporting success. 575 fake_cros_disks_client_->SendFormatCompletedEvent( 576 chromeos::FORMAT_ERROR_NONE, "/device/source_path"); 577} 578 579} // namespace 580