[gnome-software] Show some non-fatal error messages if installing fails
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Show some non-fatal error messages if installing fails
- Date: Mon, 18 Apr 2016 07:58:23 +0000 (UTC)
commit b4d22eb4fdf6859e50b3e2e7d5d54bd42451ddef
Author: Richard Hughes <richard hughsie com>
Date: Sun Apr 17 16:49:28 2016 +0100
Show some non-fatal error messages if installing fails
src/gs-app.c | 36 +++++++++++++++++++++-
src/gs-app.h | 3 ++
src/gs-page.c | 28 +++++++++++++++++
src/gs-plugin-loader.c | 25 +++++++++++++++
src/gs-self-test.c | 34 +++++++++++++++++++++
src/gs-utils.c | 67 +++++++++++++++++++++++++++++++++++++----
src/plugins/gs-plugin-dummy.c | 21 +++++++++++++
7 files changed, 206 insertions(+), 8 deletions(-)
---
diff --git a/src/gs-app.c b/src/gs-app.c
index cc4c73b..1c06828 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -68,6 +68,7 @@ struct _GsApp
gchar *summary_missing;
gchar *description;
GsAppQuality description_quality;
+ GError *last_error;
GPtrArray *screenshots;
GPtrArray *categories;
GPtrArray *keywords;
@@ -175,6 +176,8 @@ gs_app_to_string (GsApp *app)
str = g_string_new ("GsApp:\n");
gs_app_kv_lpad (str, "kind", as_app_kind_to_string (app->kind));
+ if (app->last_error != NULL)
+ gs_app_kv_lpad (str, "last-error", app->last_error->message);
gs_app_kv_lpad (str, "compulsory",
gs_app_has_quirk (app, AS_APP_QUIRK_COMPULSORY)
? "True" : "False");
@@ -445,8 +448,13 @@ gs_app_get_progress (GsApp *app)
void
gs_app_set_state_recover (GsApp *app)
{
- if (app->state_recover != AS_APP_STATE_UNKNOWN)
- gs_app_set_state (app, app->state_recover);
+ if (app->state_recover == AS_APP_STATE_UNKNOWN)
+ return;
+ if (app->state_recover == app->state)
+ return;
+ app->state = app->state_recover;
+ app->state_recover = AS_APP_STATE_UNKNOWN;
+ gs_app_queue_notify (app, "state");
}
/**
@@ -564,6 +572,9 @@ gs_app_set_state_internal (GsApp *app, AsAppState state)
g_debug ("non-transient state now %s",
as_app_state_to_string (state));
app->state_recover = state;
+
+ /* clear the error as the application has changed state */
+ g_clear_error (&app->last_error);
break;
}
@@ -2224,6 +2235,25 @@ gs_app_get_match_value (GsApp *app)
}
/**
+ * gs_app_get_last_error:
+ */
+GError *
+gs_app_get_last_error (GsApp *app)
+{
+ return app->last_error;
+}
+
+/**
+ * gs_app_set_last_error:
+ */
+void
+gs_app_set_last_error (GsApp *app, GError *error)
+{
+ g_clear_error (&app->last_error);
+ app->last_error = g_error_copy (error);
+}
+
+/**
* gs_app_get_property:
*/
static void
@@ -2375,6 +2405,8 @@ gs_app_finalize (GObject *object)
g_ptr_array_unref (app->categories);
if (app->keywords != NULL)
g_ptr_array_unref (app->keywords);
+ if (app->last_error != NULL)
+ g_error_free (app->last_error);
G_OBJECT_CLASS (gs_app_parent_class)->finalize (object);
}
diff --git a/src/gs-app.h b/src/gs-app.h
index 8b1ff4d..7e10980 100644
--- a/src/gs-app.h
+++ b/src/gs-app.h
@@ -242,6 +242,9 @@ gboolean gs_app_has_quirk (GsApp *app,
AsAppQuirk quirk);
void gs_app_add_quirk (GsApp *app,
AsAppQuirk quirk);
+GError *gs_app_get_last_error (GsApp *app);
+void gs_app_set_last_error (GsApp *app,
+ GError *error);
G_END_DECLS
diff --git a/src/gs-page.c b/src/gs-page.c
index db91dea..b7dd9da 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -60,6 +60,7 @@ gs_page_app_installed_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
+ GError *last_error;
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
GsPageHelper *helper = (GsPageHelper *) user_data;
GsPage *page = helper->page;
@@ -81,6 +82,19 @@ gs_page_app_installed_cb (GObject *source,
goto out;
}
+ /* non-fatal error */
+ last_error = gs_app_get_last_error (helper->app);
+ if (last_error != NULL) {
+ g_warning ("failed to install %s: %s",
+ gs_app_get_id (helper->app),
+ last_error->message);
+ gs_app_notify_failed_modal (helper->app,
+ gs_shell_get_window (priv->shell),
+ GS_PLUGIN_LOADER_ACTION_INSTALL,
+ last_error);
+ goto out;
+ }
+
/* only show this if the window is not active */
if (gs_app_get_state (helper->app) != AS_APP_STATE_QUEUED_FOR_INSTALL &&
!gs_shell_is_active (priv->shell))
@@ -98,6 +112,7 @@ gs_page_app_removed_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
+ GError *last_error;
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
GsPageHelper *helper = (GsPageHelper *) user_data;
GsPage *page = helper->page;
@@ -117,6 +132,19 @@ gs_page_app_removed_cb (GObject *source,
goto out;
}
+ /* non-fatal error */
+ last_error = gs_app_get_last_error (helper->app);
+ if (last_error != NULL) {
+ g_warning ("failed to remove %s: %s",
+ gs_app_get_id (helper->app),
+ last_error->message);
+ gs_app_notify_failed_modal (helper->app,
+ gs_shell_get_window (priv->shell),
+ GS_PLUGIN_LOADER_ACTION_REMOVE,
+ last_error);
+ goto out;
+ }
+
if (GS_PAGE_GET_CLASS (page)->app_removed != NULL)
GS_PAGE_GET_CLASS (page)->app_removed (page, helper->app);
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 68fa9e7..107e1bd 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -615,6 +615,29 @@ gs_plugin_loader_get_app_is_compatible (GsApp *app, gpointer user_data)
}
/**
+ * gs_plugin_loader_set_app_error:
+ **/
+static void
+gs_plugin_loader_set_app_error (GsApp *app, GError *error)
+{
+ if (error == NULL)
+ return;
+
+ /* random, non-plugin error domains are never shown to the user */
+ if (error->domain == GS_PLUGIN_ERROR &&
+ gs_app_get_last_error (app) == NULL) {
+ g_debug ("saving error for %s: %s",
+ gs_app_get_id (app),
+ error->message);
+ gs_app_set_last_error (app, error);
+ } else {
+ g_warning ("not saving error for %s: %s",
+ gs_app_get_id (app),
+ error->message);
+ }
+}
+
+/**
* gs_plugin_loader_run_action:
**/
static gboolean
@@ -658,6 +681,7 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
g_warning ("failed to call %s on %s: %s",
function_name, plugin->name,
error_local->message);
+ gs_plugin_loader_set_app_error (app, error_local);
continue;
}
anything_ran = TRUE;
@@ -3931,6 +3955,7 @@ gs_plugin_loader_update_thread_cb (GTask *task,
g_warning ("failed to call %s on %s: %s",
function_name, plugin->name,
error_local->message);
+ gs_plugin_loader_set_app_error (app, error_local);
continue;
}
}
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index cfbf395..993a614 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -196,6 +196,37 @@ gs_plugin_loader_install_func (GsPluginLoader *plugin_loader)
}
static void
+gs_plugin_loader_error_func (GsPluginLoader *plugin_loader)
+{
+ gboolean ret;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GError) error = NULL;
+ GError *last_error;
+
+ /* suppress this */
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "failed to call gs_plugin_update_app on dummy*");
+
+ /* update, which should cause an error to be emitted */
+ app = gs_app_new ("chiron.desktop");
+ gs_app_set_management_plugin (app, "dummy");
+ gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
+ ret = gs_plugin_loader_app_action (plugin_loader, app,
+ GS_PLUGIN_LOADER_ACTION_UPDATE,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* ensure we failed the plugin action */
+ g_test_assert_expected_messages ();
+
+ /* retrieve the error from the application */
+ last_error = gs_app_get_last_error (app);
+ g_assert_error (last_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NO_NETWORK);
+}
+
+static void
gs_plugin_loader_refine_func (GsPluginLoader *plugin_loader)
{
gboolean ret;
@@ -515,6 +546,9 @@ main (int argc, char **argv)
g_test_add_data_func ("/gnome-software/plugin-loader{install}",
plugin_loader,
(GTestDataFunc) gs_plugin_loader_install_func);
+ g_test_add_data_func ("/gnome-software/plugin-loader{error}",
+ plugin_loader,
+ (GTestDataFunc) gs_plugin_loader_error_func);
g_test_add_data_func ("/gnome-software/plugin-loader{installed}",
plugin_loader,
(GTestDataFunc) gs_plugin_loader_installed_func);
diff --git a/src/gs-utils.c b/src/gs-utils.c
index fc8c7ba..f727b72 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -161,32 +161,87 @@ gs_app_notify_failed_modal (GsApp *app,
{
GtkWidget *dialog;
const gchar *title;
- g_autofree gchar *msg = NULL;
+ gboolean show_detailed_error;
+ g_autoptr(GString) msg = NULL;
+ /* TRANSLATORS: install or removed failed */
title = _("Sorry, this did not work");
+
+ /* say what we tried to do */
+ msg = g_string_new ("");
switch (action) {
case GS_PLUGIN_LOADER_ACTION_INSTALL:
/* TRANSLATORS: this is when the install fails */
- msg = g_strdup_printf (_("Installation of %s failed."),
- gs_app_get_name (app));
+ g_string_append_printf (msg, _("Installation of %s failed."),
+ gs_app_get_name (app));
break;
case GS_PLUGIN_LOADER_ACTION_REMOVE:
/* TRANSLATORS: this is when the remove fails */
- msg = g_strdup_printf (_("Removal of %s failed."),
- gs_app_get_name (app));
+ g_string_append_printf (msg, _("Removal of %s failed."),
+ gs_app_get_name (app));
break;
default:
g_assert_not_reached ();
break;
}
+ g_string_append (msg, " ");
+
+ /* give details about the error */
+ switch (error->code) {
+ case GS_PLUGIN_ERROR_NO_NETWORK:
+ /* TRANSLATORS: the package manager needed to download
+ * something with no network available */
+ g_string_append (msg, _("Internet access was required but wasn’t available."));
+ g_string_append (msg, " ");
+ /* TRANSLATORS: plug in the network cable... */
+ g_string_append (msg, _("Please make sure that you have internet access and try again."));
+ show_detailed_error = FALSE;
+ break;
+ case GS_PLUGIN_ERROR_NO_SPACE:
+ /* TRANSLATORS: we ran out of disk space */
+ g_string_append (msg, _("There wasn’t enough disk space."));
+ g_string_append (msg, " ");
+ /* TRANSLATORS: delete some stuff! */
+ g_string_append (msg, _("Please free up some space and try again."));
+ show_detailed_error = FALSE;
+ break;
+ default:
+ /* TRANSLATORS: we didn't handle the error type */
+ g_string_append (msg, _("If the problem persists, contact your software provider."));
+ show_detailed_error = TRUE;
+ }
+
dialog = gtk_message_dialog_new (parent_window,
GTK_DIALOG_MODAL |
+ GTK_DIALOG_USE_HEADER_BAR |
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", title);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
- "%s", msg);
+ "%s", msg->str);
+
+ /* detailed error in an expander */
+ if (show_detailed_error) {
+ GtkWidget *vbox;
+ GtkWidget *expander;
+ GtkWidget *scrolled_window;
+ GtkWidget *label;
+
+ vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ /* TRANSLATORS: this is an expander title */
+ expander = gtk_expander_new (_("Show Details"));
+ gtk_widget_set_margin_start (expander, 36);
+ gtk_widget_set_margin_end (expander, 36);
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
+ label = gtk_label_new (error->message);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), label);
+ gtk_box_pack_end (GTK_BOX (vbox), expander, FALSE, TRUE, 4);
+ gtk_widget_show_all (expander);
+
+ }
+
g_signal_connect (dialog, "response",
G_CALLBACK (gtk_widget_destroy), NULL);
gtk_window_present (GTK_WINDOW (dialog));
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index c5862b5..6426fd0 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -297,6 +297,27 @@ gs_plugin_app_install (GsPlugin *plugin,
}
/**
+ * gs_plugin_update_app:
+ */
+gboolean
+gs_plugin_update_app (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* only process this app if was created by this plugin */
+ if (g_strcmp0 (gs_app_get_management_plugin (app), plugin->name) != 0)
+ return TRUE;
+
+ /* always fail */
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_NO_NETWORK,
+ "no network connection is available");
+ return FALSE;
+}
+
+/**
* gs_plugin_refine_app:
*/
gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]