blob: 8e0c78301700f0a365c224d4a927689490408b1d [file] [log] [blame]
// Copyright 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/file_manager/volume_manager.h"
#include <stddef.h>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_running_on_chromeos.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h"
#include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
#include "chrome/browser/chromeos/file_system_provider/fake_extension_provider.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "chromeos/disks/disk.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/prefs/pref_service.h"
#include "components/storage_monitor/storage_info.h"
#include "components/user_manager/user.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/extension_registry.h"
#include "services/device/public/mojom/mtp_storage_info.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
using chromeos::disks::Disk;
using chromeos::disks::DiskMountManager;
namespace file_manager {
namespace {
class LoggingObserver : public VolumeManagerObserver {
public:
struct Event {
enum EventType {
DISK_ADDED,
DISK_REMOVED,
DEVICE_ADDED,
DEVICE_REMOVED,
VOLUME_MOUNTED,
VOLUME_UNMOUNTED,
FORMAT_STARTED,
FORMAT_COMPLETED,
PARTITION_STARTED,
PARTITION_COMPLETED,
RENAME_STARTED,
RENAME_COMPLETED
} type;
// Available on DEVICE_ADDED, DEVICE_REMOVED, VOLUME_MOUNTED,
// VOLUME_UNMOUNTED, FORMAT_STARTED, FORMAT_COMPLETED. PARTITION_STARTED,
// PARTITION_COMPLETED.
std::string device_path;
// Available on FORMAT_STARTED, FORMAT_COMPLETED, RENAME_STARTED and
// RENAME_COMPLETED, PARTITION_STARTED, PARTITION_COMPLETED.
std::string device_label;
// Available on DISK_ADDED.
bool mounting;
// Available on VOLUME_MOUNTED and VOLUME_UNMOUNTED.
chromeos::MountError mount_error;
// Available on FORMAT_STARTED and FORMAT_COMPLETED, PARTITION_STARTED,
// PARTITION_COMPLETED.
bool success;
};
LoggingObserver() = default;
~LoggingObserver() override = default;
const std::vector<Event>& events() const { return events_; }
// VolumeManagerObserver overrides.
void OnDiskAdded(const Disk& disk, bool mounting) override {
Event event;
event.type = Event::DISK_ADDED;
event.device_path = disk.device_path(); // Keep only device_path.
event.mounting = mounting;
events_.push_back(event);
}
void OnDiskRemoved(const Disk& disk) override {
Event event;
event.type = Event::DISK_REMOVED;
event.device_path = disk.device_path(); // Keep only device_path.
events_.push_back(event);
}
void OnDeviceAdded(const std::string& device_path) override {
Event event;
event.type = Event::DEVICE_ADDED;
event.device_path = device_path;
events_.push_back(event);
}
void OnDeviceRemoved(const std::string& device_path) override {
Event event;
event.type = Event::DEVICE_REMOVED;
event.device_path = device_path;
events_.push_back(event);
}
void OnVolumeMounted(chromeos::MountError error_code,
const Volume& volume) override {
Event event;
event.type = Event::VOLUME_MOUNTED;
event.device_path = volume.source_path().AsUTF8Unsafe();
event.mount_error = error_code;
events_.push_back(event);
}
void OnVolumeUnmounted(chromeos::MountError error_code,
const Volume& volume) override {
Event event;
event.type = Event::VOLUME_UNMOUNTED;
event.device_path = volume.source_path().AsUTF8Unsafe();
event.mount_error = error_code;
events_.push_back(event);
}
void OnFormatStarted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::FORMAT_STARTED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
void OnFormatCompleted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::FORMAT_COMPLETED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
void OnPartitionStarted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::PARTITION_STARTED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
void OnPartitionCompleted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::PARTITION_COMPLETED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
void OnRenameStarted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::RENAME_STARTED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
void OnRenameCompleted(const std::string& device_path,
const std::string& device_label,
bool success) override {
Event event;
event.type = Event::RENAME_COMPLETED;
event.device_path = device_path;
event.device_label = device_label;
event.success = success;
events_.push_back(event);
}
private:
std::vector<Event> events_;
DISALLOW_COPY_AND_ASSIGN(LoggingObserver);
};
class FakeUser : public user_manager::User {
public:
explicit FakeUser(const AccountId& account_id) : User(account_id) {}
user_manager::UserType GetType() const override {
return user_manager::USER_TYPE_REGULAR;
}
};
} // namespace
class VolumeManagerTest : public testing::Test {
protected:
// Helper class that contains per-profile objects.
class ProfileEnvironment {
public:
ProfileEnvironment(chromeos::PowerManagerClient* power_manager_client,
DiskMountManager* disk_manager)
: profile_(std::make_unique<TestingProfile>()),
extension_registry_(
std::make_unique<extensions::ExtensionRegistry>(profile_.get())),
file_system_provider_service_(
std::make_unique<chromeos::file_system_provider::Service>(
profile_.get(),
extension_registry_.get())),
drive_integration_service_(
std::make_unique<drive::DriveIntegrationService>(
profile_.get(),
std::string(),
base::FilePath())),
volume_manager_(std::make_unique<VolumeManager>(
profile_.get(),
drive_integration_service_.get(), // DriveIntegrationService
power_manager_client,
disk_manager,
file_system_provider_service_.get(),
base::BindRepeating(&ProfileEnvironment::GetFakeMtpStorageInfo,
base::Unretained(this)))),
account_id_(
AccountId::FromUserEmailGaiaId(profile_->GetProfileUserName(),
"id")),
user_(account_id_) {
chromeos::ProfileHelper::Get()->SetProfileToUserMappingForTesting(&user_);
chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
&user_, profile_.get());
}
~ProfileEnvironment() {
// In production, KeyedServices have Shutdown() called before destruction.
volume_manager_->Shutdown();
drive_integration_service_->Shutdown();
}
Profile* profile() const { return profile_.get(); }
VolumeManager* volume_manager() const { return volume_manager_.get(); }
private:
void GetFakeMtpStorageInfo(
const std::string& storage_name,
device::mojom::MtpManager::GetStorageInfoCallback callback) {
std::move(callback).Run(device::mojom::MtpStorageInfo::New());
}
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<extensions::ExtensionRegistry> extension_registry_;
std::unique_ptr<chromeos::file_system_provider::Service>
file_system_provider_service_;
std::unique_ptr<drive::DriveIntegrationService> drive_integration_service_;
std::unique_ptr<VolumeManager> volume_manager_;
AccountId account_id_;
FakeUser user_;
};
void SetUp() override {
chromeos::PowerManagerClient::InitializeFake();
disk_mount_manager_ = std::make_unique<FakeDiskMountManager>();
main_profile_ = std::make_unique<ProfileEnvironment>(
chromeos::PowerManagerClient::Get(), disk_mount_manager_.get());
}
void TearDown() override {
main_profile_.reset();
disk_mount_manager_.reset();
chromeos::PowerManagerClient::Shutdown();
task_environment_.RunUntilIdle();
}
Profile* profile() const { return main_profile_->profile(); }
VolumeManager* volume_manager() const {
return main_profile_->volume_manager();
}
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<FakeDiskMountManager> disk_mount_manager_;
std::unique_ptr<ProfileEnvironment> main_profile_;
};
TEST_F(VolumeManagerTest, OnDriveFileSystemMountAndUnmount) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFileSystemMounted();
ASSERT_EQ(1U, observer.events().size());
LoggingObserver::Event event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::VOLUME_MOUNTED, event.type);
EXPECT_EQ(drive::DriveIntegrationServiceFactory::GetForProfile(profile())
->GetMountPointPath()
.AsUTF8Unsafe(),
event.device_path);
EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, event.mount_error);
volume_manager()->OnFileSystemBeingUnmounted();
ASSERT_EQ(2U, observer.events().size());
event = observer.events()[1];
EXPECT_EQ(LoggingObserver::Event::VOLUME_UNMOUNTED, event.type);
EXPECT_EQ(drive::DriveIntegrationServiceFactory::GetForProfile(profile())
->GetMountPointPath()
.AsUTF8Unsafe(),
event.device_path);
EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, event.mount_error);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnDriveFileSystemUnmountWithoutMount) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFileSystemBeingUnmounted();
// Unmount event for non-mounted volume is not reported.
ASSERT_EQ(0U, observer.events().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnBootDeviceDiskEvent) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> disk =
Disk::Builder().SetDevicePath("device1").SetOnBootDevice(true).Build();
volume_manager()->OnBootDeviceDiskEvent(DiskMountManager::DISK_ADDED, *disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->OnBootDeviceDiskEvent(DiskMountManager::DISK_REMOVED,
*disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->OnBootDeviceDiskEvent(DiskMountManager::DISK_CHANGED,
*disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Hidden) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> disk =
Disk::Builder().SetDevicePath("device1").SetIsHidden(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_REMOVED,
*disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_CHANGED,
*disk);
EXPECT_EQ(0U, observer.events().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Added) {
// Enable external storage.
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> empty_device_path_disk = Disk::Builder().Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*empty_device_path_disk);
EXPECT_EQ(0U, observer.events().size());
std::unique_ptr<const Disk> media_disk =
Disk::Builder().SetDevicePath("device1").SetHasMedia(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*media_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_TRUE(event.mounting);
ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
const FakeDiskMountManager::MountRequest& mount_request =
disk_mount_manager_->mount_requests()[0];
EXPECT_EQ("device1", mount_request.source_path);
EXPECT_EQ("", mount_request.source_format);
EXPECT_EQ("", mount_request.mount_label);
EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_AddedNonMounting) {
// Enable external storage.
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
// Device which is already mounted.
{
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> mounted_media_disk =
Disk::Builder()
.SetDevicePath("device1")
.SetMountPath("mounted")
.SetHasMedia(true)
.Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*mounted_media_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_FALSE(event.mounting);
ASSERT_EQ(0U, disk_mount_manager_->mount_requests().size());
volume_manager()->RemoveObserver(&observer);
}
// Device without media.
{
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> no_media_disk =
Disk::Builder().SetDevicePath("device1").Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*no_media_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_FALSE(event.mounting);
ASSERT_EQ(0U, disk_mount_manager_->mount_requests().size());
volume_manager()->RemoveObserver(&observer);
}
// External storage is disabled.
{
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, true);
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> media_disk =
Disk::Builder().SetDevicePath("device1").SetHasMedia(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*media_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_ADDED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_FALSE(event.mounting);
ASSERT_EQ(0U, disk_mount_manager_->mount_requests().size());
volume_manager()->RemoveObserver(&observer);
}
}
TEST_F(VolumeManagerTest, OnDiskAutoMountableEvent_Removed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> mounted_disk = Disk::Builder()
.SetDevicePath("device1")
.SetMountPath("mount_path")
.Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_REMOVED,
*mounted_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_REMOVED, event.type);
EXPECT_EQ("device1", event.device_path);
ASSERT_EQ(1U, disk_mount_manager_->unmount_requests().size());
EXPECT_EQ("mount_path", disk_mount_manager_->unmount_requests()[0]);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_RemovedNotMounted) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> not_mounted_disk =
Disk::Builder().SetDevicePath("device1").Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_REMOVED,
*not_mounted_disk);
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DISK_REMOVED, event.type);
EXPECT_EQ("device1", event.device_path);
ASSERT_EQ(0U, disk_mount_manager_->unmount_requests().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_Changed) {
// Changed event should cause mounting (if possible).
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> disk =
Disk::Builder().SetDevicePath("device1").SetHasMedia(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_CHANGED,
*disk);
EXPECT_EQ(1U, observer.events().size());
EXPECT_EQ(1U, disk_mount_manager_->mount_requests().size());
EXPECT_EQ(0U, disk_mount_manager_->unmount_requests().size());
// Read-write mode by default.
EXPECT_EQ(chromeos::MOUNT_ACCESS_MODE_READ_WRITE,
disk_mount_manager_->mount_requests()[0].access_mode);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnAutoMountableDiskEvent_ChangedInReadonly) {
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageReadOnly, true);
// Changed event should cause mounting (if possible).
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
std::unique_ptr<const Disk> disk =
Disk::Builder().SetDevicePath("device1").SetHasMedia(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_CHANGED,
*disk);
EXPECT_EQ(1U, observer.events().size());
EXPECT_EQ(1U, disk_mount_manager_->mount_requests().size());
EXPECT_EQ(0U, disk_mount_manager_->unmount_requests().size());
// Shoule mount a disk in read-only mode.
EXPECT_EQ(chromeos::MOUNT_ACCESS_MODE_READ_ONLY,
disk_mount_manager_->mount_requests()[0].access_mode);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnDeviceEvent_Added) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnDeviceEvent(DiskMountManager::DEVICE_ADDED, "device1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DEVICE_ADDED, event.type);
EXPECT_EQ("device1", event.device_path);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnDeviceEvent_Removed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnDeviceEvent(DiskMountManager::DEVICE_REMOVED, "device1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::DEVICE_REMOVED, event.type);
EXPECT_EQ("device1", event.device_path);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnDeviceEvent_Scanned) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnDeviceEvent(DiskMountManager::DEVICE_SCANNED, "device1");
// SCANNED event is just ignored.
EXPECT_EQ(0U, observer.events().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnMountEvent_MountingAndUnmounting) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
const DiskMountManager::MountPointInfo kMountPoint(
"device1", "mount1", chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE);
volume_manager()->OnMountEvent(DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
ASSERT_EQ(1U, observer.events().size());
LoggingObserver::Event event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::VOLUME_MOUNTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, event.mount_error);
volume_manager()->OnMountEvent(DiskMountManager::UNMOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
ASSERT_EQ(2U, observer.events().size());
event = observer.events()[1];
EXPECT_EQ(LoggingObserver::Event::VOLUME_UNMOUNTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, event.mount_error);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnMountEvent_Remounting) {
std::unique_ptr<Disk> disk = Disk::Builder()
.SetDevicePath("device1")
.SetFileSystemUUID("uuid1")
.Build();
disk_mount_manager_->AddDiskForTest(std::move(disk));
disk_mount_manager_->MountPath("device1", "", "", {},
chromeos::MOUNT_TYPE_DEVICE,
chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
const DiskMountManager::MountPointInfo kMountPoint(
"device1", "mount1", chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE);
volume_manager()->OnMountEvent(DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
LoggingObserver observer;
// Emulate system suspend and then resume.
{
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
power_manager::SuspendImminent_Reason_OTHER);
chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
// After resume, the device is unmounted and then mounted.
volume_manager()->OnMountEvent(DiskMountManager::UNMOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
// Observe what happened for the mount event.
volume_manager()->AddObserver(&observer);
volume_manager()->OnMountEvent(DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
}
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::VOLUME_MOUNTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, event.mount_error);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnMountEvent_UnmountingWithoutMounting) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
const DiskMountManager::MountPointInfo kMountPoint(
"device1", "mount1", chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE);
volume_manager()->OnMountEvent(DiskMountManager::UNMOUNTING,
chromeos::MOUNT_ERROR_NONE, kMountPoint);
// Unmount event for a disk not mounted in this manager is not reported.
ASSERT_EQ(0U, observer.events().size());
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnFormatEvent_Started) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFormatEvent(DiskMountManager::FORMAT_STARTED,
chromeos::FORMAT_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::FORMAT_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnFormatEvent_StartFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFormatEvent(DiskMountManager::FORMAT_STARTED,
chromeos::FORMAT_ERROR_UNKNOWN, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::FORMAT_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnFormatEvent_Completed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
chromeos::FORMAT_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::FORMAT_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
// When "format" is done, VolumeManager requests to mount it.
ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
const FakeDiskMountManager::MountRequest& mount_request =
disk_mount_manager_->mount_requests()[0];
EXPECT_EQ("device1", mount_request.source_path);
EXPECT_EQ("", mount_request.source_format);
EXPECT_EQ("", mount_request.mount_label);
EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnFormatEvent_CompletedFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
chromeos::FORMAT_ERROR_UNKNOWN, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::FORMAT_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
// When "format" is done, VolumeManager requests to mount it.
ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
const FakeDiskMountManager::MountRequest& mount_request =
disk_mount_manager_->mount_requests()[0];
EXPECT_EQ("device1", mount_request.source_path);
EXPECT_EQ("", mount_request.source_format);
EXPECT_EQ("", mount_request.mount_label);
EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnPartitionEvent_Started) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnPartitionEvent(DiskMountManager::PARTITION_STARTED,
chromeos::PARTITION_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::PARTITION_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnPartitionEvent_StartFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnPartitionEvent(DiskMountManager::PARTITION_STARTED,
chromeos::PARTITION_ERROR_UNKNOWN,
"device1", "label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::PARTITION_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnPartitionEvent_Completed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnPartitionEvent(DiskMountManager::PARTITION_COMPLETED,
chromeos::PARTITION_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::PARTITION_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnPartitionEvent_CompletedFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnPartitionEvent(DiskMountManager::PARTITION_COMPLETED,
chromeos::PARTITION_ERROR_UNKNOWN,
"device1", "label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::PARTITION_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
// When "partitioning" fails, VolumeManager requests to mount it for retry.
ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
const FakeDiskMountManager::MountRequest& mount_request =
disk_mount_manager_->mount_requests()[0];
EXPECT_EQ("device1", mount_request.source_path);
EXPECT_EQ("", mount_request.source_format);
EXPECT_EQ("", mount_request.mount_label);
EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnExternalStorageDisabledChanged) {
// Here create four mount points.
disk_mount_manager_->MountPath("mount1", "", "", {},
chromeos::MOUNT_TYPE_DEVICE,
chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
disk_mount_manager_->MountPath("mount2", "", "", {},
chromeos::MOUNT_TYPE_DEVICE,
chromeos::MOUNT_ACCESS_MODE_READ_ONLY);
disk_mount_manager_->MountPath("mount3", "", "", {},
chromeos::MOUNT_TYPE_NETWORK_STORAGE,
chromeos::MOUNT_ACCESS_MODE_READ_ONLY);
disk_mount_manager_->MountPath("failed_unmount", "", "", {},
chromeos::MOUNT_TYPE_DEVICE,
chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
disk_mount_manager_->FailUnmountRequest("failed_unmount",
chromeos::MOUNT_ERROR_UNKNOWN);
// Initially, there are four mount points.
ASSERT_EQ(4U, disk_mount_manager_->mount_points().size());
ASSERT_EQ(0U, disk_mount_manager_->unmount_requests().size());
// Emulate to set kExternalStorageDisabled to false.
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
volume_manager()->OnExternalStorageDisabledChanged();
// Expect no effects.
EXPECT_EQ(4U, disk_mount_manager_->mount_points().size());
EXPECT_EQ(0U, disk_mount_manager_->unmount_requests().size());
// Emulate to set kExternalStorageDisabled to true.
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, true);
volume_manager()->OnExternalStorageDisabledChanged();
// Wait until all unmount request finishes, so that callback chain to unmount
// all the mount points will be invoked.
disk_mount_manager_->FinishAllUnmountPathRequests();
// The external media mount points should be unmounted. Other mount point
// types should remain. The failing unmount should also remain.
EXPECT_EQ(2U, disk_mount_manager_->mount_points().size());
std::set<std::string> expected_unmount_requests = {
"mount1",
"mount2",
"failed_unmount",
};
for (const auto& request : disk_mount_manager_->unmount_requests()) {
EXPECT_TRUE(base::Contains(expected_unmount_requests, request));
expected_unmount_requests.erase(request);
}
EXPECT_TRUE(expected_unmount_requests.empty());
}
TEST_F(VolumeManagerTest, ExternalStorageDisabledPolicyMultiProfile) {
ProfileEnvironment secondary(chromeos::PowerManagerClient::Get(),
disk_mount_manager_.get());
volume_manager()->Initialize();
secondary.volume_manager()->Initialize();
// Simulates the case that the main profile has kExternalStorageDisabled set
// as false, and the secondary profile has the config set to true.
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled, false);
secondary.profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageDisabled,
true);
LoggingObserver main_observer, secondary_observer;
volume_manager()->AddObserver(&main_observer);
secondary.volume_manager()->AddObserver(&secondary_observer);
// Add 1 disk.
std::unique_ptr<const Disk> media_disk =
Disk::Builder().SetDevicePath("device1").SetHasMedia(true).Build();
volume_manager()->OnAutoMountableDiskEvent(DiskMountManager::DISK_ADDED,
*media_disk);
secondary.volume_manager()->OnAutoMountableDiskEvent(
DiskMountManager::DISK_ADDED, *media_disk);
// The profile with external storage enabled should have mounted the volume.
bool has_volume_mounted = false;
for (size_t i = 0; i < main_observer.events().size(); ++i) {
if (main_observer.events()[i].type ==
LoggingObserver::Event::VOLUME_MOUNTED)
has_volume_mounted = true;
}
EXPECT_TRUE(has_volume_mounted);
// The other profiles with external storage disabled should have not.
has_volume_mounted = false;
for (size_t i = 0; i < secondary_observer.events().size(); ++i) {
if (secondary_observer.events()[i].type ==
LoggingObserver::Event::VOLUME_MOUNTED)
has_volume_mounted = true;
}
EXPECT_FALSE(has_volume_mounted);
volume_manager()->RemoveObserver(&main_observer);
secondary.volume_manager()->RemoveObserver(&secondary_observer);
}
TEST_F(VolumeManagerTest, OnExternalStorageReadOnlyChanged) {
// Emulate updates of kExternalStorageReadOnly (change to true, then false).
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageReadOnly, true);
volume_manager()->OnExternalStorageReadOnlyChanged();
profile()->GetPrefs()->SetBoolean(prefs::kExternalStorageReadOnly, false);
volume_manager()->OnExternalStorageReadOnlyChanged();
// Verify that remount of removable disks is triggered for each update.
ASSERT_EQ(2U, disk_mount_manager_->remount_all_requests().size());
const FakeDiskMountManager::RemountAllRequest& remount_request1 =
disk_mount_manager_->remount_all_requests()[0];
EXPECT_EQ(chromeos::MOUNT_ACCESS_MODE_READ_ONLY,
remount_request1.access_mode);
const FakeDiskMountManager::RemountAllRequest& remount_request2 =
disk_mount_manager_->remount_all_requests()[1];
EXPECT_EQ(chromeos::MOUNT_ACCESS_MODE_READ_WRITE,
remount_request2.access_mode);
}
TEST_F(VolumeManagerTest, GetVolumeList) {
volume_manager()->Initialize(); // Adds "Downloads"
std::vector<base::WeakPtr<Volume>> volume_list =
volume_manager()->GetVolumeList();
ASSERT_EQ(1u, volume_list.size());
EXPECT_EQ("downloads:MyFiles", volume_list[0]->volume_id());
EXPECT_EQ(VOLUME_TYPE_DOWNLOADS_DIRECTORY, volume_list[0]->type());
}
TEST_F(VolumeManagerTest, VolumeManagerInitializeMyFilesVolume) {
// Emulate running inside ChromeOS.
base::test::ScopedRunningOnChromeOS running_on_chromeos;
volume_manager()->Initialize(); // Adds "Downloads"
std::vector<base::WeakPtr<Volume>> volume_list =
volume_manager()->GetVolumeList();
ASSERT_EQ(1u, volume_list.size());
auto volume = volume_list[0];
EXPECT_EQ("downloads:MyFiles", volume->volume_id());
EXPECT_EQ(VOLUME_TYPE_DOWNLOADS_DIRECTORY, volume->type());
}
TEST_F(VolumeManagerTest, FindVolumeById) {
volume_manager()->Initialize(); // Adds "Downloads"
base::WeakPtr<Volume> bad_volume =
volume_manager()->FindVolumeById("nonexistent");
ASSERT_FALSE(bad_volume.get());
base::WeakPtr<Volume> good_volume =
volume_manager()->FindVolumeById("downloads:MyFiles");
ASSERT_TRUE(good_volume.get());
EXPECT_EQ("downloads:MyFiles", good_volume->volume_id());
EXPECT_EQ(VOLUME_TYPE_DOWNLOADS_DIRECTORY, good_volume->type());
}
TEST_F(VolumeManagerTest, ArchiveSourceFiltering) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
// Mount a USB stick.
volume_manager()->OnMountEvent(
DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
DiskMountManager::MountPointInfo("/removable/usb", "/removable/usb",
chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE));
// Mount a zip archive in the stick.
volume_manager()->OnMountEvent(
DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
DiskMountManager::MountPointInfo("/removable/usb/1.zip", "/archive/1",
chromeos::MOUNT_TYPE_ARCHIVE,
chromeos::disks::MOUNT_CONDITION_NONE));
base::WeakPtr<Volume> volume = volume_manager()->FindVolumeById("archive:1");
ASSERT_TRUE(volume.get());
EXPECT_EQ("/archive/1", volume->mount_path().AsUTF8Unsafe());
EXPECT_EQ(2u, observer.events().size());
// Mount a zip archive in the previous zip archive.
volume_manager()->OnMountEvent(
DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
DiskMountManager::MountPointInfo("/archive/1/2.zip", "/archive/2",
chromeos::MOUNT_TYPE_ARCHIVE,
chromeos::disks::MOUNT_CONDITION_NONE));
base::WeakPtr<Volume> second_volume =
volume_manager()->FindVolumeById("archive:2");
ASSERT_TRUE(second_volume.get());
EXPECT_EQ("/archive/2", second_volume->mount_path().AsUTF8Unsafe());
EXPECT_EQ(3u, observer.events().size());
// A zip file is mounted from other profile. It must be ignored in the current
// VolumeManager.
volume_manager()->OnMountEvent(
DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
DiskMountManager::MountPointInfo(
"/other/profile/drive/folder/3.zip", "/archive/3",
chromeos::MOUNT_TYPE_ARCHIVE, chromeos::disks::MOUNT_CONDITION_NONE));
base::WeakPtr<Volume> third_volume =
volume_manager()->FindVolumeById("archive:3");
ASSERT_FALSE(third_volume.get());
EXPECT_EQ(3u, observer.events().size());
}
TEST_F(VolumeManagerTest, MTPPlugAndUnplug) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
storage_monitor::StorageInfo info(
storage_monitor::StorageInfo::MakeDeviceId(
storage_monitor::StorageInfo::MTP_OR_PTP, "dummy-device-id"),
FILE_PATH_LITERAL("/dummy/device/location"), base::UTF8ToUTF16("label"),
base::UTF8ToUTF16("vendor"), base::UTF8ToUTF16("model"),
12345 /* size */);
storage_monitor::StorageInfo non_mtp_info(
storage_monitor::StorageInfo::MakeDeviceId(
storage_monitor::StorageInfo::FIXED_MASS_STORAGE, "dummy-device-id2"),
FILE_PATH_LITERAL("/dummy/device/location2"), base::UTF8ToUTF16("label2"),
base::UTF8ToUTF16("vendor2"), base::UTF8ToUTF16("model2"),
12345 /* size */);
// Attach
volume_manager()->OnRemovableStorageAttached(info);
ASSERT_EQ(1u, observer.events().size());
EXPECT_EQ(LoggingObserver::Event::VOLUME_MOUNTED, observer.events()[0].type);
base::WeakPtr<Volume> volume = volume_manager()->FindVolumeById("mtp:model");
EXPECT_EQ(VOLUME_TYPE_MTP, volume->type());
// Non MTP events from storage monitor are ignored.
volume_manager()->OnRemovableStorageAttached(non_mtp_info);
EXPECT_EQ(1u, observer.events().size());
// Detach
volume_manager()->OnRemovableStorageDetached(info);
ASSERT_EQ(2u, observer.events().size());
EXPECT_EQ(LoggingObserver::Event::VOLUME_UNMOUNTED,
observer.events()[1].type);
EXPECT_FALSE(volume.get());
}
TEST_F(VolumeManagerTest, OnRenameEvent_Started) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnRenameEvent(DiskMountManager::RENAME_STARTED,
chromeos::RENAME_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnRenameEvent_StartFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnRenameEvent(DiskMountManager::RENAME_STARTED,
chromeos::RENAME_ERROR_UNKNOWN, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnRenameEvent_Completed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnRenameEvent(DiskMountManager::RENAME_COMPLETED,
chromeos::RENAME_ERROR_NONE, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_TRUE(event.success);
// When "rename" is successfully done, VolumeManager requests to mount it.
ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
const FakeDiskMountManager::MountRequest& mount_request =
disk_mount_manager_->mount_requests()[0];
EXPECT_EQ("device1", mount_request.source_path);
EXPECT_EQ("", mount_request.source_format);
EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
volume_manager()->RemoveObserver(&observer);
}
TEST_F(VolumeManagerTest, OnRenameEvent_CompletedFailed) {
LoggingObserver observer;
volume_manager()->AddObserver(&observer);
volume_manager()->OnRenameEvent(DiskMountManager::RENAME_COMPLETED,
chromeos::RENAME_ERROR_UNKNOWN, "device1",
"label1");
ASSERT_EQ(1U, observer.events().size());
const LoggingObserver::Event& event = observer.events()[0];
EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type);
EXPECT_EQ("device1", event.device_path);
EXPECT_EQ("label1", event.device_label);
EXPECT_FALSE(event.success);
EXPECT_EQ(1U, disk_mount_manager_->mount_requests().size());
volume_manager()->RemoveObserver(&observer);
}
} // namespace file_manager