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