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