Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / storage_monitor / storage_monitor_linux_unittest.cc
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 // StorageMonitorLinux unit tests.
6
7 #include "components/storage_monitor/storage_monitor_linux.h"
8
9 #include <mntent.h>
10 #include <stdio.h>
11
12 #include <string>
13
14 #include "base/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/run_loop.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "components/storage_monitor/mock_removable_storage_observer.h"
21 #include "components/storage_monitor/removable_device_constants.h"
22 #include "components/storage_monitor/storage_info.h"
23 #include "components/storage_monitor/storage_monitor.h"
24 #include "components/storage_monitor/test_media_transfer_protocol_manager_linux.h"
25 #include "components/storage_monitor/test_storage_monitor.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace {
30
31 const char kValidFS[] = "vfat";
32 const char kInvalidFS[] = "invalidfs";
33
34 const char kInvalidPath[] = "invalid path does not exist";
35
36 const char kDeviceDCIM1[] = "d1";
37 const char kDeviceDCIM2[] = "d2";
38 const char kDeviceDCIM3[] = "d3";
39 const char kDeviceNoDCIM[] = "d4";
40 const char kDeviceFixed[] = "d5";
41
42 const char kInvalidDevice[] = "invalid_device";
43
44 const char kMountPointA[] = "mnt_a";
45 const char kMountPointB[] = "mnt_b";
46 const char kMountPointC[] = "mnt_c";
47
48 struct TestDeviceData {
49   const char* device_path;
50   const char* unique_id;
51   StorageInfo::Type type;
52   uint64 partition_size_in_bytes;
53 };
54
55 const TestDeviceData kTestDeviceData[] = {
56   { kDeviceDCIM1, "UUID:FFF0-000F",
57     StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 88788 },
58   { kDeviceDCIM2, "VendorModelSerial:ComName:Model2010:8989",
59     StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
60     8773 },
61   { kDeviceDCIM3, "VendorModelSerial:::WEM319X792",
62     StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 22837 },
63   { kDeviceNoDCIM, "UUID:ABCD-1234",
64     StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 512 },
65   { kDeviceFixed, "UUID:743A-2349",
66     StorageInfo::FIXED_MASS_STORAGE, 17282 },
67 };
68
69 scoped_ptr<StorageInfo> GetDeviceInfo(const base::FilePath& device_path,
70                                       const base::FilePath& mount_point) {
71   bool device_found = false;
72   size_t i = 0;
73   for (; i < arraysize(kTestDeviceData); i++) {
74     if (device_path.value() == kTestDeviceData[i].device_path) {
75       device_found = true;
76       break;
77     }
78   }
79
80   scoped_ptr<StorageInfo> storage_info;
81   if (!device_found) {
82     NOTREACHED();
83     return storage_info.Pass();
84   }
85
86   StorageInfo::Type type = kTestDeviceData[i].type;
87   storage_info.reset(new StorageInfo(
88       StorageInfo::MakeDeviceId(type, kTestDeviceData[i].unique_id),
89       base::string16(),
90       mount_point.value(),
91       base::ASCIIToUTF16("volume label"),
92       base::ASCIIToUTF16("vendor name"),
93       base::ASCIIToUTF16("model name"),
94       kTestDeviceData[i].partition_size_in_bytes));
95   return storage_info.Pass();
96 }
97
98 uint64 GetDevicePartitionSize(const std::string& device) {
99   for (size_t i = 0; i < arraysize(kTestDeviceData); ++i) {
100     if (device == kTestDeviceData[i].device_path)
101       return kTestDeviceData[i].partition_size_in_bytes;
102   }
103   return 0;
104 }
105
106 std::string GetDeviceId(const std::string& device) {
107   for (size_t i = 0; i < arraysize(kTestDeviceData); ++i) {
108     if (device == kTestDeviceData[i].device_path) {
109       return StorageInfo::MakeDeviceId(kTestDeviceData[i].type,
110                                             kTestDeviceData[i].unique_id);
111     }
112   }
113   if (device == kInvalidDevice) {
114     return StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE,
115                                           kInvalidDevice);
116   }
117   return std::string();
118 }
119
120 class TestStorageMonitorLinux : public StorageMonitorLinux {
121  public:
122   explicit TestStorageMonitorLinux(const base::FilePath& path)
123       : StorageMonitorLinux(path) {
124     SetMediaTransferProtocolManagerForTest(
125         new TestMediaTransferProtocolManagerLinux());
126     SetGetDeviceInfoCallbackForTest(base::Bind(&GetDeviceInfo));
127   }
128   virtual ~TestStorageMonitorLinux() {}
129
130  private:
131   virtual void UpdateMtab(
132       const MtabWatcherLinux::MountPointDeviceMap& new_mtab) OVERRIDE {
133     StorageMonitorLinux::UpdateMtab(new_mtab);
134     base::MessageLoopProxy::current()->PostTask(
135         FROM_HERE, base::MessageLoop::QuitClosure());
136   }
137
138   DISALLOW_COPY_AND_ASSIGN(TestStorageMonitorLinux);
139 };
140
141 class StorageMonitorLinuxTest : public testing::Test {
142  public:
143   struct MtabTestData {
144     MtabTestData(const std::string& mount_device,
145                  const std::string& mount_point,
146                  const std::string& mount_type)
147         : mount_device(mount_device),
148           mount_point(mount_point),
149           mount_type(mount_type) {
150     }
151
152     const std::string mount_device;
153     const std::string mount_point;
154     const std::string mount_type;
155   };
156
157   StorageMonitorLinuxTest()
158       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
159   virtual ~StorageMonitorLinuxTest() {}
160
161  protected:
162   virtual void SetUp() OVERRIDE {
163     // Create and set up a temp dir with files for the test.
164     ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
165     base::FilePath test_dir = scoped_temp_dir_.path().AppendASCII("test_etc");
166     ASSERT_TRUE(base::CreateDirectory(test_dir));
167     mtab_file_ = test_dir.AppendASCII("test_mtab");
168     MtabTestData initial_test_data[] = {
169       MtabTestData("dummydevice", "dummydir", kInvalidFS),
170     };
171     WriteToMtab(initial_test_data,
172                 arraysize(initial_test_data),
173                 true  /* overwrite */);
174
175     monitor_.reset(new TestStorageMonitorLinux(mtab_file_));
176
177     mock_storage_observer_.reset(new MockRemovableStorageObserver);
178     monitor_->AddObserver(mock_storage_observer_.get());
179
180     monitor_->Init();
181     base::RunLoop().RunUntilIdle();
182   }
183
184   virtual void TearDown() OVERRIDE {
185     base::RunLoop().RunUntilIdle();
186     monitor_->RemoveObserver(mock_storage_observer_.get());
187     base::RunLoop().RunUntilIdle();
188
189     // Linux storage monitor must be destroyed on the UI thread, so do it here.
190     monitor_.reset();
191   }
192
193   // Append mtab entries from the |data| array of size |data_size| to the mtab
194   // file, and run the message loop.
195   void AppendToMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
196     WriteToMtab(data, data_size, false  /* do not overwrite */);
197     base::RunLoop().Run();
198   }
199
200   // Overwrite the mtab file with mtab entries from the |data| array of size
201   // |data_size|, and run the message loop.
202   void OverwriteMtabAndRunLoop(const MtabTestData* data, size_t data_size) {
203     WriteToMtab(data, data_size, true  /* overwrite */);
204     base::RunLoop().Run();
205   }
206
207   // Simplied version of OverwriteMtabAndRunLoop() that just deletes all the
208   // entries in the mtab file.
209   void WriteEmptyMtabAndRunLoop() {
210     OverwriteMtabAndRunLoop(NULL,  // No data.
211                             0);    // No data length.
212   }
213
214   // Create a directory named |dir| relative to the test directory.
215   // It has a DCIM directory, so StorageMonitorLinux recognizes it as a media
216   // directory.
217   base::FilePath CreateMountPointWithDCIMDir(const std::string& dir) {
218     return CreateMountPoint(dir, true  /* create DCIM dir */);
219   }
220
221   // Create a directory named |dir| relative to the test directory.
222   // It does not have a DCIM directory, so StorageMonitorLinux does not
223   // recognize it as a media directory.
224   base::FilePath CreateMountPointWithoutDCIMDir(const std::string& dir) {
225     return CreateMountPoint(dir, false  /* do not create DCIM dir */);
226   }
227
228   void RemoveDCIMDirFromMountPoint(const std::string& dir) {
229     base::FilePath dcim =
230         scoped_temp_dir_.path().AppendASCII(dir).Append(kDCIMDirectoryName);
231     base::DeleteFile(dcim, false);
232   }
233
234   MockRemovableStorageObserver& observer() {
235     return *mock_storage_observer_;
236   }
237
238   StorageMonitor* notifier() {
239     return monitor_.get();
240   }
241
242   uint64 GetStorageSize(const base::FilePath& path) {
243     StorageInfo info;
244     if (!notifier()->GetStorageInfoForPath(path, &info))
245       return 0;
246
247     return info.total_size_in_bytes();
248   }
249
250  private:
251   // Create a directory named |dir| relative to the test directory.
252   // Set |with_dcim_dir| to true if the created directory will have a "DCIM"
253   // subdirectory.
254   // Returns the full path to the created directory on success, or an empty
255   // path on failure.
256   base::FilePath CreateMountPoint(const std::string& dir, bool with_dcim_dir) {
257     base::FilePath return_path(scoped_temp_dir_.path());
258     return_path = return_path.AppendASCII(dir);
259     base::FilePath path(return_path);
260     if (with_dcim_dir)
261       path = path.Append(kDCIMDirectoryName);
262     if (!base::CreateDirectory(path))
263       return base::FilePath();
264     return return_path;
265   }
266
267   // Write the test mtab data to |mtab_file_|.
268   // |data| is an array of mtab entries.
269   // |data_size| is the array size of |data|.
270   // |overwrite| specifies whether to overwrite |mtab_file_|.
271   void WriteToMtab(const MtabTestData* data,
272                    size_t data_size,
273                    bool overwrite) {
274     FILE* file = setmntent(mtab_file_.value().c_str(), overwrite ? "w" : "a");
275     ASSERT_TRUE(file);
276
277     // Due to the glibc *mntent() interface design, which is out of our
278     // control, the mtnent struct has several char* fields, even though
279     // addmntent() does not write to them in the calls below. To make the
280     // compiler happy while avoiding making additional copies of strings,
281     // we just const_cast() the strings' c_str()s.
282     // Assuming addmntent() does not write to the char* fields, this is safe.
283     // It is unlikely the platforms this test suite runs on will have an
284     // addmntent() implementation that does change the char* fields. If that
285     // was ever the case, the test suite will start crashing or failing.
286     mntent entry;
287     static const char kMountOpts[] = "rw";
288     entry.mnt_opts = const_cast<char*>(kMountOpts);
289     entry.mnt_freq = 0;
290     entry.mnt_passno = 0;
291     for (size_t i = 0; i < data_size; ++i) {
292       entry.mnt_fsname = const_cast<char*>(data[i].mount_device.c_str());
293       entry.mnt_dir = const_cast<char*>(data[i].mount_point.c_str());
294       entry.mnt_type = const_cast<char*>(data[i].mount_type.c_str());
295       ASSERT_EQ(0, addmntent(file, &entry));
296     }
297     ASSERT_EQ(1, endmntent(file));
298   }
299
300   content::TestBrowserThreadBundle thread_bundle_;
301
302   scoped_ptr<MockRemovableStorageObserver> mock_storage_observer_;
303
304   // Temporary directory for created test data.
305   base::ScopedTempDir scoped_temp_dir_;
306   // Path to the test mtab file.
307   base::FilePath mtab_file_;
308
309   scoped_ptr<TestStorageMonitorLinux> monitor_;
310
311   DISALLOW_COPY_AND_ASSIGN(StorageMonitorLinuxTest);
312 };
313
314 // Simple test case where we attach and detach a media device.
315 TEST_F(StorageMonitorLinuxTest, BasicAttachDetach) {
316   base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA);
317   ASSERT_FALSE(test_path.empty());
318   MtabTestData test_data[] = {
319     MtabTestData(kDeviceDCIM2, test_path.value(), kValidFS),
320     MtabTestData(kDeviceFixed, kInvalidPath, kValidFS),
321   };
322   // Only |kDeviceDCIM2| should be attached, since |kDeviceFixed| has a bad
323   // path.
324   AppendToMtabAndRunLoop(test_data, arraysize(test_data));
325
326   EXPECT_EQ(1, observer().attach_calls());
327   EXPECT_EQ(0, observer().detach_calls());
328   EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_attached().device_id());
329   EXPECT_EQ(base::string16(), observer().last_attached().name());
330   EXPECT_EQ(test_path.value(), observer().last_attached().location());
331
332   // |kDeviceDCIM2| should be detached here.
333   WriteEmptyMtabAndRunLoop();
334   EXPECT_EQ(1, observer().attach_calls());
335   EXPECT_EQ(1, observer().detach_calls());
336   EXPECT_EQ(GetDeviceId(kDeviceDCIM2), observer().last_detached().device_id());
337 }
338
339 // Only removable devices are recognized.
340 TEST_F(StorageMonitorLinuxTest, Removable) {
341   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
342   ASSERT_FALSE(test_path_a.empty());
343   MtabTestData test_data1[] = {
344     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
345   };
346   // |kDeviceDCIM1| should be attached as expected.
347   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
348
349   EXPECT_EQ(1, observer().attach_calls());
350   EXPECT_EQ(0, observer().detach_calls());
351   EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_attached().device_id());
352   EXPECT_EQ(base::string16(), observer().last_attached().name());
353   EXPECT_EQ(test_path_a.value(), observer().last_attached().location());
354
355   // This should do nothing, since |kDeviceFixed| is not removable.
356   base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
357   ASSERT_FALSE(test_path_b.empty());
358   MtabTestData test_data2[] = {
359     MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
360   };
361   AppendToMtabAndRunLoop(test_data2, arraysize(test_data2));
362   EXPECT_EQ(1, observer().attach_calls());
363   EXPECT_EQ(0, observer().detach_calls());
364
365   // |kDeviceDCIM1| should be detached as expected.
366   WriteEmptyMtabAndRunLoop();
367   EXPECT_EQ(1, observer().attach_calls());
368   EXPECT_EQ(1, observer().detach_calls());
369   EXPECT_EQ(GetDeviceId(kDeviceDCIM1), observer().last_detached().device_id());
370
371   // |kDeviceNoDCIM| should be attached as expected.
372   MtabTestData test_data3[] = {
373     MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
374   };
375   AppendToMtabAndRunLoop(test_data3, arraysize(test_data3));
376   EXPECT_EQ(2, observer().attach_calls());
377   EXPECT_EQ(1, observer().detach_calls());
378   EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_attached().device_id());
379   EXPECT_EQ(base::string16(), observer().last_attached().name());
380   EXPECT_EQ(test_path_b.value(), observer().last_attached().location());
381
382   // |kDeviceNoDCIM| should be detached as expected.
383   WriteEmptyMtabAndRunLoop();
384   EXPECT_EQ(2, observer().attach_calls());
385   EXPECT_EQ(2, observer().detach_calls());
386   EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), observer().last_detached().device_id());
387 }
388
389 // More complicated test case with multiple devices on multiple mount points.
390 TEST_F(StorageMonitorLinuxTest, SwapMountPoints) {
391   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
392   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
393   ASSERT_FALSE(test_path_a.empty());
394   ASSERT_FALSE(test_path_b.empty());
395
396   // Attach two devices.
397   // (*'d mounts are those StorageMonitor knows about.)
398   // kDeviceDCIM1 -> kMountPointA *
399   // kDeviceDCIM2 -> kMountPointB *
400   MtabTestData test_data1[] = {
401     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
402     MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
403   };
404   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
405   EXPECT_EQ(2, observer().attach_calls());
406   EXPECT_EQ(0, observer().detach_calls());
407
408   // Detach two devices from old mount points and attach the devices at new
409   // mount points.
410   // kDeviceDCIM1 -> kMountPointB *
411   // kDeviceDCIM2 -> kMountPointA *
412   MtabTestData test_data2[] = {
413     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
414     MtabTestData(kDeviceDCIM2, test_path_a.value(), kValidFS),
415   };
416   OverwriteMtabAndRunLoop(test_data2, arraysize(test_data2));
417   EXPECT_EQ(4, observer().attach_calls());
418   EXPECT_EQ(2, observer().detach_calls());
419
420   // Detach all devices.
421   WriteEmptyMtabAndRunLoop();
422   EXPECT_EQ(4, observer().attach_calls());
423   EXPECT_EQ(4, observer().detach_calls());
424 }
425
426 // More complicated test case with multiple devices on multiple mount points.
427 TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) {
428   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
429   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
430   ASSERT_FALSE(test_path_a.empty());
431   ASSERT_FALSE(test_path_b.empty());
432
433   // Attach two devices.
434   // (*'d mounts are those StorageMonitor knows about.)
435   // kDeviceDCIM1 -> kMountPointA *
436   // kDeviceDCIM2 -> kMountPointB *
437   MtabTestData test_data1[] = {
438     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
439     MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
440   };
441   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
442   EXPECT_EQ(2, observer().attach_calls());
443   EXPECT_EQ(0, observer().detach_calls());
444
445   // Attach |kDeviceDCIM1| to |kMountPointB|.
446   // |kDeviceDCIM2| is inaccessible, so it is detached. |kDeviceDCIM1| has been
447   // attached at |kMountPointB|, but is still accessible from |kMountPointA|.
448   // kDeviceDCIM1 -> kMountPointA *
449   // kDeviceDCIM2 -> kMountPointB
450   // kDeviceDCIM1 -> kMountPointB
451   MtabTestData test_data2[] = {
452     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
453   };
454   AppendToMtabAndRunLoop(test_data2, arraysize(test_data2));
455   EXPECT_EQ(2, observer().attach_calls());
456   EXPECT_EQ(1, observer().detach_calls());
457
458   // Detach |kDeviceDCIM1| from |kMountPointA|, causing a detach and attach
459   // event.
460   // kDeviceDCIM2 -> kMountPointB
461   // kDeviceDCIM1 -> kMountPointB *
462   MtabTestData test_data3[] = {
463     MtabTestData(kDeviceDCIM2, test_path_b.value(), kValidFS),
464     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
465   };
466   OverwriteMtabAndRunLoop(test_data3, arraysize(test_data3));
467   EXPECT_EQ(3, observer().attach_calls());
468   EXPECT_EQ(2, observer().detach_calls());
469
470   // Attach |kDeviceDCIM1| to |kMountPointA|.
471   // kDeviceDCIM2 -> kMountPointB
472   // kDeviceDCIM1 -> kMountPointB *
473   // kDeviceDCIM1 -> kMountPointA
474   MtabTestData test_data4[] = {
475     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
476   };
477   AppendToMtabAndRunLoop(test_data4, arraysize(test_data4));
478   EXPECT_EQ(3, observer().attach_calls());
479   EXPECT_EQ(2, observer().detach_calls());
480
481   // Detach |kDeviceDCIM1| from |kMountPointB|.
482   // kDeviceDCIM1 -> kMountPointA *
483   // kDeviceDCIM2 -> kMountPointB *
484   OverwriteMtabAndRunLoop(test_data1, arraysize(test_data1));
485   EXPECT_EQ(5, observer().attach_calls());
486   EXPECT_EQ(3, observer().detach_calls());
487
488   // Detach all devices.
489   WriteEmptyMtabAndRunLoop();
490   EXPECT_EQ(5, observer().attach_calls());
491   EXPECT_EQ(5, observer().detach_calls());
492 }
493
494 TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) {
495   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
496   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
497   ASSERT_FALSE(test_path_a.empty());
498   ASSERT_FALSE(test_path_b.empty());
499
500   // Attach to one first.
501   // (*'d mounts are those StorageMonitor knows about.)
502   // kDeviceDCIM1 -> kMountPointA *
503   MtabTestData test_data1[] = {
504     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
505   };
506   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
507   EXPECT_EQ(1, observer().attach_calls());
508   EXPECT_EQ(0, observer().detach_calls());
509
510   // Attach |kDeviceDCIM1| to |kMountPointB|.
511   // kDeviceDCIM1 -> kMountPointA *
512   // kDeviceDCIM1 -> kMountPointB
513   MtabTestData test_data2[] = {
514     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
515   };
516   AppendToMtabAndRunLoop(test_data2, arraysize(test_data2));
517   EXPECT_EQ(1, observer().attach_calls());
518   EXPECT_EQ(0, observer().detach_calls());
519
520   // Attach |kDeviceFixed| (a non-removable device) to |kMountPointA|.
521   // kDeviceDCIM1 -> kMountPointA
522   // kDeviceDCIM1 -> kMountPointB *
523   // kDeviceFixed -> kMountPointA
524   MtabTestData test_data3[] = {
525     MtabTestData(kDeviceFixed, test_path_a.value(), kValidFS),
526   };
527   RemoveDCIMDirFromMountPoint(kMountPointA);
528   AppendToMtabAndRunLoop(test_data3, arraysize(test_data3));
529   EXPECT_EQ(2, observer().attach_calls());
530   EXPECT_EQ(1, observer().detach_calls());
531
532   // Detach |kDeviceFixed|.
533   // kDeviceDCIM1 -> kMountPointA
534   // kDeviceDCIM1 -> kMountPointB *
535   MtabTestData test_data4[] = {
536     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
537     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
538   };
539   CreateMountPointWithDCIMDir(kMountPointA);
540   OverwriteMtabAndRunLoop(test_data4, arraysize(test_data4));
541   EXPECT_EQ(2, observer().attach_calls());
542   EXPECT_EQ(1, observer().detach_calls());
543
544   // Attach |kDeviceNoDCIM| (a non-DCIM device) to |kMountPointB|.
545   // kDeviceDCIM1  -> kMountPointA *
546   // kDeviceDCIM1  -> kMountPointB
547   // kDeviceNoDCIM -> kMountPointB *
548   MtabTestData test_data5[] = {
549     MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
550   };
551   base::DeleteFile(test_path_b.Append(kDCIMDirectoryName), false);
552   AppendToMtabAndRunLoop(test_data5, arraysize(test_data5));
553   EXPECT_EQ(4, observer().attach_calls());
554   EXPECT_EQ(2, observer().detach_calls());
555
556   // Detach |kDeviceNoDCIM|.
557   // kDeviceDCIM1 -> kMountPointA *
558   // kDeviceDCIM1 -> kMountPointB
559   MtabTestData test_data6[] = {
560     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
561     MtabTestData(kDeviceDCIM1, test_path_b.value(), kValidFS),
562   };
563   CreateMountPointWithDCIMDir(kMountPointB);
564   OverwriteMtabAndRunLoop(test_data6, arraysize(test_data6));
565   EXPECT_EQ(4, observer().attach_calls());
566   EXPECT_EQ(3, observer().detach_calls());
567
568   // Detach |kDeviceDCIM1| from |kMountPointB|.
569   // kDeviceDCIM1 -> kMountPointA *
570   OverwriteMtabAndRunLoop(test_data1, arraysize(test_data1));
571   EXPECT_EQ(4, observer().attach_calls());
572   EXPECT_EQ(3, observer().detach_calls());
573
574   // Detach all devices.
575   WriteEmptyMtabAndRunLoop();
576   EXPECT_EQ(4, observer().attach_calls());
577   EXPECT_EQ(4, observer().detach_calls());
578 }
579
580 TEST_F(StorageMonitorLinuxTest, DeviceLookUp) {
581   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
582   base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
583   base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC);
584   ASSERT_FALSE(test_path_a.empty());
585   ASSERT_FALSE(test_path_b.empty());
586   ASSERT_FALSE(test_path_c.empty());
587
588   // Attach to one first.
589   // (starred mounts are those StorageMonitor knows about.)
590   // kDeviceDCIM1  -> kMountPointA *
591   // kDeviceNoDCIM -> kMountPointB *
592   // kDeviceFixed  -> kMountPointC
593   MtabTestData test_data1[] = {
594     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
595     MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
596     MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
597   };
598   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
599   EXPECT_EQ(2, observer().attach_calls());
600   EXPECT_EQ(0, observer().detach_calls());
601
602   StorageInfo device_info;
603   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
604   EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
605   EXPECT_EQ(test_path_a.value(), device_info.location());
606   EXPECT_EQ(base::string16(), device_info.name());
607   EXPECT_EQ(88788ULL, device_info.total_size_in_bytes());
608   EXPECT_EQ(base::ASCIIToUTF16("volume label"), device_info.storage_label());
609   EXPECT_EQ(base::ASCIIToUTF16("vendor name"), device_info.vendor_name());
610   EXPECT_EQ(base::ASCIIToUTF16("model name"), device_info.model_name());
611
612   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
613   EXPECT_EQ(GetDeviceId(kDeviceNoDCIM), device_info.device_id());
614   EXPECT_EQ(test_path_b.value(), device_info.location());
615   EXPECT_EQ(base::string16(), device_info.name());
616
617   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
618   EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
619   EXPECT_EQ(test_path_c.value(), device_info.location());
620   EXPECT_EQ(base::string16(), device_info.name());
621
622   // An invalid path.
623   EXPECT_FALSE(notifier()->GetStorageInfoForPath(base::FilePath(kInvalidPath),
624                                                  &device_info));
625
626   // Test filling in of the mount point.
627   EXPECT_TRUE(
628       notifier()->GetStorageInfoForPath(test_path_a.Append("some/other/path"),
629       &device_info));
630   EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
631   EXPECT_EQ(test_path_a.value(), device_info.location());
632   EXPECT_EQ(base::string16(), device_info.name());
633
634   // One device attached at multiple points.
635   // kDeviceDCIM1 -> kMountPointA *
636   // kDeviceFixed -> kMountPointB
637   // kDeviceFixed -> kMountPointC
638   MtabTestData test_data2[] = {
639     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
640     MtabTestData(kDeviceFixed, test_path_b.value(), kValidFS),
641     MtabTestData(kDeviceFixed, test_path_c.value(), kValidFS),
642   };
643   AppendToMtabAndRunLoop(test_data2, arraysize(test_data2));
644
645   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_a, &device_info));
646   EXPECT_EQ(GetDeviceId(kDeviceDCIM1), device_info.device_id());
647
648   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_b, &device_info));
649   EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
650
651   EXPECT_TRUE(notifier()->GetStorageInfoForPath(test_path_c, &device_info));
652   EXPECT_EQ(GetDeviceId(kDeviceFixed), device_info.device_id());
653
654   EXPECT_EQ(2, observer().attach_calls());
655   EXPECT_EQ(1, observer().detach_calls());
656 }
657
658 TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) {
659   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
660   base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
661   ASSERT_FALSE(test_path_a.empty());
662   ASSERT_FALSE(test_path_b.empty());
663
664   MtabTestData test_data1[] = {
665     MtabTestData(kDeviceDCIM1, test_path_a.value(), kValidFS),
666     MtabTestData(kDeviceNoDCIM, test_path_b.value(), kValidFS),
667     MtabTestData(kDeviceFixed, kInvalidPath, kInvalidFS),
668   };
669   AppendToMtabAndRunLoop(test_data1, arraysize(test_data1));
670   EXPECT_EQ(2, observer().attach_calls());
671   EXPECT_EQ(0, observer().detach_calls());
672
673   EXPECT_EQ(GetDevicePartitionSize(kDeviceDCIM1),
674             GetStorageSize(test_path_a));
675   EXPECT_EQ(GetDevicePartitionSize(kDeviceNoDCIM),
676             GetStorageSize(test_path_b));
677   EXPECT_EQ(GetDevicePartitionSize(kInvalidPath),
678             GetStorageSize(base::FilePath(kInvalidPath)));
679 }
680
681 }  // namespace