Back to index

dbus-test-runner  12.10.0
service.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 typedef enum _ServiceState ServiceState;
00027 enum _ServiceState {
00028        STATE_INIT,
00029        STATE_DAEMON_STARTING,
00030        STATE_DAEMON_STARTED,
00031        STATE_STARTING,
00032        STATE_STARTED,
00033        STATE_RUNNING,
00034        STATE_FINISHED
00035 };
00036 
00037 struct _DbusTestServicePrivate {
00038        GQueue tasks_first;
00039        GQueue tasks_normal;
00040        GQueue tasks_last;
00041 
00042        GMainLoop * mainloop;
00043        ServiceState state;
00044 
00045        gboolean daemon_crashed;
00046 
00047        GPid dbus;
00048        guint dbus_watch;
00049        GIOChannel * dbus_io;
00050        guint dbus_io_watch;
00051        gchar * dbus_daemon;
00052        gchar * dbus_configfile;
00053 
00054        gboolean first_time;
00055 };
00056 
00057 #define SERVICE_CHANGE_HANDLER  "dbus-test-service-change-handler"
00058 
00059 #define DBUS_TEST_SERVICE_GET_PRIVATE(o) \
00060 (G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUS_TEST_TYPE_SERVICE, DbusTestServicePrivate))
00061 
00062 static void dbus_test_service_class_init (DbusTestServiceClass *klass);
00063 static void dbus_test_service_init       (DbusTestService *self);
00064 static void dbus_test_service_dispose    (GObject *object);
00065 static void dbus_test_service_finalize   (GObject *object);
00066 
00067 G_DEFINE_TYPE (DbusTestService, dbus_test_service, G_TYPE_OBJECT);
00068 
00069 static void
00070 dbus_test_service_class_init (DbusTestServiceClass *klass)
00071 {
00072        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00073 
00074        g_type_class_add_private (klass, sizeof (DbusTestServicePrivate));
00075 
00076        object_class->dispose = dbus_test_service_dispose;
00077        object_class->finalize = dbus_test_service_finalize;
00078 
00079        return;
00080 }
00081 
00082 static void
00083 dbus_test_service_init (DbusTestService *self)
00084 {
00085        self->priv = DBUS_TEST_SERVICE_GET_PRIVATE(self);
00086 
00087        g_queue_init(&self->priv->tasks_first);
00088        g_queue_init(&self->priv->tasks_normal);
00089        g_queue_init(&self->priv->tasks_last);
00090 
00091        self->priv->mainloop = g_main_loop_new(NULL, FALSE);
00092        self->priv->state = STATE_INIT;
00093 
00094        self->priv->daemon_crashed = FALSE;
00095 
00096        self->priv->dbus = 0;
00097        self->priv->dbus_watch = 0;
00098        self->priv->dbus_io = NULL;
00099        self->priv->dbus_io_watch = 0;
00100        self->priv->dbus_daemon = g_strdup("dbus-daemon");
00101        self->priv->dbus_configfile = g_strdup(DEFAULT_SESSION_CONF);
00102 
00103        self->priv->first_time = TRUE;
00104 
00105        return;
00106 }
00107 
00108 static void
00109 task_unref (gpointer data, gpointer user_data)
00110 {
00111        DbusTestTask * task = DBUS_TEST_TASK(data);
00112 
00113        gulong handler = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(task), SERVICE_CHANGE_HANDLER));
00114        if (handler != 0) {
00115               g_signal_handler_disconnect(G_OBJECT(task), handler);
00116        }
00117 
00118        g_object_unref(task);
00119        return;
00120 }
00121 
00122 static void
00123 dbus_test_service_dispose (GObject *object)
00124 {
00125        g_return_if_fail(DBUS_TEST_IS_SERVICE(object));
00126        DbusTestService * self = DBUS_TEST_SERVICE(object);
00127 
00128        if (self->priv->dbus_watch != 0) {
00129               g_source_remove(self->priv->dbus_watch);
00130               self->priv->dbus_watch = 0;
00131        }
00132 
00133        if (self->priv->dbus_io_watch != 0) {
00134               g_source_remove(self->priv->dbus_io_watch);
00135               self->priv->dbus_io_watch = 0;
00136        }
00137 
00138        if (self->priv->dbus_io != NULL) {
00139               g_io_channel_shutdown(self->priv->dbus_io, TRUE, NULL);
00140               g_io_channel_unref(self->priv->dbus_io);
00141               self->priv->dbus_io = NULL;
00142        }
00143 
00144        if (self->priv->dbus != 0) {
00145               gchar * cmd = g_strdup_printf("kill -9 %d", self->priv->dbus);
00146               g_spawn_command_line_async(cmd, NULL);
00147               g_free(cmd);
00148 
00149               g_spawn_close_pid(self->priv->dbus);
00150               self->priv->dbus = 0;
00151        }
00152 
00153        if (!g_queue_is_empty(&self->priv->tasks_first)) {
00154               g_queue_foreach(&self->priv->tasks_first, task_unref, NULL);
00155               g_queue_clear(&self->priv->tasks_first);
00156        }
00157 
00158        if (!g_queue_is_empty(&self->priv->tasks_normal)) {
00159               g_queue_foreach(&self->priv->tasks_normal, task_unref, NULL);
00160               g_queue_clear(&self->priv->tasks_normal);
00161        }
00162 
00163        if (!g_queue_is_empty(&self->priv->tasks_last)) {
00164               g_queue_foreach(&self->priv->tasks_last, task_unref, NULL);
00165               g_queue_clear(&self->priv->tasks_last);
00166        }
00167 
00168        if (self->priv->mainloop != NULL) {
00169               g_main_loop_unref(self->priv->mainloop);
00170               self->priv->mainloop = NULL;
00171        }
00172 
00173        G_OBJECT_CLASS (dbus_test_service_parent_class)->dispose (object);
00174        return;
00175 }
00176 
00177 static void
00178 dbus_test_service_finalize (GObject *object)
00179 {
00180        g_return_if_fail(DBUS_TEST_IS_SERVICE(object));
00181        DbusTestService * self = DBUS_TEST_SERVICE(object);
00182 
00183        g_free(self->priv->dbus_daemon);
00184        self->priv->dbus_daemon = NULL;
00185        g_free(self->priv->dbus_configfile);
00186        self->priv->dbus_configfile = NULL;
00187 
00188        G_OBJECT_CLASS (dbus_test_service_parent_class)->finalize (object);
00189        return;
00190 }
00191 
00192 DbusTestService *
00193 dbus_test_service_new (const gchar * address)
00194 {
00195        DbusTestService * service = g_object_new(DBUS_TEST_TYPE_SERVICE,
00196                                                 NULL);
00197 
00198        /* TODO: Use the address */
00199 
00200        return service;
00201 }
00202 
00203 static void
00204 all_tasks_finished_helper (gpointer data, gpointer user_data)
00205 {
00206        DbusTestTask * task = DBUS_TEST_TASK(data);
00207        gboolean * all_finished = (gboolean *)user_data;
00208 
00209        DbusTestTaskState state = dbus_test_task_get_state(task);
00210        DbusTestTaskReturn ret  = dbus_test_task_get_return(task);
00211 
00212        if (state != DBUS_TEST_TASK_STATE_FINISHED && ret != DBUS_TEST_TASK_RETURN_IGNORE) {
00213               *all_finished = FALSE;
00214        }
00215 
00216        return;
00217 }
00218 
00219 static void
00220 all_tasks_started_helper (gpointer data, gpointer user_data)
00221 {
00222        DbusTestTask * task = DBUS_TEST_TASK(data);
00223        gboolean * all_started = (gboolean *)user_data;
00224 
00225        DbusTestTaskState state = dbus_test_task_get_state(task);
00226 
00227        if (state == DBUS_TEST_TASK_STATE_INIT || state == DBUS_TEST_TASK_STATE_WAITING) {
00228               *all_started = FALSE;
00229        }
00230 
00231        return;
00232 }
00233 
00234 static gboolean
00235 all_tasks (DbusTestService * service, GFunc helper)
00236 {
00237        gboolean breaknow = TRUE;
00238 
00239        g_queue_foreach(&service->priv->tasks_first, helper, &breaknow);
00240        if (!breaknow) {
00241               return FALSE;
00242        }
00243 
00244        g_queue_foreach(&service->priv->tasks_normal, helper, &breaknow);
00245        if (!breaknow) {
00246               return FALSE;
00247        }
00248 
00249        g_queue_foreach(&service->priv->tasks_last, helper, &breaknow);
00250        if (!breaknow) {
00251               return FALSE;
00252        }
00253 
00254        return TRUE;
00255 }
00256 
00257 static void
00258 task_set_name_length (gpointer data, gpointer user_data)
00259 {
00260        DbusTestTask * task = DBUS_TEST_TASK(data);
00261        glong * length = (glong *)user_data;
00262 
00263        dbus_test_task_set_name_spacing(task, *length);
00264        return;
00265 }
00266 
00267 static void
00268 task_get_name_length (gpointer data, gpointer user_data)
00269 {
00270        DbusTestTask * task = DBUS_TEST_TASK(data);
00271        glong * length = (glong *)user_data;
00272 
00273        const gchar * name = dbus_test_task_get_name(task);
00274        g_return_if_fail(name != NULL);
00275 
00276        glong nlength = g_utf8_strlen(name, -1);
00277        *length = MAX(*length, nlength);
00278 
00279        return;
00280 }
00281 
00282 static void
00283 normalize_name_lengths (DbusTestService * service)
00284 {
00285        glong length = 0;
00286 
00287        g_queue_foreach(&service->priv->tasks_first, task_get_name_length, &length);
00288        g_queue_foreach(&service->priv->tasks_normal, task_get_name_length, &length);
00289        g_queue_foreach(&service->priv->tasks_last, task_get_name_length, &length);
00290 
00291        g_queue_foreach(&service->priv->tasks_first, task_set_name_length, &length);
00292        g_queue_foreach(&service->priv->tasks_normal, task_set_name_length, &length);
00293        g_queue_foreach(&service->priv->tasks_last, task_set_name_length, &length);
00294 
00295        return;
00296 }
00297 
00298 static void
00299 task_starter (gpointer data, gpointer user_data)
00300 {
00301        DbusTestTask * task = DBUS_TEST_TASK(data);
00302 
00303        dbus_test_task_run(task);
00304 
00305        return;
00306 }
00307 
00308 static gboolean
00309 dbus_writes (GIOChannel * channel, GIOCondition condition, gpointer data)
00310 {
00311        DbusTestService * service = DBUS_TEST_SERVICE(data);
00312 
00313        if (condition & G_IO_ERR) {
00314               g_critical("DBus writing failure!");
00315               return FALSE;
00316        }
00317 
00318        gchar * line;
00319        gsize termloc;
00320        GIOStatus status = g_io_channel_read_line (channel, &line, NULL, &termloc, NULL);
00321        g_return_val_if_fail(status == G_IO_STATUS_NORMAL, FALSE);
00322        line[termloc] = '\0';
00323 
00324        g_print("DBus daemon: %s\n", line);
00325 
00326        if (service->priv->first_time) {
00327               service->priv->first_time = FALSE;
00328 
00329               g_setenv("DBUS_SESSION_BUS_ADDRESS", line, TRUE);
00330               g_setenv("DBUS_STARTER_ADDRESS", line, TRUE);
00331               g_setenv("DBUS_STARTER_BUS_TYPE", "session", TRUE);
00332 
00333               if (service->priv->state == STATE_DAEMON_STARTING) {
00334                      g_main_loop_quit(service->priv->mainloop);
00335               }
00336        }
00337 
00338        g_free(line);
00339 
00340        return TRUE;
00341 }
00342 
00343 static void
00344 dbus_watcher (GPid pid, gint status, gpointer data)
00345 {
00346        DbusTestService * service = DBUS_TEST_SERVICE(data);
00347        g_critical("DBus Daemon exited abruptly!");
00348 
00349        service->priv->daemon_crashed = TRUE;
00350        g_main_loop_quit(DBUS_TEST_SERVICE(data)->priv->mainloop);
00351 
00352        if (pid != 0) {
00353               g_spawn_close_pid(pid);
00354        }
00355 
00356        return;
00357 }
00358 
00359 static void
00360 start_daemon (DbusTestService * service)
00361 {
00362        if (service->priv->dbus != 0) {
00363               return;
00364        }
00365 
00366        service->priv->state = STATE_DAEMON_STARTING;
00367 
00368        gint dbus_stdout = 0;
00369        GError * error = NULL;
00370        gchar * blank[1] = {NULL};
00371        gchar * dbus_startup[] = {service->priv->dbus_daemon, "--config-file", service->priv->dbus_configfile, "--print-address", NULL};
00372        g_spawn_async_with_pipes(g_get_current_dir(),
00373                                 dbus_startup, /* argv */
00374                                 blank, /* envp */
00375                                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, /* flags */
00376                                 NULL, /* child setup func */
00377                                 NULL, /* child setup data */
00378                                 &service->priv->dbus, /* PID */
00379                                 NULL, /* stdin */
00380                                 &dbus_stdout, /* stdout */
00381                                 NULL, /* stderr */
00382                                 &error); /* error */
00383 
00384        if (error != NULL) {
00385               g_critical("Unable to start dbus daemon: %s", error->message);
00386               service->priv->daemon_crashed = TRUE;
00387               return;
00388        }
00389 
00390        service->priv->dbus_watch = g_child_watch_add(service->priv->dbus, dbus_watcher, service);
00391 
00392        service->priv->dbus_io = g_io_channel_unix_new(dbus_stdout);
00393        service->priv->dbus_io_watch = g_io_add_watch(service->priv->dbus_io,
00394                                                      G_IO_IN | G_IO_ERR, /* conditions */
00395                                                      dbus_writes, /* func */
00396                                                      service); /* func data */
00397 
00398        g_main_loop_run(service->priv->mainloop);
00399        service->priv->state = STATE_DAEMON_STARTED;
00400 
00401        return;
00402 }
00403 
00404 void
00405 dbus_test_service_start_tasks (DbusTestService * service)
00406 {
00407        g_return_if_fail(DBUS_TEST_SERVICE(service));
00408 
00409        start_daemon(service);
00410        g_return_if_fail(g_getenv("DBUS_SESSION_BUS_ADDRESS") != NULL);
00411 
00412        if (all_tasks(service, all_tasks_started_helper)) {
00413               /* If we have all started we can mark it as such as long
00414                  as we understand where we could hit this case */
00415               if (service->priv->state == STATE_INIT || service->priv->state == STATE_DAEMON_STARTED) {
00416                      service->priv->state = STATE_STARTED;
00417               }
00418               return;
00419        }
00420 
00421        normalize_name_lengths(service);
00422 
00423        g_queue_foreach(&service->priv->tasks_first, task_starter, NULL);
00424        if (!g_queue_is_empty(&service->priv->tasks_first)) {
00425               g_usleep(100000);
00426        }
00427 
00428        g_queue_foreach(&service->priv->tasks_normal, task_starter, NULL);
00429 
00430        if (!g_queue_is_empty(&service->priv->tasks_last)) {
00431               g_usleep(100000);
00432        }
00433        g_queue_foreach(&service->priv->tasks_last, task_starter, NULL);
00434 
00435        if (!all_tasks(service, all_tasks_started_helper)) {
00436               service->priv->state = STATE_STARTING;
00437               g_main_loop_run(service->priv->mainloop);
00438 
00439               /* This should never happen, but let's be sure */
00440               g_return_if_fail(all_tasks(service, all_tasks_started_helper));
00441        }
00442 
00443        service->priv->state = STATE_STARTED;
00444 
00445        return;
00446 }
00447 
00448 static void
00449 all_tasks_passed_helper (gpointer data, gpointer user_data)
00450 {
00451        DbusTestTask * task = DBUS_TEST_TASK(data);
00452        gboolean * all_passed = (gboolean *)user_data;
00453 
00454        if (!dbus_test_task_passed(task)) {
00455               *all_passed = FALSE;
00456        }
00457 
00458        return;
00459 }
00460 
00461 static int
00462 get_status (DbusTestService * service)
00463 {
00464        if (service->priv->daemon_crashed) {
00465               return -1;
00466        }
00467 
00468        if (all_tasks(service, all_tasks_passed_helper)) {
00469               return 0;
00470        } else {
00471               return -1;
00472        }
00473 }
00474 
00475 int
00476 dbus_test_service_run (DbusTestService * service)
00477 {
00478        g_return_val_if_fail(DBUS_TEST_SERVICE(service), -1);
00479 
00480        dbus_test_service_start_tasks(service);
00481        g_return_val_if_fail(service->priv->state == STATE_STARTED, get_status(service));
00482 
00483        if (all_tasks(service, all_tasks_finished_helper)) {
00484               return get_status(service);
00485        }
00486 
00487        service->priv->state = STATE_RUNNING;
00488        g_main_loop_run(service->priv->mainloop);
00489 
00490        /* This should never happen, but let's be sure */
00491        g_return_val_if_fail(all_tasks(service, all_tasks_finished_helper), -1);
00492        service->priv->state = STATE_FINISHED;
00493 
00494        return get_status(service);
00495 }
00496 
00497 static void
00498 task_state_changed (DbusTestTask * task, DbusTestTaskState state, gpointer user_data)
00499 {
00500        g_return_if_fail(DBUS_TEST_IS_SERVICE(user_data));
00501        DbusTestService * service = DBUS_TEST_SERVICE(user_data);
00502 
00503        if (service->priv->state == STATE_STARTING && all_tasks(service, all_tasks_started_helper)) {
00504               g_main_loop_quit(service->priv->mainloop);
00505               return;
00506        }
00507 
00508        if (service->priv->state == STATE_RUNNING && all_tasks(service, all_tasks_finished_helper)) {
00509               g_main_loop_quit(service->priv->mainloop);
00510               return;
00511        }
00512 
00513        return;
00514 }
00515 
00516 void
00517 dbus_test_service_add_task (DbusTestService * service, DbusTestTask * task)
00518 {
00519        return dbus_test_service_add_task_with_priority(service, task, DBUS_TEST_SERVICE_PRIORITY_NORMAL);
00520 }
00521 
00522 void
00523 dbus_test_service_add_task_with_priority (DbusTestService * service, DbusTestTask * task, DbusTestServicePriority prio)
00524 {
00525        g_return_if_fail(DBUS_TEST_IS_SERVICE(service));
00526        g_return_if_fail(DBUS_TEST_IS_TASK(task));
00527 
00528        GQueue * queue = NULL;
00529 
00530        switch (prio) {
00531        case DBUS_TEST_SERVICE_PRIORITY_FIRST:
00532               queue = &service->priv->tasks_first;
00533               break;
00534        case DBUS_TEST_SERVICE_PRIORITY_NORMAL:
00535               queue = &service->priv->tasks_normal;
00536               break;
00537        case DBUS_TEST_SERVICE_PRIORITY_LAST:
00538               queue = &service->priv->tasks_last;
00539               break;
00540        default:
00541               g_assert_not_reached();
00542               break;
00543        }
00544 
00545        g_queue_push_tail(queue, g_object_ref(task));
00546 
00547        gulong connect = g_signal_connect(G_OBJECT(task), DBUS_TEST_TASK_SIGNAL_STATE_CHANGED, G_CALLBACK(task_state_changed), service);
00548        g_object_set_data(G_OBJECT(task), SERVICE_CHANGE_HANDLER, GUINT_TO_POINTER(connect));
00549 
00550        return;
00551 }
00552 
00553 void
00554 dbus_test_service_set_daemon (DbusTestService * service, const gchar * daemon)
00555 {
00556        g_return_if_fail(DBUS_TEST_IS_SERVICE(service));
00557        g_free(service->priv->dbus_daemon);
00558        service->priv->dbus_daemon = g_strdup(daemon);
00559        return;
00560 }
00561 
00562 void
00563 dbus_test_service_set_conf_file (DbusTestService * service, const gchar * conffile)
00564 {
00565        g_return_if_fail(DBUS_TEST_IS_SERVICE(service));
00566        g_free(service->priv->dbus_configfile);
00567        service->priv->dbus_configfile = g_strdup(conffile);
00568        return;
00569 }
00570 
00571 void
00572 dbus_test_service_stop (DbusTestService * service)
00573 {
00574        g_return_if_fail(DBUS_TEST_IS_SERVICE(service));
00575        g_main_loop_quit(service->priv->mainloop);
00576        return;
00577 }