Commit 7a1d56b0 authored by Nicholas Verne's avatar Nicholas Verne Committed by Commit Bot

Refactor Crostini Export/Import business logic.

This is to allow different UI treatments.

Currently, Export/Import progress is surfaced in ChromeOS notifications.
The current design for the container upgrade needs to show export progress in
main dialog rather than a notification. This refactor enables both UI options.

Bug: 1024693
Change-Id: Icb39ae531dab34b673ad0e5f86ee4de804665c8b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1930271
Auto-Submit: Nicholas Verne <nverne@chromium.org>
Commit-Queue: Nicholas Verne <nverne@chromium.org>
Reviewed-by: default avatarJulian Watson <juwa@google.com>
Cr-Commit-Position: refs/heads/master@{#720366}
parent ca55861f
......@@ -820,6 +820,8 @@ source_set("chromeos") {
"crostini/crostini_export_import.h",
"crostini/crostini_export_import_notification.cc",
"crostini/crostini_export_import_notification.h",
"crostini/crostini_export_import_status_tracker.cc",
"crostini/crostini_export_import_status_tracker.h",
"crostini/crostini_features.cc",
"crostini/crostini_features.h",
"crostini/crostini_force_close_watcher.cc",
......
......@@ -81,15 +81,52 @@ void CrostiniExportImport::Shutdown() {
manager->RemoveImportContainerProgressObserver(this);
}
CrostiniExportImport::OperationData::OperationData(
ExportImportType type,
ContainerId container_id,
TrackerFactory tracker_factory)
: type(type),
container_id(std::move(container_id)),
tracker_factory(std::move(tracker_factory)) {}
CrostiniExportImport::OperationData::~OperationData() = default;
CrostiniExportImport::OperationData* CrostiniExportImport::NewOperationData(
ExportImportType type,
ContainerId container_id,
TrackerFactory factory) {
auto operation_data = std::make_unique<OperationData>(
type, std::move(container_id), std::move(factory));
OperationData* operation_data_ptr = operation_data.get();
// |operation_data_storage_| takes ownership.
operation_data_storage_[operation_data_ptr] = std::move(operation_data);
return operation_data_ptr;
}
CrostiniExportImport::OperationData* CrostiniExportImport::NewOperationData(
ExportImportType type,
ContainerId container_id) {
TrackerFactory factory =
base::BindOnce(&CrostiniExportImportNotification::Create, profile_,
container_id, GetUniqueNotificationId());
return NewOperationData(type, std::move(container_id), std::move(factory));
}
CrostiniExportImport::OperationData* CrostiniExportImport::NewOperationData(
ExportImportType type) {
return NewOperationData(
type, ContainerId(kCrostiniDefaultVmName, kCrostiniDefaultContainerName));
}
void CrostiniExportImport::ExportContainer(content::WebContents* web_contents) {
OpenFileDialog(ExportImportType::EXPORT, web_contents);
OpenFileDialog(NewOperationData(ExportImportType::EXPORT), web_contents);
}
void CrostiniExportImport::ImportContainer(content::WebContents* web_contents) {
OpenFileDialog(ExportImportType::IMPORT, web_contents);
OpenFileDialog(NewOperationData(ExportImportType::IMPORT), web_contents);
}
void CrostiniExportImport::OpenFileDialog(ExportImportType type,
void CrostiniExportImport::OpenFileDialog(OperationData* operation_data,
content::WebContents* web_contents) {
if (!crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile_)) {
return;
......@@ -102,7 +139,7 @@ void CrostiniExportImport::OpenFileDialog(ExportImportType type,
file_types.allowed_paths = ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH;
file_types.extensions = {{"tini", "tar.gz", "tgz"}};
switch (type) {
switch (operation_data->type) {
case ExportImportType::EXPORT:
file_selector_mode = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
title = IDS_SETTINGS_CROSTINI_EXPORT;
......@@ -126,60 +163,68 @@ void CrostiniExportImport::OpenFileDialog(ExportImportType type,
select_folder_dialog_->SelectFile(
file_selector_mode, l10n_util::GetStringUTF16(title), default_path,
&file_types, 0, base::FilePath::StringType(),
web_contents->GetTopLevelNativeWindow(), reinterpret_cast<void*>(type));
web_contents->GetTopLevelNativeWindow(),
static_cast<void*>(operation_data));
}
void CrostiniExportImport::FileSelected(const base::FilePath& path,
int index,
void* params) {
ExportImportType type =
static_cast<ExportImportType>(reinterpret_cast<uintptr_t>(params));
Start(type,
ContainerId(kCrostiniDefaultVmName, kCrostiniDefaultContainerName),
path, base::DoNothing());
Start(static_cast<OperationData*>(params), path, base::DoNothing());
}
void CrostiniExportImport::FileSelectionCanceled(void* params) {
operation_data_storage_.erase(static_cast<OperationData*>(params));
}
void CrostiniExportImport::ExportContainer(
ContainerId container_id,
base::FilePath path,
CrostiniManager::CrostiniResultCallback callback) {
Start(ExportImportType::EXPORT, container_id, path, std::move(callback));
Start(NewOperationData(ExportImportType::EXPORT, std::move(container_id)),
path, std::move(callback));
}
void CrostiniExportImport::ImportContainer(
ContainerId container_id,
base::FilePath path,
CrostiniManager::CrostiniResultCallback callback) {
Start(ExportImportType::IMPORT, container_id, path, std::move(callback));
Start(NewOperationData(ExportImportType::IMPORT, std::move(container_id)),
path, std::move(callback));
}
void CrostiniExportImport::Start(
ExportImportType type,
ContainerId container_id,
OperationData* operation_data,
base::FilePath path,
CrostiniManager::CrostiniResultCallback callback) {
std::unique_ptr<OperationData> operation_data_deleter(
operation_data_storage_[operation_data].release());
operation_data_storage_.erase(operation_data);
if (!crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile_)) {
return std::move(callback).Run(CrostiniResult::NOT_ALLOWED);
}
auto* notification = CrostiniExportImportNotification::Create(
profile_, type, GetUniqueNotificationId(), path, container_id);
auto it = notifications_.find(container_id);
if (it != notifications_.end()) {
auto* status_tracker = std::move(operation_data->tracker_factory)
.Run(operation_data->type, path);
auto it = status_trackers_.find(operation_data->container_id);
if (it != status_trackers_.end()) {
// There is already an operation in progress. Ensure the existing
// notification is (re)displayed so the user knows why this new concurrent
// operation failed, and show a failure notification for the new request.
// status_tracker is (re)displayed so the user knows why this new concurrent
// operation failed, and show a failure status_tracker for the new request.
it->second->ForceRedisplay();
notification->SetStatusFailedConcurrentOperation(it->second->type());
status_tracker->SetStatusFailedConcurrentOperation(it->second->type());
return;
} else {
notifications_.emplace_hint(it, container_id, notification);
status_trackers_.emplace_hint(it, operation_data->container_id,
status_tracker);
for (auto& observer : observers_) {
observer.OnCrostiniExportImportOperationStatusChanged(true);
}
}
switch (type) {
switch (operation_data->type) {
case ExportImportType::EXPORT:
base::PostTaskAndReply(
FROM_HERE, {base::ThreadPool(), base::MayBlock()},
......@@ -198,7 +243,7 @@ void CrostiniExportImport::Start(
kCrostiniDefaultVmName, path, false,
base::BindOnce(&CrostiniExportImport::ExportAfterSharing,
weak_ptr_factory_.GetWeakPtr(),
std::move(container_id), std::move(callback))
operation_data->container_id, std::move(callback))
));
break;
......@@ -207,7 +252,7 @@ void CrostiniExportImport::Start(
kCrostiniDefaultVmName, path, false,
base::BindOnce(&CrostiniExportImport::ImportAfterSharing,
weak_ptr_factory_.GetWeakPtr(),
std::move(container_id), std::move(callback)));
operation_data->container_id, std::move(callback)));
break;
}
}
......@@ -221,11 +266,11 @@ void CrostiniExportImport::ExportAfterSharing(
if (!result) {
LOG(ERROR) << "Error sharing for export " << container_path.value() << ": "
<< failure_reason;
auto it = notifications_.find(container_id);
if (it != notifications_.end()) {
RemoveNotification(it).SetStatusFailed();
auto it = status_trackers_.find(container_id);
if (it != status_trackers_.end()) {
RemoveTracker(it).SetStatusFailed();
} else {
NOTREACHED() << container_id << " has no notification to update";
NOTREACHED() << container_id << " has no status_tracker to update";
}
return;
}
......@@ -243,16 +288,16 @@ void CrostiniExportImport::OnExportComplete(
CrostiniResult result,
uint64_t container_size,
uint64_t compressed_size) {
auto it = notifications_.find(container_id);
if (it == notifications_.end()) {
NOTREACHED() << container_id << " has no notification to update";
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
NOTREACHED() << container_id << " has no status_tracker to update";
return;
}
ExportContainerResult enum_hist_result = ExportContainerResult::kSuccess;
if (result == CrostiniResult::SUCCESS) {
switch (it->second->status()) {
case CrostiniExportImportNotification::Status::CANCELLING: {
case CrostiniExportImportStatusTracker::Status::CANCELLING: {
// If a user requests to cancel, but the export completes before the
// cancel can happen (|result| == SUCCESS), then removing the exported
// file is functionally the same as a successful cancel.
......@@ -261,10 +306,10 @@ void CrostiniExportImport::OnExportComplete(
base::TaskPriority::BEST_EFFORT},
base::BindOnce(base::IgnoreResult(&base::DeleteFile),
it->second->path(), false));
RemoveNotification(it).SetStatusCancelled();
RemoveTracker(it).SetStatusCancelled();
break;
}
case CrostiniExportImportNotification::Status::RUNNING:
case CrostiniExportImportStatusTracker::Status::RUNNING:
UMA_HISTOGRAM_LONG_TIMES("Crostini.BackupTimeSuccess",
base::Time::Now() - start);
// Log backup size statistics.
......@@ -279,14 +324,14 @@ void CrostiniExportImport::OnExportComplete(
"Crostini.BackupSizeRatio",
std::round(compressed_size * 100.0 / container_size));
}
RemoveNotification(it).SetStatusDone();
RemoveTracker(it).SetStatusDone();
break;
default:
NOTREACHED();
}
} else if (result == CrostiniResult::CONTAINER_EXPORT_IMPORT_CANCELLED) {
switch (it->second->status()) {
case CrostiniExportImportNotification::Status::CANCELLING: {
case CrostiniExportImportStatusTracker::Status::CANCELLING: {
// If a user requests to cancel, and the export is cancelled (|result|
// == CONTAINER_EXPORT_IMPORT_CANCELLED), then the partially exported
// file needs to be cleaned up.
......@@ -295,7 +340,7 @@ void CrostiniExportImport::OnExportComplete(
base::TaskPriority::BEST_EFFORT},
base::BindOnce(base::IgnoreResult(&base::DeleteFile),
it->second->path(), false));
RemoveNotification(it).SetStatusCancelled();
RemoveTracker(it).SetStatusCancelled();
break;
}
default:
......@@ -322,10 +367,10 @@ void CrostiniExportImport::OnExportComplete(
UMA_HISTOGRAM_LONG_TIMES("Crostini.BackupTimeFailed",
base::Time::Now() - start);
DCHECK(it->second->status() ==
CrostiniExportImportNotification::Status::RUNNING ||
CrostiniExportImportStatusTracker::Status::RUNNING ||
it->second->status() ==
CrostiniExportImportNotification::Status::CANCELLING);
RemoveNotification(it).SetStatusFailed();
CrostiniExportImportStatusTracker::Status::CANCELLING);
RemoveTracker(it).SetStatusFailed();
}
UMA_HISTOGRAM_ENUMERATION("Crostini.Backup", enum_hist_result);
std::move(callback).Run(result);
......@@ -336,9 +381,9 @@ void CrostiniExportImport::OnExportContainerProgress(
ExportContainerProgressStatus status,
int progress_percent,
uint64_t progress_speed) {
auto it = notifications_.find(container_id);
if (it == notifications_.end()) {
NOTREACHED() << container_id << " has no notification to update";
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
NOTREACHED() << container_id << " has no status_tracker to update";
return;
}
......@@ -359,9 +404,9 @@ void CrostiniExportImport::OnExportContainerProgress(
void CrostiniExportImport::OnExportContainerProgress(
const ContainerId& container_id,
const StreamingExportStatus& status) {
auto it = notifications_.find(container_id);
if (it == notifications_.end()) {
NOTREACHED() << container_id << " has no notification to update";
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
NOTREACHED() << container_id << " has no status_tracker to update";
return;
}
......@@ -384,11 +429,11 @@ void CrostiniExportImport::ImportAfterSharing(
if (!result) {
LOG(ERROR) << "Error sharing for import " << container_path.value() << ": "
<< failure_reason;
auto it = notifications_.find(container_id);
if (it != notifications_.end()) {
RemoveNotification(it).SetStatusFailed();
auto it = status_trackers_.find(container_id);
if (it != status_trackers_.end()) {
RemoveTracker(it).SetStatusFailed();
} else {
NOTREACHED() << container_id << " has no notification to update";
NOTREACHED() << container_id << " has no status_tracker to update";
}
return;
}
......@@ -404,42 +449,42 @@ void CrostiniExportImport::OnImportComplete(
const ContainerId& container_id,
CrostiniManager::CrostiniResultCallback callback,
CrostiniResult result) {
auto it = notifications_.find(container_id);
auto it = status_trackers_.find(container_id);
ImportContainerResult enum_hist_result = ImportContainerResult::kSuccess;
if (result == CrostiniResult::SUCCESS) {
UMA_HISTOGRAM_LONG_TIMES("Crostini.RestoreTimeSuccess",
base::Time::Now() - start);
if (it != notifications_.end()) {
if (it != status_trackers_.end()) {
switch (it->second->status()) {
case CrostiniExportImportNotification::Status::RUNNING:
case CrostiniExportImportStatusTracker::Status::RUNNING:
// If a user requests to cancel, but the import completes before the
// cancel can happen, then the container will have been imported over
// and the cancel will have failed. However the period of time in
// which this can happen is very small (<5s), so it feels quite
// natural to pretend the cancel did not happen, and instead display
// success.
case CrostiniExportImportNotification::Status::CANCELLING:
RemoveNotification(it).SetStatusDone();
case CrostiniExportImportStatusTracker::Status::CANCELLING:
RemoveTracker(it).SetStatusDone();
break;
default:
NOTREACHED();
}
} else {
NOTREACHED() << container_id << " has no notification to update";
NOTREACHED() << container_id << " has no status_tracker to update";
}
} else if (result ==
crostini::CrostiniResult::CONTAINER_EXPORT_IMPORT_CANCELLED) {
if (it != notifications_.end()) {
if (it != status_trackers_.end()) {
switch (it->second->status()) {
case CrostiniExportImportNotification::Status::CANCELLING:
RemoveNotification(it).SetStatusCancelled();
case CrostiniExportImportStatusTracker::Status::CANCELLING:
RemoveTracker(it).SetStatusCancelled();
break;
default:
NOTREACHED();
}
} else {
NOTREACHED() << container_id << " has no notification to update";
NOTREACHED() << container_id << " has no status_tracker to update";
}
} else {
LOG(ERROR) << "Error importing " << int(result);
......@@ -461,21 +506,21 @@ void CrostiniExportImport::OnImportComplete(
enum_hist_result = ImportContainerResult::kFailed;
}
// If the operation didn't start successfully or the vm stops during the
// import, then the notification status will not have been set in
// import, then the status_tracker status will not have been set in
// OnImportContainerProgress, so it needs to be updated.
if (result == CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED ||
result == CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STOPPED ||
result == CrostiniResult::CONTAINER_EXPORT_IMPORT_FAILED_VM_STARTED) {
if (it != notifications_.end()) {
if (it != status_trackers_.end()) {
DCHECK(it->second->status() ==
CrostiniExportImportNotification::Status::RUNNING);
RemoveNotification(it).SetStatusFailed();
CrostiniExportImportStatusTracker::Status::RUNNING);
RemoveTracker(it).SetStatusFailed();
} else {
NOTREACHED() << container_id << " has no notification to update";
NOTREACHED() << container_id << " has no status_tracker to update";
}
} else {
DCHECK(it == notifications_.end())
<< container_id << " has unexpected notification";
DCHECK(it == status_trackers_.end())
<< container_id << " has unexpected status_tracker";
}
UMA_HISTOGRAM_LONG_TIMES("Crostini.RestoreTimeFailed",
base::Time::Now() - start);
......@@ -496,9 +541,9 @@ void CrostiniExportImport::OnImportContainerProgress(
const std::string& architecture_container,
uint64_t available_space,
uint64_t minimum_required_space) {
auto it = notifications_.find(container_id);
if (it == notifications_.end()) {
NOTREACHED() << container_id << " has no notification to update";
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
NOTREACHED() << container_id << " has no status_tracker to update";
return;
}
......@@ -513,12 +558,12 @@ void CrostiniExportImport::OnImportContainerProgress(
break;
// Failure, set error message.
case ImportContainerProgressStatus::FAILURE_ARCHITECTURE:
RemoveNotification(it).SetStatusFailedArchitectureMismatch(
RemoveTracker(it).SetStatusFailedArchitectureMismatch(
architecture_container, architecture_device);
break;
case ImportContainerProgressStatus::FAILURE_SPACE:
DCHECK_GE(minimum_required_space, available_space);
RemoveNotification(it).SetStatusFailedInsufficientSpace(
RemoveTracker(it).SetStatusFailedInsufficientSpace(
minimum_required_space - available_space);
break;
default:
......@@ -528,25 +573,25 @@ void CrostiniExportImport::OnImportContainerProgress(
std::string CrostiniExportImport::GetUniqueNotificationId() {
return base::StringPrintf("crostini_export_import_%d",
next_notification_id_++);
next_status_tracker_id_++);
}
CrostiniExportImportNotification& CrostiniExportImport::RemoveNotification(
std::map<ContainerId, CrostiniExportImportNotification*>::iterator it) {
DCHECK(it != notifications_.end());
auto& notification = *it->second;
notifications_.erase(it);
CrostiniExportImportStatusTracker& CrostiniExportImport::RemoveTracker(
std::map<ContainerId, CrostiniExportImportStatusTracker*>::iterator it) {
DCHECK(it != status_trackers_.end());
auto& status_tracker = *it->second;
status_trackers_.erase(it);
for (auto& observer : observers_) {
observer.OnCrostiniExportImportOperationStatusChanged(false);
}
return notification;
return status_tracker;
}
void CrostiniExportImport::CancelOperation(ExportImportType type,
ContainerId container_id) {
auto it = notifications_.find(container_id);
if (it == notifications_.end()) {
NOTREACHED() << container_id << " has no notification to cancel";
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
NOTREACHED() << container_id << " has no status_tracker to cancel";
return;
}
......@@ -568,13 +613,16 @@ void CrostiniExportImport::CancelOperation(ExportImportType type,
bool CrostiniExportImport::GetExportImportOperationStatus() const {
ContainerId id(kCrostiniDefaultVmName, kCrostiniDefaultContainerName);
return notifications_.find(id) != notifications_.end();
return status_trackers_.find(id) != status_trackers_.end();
}
CrostiniExportImportNotification*
CrostiniExportImport::GetNotificationForTesting(ContainerId container_id) {
auto it = notifications_.find(container_id);
return it != notifications_.end() ? it->second : nullptr;
auto it = status_trackers_.find(container_id);
if (it == status_trackers_.end()) {
return nullptr;
}
return static_cast<CrostiniExportImportNotification*>(it->second);
}
} // namespace crostini
......@@ -68,6 +68,21 @@ class CrostiniExportImport : public KeyedService,
bool in_progress) = 0;
};
using TrackerFactory =
base::OnceCallback<CrostiniExportImportStatusTracker*(ExportImportType,
base::FilePath)>;
struct OperationData {
OperationData(ExportImportType type,
ContainerId id,
TrackerFactory factory);
~OperationData();
ExportImportType type;
ContainerId container_id;
TrackerFactory tracker_factory;
};
static CrostiniExportImport* GetForProfile(Profile* profile);
explicit CrostiniExportImport(Profile* profile);
......@@ -122,13 +137,19 @@ class CrostiniExportImport : public KeyedService,
TestImportFailArchitecture);
FRIEND_TEST_ALL_PREFIXES(CrostiniExportImportTest, TestImportFailSpace);
OperationData* NewOperationData(ExportImportType type,
ContainerId id,
TrackerFactory cb);
OperationData* NewOperationData(ExportImportType type, ContainerId id);
OperationData* NewOperationData(ExportImportType type);
// ui::SelectFileDialog::Listener implementation.
void FileSelected(const base::FilePath& path,
int index,
void* params) override;
void FileSelectionCanceled(void* params) override;
void Start(ExportImportType type,
ContainerId container_id,
void Start(OperationData* params,
base::FilePath path,
CrostiniManager::CrostiniResultCallback callback);
......@@ -175,20 +196,24 @@ class CrostiniExportImport : public KeyedService,
CrostiniManager::CrostiniResultCallback callback,
CrostiniResult result);
void OpenFileDialog(ExportImportType type,
void OpenFileDialog(OperationData* params,
content::WebContents* web_contents);
std::string GetUniqueNotificationId();
CrostiniExportImportNotification& RemoveNotification(
std::map<ContainerId, CrostiniExportImportNotification*>::iterator it);
CrostiniExportImportStatusTracker& RemoveTracker(
std::map<ContainerId, CrostiniExportImportStatusTracker*>::iterator it);
Profile* profile_;
scoped_refptr<ui::SelectFileDialog> select_folder_dialog_;
std::map<ContainerId, CrostiniExportImportNotification*> notifications_;
// Notifications must have unique-per-profile identifiers.
std::map<ContainerId, CrostiniExportImportStatusTracker*> status_trackers_;
// |operation_data_storage_| persists the data required to complete an
// operation while the file selection dialog is open/operation is in progress.
std::unordered_map<OperationData*, std::unique_ptr<OperationData>>
operation_data_storage_;
// Trackers must have unique-per-profile identifiers.
// A non-static member on a profile-keyed-service will suffice.
int next_notification_id_;
int next_status_tracker_id_;
base::ObserverList<Observer> observers_;
// weak_ptr_factory_ should always be last member.
base::WeakPtrFactory<CrostiniExportImport> weak_ptr_factory_{this};
......
......@@ -38,12 +38,9 @@ CrostiniExportImportNotification::CrostiniExportImportNotification(
const std::string& notification_id,
base::FilePath path,
ContainerId container_id)
: profile_(profile),
type_(type),
path_(std::move(path)),
: CrostiniExportImportStatusTracker(type, std::move(path)),
profile_(profile),
container_id_(std::move(container_id)) {
DCHECK(type == ExportImportType::EXPORT || type == ExportImportType::IMPORT);
message_center::RichNotificationData rich_notification_data;
rich_notification_data.vector_small_image = &kNotificationLinuxIcon;
rich_notification_data.accent_color = ash::kSystemNotificationColorNormal;
......@@ -60,7 +57,6 @@ CrostiniExportImportNotification::CrostiniExportImportNotification(
rich_notification_data,
base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
weak_ptr_factory_.GetWeakPtr()));
SetStatusRunning(0);
}
......@@ -74,17 +70,8 @@ void CrostiniExportImportNotification::ForceRedisplay() {
/*metadata=*/nullptr);
}
void CrostiniExportImportNotification::SetStatusRunning(int progress_percent) {
DCHECK(status_ == Status::RUNNING || status_ == Status::CANCELLING);
// Progress updates can still be received while the notification is being
// cancelled. These should not be displayed, as the operation will eventually
// cancel (or fail to cancel).
if (status_ == Status::CANCELLING) {
return;
}
status_ = Status::RUNNING;
void CrostiniExportImportNotification::SetStatusRunningUI(
int progress_percent) {
if (hidden_) {
return;
}
......@@ -92,7 +79,7 @@ void CrostiniExportImportNotification::SetStatusRunning(int progress_percent) {
notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
notification_->set_accent_color(ash::kSystemNotificationColorNormal);
notification_->set_title(l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_TITLE_RUNNING
: IDS_CROSTINI_IMPORT_NOTIFICATION_TITLE_RUNNING));
notification_->set_message(
......@@ -106,11 +93,7 @@ void CrostiniExportImportNotification::SetStatusRunning(int progress_percent) {
ForceRedisplay();
}
void CrostiniExportImportNotification::SetStatusCancelling() {
DCHECK(status_ == Status::RUNNING);
status_ = Status::CANCELLING;
void CrostiniExportImportNotification::SetStatusCancellingUI() {
if (hidden_) {
return;
}
......@@ -118,7 +101,7 @@ void CrostiniExportImportNotification::SetStatusCancelling() {
notification_->set_type(message_center::NOTIFICATION_TYPE_PROGRESS);
notification_->set_accent_color(ash::kSystemNotificationColorNormal);
notification_->set_title(l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_TITLE_CANCELLING
: IDS_CROSTINI_IMPORT_NOTIFICATION_TITLE_CANCELLING));
notification_->set_message({});
......@@ -130,20 +113,15 @@ void CrostiniExportImportNotification::SetStatusCancelling() {
ForceRedisplay();
}
void CrostiniExportImportNotification::SetStatusDone() {
DCHECK(status_ == Status::RUNNING ||
(type_ == ExportImportType::IMPORT && status_ == Status::CANCELLING));
status_ = Status::DONE;
void CrostiniExportImportNotification::SetStatusDoneUI() {
notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
notification_->set_accent_color(ash::kSystemNotificationColorNormal);
notification_->set_title(l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_TITLE_DONE
: IDS_CROSTINI_IMPORT_NOTIFICATION_TITLE_DONE));
notification_->set_message(l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_MESSAGE_DONE
: IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_DONE));
notification_->set_buttons({});
......@@ -153,64 +131,18 @@ void CrostiniExportImportNotification::SetStatusDone() {
ForceRedisplay();
}
void CrostiniExportImportNotification::SetStatusCancelled() {
DCHECK(status_ == Status::CANCELLING);
status_ = Status::CANCELLED;
void CrostiniExportImportNotification::SetStatusCancelledUI() {
NotificationDisplayService::GetForProfile(profile_)->Close(
NotificationHandler::Type::TRANSIENT, notification_->id());
}
void CrostiniExportImportNotification::SetStatusFailed() {
SetStatusFailed(Status::FAILED_UNKNOWN_REASON,
l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_MESSAGE_FAILED
: IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED));
}
void CrostiniExportImportNotification::SetStatusFailedArchitectureMismatch(
const std::string& architecture_container,
const std::string& architecture_device) {
DCHECK(type_ == ExportImportType::IMPORT);
SetStatusFailed(
Status::FAILED_ARCHITECTURE_MISMATCH,
l10n_util::GetStringFUTF16(
IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_ARCHITECTURE,
base::ASCIIToUTF16(architecture_container),
base::ASCIIToUTF16(architecture_device)));
}
void CrostiniExportImportNotification::SetStatusFailedInsufficientSpace(
uint64_t additional_required_space) {
DCHECK(type_ == ExportImportType::IMPORT);
SetStatusFailed(Status::FAILED_INSUFFICIENT_SPACE,
l10n_util::GetStringFUTF16(
IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_SPACE,
ui::FormatBytes(additional_required_space)));
}
void CrostiniExportImportNotification::SetStatusFailedConcurrentOperation(
ExportImportType in_progress_operation_type) {
SetStatusFailed(
Status::FAILED_CONCURRENT_OPERATION,
l10n_util::GetStringUTF16(
in_progress_operation_type == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_MESSAGE_FAILED_IN_PROGRESS
: IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_IN_PROGRESS));
}
void CrostiniExportImportNotification::SetStatusFailed(
void CrostiniExportImportNotification::SetStatusFailedWithMessageUI(
Status status,
const base::string16& message) {
DCHECK(status_ == Status::RUNNING || status_ == Status::CANCELLING);
status_ = status;
notification_->set_type(message_center::NOTIFICATION_TYPE_SIMPLE);
notification_->set_accent_color(ash::kSystemNotificationColorCriticalWarning);
notification_->set_title(l10n_util::GetStringUTF16(
type_ == ExportImportType::EXPORT
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_TITLE_FAILED
: IDS_CROSTINI_IMPORT_NOTIFICATION_TITLE_FAILED));
notification_->set_message(message);
......@@ -222,7 +154,7 @@ void CrostiniExportImportNotification::SetStatusFailed(
}
void CrostiniExportImportNotification::Close(bool by_user) {
switch (status_) {
switch (status()) {
case Status::RUNNING:
case Status::CANCELLING:
hidden_ = true;
......@@ -243,18 +175,18 @@ void CrostiniExportImportNotification::Close(bool by_user) {
void CrostiniExportImportNotification::Click(
const base::Optional<int>& button_index,
const base::Optional<base::string16>&) {
switch (status_) {
switch (status()) {
case Status::RUNNING:
if (button_index) {
DCHECK(*button_index == 1);
CrostiniExportImport::GetForProfile(profile_)->CancelOperation(
type_, container_id_);
type(), container_id_);
}
return;
case Status::DONE:
DCHECK(!button_index);
if (type_ == ExportImportType::EXPORT) {
platform_util::ShowItemInFolder(profile_, path_);
if (type() == ExportImportType::EXPORT) {
platform_util::ShowItemInFolder(profile_, path());
}
return;
case Status::FAILED_UNKNOWN_REASON:
......
......@@ -10,6 +10,7 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/crostini/crostini_export_import_status_tracker.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
......@@ -25,53 +26,24 @@ enum class ExportImportType;
// Notification for Crostini export and import.
class CrostiniExportImportNotification
: public message_center::NotificationObserver {
: public CrostiniExportImportStatusTracker,
public message_center::NotificationObserver {
public:
enum class Status {
RUNNING,
CANCELLING,
DONE,
CANCELLED,
FAILED_UNKNOWN_REASON,
FAILED_ARCHITECTURE_MISMATCH,
FAILED_INSUFFICIENT_SPACE,
FAILED_CONCURRENT_OPERATION,
};
// Used to construct CrostiniExportImportNotification to ensure it controls
// its lifetime.
static CrostiniExportImportNotification* Create(
static CrostiniExportImportStatusTracker* Create(
Profile* profile,
ExportImportType type,
ContainerId container_id,
const std::string& notification_id,
base::FilePath path,
ContainerId container_id) {
ExportImportType type,
base::FilePath path) {
return new CrostiniExportImportNotification(profile, type, notification_id,
std::move(path),
std::move(container_id));
}
virtual ~CrostiniExportImportNotification();
// Can be used to draw attention to the notification without changing its
// status, even if it has been hidden.
void ForceRedisplay();
void SetStatusRunning(int progress_percent);
void SetStatusCancelling();
void SetStatusDone();
void SetStatusCancelled();
void SetStatusFailed();
void SetStatusFailedArchitectureMismatch(
const std::string& architecture_container,
const std::string& architecture_device);
void SetStatusFailedInsufficientSpace(uint64_t additional_required_space);
void SetStatusFailedConcurrentOperation(
ExportImportType in_progress_operation_type);
Status status() const { return status_; }
ExportImportType type() const { return type_; }
const base::FilePath& path() const { return path_; }
~CrostiniExportImportNotification() override;
// Getters for testing.
message_center::Notification* get_notification() {
return notification_.get();
......@@ -89,13 +61,18 @@ class CrostiniExportImportNotification
base::FilePath path,
ContainerId container_id);
void SetStatusFailed(Status status, const base::string16& message);
// CrostiniExportImportStatusTracker:
void ForceRedisplay() override;
void SetStatusRunningUI(int progress_percent) override;
void SetStatusCancellingUI() override;
void SetStatusDoneUI() override;
void SetStatusCancelledUI() override;
void SetStatusFailedWithMessageUI(Status status,
const base::string16& message) override;
Profile* profile_;
ExportImportType type_;
base::FilePath path_;
Profile* profile_; // Not owned.
ContainerId container_id_;
Status status_ = Status::RUNNING;
// Time when the operation started. Used for estimating time remaining.
base::TimeTicks started_ = base::TimeTicks::Now();
std::unique_ptr<message_center::Notification> notification_;
......
// Copyright 2019 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/crostini/crostini_export_import_status_tracker.h"
#include "base/logging.h"
#include "chrome/browser/chromeos/crostini/crostini_export_import.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/text/bytes_formatting.h"
namespace crostini {
CrostiniExportImportStatusTracker::CrostiniExportImportStatusTracker(
ExportImportType type,
base::FilePath path)
: type_(type), path_(path) {
DCHECK(type == ExportImportType::EXPORT || type == ExportImportType::IMPORT);
}
CrostiniExportImportStatusTracker::~CrostiniExportImportStatusTracker() =
default;
void CrostiniExportImportStatusTracker::SetStatusRunning(int progress_percent) {
DCHECK(status_ == Status::RUNNING || status_ == Status::CANCELLING);
// Progress updates can still be received while the notification is being
// cancelled. These should not be displayed, as the operation will eventually
// cancel (or fail to cancel).
if (status_ == Status::CANCELLING) {
return;
}
status_ = Status::RUNNING;
SetStatusRunningUI(progress_percent);
}
void CrostiniExportImportStatusTracker::SetStatusCancelling() {
DCHECK(status_ == Status::RUNNING);
status_ = Status::CANCELLING;
SetStatusCancellingUI();
}
void CrostiniExportImportStatusTracker::SetStatusDone() {
DCHECK(status_ == Status::RUNNING ||
(type() == ExportImportType::IMPORT && status_ == Status::CANCELLING));
status_ = Status::DONE;
SetStatusDoneUI();
}
void CrostiniExportImportStatusTracker::SetStatusCancelled() {
DCHECK(status_ == Status::CANCELLING);
status_ = Status::CANCELLED;
SetStatusCancelledUI();
}
void CrostiniExportImportStatusTracker::SetStatusFailed() {
SetStatusFailedWithMessage(
Status::FAILED_UNKNOWN_REASON,
l10n_util::GetStringUTF16(
type() == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_MESSAGE_FAILED
: IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED));
}
void CrostiniExportImportStatusTracker::SetStatusFailedArchitectureMismatch(
const std::string& architecture_container,
const std::string& architecture_device) {
DCHECK(type() == ExportImportType::IMPORT);
SetStatusFailedWithMessage(
Status::FAILED_ARCHITECTURE_MISMATCH,
l10n_util::GetStringFUTF16(
IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_ARCHITECTURE,
base::ASCIIToUTF16(architecture_container),
base::ASCIIToUTF16(architecture_device)));
}
void CrostiniExportImportStatusTracker::SetStatusFailedInsufficientSpace(
uint64_t additional_required_space) {
DCHECK(type() == ExportImportType::IMPORT);
SetStatusFailedWithMessage(
Status::FAILED_INSUFFICIENT_SPACE,
l10n_util::GetStringFUTF16(
IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_SPACE,
ui::FormatBytes(additional_required_space)));
}
void CrostiniExportImportStatusTracker::SetStatusFailedConcurrentOperation(
ExportImportType in_progress_operation_type) {
SetStatusFailedWithMessage(
Status::FAILED_CONCURRENT_OPERATION,
l10n_util::GetStringUTF16(
in_progress_operation_type == ExportImportType::EXPORT
? IDS_CROSTINI_EXPORT_NOTIFICATION_MESSAGE_FAILED_IN_PROGRESS
: IDS_CROSTINI_IMPORT_NOTIFICATION_MESSAGE_FAILED_IN_PROGRESS));
}
void CrostiniExportImportStatusTracker::SetStatusFailedWithMessage(
Status status,
const base::string16& message) {
DCHECK(status_ == Status::RUNNING || status_ == Status::CANCELLING);
status_ = status;
SetStatusFailedWithMessageUI(status, message);
}
} // namespace crostini
// Copyright 2019 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.
#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_EXPORT_IMPORT_STATUS_TRACKER_H_
#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_EXPORT_IMPORT_STATUS_TRACKER_H_
#include <memory>
#include <string>
#include "base/files/file_path.h"
#include "base/strings/string16.h"
namespace crostini {
enum class ExportImportType;
class CrostiniExportImportStatusTracker {
public:
enum class Status {
RUNNING,
CANCELLING,
DONE,
CANCELLED,
FAILED_UNKNOWN_REASON,
FAILED_ARCHITECTURE_MISMATCH,
FAILED_INSUFFICIENT_SPACE,
FAILED_CONCURRENT_OPERATION,
};
CrostiniExportImportStatusTracker(ExportImportType type, base::FilePath path);
virtual ~CrostiniExportImportStatusTracker();
Status status() const { return status_; }
ExportImportType type() const { return type_; }
const base::FilePath& path() const { return path_; }
// Can be used to draw attention to the UI without changing its
// status, even if it has been hidden.
virtual void ForceRedisplay() {}
virtual void SetStatusRunningUI(int progress_percent) = 0;
virtual void SetStatusCancellingUI() = 0;
virtual void SetStatusDoneUI() = 0;
virtual void SetStatusCancelledUI() = 0;
virtual void SetStatusFailedWithMessageUI(Status status,
const base::string16& message) = 0;
void SetStatusRunning(int progress_percent);
void SetStatusCancelling();
void SetStatusDone();
void SetStatusCancelled();
void SetStatusFailed();
void SetStatusFailedArchitectureMismatch(
const std::string& architecture_container,
const std::string& architecture_device);
void SetStatusFailedInsufficientSpace(uint64_t additional_required_space);
void SetStatusFailedConcurrentOperation(
ExportImportType in_progress_operation_type);
private:
void SetStatusFailedWithMessage(Status status, const base::string16& message);
ExportImportType type_;
base::FilePath path_;
Status status_ = Status::RUNNING;
};
} // namespace crostini
#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_EXPORT_IMPORT_STATUS_TRACKER_H_
......@@ -155,13 +155,14 @@ TEST_F(CrostiniExportImportTest, TestNotAllowed) {
// TODO(juwa): remove this once tremplin has been shipped.
TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::EXPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::EXPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -170,7 +171,7 @@ TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
ExportLxdContainerProgressSignal_Status_EXPORTING_PACK,
{.progress_percent = 20});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 10);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -180,7 +181,7 @@ TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
ExportLxdContainerProgressSignal_Status_EXPORTING_DOWNLOAD,
{.progress_percent = 20});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -191,7 +192,7 @@ TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
ExportLxdContainerProgressSignal_Status_EXPORTING_DOWNLOAD,
{.progress_percent = 40});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -199,7 +200,7 @@ TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
SendExportProgress(
vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::DONE);
CrostiniExportImportStatusTracker::Status::DONE);
EXPECT_FALSE(notification->get_notification()->pinned());
// CrostiniExportImport should've created the exported file.
task_environment_.RunUntilIdle();
......@@ -208,13 +209,14 @@ TEST_F(CrostiniExportImportTest, TestDeprecatedExportSuccess) {
TEST_F(CrostiniExportImportTest, TestExportSuccess) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::EXPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::EXPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -227,7 +229,7 @@ TEST_F(CrostiniExportImportTest, TestExportSuccess) {
.files_streamed = 30,
.bytes_streamed = 10});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 20);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -240,7 +242,7 @@ TEST_F(CrostiniExportImportTest, TestExportSuccess) {
.files_streamed = 55,
.bytes_streamed = 66});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -254,7 +256,7 @@ TEST_F(CrostiniExportImportTest, TestExportSuccess) {
.files_streamed = 90,
.bytes_streamed = 85});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -262,7 +264,7 @@ TEST_F(CrostiniExportImportTest, TestExportSuccess) {
SendExportProgress(
vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_DONE);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::DONE);
CrostiniExportImportStatusTracker::Status::DONE);
EXPECT_FALSE(notification->get_notification()->pinned());
// CrostiniExportImport should've created the exported file.
task_environment_.RunUntilIdle();
......@@ -271,7 +273,8 @@ TEST_F(CrostiniExportImportTest, TestExportSuccess) {
TEST_F(CrostiniExportImportTest, TestExportFail) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::EXPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::EXPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
......@@ -280,7 +283,7 @@ TEST_F(CrostiniExportImportTest, TestExportFail) {
SendExportProgress(
vm_tools::cicerone::ExportLxdContainerProgressSignal_Status_FAILED);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::FAILED_UNKNOWN_REASON);
CrostiniExportImportStatusTracker::Status::FAILED_UNKNOWN_REASON);
EXPECT_FALSE(notification->get_notification()->pinned());
// CrostiniExportImport should cleanup the file if an export fails.
task_environment_.RunUntilIdle();
......@@ -289,13 +292,14 @@ TEST_F(CrostiniExportImportTest, TestExportFail) {
TEST_F(CrostiniExportImportTest, TestExportCancelled) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::EXPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::EXPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -303,7 +307,7 @@ TEST_F(CrostiniExportImportTest, TestExportCancelled) {
crostini_export_import_->CancelOperation(ExportImportType::EXPORT,
container_id_);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
EXPECT_TRUE(base::PathExists(tarball_));
......@@ -317,7 +321,7 @@ TEST_F(CrostiniExportImportTest, TestExportCancelled) {
.files_streamed = 50,
.bytes_streamed = 50});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
EXPECT_TRUE(base::PathExists(tarball_));
......@@ -333,13 +337,14 @@ TEST_F(CrostiniExportImportTest, TestExportCancelled) {
TEST_F(CrostiniExportImportTest, TestExportDoneBeforeCancelled) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::EXPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::EXPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -347,7 +352,7 @@ TEST_F(CrostiniExportImportTest, TestExportDoneBeforeCancelled) {
crostini_export_import_->CancelOperation(ExportImportType::EXPORT,
container_id_);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
EXPECT_TRUE(base::PathExists(tarball_));
......@@ -363,13 +368,14 @@ TEST_F(CrostiniExportImportTest, TestExportDoneBeforeCancelled) {
TEST_F(CrostiniExportImportTest, TestImportSuccess) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -379,7 +385,7 @@ TEST_F(CrostiniExportImportTest, TestImportSuccess) {
ImportLxdContainerProgressSignal_Status_IMPORTING_UPLOAD,
{.progress_percent = 20});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 10);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -389,7 +395,7 @@ TEST_F(CrostiniExportImportTest, TestImportSuccess) {
ImportLxdContainerProgressSignal_Status_IMPORTING_UNPACK,
{.progress_percent = 20});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -400,7 +406,7 @@ TEST_F(CrostiniExportImportTest, TestImportSuccess) {
ImportLxdContainerProgressSignal_Status_IMPORTING_UNPACK,
{.progress_percent = 40});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 60);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -408,13 +414,14 @@ TEST_F(CrostiniExportImportTest, TestImportSuccess) {
SendImportProgress(
vm_tools::cicerone::ImportLxdContainerProgressSignal_Status_DONE);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::DONE);
CrostiniExportImportStatusTracker::Status::DONE);
EXPECT_FALSE(notification->get_notification()->pinned());
}
TEST_F(CrostiniExportImportTest, TestImportFail) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
......@@ -423,7 +430,7 @@ TEST_F(CrostiniExportImportTest, TestImportFail) {
SendImportProgress(
vm_tools::cicerone::ImportLxdContainerProgressSignal_Status_FAILED);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::FAILED_UNKNOWN_REASON);
CrostiniExportImportStatusTracker::Status::FAILED_UNKNOWN_REASON);
EXPECT_FALSE(notification->get_notification()->pinned());
std::string msg("Restoring couldn't be completed due to an error");
EXPECT_EQ(notification->get_notification()->message(),
......@@ -432,13 +439,14 @@ TEST_F(CrostiniExportImportTest, TestImportFail) {
TEST_F(CrostiniExportImportTest, TestImportCancelled) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -446,7 +454,7 @@ TEST_F(CrostiniExportImportTest, TestImportCancelled) {
crostini_export_import_->CancelOperation(ExportImportType::IMPORT,
container_id_);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
......@@ -456,7 +464,7 @@ TEST_F(CrostiniExportImportTest, TestImportCancelled) {
ImportLxdContainerProgressSignal_Status_IMPORTING_UPLOAD,
{.progress_percent = 50});
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
......@@ -469,13 +477,14 @@ TEST_F(CrostiniExportImportTest, TestImportCancelled) {
TEST_F(CrostiniExportImportTest, TestImportDoneBeforeCancelled) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
ASSERT_NE(notification, nullptr);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::RUNNING);
CrostiniExportImportStatusTracker::Status::RUNNING);
EXPECT_EQ(notification->get_notification()->progress(), 0);
EXPECT_TRUE(notification->get_notification()->pinned());
......@@ -483,7 +492,7 @@ TEST_F(CrostiniExportImportTest, TestImportDoneBeforeCancelled) {
crostini_export_import_->CancelOperation(ExportImportType::IMPORT,
container_id_);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::CANCELLING);
CrostiniExportImportStatusTracker::Status::CANCELLING);
EXPECT_EQ(notification->get_notification()->progress(), -1);
EXPECT_FALSE(notification->get_notification()->pinned());
......@@ -491,13 +500,14 @@ TEST_F(CrostiniExportImportTest, TestImportDoneBeforeCancelled) {
SendImportProgress(
vm_tools::cicerone::ImportLxdContainerProgressSignal_Status_DONE);
EXPECT_EQ(notification->status(),
CrostiniExportImportNotification::Status::DONE);
CrostiniExportImportStatusTracker::Status::DONE);
EXPECT_FALSE(notification->get_notification()->pinned());
}
TEST_F(CrostiniExportImportTest, TestImportFailArchitecture) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
......@@ -508,7 +518,7 @@ TEST_F(CrostiniExportImportTest, TestImportFailArchitecture) {
ImportLxdContainerProgressSignal_Status_FAILED_ARCHITECTURE);
EXPECT_EQ(
notification->status(),
CrostiniExportImportNotification::Status::FAILED_ARCHITECTURE_MISMATCH);
CrostiniExportImportStatusTracker::Status::FAILED_ARCHITECTURE_MISMATCH);
EXPECT_FALSE(notification->get_notification()->pinned());
std::string msg(
"Cannot import container architecture type arch_con with this device "
......@@ -521,7 +531,8 @@ TEST_F(CrostiniExportImportTest, TestImportFailArchitecture) {
TEST_F(CrostiniExportImportTest, TestImportFailSpace) {
crostini_export_import_->FileSelected(
tarball_, 0, reinterpret_cast<void*>(ExportImportType::IMPORT));
tarball_, 0,
crostini_export_import_->NewOperationData(ExportImportType::IMPORT));
task_environment_.RunUntilIdle();
CrostiniExportImportNotification* notification =
crostini_export_import_->GetNotificationForTesting(container_id_);
......@@ -535,7 +546,7 @@ TEST_F(CrostiniExportImportTest, TestImportFailSpace) {
});
EXPECT_EQ(
notification->status(),
CrostiniExportImportNotification::Status::FAILED_INSUFFICIENT_SPACE);
CrostiniExportImportStatusTracker::Status::FAILED_INSUFFICIENT_SPACE);
EXPECT_FALSE(notification->get_notification()->pinned());
std::string msg =
"Cannot restore due to lack of storage space. Free up 15.0 GB from the "
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment