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