storage_monitor_chromeos_unittest.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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/message_loop/message_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 "chromeos/disks/mock_disk_mount_manager.h"
20#include "content/public/test/test_browser_thread.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace chromeos {
24
25namespace {
26
27using content::BrowserThread;
28using disks::DiskMountManager;
29using testing::_;
30
31const char kDeviceNameWithManufacturerDetails[] = "110 KB (CompanyA, Z101)";
32const char kDevice1[] = "/dev/d1";
33const char kDevice1Name[] = "d1";
34const char kDevice1NameWithSizeInfo[] = "110 KB d1";
35const char kDevice2[] = "/dev/disk/d2";
36const char kDevice2Name[] = "d2";
37const char kDevice2NameWithSizeInfo[] = "207 KB 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 chrome::StorageInfo::MakeDeviceId(
56      chrome::StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
57      chrome::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 chrome::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(
81      const base::FilePath& path,
82      chrome::StorageInfo* device_info) const OVERRIDE {
83    return StorageMonitorCros::GetStorageInfoForPath(path, device_info);
84  }
85  virtual void EjectDevice(
86      const std::string& device_id,
87      base::Callback<void(EjectStatus)> callback) OVERRIDE {
88    StorageMonitorCros::EjectDevice(device_id, callback);
89  }
90
91 private:
92  DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros);
93};
94
95// Wrapper class to test StorageMonitorCros.
96class StorageMonitorCrosTest : public testing::Test {
97 public:
98  StorageMonitorCrosTest();
99  virtual ~StorageMonitorCrosTest();
100
101  void EjectNotify(chrome::StorageMonitor::EjectStatus status);
102
103 protected:
104  // testing::Test:
105  virtual void SetUp() OVERRIDE;
106  virtual void TearDown() OVERRIDE;
107
108  void MountDevice(MountError error_code,
109                   const DiskMountManager::MountPointInfo& mount_info,
110                   const std::string& unique_id,
111                   const std::string& device_label,
112                   const std::string& vendor_name,
113                   const std::string& product_name,
114                   DeviceType device_type,
115                   uint64 device_size_in_bytes);
116
117  void UnmountDevice(MountError error_code,
118                     const DiskMountManager::MountPointInfo& mount_info);
119
120  uint64 GetDeviceStorageSize(const std::string& device_location);
121
122  // Create a directory named |dir| relative to the test directory.
123  // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
124  // subdirectory.
125  // Returns the full path to the created directory on success, or an empty
126  // path on failure.
127  base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir);
128
129  static void PostQuitToUIThread();
130  static void WaitForFileThread();
131
132  chrome::MockRemovableStorageObserver& observer() {
133    return *mock_storage_observer_;
134  }
135
136  base::MessageLoop ui_loop_;
137
138  scoped_ptr<TestStorageMonitorCros> monitor_;
139
140  // Owned by DiskMountManager.
141  disks::MockDiskMountManager* disk_mount_manager_mock_;
142
143  chrome::StorageMonitor::EjectStatus status_;
144
145 private:
146  content::TestBrowserThread ui_thread_;
147  content::TestBrowserThread file_thread_;
148
149  // Temporary directory for created test data.
150  base::ScopedTempDir scoped_temp_dir_;
151
152  // Objects that talks with StorageMonitorCros.
153  scoped_ptr<chrome::MockRemovableStorageObserver> mock_storage_observer_;
154
155  DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest);
156};
157
158StorageMonitorCrosTest::StorageMonitorCrosTest()
159    : disk_mount_manager_mock_(NULL),
160      status_(chrome::StorageMonitor::EJECT_FAILURE),
161      ui_thread_(BrowserThread::UI, &ui_loop_),
162      file_thread_(BrowserThread::FILE) {
163}
164
165StorageMonitorCrosTest::~StorageMonitorCrosTest() {
166}
167
168void StorageMonitorCrosTest::SetUp() {
169  ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
170  ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
171  file_thread_.Start();
172  disk_mount_manager_mock_ = new disks::MockDiskMountManager();
173  DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
174  disk_mount_manager_mock_->SetupDefaultReplies();
175
176  mock_storage_observer_.reset(new chrome::MockRemovableStorageObserver);
177
178  // Initialize the test subject.
179  monitor_.reset(new TestStorageMonitorCros());
180  monitor_->Init();
181  monitor_->AddObserver(mock_storage_observer_.get());
182}
183
184void StorageMonitorCrosTest::TearDown() {
185  monitor_->RemoveObserver(mock_storage_observer_.get());
186  monitor_.reset();
187
188  disk_mount_manager_mock_ = NULL;
189  DiskMountManager::Shutdown();
190  WaitForFileThread();
191}
192
193void StorageMonitorCrosTest::MountDevice(
194    MountError error_code,
195    const DiskMountManager::MountPointInfo& mount_info,
196    const std::string& unique_id,
197    const std::string& device_label,
198    const std::string& vendor_name,
199    const std::string& product_name,
200    DeviceType device_type,
201    uint64 device_size_in_bytes) {
202  if (error_code == MOUNT_ERROR_NONE) {
203    disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
204        mount_info, unique_id, device_label, vendor_name, product_name,
205        device_type, device_size_in_bytes);
206  }
207  monitor_->OnMountEvent(disks::DiskMountManager::MOUNTING, error_code,
208                         mount_info);
209  WaitForFileThread();
210}
211
212void StorageMonitorCrosTest::UnmountDevice(
213    MountError error_code,
214    const DiskMountManager::MountPointInfo& mount_info) {
215  monitor_->OnMountEvent(disks::DiskMountManager::UNMOUNTING, error_code,
216                         mount_info);
217  if (error_code == MOUNT_ERROR_NONE)
218    disk_mount_manager_mock_->RemoveDiskEntryForMountDevice(mount_info);
219  WaitForFileThread();
220}
221
222uint64 StorageMonitorCrosTest::GetDeviceStorageSize(
223    const std::string& device_location) {
224  chrome::StorageInfo info;
225  if (!monitor_->GetStorageInfoForPath(base::FilePath(device_location), &info))
226    return 0;
227
228  return info.total_size_in_bytes();
229}
230
231base::FilePath StorageMonitorCrosTest::CreateMountPoint(
232    const std::string& dir, bool with_dcim_dir) {
233  base::FilePath return_path(scoped_temp_dir_.path());
234  return_path = return_path.AppendASCII(dir);
235  base::FilePath path(return_path);
236  if (with_dcim_dir)
237    path = path.Append(chrome::kDCIMDirectoryName);
238  if (!file_util::CreateDirectory(path))
239    return base::FilePath();
240  return return_path;
241}
242
243// static
244void StorageMonitorCrosTest::PostQuitToUIThread() {
245  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
246                          base::MessageLoop::QuitClosure());
247}
248
249// static
250void StorageMonitorCrosTest::WaitForFileThread() {
251  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
252                          base::Bind(&PostQuitToUIThread));
253  base::MessageLoop::current()->Run();
254}
255
256void StorageMonitorCrosTest::EjectNotify(
257    chrome::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 = chrome::StorageInfo::MakeDeviceId(
317      chrome::StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
318      chrome::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  ui_loop_.RunUntilIdle();
478
479  EXPECT_EQ(chrome::StorageMonitor::EJECT_OK, status_);
480}
481
482}  // namespace
483
484}  // namespace chromeos
485