Home | History | Annotate | Download | only in storage_monitor
      1 // Copyright 2014 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 "components/storage_monitor/storage_monitor_chromeos.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/run_loop.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chromeos/disks/mock_disk_mount_manager.h"
     14 #include "components/storage_monitor/mock_removable_storage_observer.h"
     15 #include "components/storage_monitor/removable_device_constants.h"
     16 #include "components/storage_monitor/storage_info.h"
     17 #include "components/storage_monitor/test_media_transfer_protocol_manager_linux.h"
     18 #include "components/storage_monitor/test_storage_monitor.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/test/test_browser_thread_bundle.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 namespace storage_monitor {
     24 
     25 namespace {
     26 
     27 using content::BrowserThread;
     28 using chromeos::disks::DiskMountManager;
     29 using testing::_;
     30 
     31 const char kDevice1[] = "/dev/d1";
     32 const char kDevice1Name[] = "d1";
     33 const char kDevice2[] = "/dev/disk/d2";
     34 const char kDevice2Name[] = "d2";
     35 const char kEmptyDeviceLabel[] = "";
     36 const char kMountPointA[] = "mnt_a";
     37 const char kMountPointB[] = "mnt_b";
     38 const char kSDCardDeviceName1[] = "8.6 MB Amy_SD";
     39 const char kSDCardDeviceName2[] = "8.6 MB SD Card";
     40 const char kSDCardMountPoint1[] = "media/removable/Amy_SD";
     41 const char kSDCardMountPoint2[] = "media/removable/SD Card";
     42 const char kProductName[] = "Z101";
     43 const char kUniqueId1[] = "FFFF-FFFF";
     44 const char kUniqueId2[] = "FFFF-FF0F";
     45 const char kVendorName[] = "CompanyA";
     46 
     47 uint64 kDevice1SizeInBytes = 113048;
     48 uint64 kDevice2SizeInBytes = 212312;
     49 uint64 kSDCardSizeInBytes = 9000000;
     50 
     51 std::string GetDCIMDeviceId(const std::string& unique_id) {
     52   return StorageInfo::MakeDeviceId(
     53       StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
     54       kFSUniqueIdPrefix + unique_id);
     55 }
     56 
     57 // A test version of StorageMonitorCros that exposes protected methods to tests.
     58 class TestStorageMonitorCros : public StorageMonitorCros {
     59  public:
     60   TestStorageMonitorCros() {}
     61 
     62   virtual ~TestStorageMonitorCros() {}
     63 
     64   virtual void Init() OVERRIDE {
     65     SetMediaTransferProtocolManagerForTest(
     66         new TestMediaTransferProtocolManagerLinux());
     67     StorageMonitorCros::Init();
     68   }
     69 
     70   virtual void OnMountEvent(DiskMountManager::MountEvent event,
     71       chromeos::MountError error_code,
     72       const DiskMountManager::MountPointInfo& mount_info) OVERRIDE {
     73     StorageMonitorCros::OnMountEvent(event, error_code, mount_info);
     74   }
     75 
     76   virtual bool GetStorageInfoForPath(const base::FilePath& path,
     77                                      StorageInfo* device_info) const OVERRIDE {
     78     return StorageMonitorCros::GetStorageInfoForPath(path, device_info);
     79   }
     80   virtual void EjectDevice(
     81       const std::string& device_id,
     82       base::Callback<void(EjectStatus)> callback) OVERRIDE {
     83     StorageMonitorCros::EjectDevice(device_id, callback);
     84   }
     85 
     86  private:
     87   DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros);
     88 };
     89 
     90 // Wrapper class to test StorageMonitorCros.
     91 class StorageMonitorCrosTest : public testing::Test {
     92  public:
     93   StorageMonitorCrosTest();
     94   virtual ~StorageMonitorCrosTest();
     95 
     96   void EjectNotify(StorageMonitor::EjectStatus status);
     97 
     98  protected:
     99   // testing::Test:
    100   virtual void SetUp() OVERRIDE;
    101   virtual void TearDown() OVERRIDE;
    102 
    103   void MountDevice(chromeos::MountError error_code,
    104                    const DiskMountManager::MountPointInfo& mount_info,
    105                    const std::string& unique_id,
    106                    const std::string& device_label,
    107                    const std::string& vendor_name,
    108                    const std::string& product_name,
    109                    chromeos::DeviceType device_type,
    110                    uint64 device_size_in_bytes);
    111 
    112   void UnmountDevice(chromeos::MountError error_code,
    113                      const DiskMountManager::MountPointInfo& mount_info);
    114 
    115   uint64 GetDeviceStorageSize(const std::string& device_location);
    116 
    117   // Create a directory named |dir| relative to the test directory.
    118   // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
    119   // subdirectory.
    120   // Returns the full path to the created directory on success, or an empty
    121   // path on failure.
    122   base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir);
    123 
    124   static void PostQuitToUIThread();
    125   static void WaitForFileThread();
    126 
    127   MockRemovableStorageObserver& observer() {
    128     return *mock_storage_observer_;
    129   }
    130 
    131   TestStorageMonitorCros* monitor_;
    132 
    133   // Owned by DiskMountManager.
    134   chromeos::disks::MockDiskMountManager* disk_mount_manager_mock_;
    135 
    136   StorageMonitor::EjectStatus status_;
    137 
    138  private:
    139   content::TestBrowserThreadBundle thread_bundle_;
    140 
    141   // Temporary directory for created test data.
    142   base::ScopedTempDir scoped_temp_dir_;
    143 
    144   // Objects that talks with StorageMonitorCros.
    145   scoped_ptr<MockRemovableStorageObserver> mock_storage_observer_;
    146 
    147   DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest);
    148 };
    149 
    150 StorageMonitorCrosTest::StorageMonitorCrosTest()
    151     : monitor_(NULL),
    152       disk_mount_manager_mock_(NULL),
    153       status_(StorageMonitor::EJECT_FAILURE),
    154       thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD) {
    155 }
    156 
    157 StorageMonitorCrosTest::~StorageMonitorCrosTest() {
    158 }
    159 
    160 void StorageMonitorCrosTest::SetUp() {
    161   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
    162   ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
    163   disk_mount_manager_mock_ = new chromeos::disks::MockDiskMountManager();
    164   DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
    165   disk_mount_manager_mock_->SetupDefaultReplies();
    166 
    167   mock_storage_observer_.reset(new MockRemovableStorageObserver);
    168 
    169   // Initialize the test subject.
    170   TestStorageMonitor::Destroy();
    171   monitor_ = new TestStorageMonitorCros();
    172   scoped_ptr<StorageMonitor> pass_monitor(monitor_);
    173   StorageMonitor::SetStorageMonitorForTesting(pass_monitor.Pass());
    174 
    175   monitor_->Init();
    176   monitor_->AddObserver(mock_storage_observer_.get());
    177 }
    178 
    179 void StorageMonitorCrosTest::TearDown() {
    180   monitor_->RemoveObserver(mock_storage_observer_.get());
    181   monitor_ = NULL;
    182 
    183   disk_mount_manager_mock_ = NULL;
    184   DiskMountManager::Shutdown();
    185   WaitForFileThread();
    186 }
    187 
    188 void StorageMonitorCrosTest::MountDevice(
    189     chromeos::MountError error_code,
    190     const DiskMountManager::MountPointInfo& mount_info,
    191     const std::string& unique_id,
    192     const std::string& device_label,
    193     const std::string& vendor_name,
    194     const std::string& product_name,
    195     chromeos::DeviceType device_type,
    196     uint64 device_size_in_bytes) {
    197   if (error_code == chromeos::MOUNT_ERROR_NONE) {
    198     disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
    199         mount_info,
    200         unique_id,
    201         device_label,
    202         vendor_name,
    203         product_name,
    204         device_type,
    205         device_size_in_bytes,
    206         false /* is_parent */,
    207         true /* has_media */,
    208         false /* on_boot_device */);
    209   }
    210   monitor_->OnMountEvent(DiskMountManager::MOUNTING, error_code, mount_info);
    211   WaitForFileThread();
    212 }
    213 
    214 void StorageMonitorCrosTest::UnmountDevice(
    215     chromeos::MountError error_code,
    216     const DiskMountManager::MountPointInfo& mount_info) {
    217   monitor_->OnMountEvent(DiskMountManager::UNMOUNTING, error_code, mount_info);
    218   if (error_code == chromeos::MOUNT_ERROR_NONE)
    219     disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info);
    220   WaitForFileThread();
    221 }
    222 
    223 uint64 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 
    232 base::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
    245 void StorageMonitorCrosTest::PostQuitToUIThread() {
    246   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    247                           base::MessageLoop::QuitClosure());
    248 }
    249 
    250 // static
    251 void StorageMonitorCrosTest::WaitForFileThread() {
    252   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    253                           base::Bind(&PostQuitToUIThread));
    254   base::MessageLoop::current()->Run();
    255 }
    256 
    257 void StorageMonitorCrosTest::EjectNotify(StorageMonitor::EjectStatus status) {
    258   status_ = status;
    259 }
    260 
    261 // Simple test case where we attach and detach a media device.
    262 TEST_F(StorageMonitorCrosTest, BasicAttachDetach) {
    263   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    264   ASSERT_FALSE(mount_path1.empty());
    265   DiskMountManager::MountPointInfo mount_info(
    266       kDevice1,
    267       mount_path1.value(),
    268       chromeos::MOUNT_TYPE_DEVICE,
    269       chromeos::disks::MOUNT_CONDITION_NONE);
    270   MountDevice(chromeos::MOUNT_ERROR_NONE,
    271               mount_info,
    272               kUniqueId1,
    273               kDevice1Name,
    274               kVendorName,
    275               kProductName,
    276               chromeos::DEVICE_TYPE_USB,
    277               kDevice1SizeInBytes);
    278   EXPECT_EQ(1, observer().attach_calls());
    279   EXPECT_EQ(0, observer().detach_calls());
    280   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    281             observer().last_attached().device_id());
    282   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    283 
    284   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    285   EXPECT_EQ(1, observer().attach_calls());
    286   EXPECT_EQ(1, observer().detach_calls());
    287   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    288             observer().last_detached().device_id());
    289 
    290   base::FilePath mount_path2 = CreateMountPoint(kMountPointB, true);
    291   ASSERT_FALSE(mount_path2.empty());
    292   DiskMountManager::MountPointInfo mount_info2(
    293       kDevice2,
    294       mount_path2.value(),
    295       chromeos::MOUNT_TYPE_DEVICE,
    296       chromeos::disks::MOUNT_CONDITION_NONE);
    297   MountDevice(chromeos::MOUNT_ERROR_NONE,
    298               mount_info2,
    299               kUniqueId2,
    300               kDevice2Name,
    301               kVendorName,
    302               kProductName,
    303               chromeos::DEVICE_TYPE_USB,
    304               kDevice2SizeInBytes);
    305   EXPECT_EQ(2, observer().attach_calls());
    306   EXPECT_EQ(1, observer().detach_calls());
    307   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    308             observer().last_attached().device_id());
    309   EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
    310 
    311   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info2);
    312   EXPECT_EQ(2, observer().attach_calls());
    313   EXPECT_EQ(2, observer().detach_calls());
    314   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    315             observer().last_detached().device_id());
    316 }
    317 
    318 // Removable mass storage devices with no dcim folder are also recognized.
    319 TEST_F(StorageMonitorCrosTest, NoDCIM) {
    320   testing::Sequence mock_sequence;
    321   base::FilePath mount_path = CreateMountPoint(kMountPointA, false);
    322   const std::string kUniqueId = "FFFF-FFFF";
    323   ASSERT_FALSE(mount_path.empty());
    324   DiskMountManager::MountPointInfo mount_info(
    325       kDevice1,
    326       mount_path.value(),
    327       chromeos::MOUNT_TYPE_DEVICE,
    328       chromeos::disks::MOUNT_CONDITION_NONE);
    329   const std::string device_id = StorageInfo::MakeDeviceId(
    330       StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
    331       kFSUniqueIdPrefix + kUniqueId);
    332   MountDevice(chromeos::MOUNT_ERROR_NONE,
    333               mount_info,
    334               kUniqueId,
    335               kDevice1Name,
    336               kVendorName,
    337               kProductName,
    338               chromeos::DEVICE_TYPE_USB,
    339               kDevice1SizeInBytes);
    340   EXPECT_EQ(1, observer().attach_calls());
    341   EXPECT_EQ(0, observer().detach_calls());
    342   EXPECT_EQ(device_id, observer().last_attached().device_id());
    343   EXPECT_EQ(mount_path.value(), observer().last_attached().location());
    344 }
    345 
    346 // Non device mounts and mount errors are ignored.
    347 TEST_F(StorageMonitorCrosTest, Ignore) {
    348   testing::Sequence mock_sequence;
    349   base::FilePath mount_path = CreateMountPoint(kMountPointA, true);
    350   const std::string kUniqueId = "FFFF-FFFF";
    351   ASSERT_FALSE(mount_path.empty());
    352 
    353   // Mount error.
    354   DiskMountManager::MountPointInfo mount_info(
    355       kDevice1,
    356       mount_path.value(),
    357       chromeos::MOUNT_TYPE_DEVICE,
    358       chromeos::disks::MOUNT_CONDITION_NONE);
    359   MountDevice(chromeos::MOUNT_ERROR_UNKNOWN,
    360               mount_info,
    361               kUniqueId,
    362               kDevice1Name,
    363               kVendorName,
    364               kProductName,
    365               chromeos::DEVICE_TYPE_USB,
    366               kDevice1SizeInBytes);
    367   EXPECT_EQ(0, observer().attach_calls());
    368   EXPECT_EQ(0, observer().detach_calls());
    369 
    370   // Not a device
    371   mount_info.mount_type = chromeos::MOUNT_TYPE_ARCHIVE;
    372   MountDevice(chromeos::MOUNT_ERROR_NONE,
    373               mount_info,
    374               kUniqueId,
    375               kDevice1Name,
    376               kVendorName,
    377               kProductName,
    378               chromeos::DEVICE_TYPE_USB,
    379               kDevice1SizeInBytes);
    380   EXPECT_EQ(0, observer().attach_calls());
    381   EXPECT_EQ(0, observer().detach_calls());
    382 
    383   // Unsupported file system.
    384   mount_info.mount_type = chromeos::MOUNT_TYPE_DEVICE;
    385   mount_info.mount_condition =
    386       chromeos::disks::MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
    387   MountDevice(chromeos::MOUNT_ERROR_NONE,
    388               mount_info,
    389               kUniqueId,
    390               kDevice1Name,
    391               kVendorName,
    392               kProductName,
    393               chromeos::DEVICE_TYPE_USB,
    394               kDevice1SizeInBytes);
    395   EXPECT_EQ(0, observer().attach_calls());
    396   EXPECT_EQ(0, observer().detach_calls());
    397 }
    398 
    399 TEST_F(StorageMonitorCrosTest, SDCardAttachDetach) {
    400   base::FilePath mount_path1 = CreateMountPoint(kSDCardMountPoint1, true);
    401   ASSERT_FALSE(mount_path1.empty());
    402   DiskMountManager::MountPointInfo mount_info1(
    403       kSDCardDeviceName1,
    404       mount_path1.value(),
    405       chromeos::MOUNT_TYPE_DEVICE,
    406       chromeos::disks::MOUNT_CONDITION_NONE);
    407   MountDevice(chromeos::MOUNT_ERROR_NONE,
    408               mount_info1,
    409               kUniqueId2,
    410               kSDCardDeviceName1,
    411               kVendorName,
    412               kProductName,
    413               chromeos::DEVICE_TYPE_SD,
    414               kSDCardSizeInBytes);
    415   EXPECT_EQ(1, observer().attach_calls());
    416   EXPECT_EQ(0, observer().detach_calls());
    417   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    418             observer().last_attached().device_id());
    419   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    420 
    421   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info1);
    422   EXPECT_EQ(1, observer().attach_calls());
    423   EXPECT_EQ(1, observer().detach_calls());
    424   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    425             observer().last_detached().device_id());
    426 
    427   base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true);
    428   ASSERT_FALSE(mount_path2.empty());
    429   DiskMountManager::MountPointInfo mount_info2(
    430       kSDCardDeviceName2,
    431       mount_path2.value(),
    432       chromeos::MOUNT_TYPE_DEVICE,
    433       chromeos::disks::MOUNT_CONDITION_NONE);
    434   MountDevice(chromeos::MOUNT_ERROR_NONE,
    435               mount_info2,
    436               kUniqueId2,
    437               kSDCardDeviceName2,
    438               kVendorName,
    439               kProductName,
    440               chromeos::DEVICE_TYPE_SD,
    441               kSDCardSizeInBytes);
    442   EXPECT_EQ(2, observer().attach_calls());
    443   EXPECT_EQ(1, observer().detach_calls());
    444   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    445             observer().last_attached().device_id());
    446   EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
    447 
    448   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info2);
    449   EXPECT_EQ(2, observer().attach_calls());
    450   EXPECT_EQ(2, observer().detach_calls());
    451   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    452             observer().last_detached().device_id());
    453 }
    454 
    455 TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) {
    456   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    457   ASSERT_FALSE(mount_path1.empty());
    458   DiskMountManager::MountPointInfo mount_info(
    459       kEmptyDeviceLabel,
    460       mount_path1.value(),
    461       chromeos::MOUNT_TYPE_DEVICE,
    462       chromeos::disks::MOUNT_CONDITION_NONE);
    463   MountDevice(chromeos::MOUNT_ERROR_NONE,
    464               mount_info,
    465               kUniqueId1,
    466               kEmptyDeviceLabel,
    467               kVendorName,
    468               kProductName,
    469               chromeos::DEVICE_TYPE_USB,
    470               kDevice1SizeInBytes);
    471   EXPECT_EQ(1, observer().attach_calls());
    472   EXPECT_EQ(0, observer().detach_calls());
    473   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    474             observer().last_attached().device_id());
    475   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    476 
    477   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    478   EXPECT_EQ(1, observer().attach_calls());
    479   EXPECT_EQ(1, observer().detach_calls());
    480   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    481             observer().last_detached().device_id());
    482 }
    483 
    484 TEST_F(StorageMonitorCrosTest, GetStorageSize) {
    485   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    486   ASSERT_FALSE(mount_path1.empty());
    487   DiskMountManager::MountPointInfo mount_info(
    488       kEmptyDeviceLabel,
    489       mount_path1.value(),
    490       chromeos::MOUNT_TYPE_DEVICE,
    491       chromeos::disks::MOUNT_CONDITION_NONE);
    492   MountDevice(chromeos::MOUNT_ERROR_NONE,
    493               mount_info,
    494               kUniqueId1,
    495               kEmptyDeviceLabel,
    496               kVendorName,
    497               kProductName,
    498               chromeos::DEVICE_TYPE_USB,
    499               kDevice1SizeInBytes);
    500   EXPECT_EQ(1, observer().attach_calls());
    501   EXPECT_EQ(0, observer().detach_calls());
    502   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    503             observer().last_attached().device_id());
    504   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    505 
    506   EXPECT_EQ(kDevice1SizeInBytes, GetDeviceStorageSize(mount_path1.value()));
    507   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    508   EXPECT_EQ(1, observer().attach_calls());
    509   EXPECT_EQ(1, observer().detach_calls());
    510   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    511             observer().last_detached().device_id());
    512 }
    513 
    514 void UnmountFake(const std::string& location,
    515                  chromeos::UnmountOptions options,
    516                  const DiskMountManager::UnmountPathCallback& cb) {
    517   cb.Run(chromeos::MOUNT_ERROR_NONE);
    518 }
    519 
    520 TEST_F(StorageMonitorCrosTest, EjectTest) {
    521   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    522   ASSERT_FALSE(mount_path1.empty());
    523   DiskMountManager::MountPointInfo mount_info(
    524       kEmptyDeviceLabel,
    525       mount_path1.value(),
    526       chromeos::MOUNT_TYPE_DEVICE,
    527       chromeos::disks::MOUNT_CONDITION_NONE);
    528   MountDevice(chromeos::MOUNT_ERROR_NONE,
    529               mount_info,
    530               kUniqueId1,
    531               kEmptyDeviceLabel,
    532               kVendorName,
    533               kProductName,
    534               chromeos::DEVICE_TYPE_USB,
    535               kDevice1SizeInBytes);
    536   EXPECT_EQ(1, observer().attach_calls());
    537   EXPECT_EQ(0, observer().detach_calls());
    538 
    539   ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _))
    540       .WillByDefault(testing::Invoke(&UnmountFake));
    541   EXPECT_CALL(*disk_mount_manager_mock_,
    542               UnmountPath(observer().last_attached().location(), _, _));
    543   monitor_->EjectDevice(observer().last_attached().device_id(),
    544                         base::Bind(&StorageMonitorCrosTest::EjectNotify,
    545                                    base::Unretained(this)));
    546   base::RunLoop().RunUntilIdle();
    547 
    548   EXPECT_EQ(StorageMonitor::EJECT_OK, status_);
    549 }
    550 
    551 }  // namespace
    552 
    553 }  // namespace storage_monitor
    554