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/files/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         true /* on_removable_device */);
    210   }
    211   monitor_->OnMountEvent(DiskMountManager::MOUNTING, error_code, mount_info);
    212   WaitForFileThread();
    213 }
    214 
    215 void StorageMonitorCrosTest::UnmountDevice(
    216     chromeos::MountError error_code,
    217     const DiskMountManager::MountPointInfo& mount_info) {
    218   monitor_->OnMountEvent(DiskMountManager::UNMOUNTING, error_code, mount_info);
    219   if (error_code == chromeos::MOUNT_ERROR_NONE)
    220     disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info);
    221   WaitForFileThread();
    222 }
    223 
    224 uint64 StorageMonitorCrosTest::GetDeviceStorageSize(
    225     const std::string& device_location) {
    226   StorageInfo info;
    227   if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info))
    228     return 0;
    229 
    230   return info.total_size_in_bytes();
    231 }
    232 
    233 base::FilePath StorageMonitorCrosTest::CreateMountPoint(
    234     const std::string& dir, bool with_dcim_dir) {
    235   base::FilePath return_path(scoped_temp_dir_.path());
    236   return_path = return_path.AppendASCII(dir);
    237   base::FilePath path(return_path);
    238   if (with_dcim_dir)
    239     path = path.Append(kDCIMDirectoryName);
    240   if (!base::CreateDirectory(path))
    241     return base::FilePath();
    242   return return_path;
    243 }
    244 
    245 // static
    246 void StorageMonitorCrosTest::PostQuitToUIThread() {
    247   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    248                           base::MessageLoop::QuitClosure());
    249 }
    250 
    251 // static
    252 void StorageMonitorCrosTest::WaitForFileThread() {
    253   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    254                           base::Bind(&PostQuitToUIThread));
    255   base::MessageLoop::current()->Run();
    256 }
    257 
    258 void StorageMonitorCrosTest::EjectNotify(StorageMonitor::EjectStatus status) {
    259   status_ = status;
    260 }
    261 
    262 // Simple test case where we attach and detach a media device.
    263 TEST_F(StorageMonitorCrosTest, BasicAttachDetach) {
    264   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    265   ASSERT_FALSE(mount_path1.empty());
    266   DiskMountManager::MountPointInfo mount_info(
    267       kDevice1,
    268       mount_path1.value(),
    269       chromeos::MOUNT_TYPE_DEVICE,
    270       chromeos::disks::MOUNT_CONDITION_NONE);
    271   MountDevice(chromeos::MOUNT_ERROR_NONE,
    272               mount_info,
    273               kUniqueId1,
    274               kDevice1Name,
    275               kVendorName,
    276               kProductName,
    277               chromeos::DEVICE_TYPE_USB,
    278               kDevice1SizeInBytes);
    279   EXPECT_EQ(1, observer().attach_calls());
    280   EXPECT_EQ(0, observer().detach_calls());
    281   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    282             observer().last_attached().device_id());
    283   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    284 
    285   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    286   EXPECT_EQ(1, observer().attach_calls());
    287   EXPECT_EQ(1, observer().detach_calls());
    288   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    289             observer().last_detached().device_id());
    290 
    291   base::FilePath mount_path2 = CreateMountPoint(kMountPointB, true);
    292   ASSERT_FALSE(mount_path2.empty());
    293   DiskMountManager::MountPointInfo mount_info2(
    294       kDevice2,
    295       mount_path2.value(),
    296       chromeos::MOUNT_TYPE_DEVICE,
    297       chromeos::disks::MOUNT_CONDITION_NONE);
    298   MountDevice(chromeos::MOUNT_ERROR_NONE,
    299               mount_info2,
    300               kUniqueId2,
    301               kDevice2Name,
    302               kVendorName,
    303               kProductName,
    304               chromeos::DEVICE_TYPE_USB,
    305               kDevice2SizeInBytes);
    306   EXPECT_EQ(2, observer().attach_calls());
    307   EXPECT_EQ(1, observer().detach_calls());
    308   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    309             observer().last_attached().device_id());
    310   EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
    311 
    312   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info2);
    313   EXPECT_EQ(2, observer().attach_calls());
    314   EXPECT_EQ(2, observer().detach_calls());
    315   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    316             observer().last_detached().device_id());
    317 }
    318 
    319 // Removable mass storage devices with no dcim folder are also recognized.
    320 TEST_F(StorageMonitorCrosTest, NoDCIM) {
    321   testing::Sequence mock_sequence;
    322   base::FilePath mount_path = CreateMountPoint(kMountPointA, false);
    323   const std::string kUniqueId = "FFFF-FFFF";
    324   ASSERT_FALSE(mount_path.empty());
    325   DiskMountManager::MountPointInfo mount_info(
    326       kDevice1,
    327       mount_path.value(),
    328       chromeos::MOUNT_TYPE_DEVICE,
    329       chromeos::disks::MOUNT_CONDITION_NONE);
    330   const std::string device_id = StorageInfo::MakeDeviceId(
    331       StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
    332       kFSUniqueIdPrefix + kUniqueId);
    333   MountDevice(chromeos::MOUNT_ERROR_NONE,
    334               mount_info,
    335               kUniqueId,
    336               kDevice1Name,
    337               kVendorName,
    338               kProductName,
    339               chromeos::DEVICE_TYPE_USB,
    340               kDevice1SizeInBytes);
    341   EXPECT_EQ(1, observer().attach_calls());
    342   EXPECT_EQ(0, observer().detach_calls());
    343   EXPECT_EQ(device_id, observer().last_attached().device_id());
    344   EXPECT_EQ(mount_path.value(), observer().last_attached().location());
    345 }
    346 
    347 // Non device mounts and mount errors are ignored.
    348 TEST_F(StorageMonitorCrosTest, Ignore) {
    349   testing::Sequence mock_sequence;
    350   base::FilePath mount_path = CreateMountPoint(kMountPointA, true);
    351   const std::string kUniqueId = "FFFF-FFFF";
    352   ASSERT_FALSE(mount_path.empty());
    353 
    354   // Mount error.
    355   DiskMountManager::MountPointInfo mount_info(
    356       kDevice1,
    357       mount_path.value(),
    358       chromeos::MOUNT_TYPE_DEVICE,
    359       chromeos::disks::MOUNT_CONDITION_NONE);
    360   MountDevice(chromeos::MOUNT_ERROR_UNKNOWN,
    361               mount_info,
    362               kUniqueId,
    363               kDevice1Name,
    364               kVendorName,
    365               kProductName,
    366               chromeos::DEVICE_TYPE_USB,
    367               kDevice1SizeInBytes);
    368   EXPECT_EQ(0, observer().attach_calls());
    369   EXPECT_EQ(0, observer().detach_calls());
    370 
    371   // Not a device
    372   mount_info.mount_type = chromeos::MOUNT_TYPE_ARCHIVE;
    373   MountDevice(chromeos::MOUNT_ERROR_NONE,
    374               mount_info,
    375               kUniqueId,
    376               kDevice1Name,
    377               kVendorName,
    378               kProductName,
    379               chromeos::DEVICE_TYPE_USB,
    380               kDevice1SizeInBytes);
    381   EXPECT_EQ(0, observer().attach_calls());
    382   EXPECT_EQ(0, observer().detach_calls());
    383 
    384   // Unsupported file system.
    385   mount_info.mount_type = chromeos::MOUNT_TYPE_DEVICE;
    386   mount_info.mount_condition =
    387       chromeos::disks::MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
    388   MountDevice(chromeos::MOUNT_ERROR_NONE,
    389               mount_info,
    390               kUniqueId,
    391               kDevice1Name,
    392               kVendorName,
    393               kProductName,
    394               chromeos::DEVICE_TYPE_USB,
    395               kDevice1SizeInBytes);
    396   EXPECT_EQ(0, observer().attach_calls());
    397   EXPECT_EQ(0, observer().detach_calls());
    398 }
    399 
    400 TEST_F(StorageMonitorCrosTest, SDCardAttachDetach) {
    401   base::FilePath mount_path1 = CreateMountPoint(kSDCardMountPoint1, true);
    402   ASSERT_FALSE(mount_path1.empty());
    403   DiskMountManager::MountPointInfo mount_info1(
    404       kSDCardDeviceName1,
    405       mount_path1.value(),
    406       chromeos::MOUNT_TYPE_DEVICE,
    407       chromeos::disks::MOUNT_CONDITION_NONE);
    408   MountDevice(chromeos::MOUNT_ERROR_NONE,
    409               mount_info1,
    410               kUniqueId2,
    411               kSDCardDeviceName1,
    412               kVendorName,
    413               kProductName,
    414               chromeos::DEVICE_TYPE_SD,
    415               kSDCardSizeInBytes);
    416   EXPECT_EQ(1, observer().attach_calls());
    417   EXPECT_EQ(0, observer().detach_calls());
    418   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    419             observer().last_attached().device_id());
    420   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    421 
    422   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info1);
    423   EXPECT_EQ(1, observer().attach_calls());
    424   EXPECT_EQ(1, observer().detach_calls());
    425   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    426             observer().last_detached().device_id());
    427 
    428   base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true);
    429   ASSERT_FALSE(mount_path2.empty());
    430   DiskMountManager::MountPointInfo mount_info2(
    431       kSDCardDeviceName2,
    432       mount_path2.value(),
    433       chromeos::MOUNT_TYPE_DEVICE,
    434       chromeos::disks::MOUNT_CONDITION_NONE);
    435   MountDevice(chromeos::MOUNT_ERROR_NONE,
    436               mount_info2,
    437               kUniqueId2,
    438               kSDCardDeviceName2,
    439               kVendorName,
    440               kProductName,
    441               chromeos::DEVICE_TYPE_SD,
    442               kSDCardSizeInBytes);
    443   EXPECT_EQ(2, observer().attach_calls());
    444   EXPECT_EQ(1, observer().detach_calls());
    445   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    446             observer().last_attached().device_id());
    447   EXPECT_EQ(mount_path2.value(), observer().last_attached().location());
    448 
    449   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info2);
    450   EXPECT_EQ(2, observer().attach_calls());
    451   EXPECT_EQ(2, observer().detach_calls());
    452   EXPECT_EQ(GetDCIMDeviceId(kUniqueId2),
    453             observer().last_detached().device_id());
    454 }
    455 
    456 TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) {
    457   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    458   ASSERT_FALSE(mount_path1.empty());
    459   DiskMountManager::MountPointInfo mount_info(
    460       kEmptyDeviceLabel,
    461       mount_path1.value(),
    462       chromeos::MOUNT_TYPE_DEVICE,
    463       chromeos::disks::MOUNT_CONDITION_NONE);
    464   MountDevice(chromeos::MOUNT_ERROR_NONE,
    465               mount_info,
    466               kUniqueId1,
    467               kEmptyDeviceLabel,
    468               kVendorName,
    469               kProductName,
    470               chromeos::DEVICE_TYPE_USB,
    471               kDevice1SizeInBytes);
    472   EXPECT_EQ(1, observer().attach_calls());
    473   EXPECT_EQ(0, observer().detach_calls());
    474   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    475             observer().last_attached().device_id());
    476   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    477 
    478   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    479   EXPECT_EQ(1, observer().attach_calls());
    480   EXPECT_EQ(1, observer().detach_calls());
    481   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    482             observer().last_detached().device_id());
    483 }
    484 
    485 TEST_F(StorageMonitorCrosTest, GetStorageSize) {
    486   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    487   ASSERT_FALSE(mount_path1.empty());
    488   DiskMountManager::MountPointInfo mount_info(
    489       kEmptyDeviceLabel,
    490       mount_path1.value(),
    491       chromeos::MOUNT_TYPE_DEVICE,
    492       chromeos::disks::MOUNT_CONDITION_NONE);
    493   MountDevice(chromeos::MOUNT_ERROR_NONE,
    494               mount_info,
    495               kUniqueId1,
    496               kEmptyDeviceLabel,
    497               kVendorName,
    498               kProductName,
    499               chromeos::DEVICE_TYPE_USB,
    500               kDevice1SizeInBytes);
    501   EXPECT_EQ(1, observer().attach_calls());
    502   EXPECT_EQ(0, observer().detach_calls());
    503   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    504             observer().last_attached().device_id());
    505   EXPECT_EQ(mount_path1.value(), observer().last_attached().location());
    506 
    507   EXPECT_EQ(kDevice1SizeInBytes, GetDeviceStorageSize(mount_path1.value()));
    508   UnmountDevice(chromeos::MOUNT_ERROR_NONE, mount_info);
    509   EXPECT_EQ(1, observer().attach_calls());
    510   EXPECT_EQ(1, observer().detach_calls());
    511   EXPECT_EQ(GetDCIMDeviceId(kUniqueId1),
    512             observer().last_detached().device_id());
    513 }
    514 
    515 void UnmountFake(const std::string& location,
    516                  chromeos::UnmountOptions options,
    517                  const DiskMountManager::UnmountPathCallback& cb) {
    518   cb.Run(chromeos::MOUNT_ERROR_NONE);
    519 }
    520 
    521 TEST_F(StorageMonitorCrosTest, EjectTest) {
    522   base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
    523   ASSERT_FALSE(mount_path1.empty());
    524   DiskMountManager::MountPointInfo mount_info(
    525       kEmptyDeviceLabel,
    526       mount_path1.value(),
    527       chromeos::MOUNT_TYPE_DEVICE,
    528       chromeos::disks::MOUNT_CONDITION_NONE);
    529   MountDevice(chromeos::MOUNT_ERROR_NONE,
    530               mount_info,
    531               kUniqueId1,
    532               kEmptyDeviceLabel,
    533               kVendorName,
    534               kProductName,
    535               chromeos::DEVICE_TYPE_USB,
    536               kDevice1SizeInBytes);
    537   EXPECT_EQ(1, observer().attach_calls());
    538   EXPECT_EQ(0, observer().detach_calls());
    539 
    540   ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _))
    541       .WillByDefault(testing::Invoke(&UnmountFake));
    542   EXPECT_CALL(*disk_mount_manager_mock_,
    543               UnmountPath(observer().last_attached().location(), _, _));
    544   monitor_->EjectDevice(observer().last_attached().device_id(),
    545                         base::Bind(&StorageMonitorCrosTest::EjectNotify,
    546                                    base::Unretained(this)));
    547   base::RunLoop().RunUntilIdle();
    548 
    549   EXPECT_EQ(StorageMonitor::EJECT_OK, status_);
    550 }
    551 
    552 }  // namespace
    553 
    554 }  // namespace storage_monitor
    555