blob: 1cf620d720bd3d76bddecd4628389f6d0cda8922 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/imageburner/burn_device_handler.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/stl_util.h"
#include "chromeos/dbus/cros_disks_client.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace imageburner {
namespace {
const bool kIsParent = true;
const bool kIsBootDevice = true;
const bool kHasMedia = true;
class FakeDiskMountManager : public disks::DiskMountManager {
public:
FakeDiskMountManager() {}
virtual ~FakeDiskMountManager() {
STLDeleteValues(&disks_);
}
// Emulates to add new disk physically (e.g., connecting a
// new USB flash to a Chrome OS).
void EmulateAddDisk(scoped_ptr<Disk> in_disk) {
DCHECK(in_disk.get());
// Keep the reference for the callback, before passing the ownership to
// InsertDisk. It should be safe, because it won't be deleted in
// InsertDisk.
Disk* disk = in_disk.get();
bool new_disk = InsertDisk(disk->device_path(), in_disk.Pass());
FOR_EACH_OBSERVER(
Observer, observers_,
OnDiskEvent(new_disk ? DISK_ADDED : DISK_CHANGED, disk));
}
// Emulates to remove a disk phyically (e.g., removing a USB flash from
// a Chrome OS).
void EmulateRemoveDisk(const std::string& source_path) {
scoped_ptr<Disk> disk(RemoveDisk(source_path));
if (disk.get()) {
FOR_EACH_OBSERVER(
Observer, observers_, OnDiskEvent(DISK_REMOVED, disk.get()));
}
}
// DiskMountManager overrides.
virtual void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
virtual void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
virtual const DiskMap& disks() const override {
return disks_;
}
// Following methods are not implemented.
virtual const Disk* FindDiskBySourcePath(
const std::string& source_path) const override {
return NULL;
}
virtual const MountPointMap& mount_points() const override {
// Note: mount_points_ will always be empty, now.
return mount_points_;
}
virtual void EnsureMountInfoRefreshed(
const EnsureMountInfoRefreshedCallback& callback) override {}
virtual void MountPath(const std::string& source_path,
const std::string& source_format,
const std::string& mount_label,
MountType type) override {}
virtual void UnmountPath(const std::string& mount_path,
UnmountOptions options,
const UnmountPathCallback& callback) override {}
virtual void FormatMountedDevice(const std::string& mount_path) override {}
virtual void UnmountDeviceRecursively(
const std::string& device_path,
const UnmountDeviceRecursivelyCallbackType& callback) override {}
virtual bool AddDiskForTest(Disk* disk) override { return false; }
virtual bool AddMountPointForTest(
const MountPointInfo& mount_point) override {
return false;
}
private:
bool InsertDisk(const std::string& path, scoped_ptr<Disk> disk) {
std::pair<DiskMap::iterator, bool> insert_result =
disks_.insert(std::pair<std::string, Disk*>(path, NULL));
if (!insert_result.second) {
// There is already an entry. Delete it before replacing.
delete insert_result.first->second;
}
insert_result.first->second = disk.release(); // Moves ownership.
return insert_result.second;
}
scoped_ptr<Disk> RemoveDisk(const std::string& path) {
DiskMap::iterator iter = disks_.find(path);
if (iter == disks_.end()) {
// Not found.
return scoped_ptr<Disk>();
}
scoped_ptr<Disk> result(iter->second);
disks_.erase(iter);
return result.Pass();
}
ObserverList<Observer> observers_;
DiskMap disks_;
MountPointMap mount_points_;
DISALLOW_COPY_AND_ASSIGN(FakeDiskMountManager);
};
void CopyDevicePathCallback(
std::string* out_path, const disks::DiskMountManager::Disk& disk) {
*out_path = disk.device_path();
}
} // namespace
class BurnDeviceHandlerTest : public testing::Test {
protected:
virtual void SetUp() override {
disk_mount_manager_.reset(new FakeDiskMountManager);
}
virtual void TearDown() override {
disk_mount_manager_.reset();
}
static scoped_ptr<disks::DiskMountManager::Disk> CreateMockDisk(
const std::string& device_path,
bool is_parent,
bool on_boot_device,
bool has_media,
DeviceType device_type) {
return scoped_ptr<disks::DiskMountManager::Disk>(
new disks::DiskMountManager::Disk(
device_path,
"", // mount path
"", // system_path
"", // file_path
"", // device label
"", // drive label
"", // vendor id
"", // vendor name
"", // product id
"", // product name
"", // fs uuid
"", // system path prefix
device_type,
0, // total size in bytes
is_parent,
false, // is read only
has_media,
on_boot_device,
true, // on_removable_device
false)); // is hidden
}
scoped_ptr<FakeDiskMountManager> disk_mount_manager_;
};
TEST_F(BurnDeviceHandlerTest, GetBurnableDevices) {
// The devices which should be retrieved as burnable.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/burnable_usb",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/burnable_sd",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_SD));
// If the device type is neither USB nor SD, it shouldn't be burnable.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk(
"/dev/non_burnable_unknown",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_UNKNOWN));
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/non_burnable_dvd",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_DVD));
// If not parent, it shouldn't be burnable.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/non_burnable_not_parent",
!kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
// If on_boot_device, it shouldn't be burnable.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/non_burnable_boot_device",
kIsParent, kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
// If no media, it shouldn't be burnable.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/non_burnable_no_media",
kIsParent, !kIsBootDevice, !kHasMedia, DEVICE_TYPE_USB));
BurnDeviceHandler handler(disk_mount_manager_.get());
const std::vector<disks::DiskMountManager::Disk>& burnable_devices =
handler.GetBurnableDevices();
ASSERT_EQ(2u, burnable_devices.size());
bool burnable_usb_found = false;
bool burnable_sd_found = false;
for (size_t i = 0; i < burnable_devices.size(); ++i) {
const std::string& device_path = burnable_devices[i].device_path();
burnable_usb_found |= (device_path == "/dev/burnable_usb");
burnable_sd_found |= (device_path == "/dev/burnable_sd");
}
EXPECT_TRUE(burnable_usb_found);
EXPECT_TRUE(burnable_sd_found);
}
TEST_F(BurnDeviceHandlerTest, Callback) {
std::string added_device;
std::string removed_device;
BurnDeviceHandler handler(disk_mount_manager_.get());
handler.SetCallbacks(
base::Bind(CopyDevicePathCallback, &added_device),
base::Bind(CopyDevicePathCallback, &removed_device));
// Emulate to connect a burnable device.
// |add_callback| should be invoked.
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/burnable",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
EXPECT_EQ("/dev/burnable", added_device);
EXPECT_TRUE(removed_device.empty());
// Emulate to change the currently connected burnable device.
// Neither |add_callback| nor |remove_callback| should be called.
added_device.clear();
removed_device.clear();
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/burnable",
kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
EXPECT_TRUE(added_device.empty());
EXPECT_TRUE(removed_device.empty());
// Emulate to disconnect the burnable device.
// |remove_callback| should be called.
added_device.clear();
removed_device.clear();
disk_mount_manager_->EmulateRemoveDisk("/dev/burnable");
EXPECT_TRUE(added_device.empty());
EXPECT_EQ("/dev/burnable", removed_device);
// Emulate to connect and unconnect an unburnable device.
// For each case, neither |add_callback| nor |remove_callback| should be
// called.
added_device.clear();
removed_device.clear();
disk_mount_manager_->EmulateAddDisk(
CreateMockDisk("/dev/unburnable",
!kIsParent, !kIsBootDevice, kHasMedia, DEVICE_TYPE_USB));
EXPECT_TRUE(added_device.empty());
EXPECT_TRUE(removed_device.empty());
added_device.clear();
removed_device.clear();
disk_mount_manager_->EmulateRemoveDisk("/dev/unburnable");
EXPECT_TRUE(added_device.empty());
EXPECT_TRUE(removed_device.empty());
}
} // namespace imageburner
} // namespace chromeos