Back to index

dbus-test-runner  12.10.0
bustle.c
Go to the documentation of this file.
00001 /*
00002 Copyright 2012 Canonical Ltd.
00003 
00004 Authors:
00005     Ted Gould <ted@canonical.com>
00006 
00007 This program is free software: you can redistribute it and/or modify it 
00008 under the terms of the GNU General Public License version 3, as published 
00009 by the Free Software Foundation.
00010 
00011 This program is distributed in the hope that it will be useful, but 
00012 WITHOUT ANY WARRANTY; without even the implied warranties of 
00013 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00014 PURPOSE.  See the GNU General Public License for more details.
00015 
00016 You should have received a copy of the GNU General Public License along 
00017 with this program.  If not, see <http://www.gnu.org/licenses/>.
00018 */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023 
00024 #include "dbus-test.h"
00025 
00026 struct _DbusTestBustlePrivate {
00027        gchar * filename;
00028        gchar * executable;
00029 
00030        guint watch;
00031        GIOChannel * stdout;
00032        GIOChannel * stderr;
00033        GIOChannel * file;
00034        GPid pid;
00035 
00036        gboolean crashed;
00037 };
00038 
00039 #define DBUS_TEST_BUSTLE_GET_PRIVATE(o) \
00040 (G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUS_TEST_TYPE_BUSTLE, DbusTestBustlePrivate))
00041 
00042 static void dbus_test_bustle_class_init (DbusTestBustleClass *klass);
00043 static void dbus_test_bustle_init       (DbusTestBustle *self);
00044 static void dbus_test_bustle_dispose    (GObject *object);
00045 static void dbus_test_bustle_finalize   (GObject *object);
00046 static void process_run                 (DbusTestTask * task);
00047 static DbusTestTaskState get_state      (DbusTestTask * task);
00048 static gboolean get_passed              (DbusTestTask * task);
00049 
00050 G_DEFINE_TYPE (DbusTestBustle, dbus_test_bustle, DBUS_TEST_TYPE_TASK);
00051 
00052 static void
00053 dbus_test_bustle_class_init (DbusTestBustleClass *klass)
00054 {
00055        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00056 
00057        g_type_class_add_private (klass, sizeof (DbusTestBustlePrivate));
00058 
00059        object_class->dispose = dbus_test_bustle_dispose;
00060        object_class->finalize = dbus_test_bustle_finalize;
00061 
00062        DbusTestTaskClass * task_class = DBUS_TEST_TASK_CLASS(klass);
00063 
00064        task_class->run = process_run;
00065        task_class->get_state = get_state;
00066        task_class->get_passed = get_passed;
00067 
00068        return;
00069 }
00070 
00071 static void
00072 dbus_test_bustle_init (DbusTestBustle *self)
00073 {
00074        self->priv = DBUS_TEST_BUSTLE_GET_PRIVATE(self);
00075 
00076        self->priv->filename = g_strconcat(g_get_current_dir(), G_DIR_SEPARATOR_S, "bustle.log", NULL);
00077        self->priv->executable = g_strdup("bustle-dbus-monitor");
00078 
00079        self->priv->watch = 0;
00080        self->priv->stdout = NULL;
00081        self->priv->stderr = NULL;
00082        self->priv->file = NULL;
00083        self->priv->pid = 0;
00084 
00085        self->priv->crashed = FALSE;
00086 
00087        return;
00088 }
00089 
00090 static void
00091 dbus_test_bustle_dispose (GObject *object)
00092 {
00093        g_return_if_fail(DBUS_TEST_IS_BUSTLE(object));
00094        DbusTestBustle * bustler = DBUS_TEST_BUSTLE(object);
00095 
00096        if (bustler->priv->watch != 0) {
00097               g_source_remove(bustler->priv->watch);
00098               bustler->priv->watch = 0;
00099        }
00100 
00101        if (bustler->priv->pid != 0) {
00102               /* TODO: Send a single dbus message */
00103 
00104               g_spawn_close_pid(bustler->priv->pid);
00105        }
00106 
00107        if (bustler->priv->stdout != NULL) {
00108               g_io_channel_shutdown(bustler->priv->stdout, TRUE, NULL);
00109               bustler->priv->stdout = NULL;
00110        }
00111 
00112        if (bustler->priv->stderr != NULL) {
00113               g_io_channel_shutdown(bustler->priv->stderr, TRUE, NULL);
00114               bustler->priv->stderr = NULL;
00115        }
00116 
00117        if (bustler->priv->file != NULL) {
00118               g_io_channel_shutdown(bustler->priv->file, TRUE, NULL);
00119               bustler->priv->file = NULL;
00120        }
00121 
00122        G_OBJECT_CLASS (dbus_test_bustle_parent_class)->dispose (object);
00123        return;
00124 }
00125 
00126 static void
00127 dbus_test_bustle_finalize (GObject *object)
00128 {
00129        g_return_if_fail(DBUS_TEST_IS_BUSTLE(object));
00130        DbusTestBustle * bustler = DBUS_TEST_BUSTLE(object);
00131 
00132        g_free(bustler->priv->filename);
00133        g_free(bustler->priv->executable);
00134 
00135        G_OBJECT_CLASS (dbus_test_bustle_parent_class)->finalize (object);
00136        return;
00137 }
00138 
00139 DbusTestBustle *
00140 dbus_test_bustle_new (const gchar * filename)
00141 {
00142        g_return_val_if_fail(filename != NULL, NULL);
00143 
00144        DbusTestBustle * bustler = g_object_new(DBUS_TEST_TYPE_BUSTLE,
00145                                                NULL);
00146 
00147        g_free(bustler->priv->filename);
00148        bustler->priv->filename = g_strdup(filename);
00149 
00150        dbus_test_task_set_name(DBUS_TEST_TASK(bustler), "Bustle");
00151 
00152        return bustler;
00153 }
00154 
00155 void
00156 dbus_test_bustle_set_executable (DbusTestBustle * bustle, const gchar * executable)
00157 {
00158        g_return_if_fail(DBUS_TEST_IS_BUSTLE(bustle));
00159        g_return_if_fail(executable != NULL);
00160 
00161        g_free(bustle->priv->executable);
00162        bustle->priv->executable = g_strdup(executable);
00163 
00164        return;
00165 }
00166 
00167 static void
00168 bustle_watcher (GPid pid, gint status, gpointer data)
00169 {
00170        g_critical("Bustle Monitor exited abruptly!");
00171        DbusTestBustle * bustler = DBUS_TEST_BUSTLE(data);
00172 
00173        if (bustler->priv->pid != 0) {
00174               g_spawn_close_pid(pid);
00175               bustler->priv->pid = 0;
00176        }
00177 
00178        bustler->priv->crashed = TRUE;
00179        g_signal_emit_by_name(G_OBJECT(bustler), DBUS_TEST_TASK_SIGNAL_STATE_CHANGED, DBUS_TEST_TASK_STATE_FINISHED, NULL);
00180 
00181        return;
00182 }
00183 
00184 static gboolean
00185 bustle_write_error (GIOChannel * channel, GIOCondition condition, gpointer data)
00186 {
00187        gchar * line;
00188        gsize termloc;
00189 
00190        do {
00191               GIOStatus status = g_io_channel_read_line (channel, &line, NULL, &termloc, NULL);
00192 
00193               if (status == G_IO_STATUS_EOF) {
00194                      return FALSE;
00195               }
00196 
00197               if (status != G_IO_STATUS_NORMAL) {
00198                      continue;
00199               }
00200 
00201               line[termloc] = '\0';
00202 
00203               dbus_test_task_print(DBUS_TEST_TASK(data), line);
00204               g_free(line);
00205        } while (G_IO_IN & g_io_channel_get_buffer_condition(channel));
00206 
00207        return TRUE;
00208 }
00209 
00210 static gboolean
00211 bustle_writes (GIOChannel * channel, GIOCondition condition, gpointer data)
00212 {
00213        gchar * line;
00214        gsize termloc;
00215 
00216        GIOStatus status = g_io_channel_read_line (channel, &line, NULL, &termloc, NULL);
00217 
00218        if (status != G_IO_STATUS_NORMAL) {
00219               return FALSE;
00220        }
00221 
00222        g_io_channel_write_chars((GIOChannel *)data,
00223                                                   line,
00224                                                   termloc,
00225                                                   NULL,
00226                                                   NULL);
00227        g_io_channel_write_chars((GIOChannel *)data,
00228                                                   "\n",
00229                                                   1,
00230                                                   NULL,
00231                                                   NULL);
00232 
00233        g_free(line);
00234 
00235        return TRUE;
00236 }
00237 
00238 static void
00239 process_run (DbusTestTask * task)
00240 {
00241        g_return_if_fail(DBUS_TEST_IS_BUSTLE(task));
00242        DbusTestBustle * bustler = DBUS_TEST_BUSTLE(task);
00243 
00244        if (bustler->priv->pid != 0) {
00245               return;
00246        }
00247        
00248        GError * error = NULL;
00249 
00250        bustler->priv->file = g_io_channel_new_file(bustler->priv->filename, "w", &error);
00251 
00252        if (error != NULL) {
00253               g_critical("Unable to open bustle file '%s': %s", bustler->priv->filename, error->message);
00254               g_error_free(error);
00255 
00256               bustler->priv->crashed = TRUE;
00257               g_signal_emit_by_name(G_OBJECT(bustler), DBUS_TEST_TASK_SIGNAL_STATE_CHANGED, DBUS_TEST_TASK_STATE_FINISHED, NULL);
00258               return;
00259        }
00260 
00261        gint bustle_stdout_num;
00262        gint bustle_stderr_num;
00263        
00264        gchar ** bustle_monitor = g_new0(gchar *, 3);
00265        bustle_monitor[0] = (gchar *)bustler->priv->executable;
00266        bustle_monitor[1] = "--session";
00267 
00268        g_spawn_async_with_pipes(g_get_current_dir(),
00269                                 bustle_monitor, /* argv */
00270                                 NULL, /* envp */
00271                                 /* G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, */ /* flags */
00272                                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, /* flags */
00273                                 NULL, /* child setup func */
00274                                 NULL, /* child setup data */
00275                                 &bustler->priv->pid, /* PID */
00276                                 NULL, /* stdin */
00277                                 &bustle_stdout_num, /* stdout */
00278                                 &bustle_stderr_num, /* stderr */
00279                                 &error); /* error */
00280 
00281        if (error != NULL) {
00282               g_critical("Unable to start bustling data: %s", error->message);
00283               g_error_free(error);
00284 
00285               bustler->priv->pid = 0; /* ensure this */
00286               bustler->priv->crashed = TRUE;
00287               g_signal_emit_by_name(G_OBJECT(bustler), DBUS_TEST_TASK_SIGNAL_STATE_CHANGED, DBUS_TEST_TASK_STATE_FINISHED, NULL);
00288               return;
00289        }
00290 
00291        if (TRUE) {
00292               gchar * start = g_strdup_printf("Starting bustle monitor.  PID: %d", bustler->priv->pid);
00293               dbus_test_task_print(DBUS_TEST_TASK(bustler), start);
00294               g_free(start);
00295        }
00296        bustler->priv->watch = g_child_watch_add(bustler->priv->pid, bustle_watcher, bustler);
00297 
00298        bustler->priv->stdout = g_io_channel_unix_new(bustle_stdout_num);
00299        g_io_add_watch(bustler->priv->stdout,
00300                       G_IO_IN | G_IO_PRI, /* conditions */
00301                       bustle_writes, /* func */
00302                       bustler->priv->file); /* func data */
00303 
00304        bustler->priv->stderr = g_io_channel_unix_new(bustle_stderr_num);
00305        g_io_add_watch(bustler->priv->stderr,
00306                       G_IO_IN, /* conditions */
00307                       bustle_write_error, /* func */
00308                       bustler); /* func data */
00309 
00310        return;
00311 }
00312 
00313 static DbusTestTaskState
00314 get_state (DbusTestTask * task)
00315 {
00316        /* We're always finished, but we want an error */
00317        g_return_val_if_fail(DBUS_TEST_IS_BUSTLE(task), DBUS_TEST_TASK_STATE_FINISHED);
00318        return DBUS_TEST_TASK_STATE_FINISHED;
00319 }
00320 
00321 static gboolean
00322 get_passed (DbusTestTask * task)
00323 {
00324        g_return_val_if_fail(DBUS_TEST_IS_BUSTLE(task), FALSE);
00325        DbusTestBustle * bustler = DBUS_TEST_BUSTLE(task);
00326 
00327        if (bustler->priv->crashed) {
00328               return FALSE;
00329        } else {
00330               return TRUE;
00331        }
00332 }
00333