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