storage_monitor_chromeos_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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.h"
14#include "base/utf_string_conversions.h"
15#include "chrome/browser/storage_monitor/media_storage_util.h"
16#include "chrome/browser/storage_monitor/mock_removable_storage_observer.h"
17#include "chrome/browser/storage_monitor/removable_device_constants.h"
18#include "chrome/browser/storage_monitor/storage_info.h"
19#include "chrome/browser/storage_monitor/test_media_transfer_protocol_manager_linux.h"
20#include "chromeos/disks/mock_disk_mount_manager.h"
21#include "content/public/test/test_browser_thread.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24namespace chromeos {
25
26namespace {
27
28using content::BrowserThread;
29using disks::DiskMountManager;
30using testing::_;
31
32const char kDeviceNameWithManufacturerDetails[] = "110 KB (CompanyA, Z101)";
33const char kDevice1[] = "/dev/d1";
34const char kDevice1Name[] = "d1";
35const char kDevice1NameWithSizeInfo[] = "110 KB d1";
36const char kDevice2[] = "/dev/disk/d2";
37const char kDevice2Name[] = "d2";
38const char kDevice2NameWithSizeInfo[] = "207 KB d2";
39const char kEmptyDeviceLabel[] = "";
40const char kMountPointA[] = "mnt_a";
41const char kMountPointB[] = "mnt_b";
42const char kSDCardDeviceName1[] = "8.6 MB Amy_SD";
43const char kSDCardDeviceName2[] = "8.6 MB SD Card";
44const char kSDCardMountPoint1[] = "media/removable/Amy_SD";
45const char kSDCardMountPoint2[] = "media/removable/SD Card";
46const char kProductName[] = "Z101";
47const char kUniqueId1[] = "FFFF-FFFF";
48const char kUniqueId2[] = "FFFF-FF0F";
49const char kVendorName[] = "CompanyA";
50
51uint64 kDevice1SizeInBytes = 113048;
52uint64 kDevice2SizeInBytes = 212312;
53uint64 kSDCardSizeInBytes = 9000000;
54
55std::string GetDCIMDeviceId(const std::string& unique_id) {
56  return chrome::MediaStorageUtil::MakeDeviceId(
57      chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM,
58      chrome::kFSUniqueIdPrefix + unique_id);
59}
60
61// A test version of StorageMonitorCros that exposes protected methods to tests.
62class TestStorageMonitorCros : public StorageMonitorCros {
63 public:
64  TestStorageMonitorCros() {}
65
66  virtual ~TestStorageMonitorCros() {}
67
68  void Init() {
69    SetMediaTransferProtocolManagerForTest(
70        new chrome::TestMediaTransferProtocolManagerLinux());
71    StorageMonitorCros::Init();
72  }
73
74  virtual void OnMountEvent(
75      disks::DiskMountManager::MountEvent event,
76      MountError error_code,
77      const disks::DiskMountManager::MountPointInfo& mount_info) OVERRIDE {
78    StorageMonitorCros::OnMountEvent(event, error_code, mount_info);
79  }
80
81  virtual bool GetStorageInfoForPath(
82      const base::FilePath& path,
83      chrome::StorageInfo* device_info) const OVERRIDE {
84    return StorageMonitorCros::GetStorageInfoForPath(path, device_info);
85  }
86  virtual void EjectDevice(
87      const std::string& device_id,
88      base::Callback<void(EjectStatus)> callback) OVERRIDE {
89    StorageMonitorCros::EjectDevice(device_id, callback);
90  }
91
92 private:
93  DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorCros);
94};
95
96// Wrapper class to test StorageMonitorCros.
97class StorageMonitorCrosTest : public testing::Test {
98 public:
99  StorageMonitorCrosTest();
100  virtual ~StorageMonitorCrosTest();
101
102  void EjectNotify(chrome::StorageMonitor::EjectStatus status);
103
104 protected:
105  // testing::Test:
106  virtual void SetUp() OVERRIDE;
107  virtual void TearDown() OVERRIDE;
108
109  void MountDevice(MountError error_code,
110                   const DiskMountManager::MountPointInfo& mount_info,
111                   const std::string& unique_id,
112                   const std::string& device_label,
113                   const std::string& vendor_name,
114                   const std::string& product_name,
115                   DeviceType device_type,
116                   uint64 device_size_in_bytes);
117
118  void UnmountDevice(MountError error_code,
119                     const DiskMountManager::MountPointInfo& mount_info);
120
121  uint64 GetDeviceStorageSize(const std::string& device_location);
122
123  // Create a directory named |dir| relative to the test directory.
124  // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
125  // subdirectory.
126  // Returns the full path to the created directory on success, or an empty
127  // path on failure.
128  base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir);
129
130  static void PostQuitToUIThread();
131  static void WaitForFileThread();
132
133  chrome::MockRemovableStorageObserver& observer() {
134    return *mock_storage_observer_;
135  }
136
137  MessageLoop ui_loop_;
138
139  scoped_ptr<TestStorageMonitorCros> monitor_;
140
141  // Owned by DiskMountManager.
142  disks::MockDiskMountManager* disk_mount_manager_mock_;
143
144  chrome::StorageMonitor::EjectStatus status_;
145
146 private:
147  content::TestBrowserThread ui_thread_;
148  content::TestBrowserThread file_thread_;
149
150  // Temporary directory for created test data.
151  base::ScopedTempDir scoped_temp_dir_;
152
153  // Objects that talks with StorageMonitorCros.
154  scoped_ptr<chrome::MockRemovableStorageObserver> mock_storage_observer_;
155
156  DISALLOW_COPY_AND_ASSIGN(StorageMonitorCrosTest);
157};
158
159StorageMonitorCrosTest::StorageMonitorCrosTest()
160    : disk_mount_manager_mock_(NULL),
161      status_(chrome::StorageMonitor::EJECT_FAILURE),
162      ui_thread_(BrowserThread::UI, &ui_loop_),
163      file_thread_(BrowserThread::FILE) {
164}
165
166StorageMonitorCrosTest::~StorageMonitorCrosTest() {
167}
168
169void StorageMonitorCrosTest::SetUp() {
170  ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
171  ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
172  file_thread_.Start();
173  disk_mount_manager_mock_ = new disks::MockDiskMountManager();
174  DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
175  disk_mount_manager_mock_->SetupDefaultReplies();
176
177  mock_storage_observer_.reset(new chrome::MockRemovableStorageObserver);
178
179  // Initialize the test subject.
180  monitor_.reset(new TestStorageMonitorCros());
181  monitor_->Init();
182  monitor_->AddObserver(mock_storage_observer_.get());
183}
184
185void StorageMonitorCrosTest::TearDown() {
186  monitor_->RemoveObserver(mock_storage_observer_.get());
187  monitor_.reset();
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  chrome::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(chrome::kDCIMDirectoryName);
239  if (!file_util::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                          MessageLoop::QuitClosure());
248}
249
250// static
251void StorageMonitorCrosTest::WaitForFileThread() {
252  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
253                          base::Bind(&PostQuitToUIThread));
254  MessageLoop::current()->Run();
255}
256
257void StorageMonitorCrosTest::EjectNotify(
258    chrome::StorageMonitor::EjectStatus status) {
259  status_ = status;
260}
261
262// Simple test case where we attach and detach a media device.
263TEST_F(StorageMonitorCrosTest, BasicAttachDetach) {
264  base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
265  ASSERT_FALSE(mount_path1.empty());
266  DiskMountManager::MountPointInfo mount_info(kDevice1,
267                                              mount_path1.value(),
268                                              MOUNT_TYPE_DEVICE,
269                                              disks::MOUNT_CONDITION_NONE);
270  MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kDevice1Name,
271              kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
272  EXPECT_EQ(1, observer().attach_calls());
273  EXPECT_EQ(0, observer().detach_calls());
274  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_attached().device_id);
275  EXPECT_EQ(ASCIIToUTF16(kDevice1NameWithSizeInfo),
276            observer().last_attached().name);
277  EXPECT_EQ(mount_path1.value(), observer().last_attached().location);
278
279  UnmountDevice(MOUNT_ERROR_NONE, mount_info);
280  EXPECT_EQ(1, observer().attach_calls());
281  EXPECT_EQ(1, observer().detach_calls());
282  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), 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), observer().last_attached().device_id);
295  EXPECT_EQ(ASCIIToUTF16(kDevice2NameWithSizeInfo),
296            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), observer().last_detached().device_id);
303}
304
305// Removable mass storage devices with no dcim folder are also recognized.
306TEST_F(StorageMonitorCrosTest, NoDCIM) {
307  testing::Sequence mock_sequence;
308  base::FilePath mount_path = CreateMountPoint(kMountPointA, false);
309  const std::string kUniqueId = "FFFF-FFFF";
310  ASSERT_FALSE(mount_path.empty());
311  DiskMountManager::MountPointInfo mount_info(kDevice1,
312                                              mount_path.value(),
313                                              MOUNT_TYPE_DEVICE,
314                                              disks::MOUNT_CONDITION_NONE);
315  const std::string device_id = chrome::MediaStorageUtil::MakeDeviceId(
316      chrome::MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM,
317      chrome::kFSUniqueIdPrefix + kUniqueId);
318  MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId, kDevice1Name,
319              kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
320  EXPECT_EQ(1, observer().attach_calls());
321  EXPECT_EQ(0, observer().detach_calls());
322  EXPECT_EQ(device_id, observer().last_attached().device_id);
323  EXPECT_EQ(ASCIIToUTF16(kDevice1NameWithSizeInfo),
324            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), observer().last_attached().device_id);
373  EXPECT_EQ(ASCIIToUTF16(kSDCardDeviceName1),
374            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), observer().last_detached().device_id);
381
382  base::FilePath mount_path2 = CreateMountPoint(kSDCardMountPoint2, true);
383  ASSERT_FALSE(mount_path2.empty());
384  DiskMountManager::MountPointInfo mount_info2(kSDCardDeviceName2,
385                                               mount_path2.value(),
386                                               MOUNT_TYPE_DEVICE,
387                                               disks::MOUNT_CONDITION_NONE);
388  MountDevice(MOUNT_ERROR_NONE, mount_info2, kUniqueId2, kSDCardDeviceName2,
389              kVendorName, kProductName, DEVICE_TYPE_SD, kSDCardSizeInBytes);
390  EXPECT_EQ(2, observer().attach_calls());
391  EXPECT_EQ(1, observer().detach_calls());
392  EXPECT_EQ(GetDCIMDeviceId(kUniqueId2), observer().last_attached().device_id);
393  EXPECT_EQ(ASCIIToUTF16(kSDCardDeviceName2),
394            observer().last_attached().name);
395  EXPECT_EQ(mount_path2.value(), observer().last_attached().location);
396
397  UnmountDevice(MOUNT_ERROR_NONE, mount_info2);
398  EXPECT_EQ(2, observer().attach_calls());
399  EXPECT_EQ(2, observer().detach_calls());
400  EXPECT_EQ(GetDCIMDeviceId(kUniqueId2), observer().last_detached().device_id);
401}
402
403TEST_F(StorageMonitorCrosTest, AttachDeviceWithEmptyLabel) {
404  base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
405  ASSERT_FALSE(mount_path1.empty());
406  DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
407                                              mount_path1.value(),
408                                              MOUNT_TYPE_DEVICE,
409                                              disks::MOUNT_CONDITION_NONE);
410  MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
411              kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
412  EXPECT_EQ(1, observer().attach_calls());
413  EXPECT_EQ(0, observer().detach_calls());
414  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_attached().device_id);
415  EXPECT_EQ(ASCIIToUTF16(kDeviceNameWithManufacturerDetails),
416            observer().last_attached().name);
417  EXPECT_EQ(mount_path1.value(), observer().last_attached().location);
418
419  UnmountDevice(MOUNT_ERROR_NONE, mount_info);
420  EXPECT_EQ(1, observer().attach_calls());
421  EXPECT_EQ(1, observer().detach_calls());
422  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_detached().device_id);
423}
424
425TEST_F(StorageMonitorCrosTest, GetStorageSize) {
426  base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
427  ASSERT_FALSE(mount_path1.empty());
428  DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
429                                              mount_path1.value(),
430                                              MOUNT_TYPE_DEVICE,
431                                              disks::MOUNT_CONDITION_NONE);
432  MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
433              kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
434  EXPECT_EQ(1, observer().attach_calls());
435  EXPECT_EQ(0, observer().detach_calls());
436  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_attached().device_id);
437  EXPECT_EQ(ASCIIToUTF16(kDeviceNameWithManufacturerDetails),
438            observer().last_attached().name);
439  EXPECT_EQ(mount_path1.value(), observer().last_attached().location);
440
441  EXPECT_EQ(kDevice1SizeInBytes, GetDeviceStorageSize(mount_path1.value()));
442  UnmountDevice(MOUNT_ERROR_NONE, mount_info);
443  EXPECT_EQ(1, observer().attach_calls());
444  EXPECT_EQ(1, observer().detach_calls());
445  EXPECT_EQ(GetDCIMDeviceId(kUniqueId1), observer().last_detached().device_id);
446}
447
448void UnmountFake(const std::string& location,
449                 UnmountOptions options,
450                 const DiskMountManager::UnmountPathCallback& cb) {
451  cb.Run(chromeos::MOUNT_ERROR_NONE);
452}
453
454TEST_F(StorageMonitorCrosTest, EjectTest) {
455  base::FilePath mount_path1 = CreateMountPoint(kMountPointA, true);
456  ASSERT_FALSE(mount_path1.empty());
457  DiskMountManager::MountPointInfo mount_info(kEmptyDeviceLabel,
458                                              mount_path1.value(),
459                                              MOUNT_TYPE_DEVICE,
460                                              disks::MOUNT_CONDITION_NONE);
461  MountDevice(MOUNT_ERROR_NONE, mount_info, kUniqueId1, kEmptyDeviceLabel,
462              kVendorName, kProductName, DEVICE_TYPE_USB, kDevice1SizeInBytes);
463  EXPECT_EQ(1, observer().attach_calls());
464  EXPECT_EQ(0, observer().detach_calls());
465
466  ON_CALL(*disk_mount_manager_mock_, UnmountPath(_, _, _))
467      .WillByDefault(testing::Invoke(&UnmountFake));
468  EXPECT_CALL(*disk_mount_manager_mock_,
469              UnmountPath(observer().last_attached().location, _, _));
470  monitor_->EjectDevice(observer().last_attached().device_id,
471                        base::Bind(&StorageMonitorCrosTest::EjectNotify,
472                                   base::Unretained(this)));
473  ui_loop_.RunUntilIdle();
474
475  EXPECT_EQ(chrome::StorageMonitor::EJECT_OK, status_);
476}
477
478}  // namespace
479
480}  // namespace chromeos
481