[gnome-desktop-testing/wip/functional] Some work on functional testing and core dump export



commit e44405ad1a1ea9b5c19a209e94e9e872c628198f
Author: Colin Walters <walters verbum org>
Date:   Sun May 26 21:29:33 2013 +0100

    Some work on functional testing and core dump export

 Makefile-tests.am                          |    9 ++
 src/gnome-ostree-systemd-coredump-export.c |  137 ++++++++++++++++++++++++++++
 tests/functional/shelleval.js              |   26 +++++
 tests/functional/startstopapps.js          |   80 ++++++++++++++++
 4 files changed, 252 insertions(+), 0 deletions(-)
---
diff --git a/Makefile-tests.am b/Makefile-tests.am
index fd246a4..0f131ea 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -20,3 +20,12 @@ gnome_desktop_testing_runner_SOURCES = src/gnome-desktop-testing-runner.c
 gnome_desktop_testing_runner_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libgsystem
 gnome_desktop_testing_runner_CFLAGS = $(GIO_UNIX_CFLAGS)
 gnome_desktop_testing_runner_LDADD = $(GIO_UNIX_LIBS) libgsystem.la
+
+if ENABLE_SYSTEMD_JOURNAL
+bin_PROGRAMS += gnome-ostree-systemd-coredump-export
+endif
+gnome_ostree_systemd_coredump_export_SOURCES = src/gnome-ostree-systemd-coredump-export.c
+gnome_ostree_systemd_coredump_export_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libgsystem
+gnome_ostree_systemd_coredump_export_CFLAGS = $(GIO_UNIX_CFLAGS)
+gnome_ostree_systemd_coredump_export_LDADD = $(GIO_UNIX_LIBS) libgsystem.la
+
diff --git a/src/gnome-ostree-systemd-coredump-export.c b/src/gnome-ostree-systemd-coredump-export.c
new file mode 100644
index 0000000..4251310
--- /dev/null
+++ b/src/gnome-ostree-systemd-coredump-export.c
@@ -0,0 +1,137 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gio.h>
+#include <json-glib.h>
+#include "libgsystem.h"
+
+static gboolean
+export_one_coredump (GDataOutputStream  *dataout,
+                     const char         *coredump_pid_str,
+                     GError            **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GSSubproessContext *proc_context = NULL;
+  gs_unref_object GSSubproess *proc = NULL;
+  char *coredumpctl_argv = { "systemd-coredumpctl", "dump", NULL /* pid */, NULL };
+  gs_unref_object GInputStream *dump_stdin = NULL;
+  GBytes *bytes = NULL;
+
+  coredumpctl_argv[2] = coredump_pid_str;
+  proc_context = gs_subprocess_context_new (coredumpctl_argv);
+  gs_subprocess_context_set_stdout_disposition (proc_context, GS_SUBPROCESS_STREAM_DISPOSITION_PIPE);
+
+  proc = gs_subprocess_new (proc_context, NULL, error);
+  if (!proc)
+    goto out;
+
+  dump_stdin = gs_subprocess_get_stdout (proc);
+  while ((bytes = g_data_input_stream_read_bytes (dump_stdin, 4096,
+                                                  cancellable, error)) != NULL)
+    {
+      GVariant *
+      g_clear_pointer (&bytes, g_bytes_unref);
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_pointer (&bytes, g_bytes_unref);
+  return ret;
+}
+
+int
+main (int argc, char **argv)
+{
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  gs_unref_object GCancellable *cancellable = NULL;
+  char* journal_argv[] = { "journalctl", "-o", "json", "-b", "--no-tail",
+                          "_MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", NULL };
+  gs_unref_object GInputStream *journal_in = NULL;
+  gs_unref_object GFile *virtio_file = NULL;
+  gs_unref_object GUnixOutputStream *virtio = NULL;
+  gs_unref_object GDataOutputStream *dataout = NULL;
+  gs_unref_object GDataInputStream *datain = NULL;
+  const char *virtio_path = "/dev/virtio-ports/org.gnome.ostree.coredump";
+
+  virtio_file = g_file_new_for_path (virtio_path);
+  if (!g_file_query_exists (virtio_file, NULL))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Unable to find virtio channel %s", virtio_path);
+      goto out;
+    }
+  virtio = g_file_append_to (virtio_file, 0, cancellable, error);
+  dataout = g_data_output_stream_new (virtio);
+
+  if (!g_spawn_async_with_pipes (NULL, journal_argv, NULL,
+                                G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                NULL, NULL,
+                                &pid, NULL,
+                                &stdout_fd, NULL, error))
+    goto out;
+
+  journal_in = g_unix_input_stream_new (stdout_fd, TRUE);
+  datain = g_data_input_stream_new (journal_in);
+  
+  while (TRUE)
+    {
+      gsize len;
+      GError *tmp_error = NULL;
+      gs_free char *line = g_data_input_stream_read_line_utf8 (datain, &len, NULL, &tmp_error);
+      gs_unref_object JsonParser *parser = NULL;
+      JsonNode *root;
+      JsonObject *rootobj;
+      const char *coredump_pid_str;
+      guint64 coredump_pid;
+
+      if (tmp_error != NULL)
+       {
+         g_propagate_error (error, tmp_error);
+         goto out;
+       }
+
+      parser = json_parser_new ();
+      if (!json_parser_load_from_data (parser, line, len, error))
+        goto out;
+      
+      root = json_parser_get_root (parser);
+      if (json_node_get_node_type (root) != JSON_NODE_OBJECT)
+        continue;
+      rootobj = json_node_get_object (root);
+      
+      coredump_pid_str = json_object_get_string_member (rootobj, "COREDUMP_PID");
+      if (coredump_pid_str == NULL)
+        continue;
+      
+      coredump_pid = g_ascii_strtoull (coredump_pid_str, NULL, 10);
+      if (!export_one_coredump (dataout, coredump_pid))
+        goto out;
+    }
+
+ out:
+  if (local_error)
+    {
+      g_printerr ("%s\n", local_error->message);
+      g_clear_error (&local_error);
+      return 1;
+    }
+  return 0;
+}
diff --git a/tests/functional/shelleval.js b/tests/functional/shelleval.js
new file mode 100755
index 0000000..a178cdc
--- /dev/null
+++ b/tests/functional/shelleval.js
@@ -0,0 +1,26 @@
+#!/usr/bin/env gjs
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+
+const sessionBus = Gio.bus_get_sync(Gio.BusType.SESSION, null);
+
+function shellEval(shell, code, cancellable) {
+    let res = shell.call_sync("Eval", GLib.Variant.new("(s)", [code]), 0, -1,
+                             cancellable).deep_unpack();
+    let [success, result] = res;
+    if (!success)
+       throw new Error("Failed to eval " + code.substr(0, 10));
+    return JSON.parse(result);
+}
+
+function main() {
+    let cancellable = null;
+    let shell = Gio.DBusProxy.new_sync(sessionBus, 0, null,
+                                      "org.gnome.Shell", "/org/gnome/Shell",
+                                      "org.gnome.Shell", cancellable);
+
+    print(shellEval(shell, ARGV[0], cancellable));
+}
+
+main();
diff --git a/tests/functional/startstopapps.js b/tests/functional/startstopapps.js
new file mode 100755
index 0000000..54c681e
--- /dev/null
+++ b/tests/functional/startstopapps.js
@@ -0,0 +1,80 @@
+#!/usr/bin/env gjs
+
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+
+const sessionBus = Gio.bus_get_sync(Gio.BusType.SESSION, null);
+
+const CLOSE_APPS = '%{apps}.forEach(function (id) { 
Shell.AppSystem.get_default().lookup_app(id).request_quit(); });';
+const GET_APP_IDS = 'Shell.AppSystem.get_default().get_running().map(function (a) { return a.get_id(); });';
+
+const StartStopApps = new GObject.Class({
+    Name: 'StartStopApps',
+    
+    _init: function(props) {
+       this._shell = Gio.DBusProxy.new_sync(sessionBus, 0, null,
+                                            "org.gnome.Shell", "/org/gnome/Shell",
+                                            "org.gnome.Shell", null);
+    },
+
+    _shellEval: function(code, cancellable) {
+       let res = this._shell.call_sync("Eval", GLib.Variant.new("(s)", [code]), 0, -1,
+                                       cancellable).deep_unpack();
+       let [success, result] = res;
+       if (!success)
+           throw new Error("Failed to eval " + code.substr(0, 20));
+       return result ? JSON.parse(result) : null;
+    },
+
+    _awaitRunningApp: function(appId, cancellable) {
+       let timeoutSecs = 10;
+       let i = 0;
+       for (let i = 0; i < timeoutSecs; i++) {
+           let runningApps = this._shellEval(GET_APP_IDS, cancellable);
+           for (let i = 0; i < runningApps.length; i++) {
+               if (runningApps[i] == appId)
+                   return;
+           }
+           GLib.usleep(GLib.USEC_PER_SEC);
+       }
+       throw new Error("Failed to find running app " + appId + " after " + timeoutSecs + "s");
+    },
+
+    _testOneApp: function(app, cancellable) {
+       let appId = app.get_id();
+       print("testing appid=" + appId);
+       app.launch([], cancellable);
+       this._awaitRunningApp(appId, cancellable);
+       this._shellEval(CLOSE_APPS.replace('%{apps}', JSON.stringify([appId])), cancellable);
+    },
+    
+    run: function(cancellable) {
+       let allApps = Gio.AppInfo.get_all();
+       
+       let runningApps = this._shellEval(GET_APP_IDS, cancellable);
+       print("Closing running apps: " + runningApps);
+       this._shellEval(CLOSE_APPS.replace('%{apps}', JSON.stringify(runningApps)), cancellable);
+       
+       for (let i = 0; i < allApps.length; i++) {
+           let app = allApps[i];
+           if (app.get_nodisplay())
+               continue;
+           // Ok, a gross hack; we should really be using gnome-menus
+           // to look up all apps.  Or maybe fix Gio?
+           if (app.has_key('Categories') &&
+               app.get_string('Categories').indexOf('X-GNOME-Settings-Panel') >= 0)
+               continue;
+           this._testOneApp(app, cancellable);
+       }
+    }
+});
+
+function main() {
+    let cancellable = null;
+
+    let startstopapps = new StartStopApps();
+    startstopapps.run(cancellable);
+}
+
+main();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]