disk_mount_manager_unittest.cc revision 010d83a9304c5a91596085d917d248abff47903a
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#include "base/bind.h"
6#include "base/message_loop/message_loop.h"
7#include "chromeos/dbus/fake_cros_disks_client.h"
8#include "chromeos/dbus/fake_dbus_thread_manager.h"
9#include "chromeos/disks/disk_mount_manager.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using chromeos::disks::DiskMountManager;
14using chromeos::CrosDisksClient;
15using chromeos::DBusThreadManager;
16using chromeos::FakeCrosDisksClient;
17using chromeos::FakeDBusThreadManager;
18using testing::_;
19using testing::Field;
20using testing::InSequence;
21
22namespace {
23
24// Holds information needed to create a DiskMountManager::Disk instance.
25struct TestDiskInfo {
26  const char* source_path;
27  const char* mount_path;
28  const char* system_path;
29  const char* file_path;
30  const char* device_label;
31  const char* drive_label;
32  const char* vendor_id;
33  const char* vendor_name;
34  const char* product_id;
35  const char* product_name;
36  const char* fs_uuid;
37  const char* system_path_prefix;
38  chromeos::DeviceType device_type;
39  uint64 size_in_bytes;
40  bool is_parent;
41  bool is_read_only;
42  bool has_media;
43  bool on_boot_device;
44  bool is_hidden;
45};
46
47// Holds information to create a DiskMOuntManager::MountPointInfo instance.
48struct TestMountPointInfo {
49  const char* source_path;
50  const char* mount_path;
51  chromeos::MountType mount_type;
52  chromeos::disks::MountCondition mount_condition;
53};
54
55// List of disks held in DiskMountManager at the begining of the test.
56const TestDiskInfo kTestDisks[] = {
57  {
58    "/device/source_path",
59    "/device/mount_path",
60    "/device/prefix/system_path",
61    "/device/file_path",
62    "/device/device_label",
63    "/device/drive_label",
64    "/device/vendor_id",
65    "/device/vendor_name",
66    "/device/product_id",
67    "/device/product_name",
68    "/device/fs_uuid",
69    "/device/prefix",
70    chromeos::DEVICE_TYPE_USB,
71    1073741824,  // size in bytes
72    false,  // is parent
73    false,  // is read only
74    true,  // has media
75    false,  // is on boot device
76    false  // is hidden
77  },
78};
79
80// List of mount points  held in DiskMountManager at the begining of the test.
81const TestMountPointInfo kTestMountPoints[] = {
82  {
83    "/archive/source_path",
84    "/archive/mount_path",
85    chromeos::MOUNT_TYPE_ARCHIVE,
86    chromeos::disks::MOUNT_CONDITION_NONE
87  },
88  {
89    "/device/source_path",
90    "/device/mount_path",
91    chromeos::MOUNT_TYPE_DEVICE,
92    chromeos::disks::MOUNT_CONDITION_NONE
93  },
94};
95
96// Mocks DiskMountManager observer.
97class MockDiskMountManagerObserver : public DiskMountManager::Observer {
98 public:
99  virtual ~MockDiskMountManagerObserver() {}
100
101  MOCK_METHOD2(OnDiskEvent, void(DiskMountManager::DiskEvent event,
102                                 const DiskMountManager::Disk* disk));
103  MOCK_METHOD2(OnDeviceEvent, void(DiskMountManager::DeviceEvent event,
104                                   const std::string& device_path));
105  MOCK_METHOD3(OnMountEvent,
106      void(DiskMountManager::MountEvent event,
107           chromeos::MountError error_code,
108           const DiskMountManager::MountPointInfo& mount_point));
109  MOCK_METHOD3(OnFormatEvent,
110      void(DiskMountManager::FormatEvent event,
111           chromeos::FormatError error_code,
112           const std::string& device_path));
113};
114
115class DiskMountManagerTest : public testing::Test {
116 public:
117  DiskMountManagerTest() {}
118  virtual ~DiskMountManagerTest() {}
119
120  // Sets up test dbus tread manager and disks mount manager.
121  // Initializes disk mount manager disks and mount points.
122  // Adds a test observer to the disk mount manager.
123  virtual void SetUp() {
124    FakeDBusThreadManager* fake_thread_manager = new FakeDBusThreadManager();
125    fake_cros_disks_client_ = new FakeCrosDisksClient;
126    fake_thread_manager->SetCrosDisksClient(
127        scoped_ptr<CrosDisksClient>(fake_cros_disks_client_));
128
129    DBusThreadManager::InitializeForTesting(fake_thread_manager);
130
131    DiskMountManager::Initialize();
132
133    InitDisksAndMountPoints();
134
135    DiskMountManager::GetInstance()->AddObserver(&observer_);
136  }
137
138  // Shuts down dbus thread manager and disk moutn manager used in the test.
139  virtual void TearDown() {
140    DiskMountManager::GetInstance()->RemoveObserver(&observer_);
141    DiskMountManager::Shutdown();
142    DBusThreadManager::Shutdown();
143  }
144
145 protected:
146  // Checks if disk mount manager contains a mount point with specified moutn
147  // path.
148  bool HasMountPoint(const std::string& mount_path) {
149    const DiskMountManager::MountPointMap& mount_points =
150        DiskMountManager::GetInstance()->mount_points();
151    return mount_points.find(mount_path) != mount_points.end();
152  }
153
154 private:
155  // Adds a new disk to the disk mount manager.
156  void AddTestDisk(const TestDiskInfo& disk) {
157    EXPECT_TRUE(DiskMountManager::GetInstance()->AddDiskForTest(
158        new DiskMountManager::Disk(disk.source_path,
159                                   disk.mount_path,
160                                   disk.system_path,
161                                   disk.file_path,
162                                   disk.device_label,
163                                   disk.drive_label,
164                                   disk.vendor_id,
165                                   disk.vendor_name,
166                                   disk.product_id,
167                                   disk.product_name,
168                                   disk.fs_uuid,
169                                   disk.system_path_prefix,
170                                   disk.device_type,
171                                   disk.size_in_bytes,
172                                   disk.is_parent,
173                                   disk.is_read_only,
174                                   disk.has_media,
175                                   disk.on_boot_device,
176                                   disk.is_hidden)));
177  }
178
179  // Adds a new mount point to the disk mount manager.
180  // If the moutn point is a device mount point, disk with its source path
181  // should already be added to the disk mount manager.
182  void AddTestMountPoint(const TestMountPointInfo& mount_point) {
183    EXPECT_TRUE(DiskMountManager::GetInstance()->AddMountPointForTest(
184        DiskMountManager::MountPointInfo(mount_point.source_path,
185                                         mount_point.mount_path,
186                                         mount_point.mount_type,
187                                         mount_point.mount_condition)));
188  }
189
190  // Adds disks and mount points to disk mount manager.
191  void InitDisksAndMountPoints() {
192    // Disks should be  added first (when adding device mount points it is
193    // expected that the corresponding disk is already added).
194    for (size_t i = 0; i < arraysize(kTestDisks); i++)
195      AddTestDisk(kTestDisks[i]);
196
197    for (size_t i = 0; i < arraysize(kTestMountPoints); i++)
198      AddTestMountPoint(kTestMountPoints[i]);
199  }
200
201 protected:
202  chromeos::FakeCrosDisksClient* fake_cros_disks_client_;
203  MockDiskMountManagerObserver observer_;
204  base::MessageLoopForUI message_loop_;
205};
206
207// Tests that the observer gets notified on attempt to format non existent mount
208// point.
209TEST_F(DiskMountManagerTest, Format_NotMounted) {
210  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
211                                       chromeos::FORMAT_ERROR_UNKNOWN,
212                                       "/mount/non_existent"))
213      .Times(1);
214  DiskMountManager::GetInstance()->FormatMountedDevice("/mount/non_existent");
215}
216
217// Tests that it is not possible to format archive mount point.
218TEST_F(DiskMountManagerTest, Format_Archive) {
219  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
220                                       chromeos::FORMAT_ERROR_UNKNOWN,
221                                       "/archive/source_path"))
222      .Times(1);
223
224  DiskMountManager::GetInstance()->FormatMountedDevice("/archive/mount_path");
225}
226
227// Tests that format fails if the device cannot be unmounted.
228TEST_F(DiskMountManagerTest, Format_FailToUnmount) {
229  // Before formatting mounted device, the device should be unmounted.
230  // In this test unmount will fail, and there should be no attempt to
231  // format the device.
232
233  // Set up expectations for observer mock.
234  // Observer should be notified that unmount attempt fails and format task
235  // failed to start.
236  {
237    InSequence s;
238
239    EXPECT_CALL(observer_,
240        OnMountEvent(DiskMountManager::UNMOUNTING,
241                     chromeos::MOUNT_ERROR_INTERNAL,
242                     Field(&DiskMountManager::MountPointInfo::mount_path,
243                           "/device/mount_path")))
244        .Times(1);
245
246    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
247                                         chromeos::FORMAT_ERROR_UNKNOWN,
248                                         "/device/source_path"))
249        .Times(1);
250  }
251
252  fake_cros_disks_client_->MakeUnmountFail();
253  // Start test.
254  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
255
256  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
257  message_loop_.RunUntilIdle();
258
259  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
260  EXPECT_EQ("/device/mount_path",
261            fake_cros_disks_client_->last_unmount_device_path());
262  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
263            fake_cros_disks_client_->last_unmount_options());
264  EXPECT_EQ(0, fake_cros_disks_client_->format_call_count());
265
266  // The device mount should still be here.
267  EXPECT_TRUE(HasMountPoint("/device/mount_path"));
268}
269
270// Tests that observer is notified when cros disks fails to start format
271// process.
272TEST_F(DiskMountManagerTest, Format_FormatFailsToStart) {
273  // Before formatting mounted device, the device should be unmounted.
274  // In this test, unmount will succeed, but call to Format method will
275  // fail.
276
277  // Set up expectations for observer mock.
278  // Observer should be notified that the device was unmounted and format task
279  // failed to start.
280  {
281    InSequence s;
282
283    EXPECT_CALL(observer_,
284        OnMountEvent(DiskMountManager::UNMOUNTING,
285                     chromeos::MOUNT_ERROR_NONE,
286                     Field(&DiskMountManager::MountPointInfo::mount_path,
287                           "/device/mount_path")))
288        .Times(1);
289
290    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
291                                         chromeos::FORMAT_ERROR_UNKNOWN,
292                                         "/device/source_path"))
293        .Times(1);
294  }
295
296  fake_cros_disks_client_->MakeFormatFail();
297  // Start the test.
298  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
299
300  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
301  message_loop_.RunUntilIdle();
302
303  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
304  EXPECT_EQ("/device/mount_path",
305            fake_cros_disks_client_->last_unmount_device_path());
306  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
307            fake_cros_disks_client_->last_unmount_options());
308  EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
309  EXPECT_EQ("/device/source_path",
310            fake_cros_disks_client_->last_format_device_path());
311  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
312
313  // The device mount should be gone.
314  EXPECT_FALSE(HasMountPoint("/device/mount_path"));
315}
316
317// Tests the case where there are two format requests for the same device.
318TEST_F(DiskMountManagerTest, Format_ConcurrentFormatCalls) {
319  // Only the first format request should be processed (the second unmount
320  // request fails because the device is already unmounted at that point).
321  // CrosDisksClient will report that the format process for the first request
322  // is successfully started.
323
324  // Set up expectations for observer mock.
325  // The observer should get a FORMAT_STARTED event for one format request and a
326  // FORMAT_COMPLETED with an error code for the other format request. The
327  // formatting will be started only for the first request.
328  // There should be only one UNMOUNTING event. The result of the second one
329  // should not be reported as the mount point will go away after the first
330  // request.
331  //
332  // Note that in this test the format completion signal will not be simulated,
333  // so the observer should not get FORMAT_COMPLETED signal.
334  {
335    InSequence s;
336
337    EXPECT_CALL(observer_,
338        OnMountEvent(DiskMountManager::UNMOUNTING,
339                     chromeos::MOUNT_ERROR_NONE,
340                     Field(&DiskMountManager::MountPointInfo::mount_path,
341                           "/device/mount_path")))
342        .Times(1);
343
344    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
345                                         chromeos::FORMAT_ERROR_UNKNOWN,
346                                         "/device/source_path"))
347        .Times(1);
348
349    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
350                                         chromeos::FORMAT_ERROR_NONE,
351                                         "/device/source_path"))
352        .Times(1);
353  }
354
355  fake_cros_disks_client_->set_unmount_listener(
356      base::Bind(&FakeCrosDisksClient::MakeUnmountFail,
357                 base::Unretained(fake_cros_disks_client_)));
358  // Start the test.
359  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
360  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
361
362  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
363  message_loop_.RunUntilIdle();
364
365  EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count());
366  EXPECT_EQ("/device/mount_path",
367            fake_cros_disks_client_->last_unmount_device_path());
368  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
369            fake_cros_disks_client_->last_unmount_options());
370  EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
371  EXPECT_EQ("/device/source_path",
372            fake_cros_disks_client_->last_format_device_path());
373  EXPECT_EQ("vfat",
374            fake_cros_disks_client_->last_format_filesystem());
375
376  // The device mount should be gone.
377  EXPECT_FALSE(HasMountPoint("/device/mount_path"));
378}
379
380// Tests the case when the format process actually starts and fails.
381TEST_F(DiskMountManagerTest, Format_FormatFails) {
382  // Both unmount and format device cals are successful in this test.
383
384  // Set up expectations for observer mock.
385  // The observer should get notified that the device was unmounted and that
386  // formatting has started.
387  // After the formatting starts, the test will simulate failing
388  // FORMAT_COMPLETED signal, so the observer should also be notified the
389  // formatting has failed (FORMAT_COMPLETED event).
390  {
391    InSequence s;
392
393    EXPECT_CALL(observer_,
394        OnMountEvent(DiskMountManager::UNMOUNTING,
395                     chromeos::MOUNT_ERROR_NONE,
396                     Field(&DiskMountManager::MountPointInfo::mount_path,
397                           "/device/mount_path")))
398        .Times(1);
399
400    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
401                                         chromeos::FORMAT_ERROR_NONE,
402                                         "/device/source_path"))
403        .Times(1);
404
405    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
406                                         chromeos::FORMAT_ERROR_UNKNOWN,
407                                         "/device/source_path"))
408        .Times(1);
409  }
410
411  // Start the test.
412  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
413
414  // Wait for Unmount and Format calls to end.
415  message_loop_.RunUntilIdle();
416
417  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
418  EXPECT_EQ("/device/mount_path",
419            fake_cros_disks_client_->last_unmount_device_path());
420  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
421            fake_cros_disks_client_->last_unmount_options());
422  EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
423  EXPECT_EQ("/device/source_path",
424            fake_cros_disks_client_->last_format_device_path());
425  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
426
427  // The device should be unmounted by now.
428  EXPECT_FALSE(HasMountPoint("/device/mount_path"));
429
430  // Send failing FORMAT_COMPLETED signal.
431  // The failure is marked by ! in fromt of the path (but this should change
432  // soon).
433  fake_cros_disks_client_->SendFormatCompletedEvent(
434      chromeos::FORMAT_ERROR_UNKNOWN, "/device/source_path");
435}
436
437// Tests the case when formatting completes successfully.
438TEST_F(DiskMountManagerTest, Format_FormatSuccess) {
439  // Set up cros disks client mocks.
440  // Both unmount and format device cals are successful in this test.
441
442  // Set up expectations for observer mock.
443  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
444  // events (all of them without an error set).
445  {
446    InSequence s;
447
448    EXPECT_CALL(observer_,
449        OnMountEvent(DiskMountManager::UNMOUNTING,
450                     chromeos::MOUNT_ERROR_NONE,
451                     Field(&DiskMountManager::MountPointInfo::mount_path,
452                           "/device/mount_path")))
453        .Times(1);
454
455    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
456                                         chromeos::FORMAT_ERROR_NONE,
457                                         "/device/source_path"))
458        .Times(1);
459
460    EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
461                                         chromeos::FORMAT_ERROR_NONE,
462                                         "/device/source_path"))
463        .Times(1);
464  }
465
466  // Start the test.
467  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
468
469  // Wait for Unmount and Format calls to end.
470  message_loop_.RunUntilIdle();
471
472  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
473  EXPECT_EQ("/device/mount_path",
474            fake_cros_disks_client_->last_unmount_device_path());
475  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
476            fake_cros_disks_client_->last_unmount_options());
477  EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
478  EXPECT_EQ("/device/source_path",
479            fake_cros_disks_client_->last_format_device_path());
480  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
481
482  // The device should be unmounted by now.
483  EXPECT_FALSE(HasMountPoint("/device/mount_path"));
484
485  // Simulate cros_disks reporting success.
486  fake_cros_disks_client_->SendFormatCompletedEvent(
487      chromeos::FORMAT_ERROR_NONE, "/device/source_path");
488}
489
490// Tests that it's possible to format the device twice in a row (this may not be
491// true if the list of pending formats is not properly cleared).
492TEST_F(DiskMountManagerTest, Format_ConsecutiveFormatCalls) {
493  // All unmount and format device cals are successful in this test.
494  // Each of the should be made twice (once for each formatting task).
495
496  // Set up expectations for observer mock.
497  // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
498  // events (all of them without an error set) twice (once for each formatting
499  // task).
500  // Also, there should be a MOUNTING event when the device remounting is
501  // simulated.
502  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
503                                       chromeos::FORMAT_ERROR_NONE,
504                                       "/device/source_path"))
505      .Times(2);
506
507  EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
508                                       chromeos::FORMAT_ERROR_NONE,
509                                       "/device/source_path"))
510      .Times(2);
511
512  EXPECT_CALL(observer_,
513      OnMountEvent(DiskMountManager::UNMOUNTING,
514                   chromeos::MOUNT_ERROR_NONE,
515                   Field(&DiskMountManager::MountPointInfo::mount_path,
516                         "/device/mount_path")))
517      .Times(2);
518
519  EXPECT_CALL(observer_,
520      OnMountEvent(DiskMountManager::MOUNTING,
521                   chromeos::MOUNT_ERROR_NONE,
522                   Field(&DiskMountManager::MountPointInfo::mount_path,
523                         "/device/mount_path")))
524      .Times(1);
525
526  // Start the test.
527  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
528
529  // Wait for Unmount and Format calls to end.
530  message_loop_.RunUntilIdle();
531
532  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
533  EXPECT_EQ("/device/mount_path",
534            fake_cros_disks_client_->last_unmount_device_path());
535  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
536            fake_cros_disks_client_->last_unmount_options());
537  EXPECT_EQ(1, fake_cros_disks_client_->format_call_count());
538  EXPECT_EQ("/device/source_path",
539            fake_cros_disks_client_->last_format_device_path());
540  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
541
542  // The device should be unmounted by now.
543  EXPECT_FALSE(HasMountPoint("/device/mount_path"));
544
545  // Simulate cros_disks reporting success.
546  fake_cros_disks_client_->SendFormatCompletedEvent(
547      chromeos::FORMAT_ERROR_NONE, "/device/source_path");
548
549  // Simulate the device remounting.
550  fake_cros_disks_client_->SendMountCompletedEvent(
551      chromeos::MOUNT_ERROR_NONE,
552      "/device/source_path",
553      chromeos::MOUNT_TYPE_DEVICE,
554      "/device/mount_path");
555
556  EXPECT_TRUE(HasMountPoint("/device/mount_path"));
557
558  // Try formatting again.
559  DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
560
561  // Wait for Unmount and Format calls to end.
562  message_loop_.RunUntilIdle();
563
564  EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count());
565  EXPECT_EQ("/device/mount_path",
566            fake_cros_disks_client_->last_unmount_device_path());
567  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
568            fake_cros_disks_client_->last_unmount_options());
569  EXPECT_EQ(2, fake_cros_disks_client_->format_call_count());
570  EXPECT_EQ("/device/source_path",
571            fake_cros_disks_client_->last_format_device_path());
572  EXPECT_EQ("vfat", fake_cros_disks_client_->last_format_filesystem());
573
574  // Simulate cros_disks reporting success.
575  fake_cros_disks_client_->SendFormatCompletedEvent(
576      chromeos::FORMAT_ERROR_NONE, "/device/source_path");
577}
578
579}  // namespace
580