Home | History | Annotate | Download | only in imageburner
      1 // Copyright (c) 2013 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 "chrome/browser/chromeos/imageburner/burn_device_handler.h"
      6 
      7 #include <string>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/logging.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/observer_list.h"
     15 #include "base/stl_util.h"
     16 #include "chromeos/dbus/cros_disks_client.h"
     17 #include "chromeos/disks/disk_mount_manager.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace chromeos {
     21 namespace imageburner {
     22 
     23 namespace {
     24 
     25 const bool kIsParent = true;
     26 const bool kIsBootDevice = true;
     27 const bool kHasMedia = true;
     28 
     29 class FakeDiskMountManager : public disks::DiskMountManager {
     30  public:
     31   FakeDiskMountManager() {}
     32 
     33   virtual ~FakeDiskMountManager() {
     34     STLDeleteValues(&disks_);
     35   }
     36 
     37   // Emulates to add new disk physically (e.g., connecting a
     38   // new USB flash to a Chrome OS).
     39   void EmulateAddDisk(scoped_ptr<Disk> in_disk) {
     40     DCHECK(in_disk.get());
     41     // Keep the reference for the callback, before passing the ownership to
     42     // InsertDisk. It should be safe, because it won't be deleted in
     43     // InsertDisk.
     44     Disk* disk = in_disk.get();
     45     bool new_disk = InsertDisk(disk->device_path(), in_disk.Pass());
     46     FOR_EACH_OBSERVER(
     47         Observer, observers_,
     48         OnDiskEvent(new_disk ? DISK_ADDED : DISK_CHANGED, disk));
     49   }
     50 
     51   // Emulates to remove a disk phyically (e.g., removing a USB flash from
     52   // a Chrome OS).
     53   void EmulateRemoveDisk(const std::string& source_path) {
     54     scoped_ptr<Disk> disk(RemoveDisk(source_path));
     55     if (disk.get()) {
     56       FOR_EACH_OBSERVER(
     57           Observer, observers_, OnDiskEvent(DISK_REMOVED, disk.get()));
     58     }
     59   }
     60 
     61   // DiskMountManager overrides.
     62   virtual void AddObserver(Observer* observer) OVERRIDE {
     63     observers_.AddObserver(observer);
     64   }
     65 
     66   virtual void RemoveObserver(Observer* observer) OVERRIDE {
     67     observers_.RemoveObserver(observer);
     68   }
     69 
     70   virtual const DiskMap& disks() const OVERRIDE {
     71     return disks_;
     72   }
     73 
     74   // Following methods are not implemented.
     75   virtual const Disk* FindDiskBySourcePath(
     76       const std::string& source_path) const OVERRIDE {
     77     return NULL;
     78   }
     79   virtual const MountPointMap& mount_points() const OVERRIDE {
     80     // Note: mount_points_ will always be empty, now.
     81     return mount_points_;
     82   }
     83   virtual void EnsureMountInfoRefreshed(
     84       const EnsureMountInfoRefreshedCallback& callback) OVERRIDE {}
     85   virtual void MountPath(const std::string& source_path,
     86                          const std::string& source_format,
     87                          const std::string& mount_label,
     88                          MountType type) OVERRIDE {}
     89   virtual void UnmountPath(const std::string& mount_path,
     90                            UnmountOptions options,
     91                            const UnmountPathCallback& callback) OVERRIDE {}
     92   virtual void FormatMountedDevice(const std::string& mount_path) OVERRIDE {}
     93   virtual void UnmountDeviceRecursively(
     94       const std::string& device_path,
     95       const UnmountDeviceRecursivelyCallbackType& callback) OVERRIDE {}
     96   virtual bool AddDiskForTest(Disk* disk) OVERRIDE { return false; }
     97   virtual bool AddMountPointForTest(
     98       const MountPointInfo& mount_point) OVERRIDE {
     99     return false;
    100   }
    101 
    102  private:
    103   bool InsertDisk(const std::string& path, scoped_ptr<Disk> disk) {
    104     std::pair<DiskMap::iterator, bool> insert_result =
    105         disks_.insert(std::pair<std::string, Disk*>(path, NULL));
    106     if (!insert_result.second) {
    107       // There is already an entry. Delete it before replacing.
    108       delete insert_result.first->second;
    109     }
    110     insert_result.first->second = disk.release();  // Moves ownership.
    111     return insert_result.second;
    112   }
    113 
    114   scoped_ptr<Disk> RemoveDisk(const std::string& path) {
    115     DiskMap::iterator iter = disks_.find(path);
    116     if (iter == disks_.end()) {
    117       // Not found.
    118       return scoped_ptr<Disk>();
    119     }
    120     scoped_ptr<Disk> result(iter->second);
    121     disks_.erase(iter);
    122     return result.Pass();
    123   }
    124 
    125   ObserverList<Observer> observers_;
    126   DiskMap disks_;
    127   MountPointMap mount_points_;
    128 
    129   DISALLOW_COPY_AND_ASSIGN(FakeDiskMountManager);
    130 };
    131 
    132 void CopyDevicePathCallback(
    133     std::string* out_path, const disks::DiskMountManager::Disk& disk) {
    134   *out_path = disk.device_path();
    135 }
    136 
    137 }  // namespace
    138 
    139 class BurnDeviceHandlerTest : public testing::Test {
    140  protected:
    141   virtual void SetUp() OVERRIDE {
    142     disk_mount_manager_.reset(new FakeDiskMountManager);
    143   }
    144 
    145   virtual void TearDown() OVERRIDE {
    146     disk_mount_manager_.reset();
    147   }
    148 
    149   static scoped_ptr<disks::DiskMountManager::Disk> CreateMockDisk(
    150       const std::string& device_path,
    151       bool is_parent,
    152       bool on_boot_device,
    153       bool has_media,
    154       DeviceType device_type) {
    155     return scoped_ptr<disks::DiskMountManager::Disk>(
    156         new disks::DiskMountManager::Disk(
    157             device_path,
    158             "",  // mount path
    159             "",  // system_path
    160             "",  // file_path
    161             "",  // device label
    162             "",  // drive label
    163             "",  // vendor id
    164             "",  // vendor name
    165             "",  // product id
    166             "",  // product name
    167             "",  // fs uuid
    168             "",  // system path prefix
    169             device_type,
    170             0,  // total size in bytes
    171             is_parent,
    172             false,  //  is read only
    173             has_media,
    174             on_boot_device,
    175             true,  // on_removable_device
    176             false));  // is hidden
    177   }
    178 
    179   scoped_ptr<FakeDiskMountManager> disk_mount_manager_;
    180 };
    181 
    182 TEST_F(BurnDeviceHandlerTest, GetBurnableDevices) {
    183   // The devices which should be retrieved as burnable.
    184   disk_mount_manager_->EmulateAddDisk(
    185       CreateMockDisk("/dev/burnable_usb",
    186                      kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    187   disk_mount_manager_->EmulateAddDisk(
    188       CreateMockDisk("/dev/burnable_sd",
    189                      kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_SD));
    190 
    191   // If the device type is neither USB nor SD, it shouldn't be burnable.
    192   disk_mount_manager_->EmulateAddDisk(
    193       CreateMockDisk(
    194           "/dev/non_burnable_unknown",
    195           kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_UNKNOWN));
    196   disk_mount_manager_->EmulateAddDisk(
    197       CreateMockDisk("/dev/non_burnable_dvd",
    198                      kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_DVD));
    199 
    200   // If not parent, it shouldn't be burnable.
    201   disk_mount_manager_->EmulateAddDisk(
    202       CreateMockDisk("/dev/non_burnable_not_parent",
    203                      !kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    204 
    205   // If on_boot_device, it shouldn't be burnable.
    206   disk_mount_manager_->EmulateAddDisk(
    207       CreateMockDisk("/dev/non_burnable_boot_device",
    208                      kIsParent, kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    209 
    210   // If no media, it shouldn't be burnable.
    211   disk_mount_manager_->EmulateAddDisk(
    212       CreateMockDisk("/dev/non_burnable_no_media",
    213                      kIsParent, !kIsBootDevice, !kHasMedia, DEVICE_TYPE_USB));
    214 
    215   BurnDeviceHandler handler(disk_mount_manager_.get());
    216 
    217   const std::vector<disks::DiskMountManager::Disk>& burnable_devices =
    218       handler.GetBurnableDevices();
    219   ASSERT_EQ(2u, burnable_devices.size());
    220   bool burnable_usb_found = false;
    221   bool burnable_sd_found = false;
    222   for (size_t i = 0; i < burnable_devices.size(); ++i) {
    223     const std::string& device_path = burnable_devices[i].device_path();
    224     burnable_usb_found |= (device_path == "/dev/burnable_usb");
    225     burnable_sd_found |= (device_path == "/dev/burnable_sd");
    226   }
    227 
    228   EXPECT_TRUE(burnable_usb_found);
    229   EXPECT_TRUE(burnable_sd_found);
    230 }
    231 
    232 TEST_F(BurnDeviceHandlerTest, Callback) {
    233   std::string added_device;
    234   std::string removed_device;
    235 
    236   BurnDeviceHandler handler(disk_mount_manager_.get());
    237   handler.SetCallbacks(
    238       base::Bind(CopyDevicePathCallback, &added_device),
    239       base::Bind(CopyDevicePathCallback, &removed_device));
    240 
    241   // Emulate to connect a burnable device.
    242   // |add_callback| should be invoked.
    243   disk_mount_manager_->EmulateAddDisk(
    244       CreateMockDisk("/dev/burnable",
    245                      kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    246   EXPECT_EQ("/dev/burnable", added_device);
    247   EXPECT_TRUE(removed_device.empty());
    248 
    249   // Emulate to change the currently connected burnable device.
    250   // Neither |add_callback| nor |remove_callback| should be called.
    251   added_device.clear();
    252   removed_device.clear();
    253   disk_mount_manager_->EmulateAddDisk(
    254       CreateMockDisk("/dev/burnable",
    255                      kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    256   EXPECT_TRUE(added_device.empty());
    257   EXPECT_TRUE(removed_device.empty());
    258 
    259   // Emulate to disconnect the burnable device.
    260   // |remove_callback| should be called.
    261   added_device.clear();
    262   removed_device.clear();
    263   disk_mount_manager_->EmulateRemoveDisk("/dev/burnable");
    264   EXPECT_TRUE(added_device.empty());
    265   EXPECT_EQ("/dev/burnable", removed_device);
    266 
    267   // Emulate to connect and unconnect an unburnable device.
    268   // For each case, neither |add_callback| nor |remove_callback| should be
    269   // called.
    270   added_device.clear();
    271   removed_device.clear();
    272   disk_mount_manager_->EmulateAddDisk(
    273       CreateMockDisk("/dev/unburnable",
    274                      !kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
    275   EXPECT_TRUE(added_device.empty());
    276   EXPECT_TRUE(removed_device.empty());
    277 
    278   added_device.clear();
    279   removed_device.clear();
    280   disk_mount_manager_->EmulateRemoveDisk("/dev/unburnable");
    281   EXPECT_TRUE(added_device.empty());
    282   EXPECT_TRUE(removed_device.empty());
    283 }
    284 
    285 }  // namespace imageburner
    286 }  // namespace chromeos
    287