#include "unit_control.h"
+#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
-
-static int async_result = UNIT_CONTROL_OK;
+#include <glib.h>
+#include <gio/gio.h>
#define TEST_SERVICE "activationd-acceptance-test-s-p.service"
-int test_unit_start()
+enum unit_state {
+ UNIT_OFF,
+ UNIT_ON,
+ UNIT_UNKNOWN
+};
+
+struct context {
+ GDBusConnection *bus;
+ GBusType bus_type;
+ int unit_state[2];
+ int unit_state_pos;
+ int unit_state_cnt;
+ guint subscription_id;
+ GMainLoop *loop;
+ guint func_timeout, test_timeout;
+ int result;
+ int (*func_sync)(GBusType bus_type, const char *unit, int timeout);
+ int (*func_async)(GBusType bus_type, const char *unit, actd_unit_cb cb, void *user_data, int timeout);
+};
+
+struct unit_test {
+ const char *id;
+ int (*func)(struct context *);
+};
+
+int translate_unit_state(const gchar *value)
{
- return actd_start_unit(G_BUS_TYPE_SYSTEM, TEST_SERVICE, -1);
+ if (!g_strcmp0(value, "active"))
+ return UNIT_ON;
+ else if (!g_strcmp0(value, "inactive") || !g_strcmp0(value, "deactivating"))
+ return UNIT_OFF;
+ return UNIT_UNKNOWN;
}
-int test_unit_stop()
+
+int get_unit_state(GVariant *dictionary)
{
- return actd_stop_unit(G_BUS_TYPE_SYSTEM, TEST_SERVICE, -1);
+ GVariantIter iter;
+ GVariant *value;
+ gchar *key;
+
+ g_variant_iter_init (&iter, dictionary);
+ while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
+ if (!g_strcmp0(key, "ActiveState")) {
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
+ return translate_unit_state(g_variant_get_string(value, NULL));
+ }
+ }
+ return UNIT_UNKNOWN;
}
-int test_unit_restart()
+static void on_properties_changed(GDBusConnection *connection,
+ const gchar *sender_name, const gchar *object_path, const gchar *interface_name,
+ const gchar *signal_name, GVariant *parameters, gpointer user_data)
{
- return actd_restart_unit(G_BUS_TYPE_SYSTEM, TEST_SERVICE, -1);
+ GVariantIter iter;
+ GVariant *child;
+ gchar *key;
+ int state;
+ struct context *ctx = (struct context *)user_data;
+
+ g_variant_iter_init (&iter, parameters);
+
+ /* skip 's' - interface name */
+ child = g_variant_iter_next_value (&iter);
+ g_variant_unref(child);
+
+ /* next is 'a{sv}' where ActiveState is */
+ child = g_variant_iter_next_value (&iter);
+ state = get_unit_state(child);
+ g_variant_unref(child);
+
+ if (ctx->unit_state[ctx->unit_state_pos] == state) {
+ ctx->unit_state_pos++;
+ if (ctx->unit_state_pos == ctx->unit_state_cnt) {
+ ctx->result = UNIT_CONTROL_OK;
+ g_main_loop_quit(ctx->loop);
+ }
+ }
}
-void handler(int status, void *user_data, GError *err)
+GDBusConnection *get_bus(GBusType bus_type)
{
- async_result = status;
- g_main_loop_quit(user_data);
+ GDBusConnection *bus = NULL;
+ bus = g_bus_get_sync(bus_type, NULL, NULL);
+
+ if (!bus) {
+ printf("unable to open dbus connection\n");
+ return NULL;
+ }
+
+ return bus;
}
-int test_async_action(GBusType bus_type, int (*func)(GBusType bus_type, const char *unit, actd_unit_cb cb, void *user_data, int timeout))
+static guint signal_subscribe(struct context *ctx)
{
- assert(func);
- int ret;
- GMainLoop *loop;
+ assert(ctx);
+ assert(ctx->bus);
- loop = g_main_loop_new(NULL, FALSE);
+ GVariant *msg = NULL;
+ char *path = NULL;
+ GError *error = NULL;
- ret = func(bus_type, TEST_SERVICE, handler, loop, -1);
- if (ret < 0)
- return ret;
+ /*load unit/get path */
+ msg = g_dbus_connection_call_sync(ctx->bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LoadUnit",
+ g_variant_new("(s)", TEST_SERVICE),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (!msg) {
+ printf("LoadUnit method call error: %s\n", error->message);
+ goto cleanup;
+ }
+
+ g_variant_get(msg, "(o)", &path);
+
+ error = NULL;
+ /* PropertiesChanged signal subscribe */
+ ctx->subscription_id = g_dbus_connection_signal_subscribe(
+ ctx->bus,
+ NULL,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ path,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_properties_changed,
+ (gpointer)ctx,
+ &error
+ );
+ if (ctx->subscription_id == 0) {
+ printf("signal subscription error: %s\n", error->message);
+ goto cleanup;
+ }
- g_main_loop_run(loop);
- g_main_loop_unref(loop);
+cleanup:
+ g_free(path);
+ return ctx->subscription_id;
+}
- return async_result == UNIT_CONTROL_OK;
+gboolean test_timeout(gpointer user_data)
+{
+ struct context *ctx = (struct context *)user_data;
+
+ ctx->result = UNIT_CONTROL_ERROR;
+ g_main_loop_quit(ctx->loop);
+ return FALSE;
}
-int test_unit_start_async()
+gboolean func_start(gpointer user_data)
{
- return test_async_action(G_BUS_TYPE_SYSTEM, actd_start_unit_async);
+ struct context *ctx = (struct context *)user_data;
+ int ret = ctx->func_sync(ctx->bus_type, TEST_SERVICE, -1);
+
+ if (ret < 0) {
+ ctx->result = UNIT_CONTROL_ERROR;
+ g_main_loop_quit(ctx->loop);
+ }
+return FALSE;
+}
+
+int test_sync_action(struct context *ctx)
+{
+ assert(ctx);
+
+ ctx->loop = g_main_loop_new(NULL, FALSE);
+ if (!ctx->loop) {
+ ctx->result = UNIT_CONTROL_ERROR;
+ goto finish_run_sync_test;
+ }
+
+ ctx->func_timeout = g_timeout_add_seconds(1, func_start, ctx);
+ ctx->test_timeout = g_timeout_add_seconds(10, test_timeout, ctx);
+ g_main_loop_run(ctx->loop);
+
+finish_run_sync_test:
+ if (ctx->loop)
+ g_main_loop_unref(ctx->loop);
+
+ ctx->loop = NULL;
+
+ return ctx->result;
}
-int test_unit_stop_async()
+int test_unit_start(struct context *ctx)
{
- return test_async_action(G_BUS_TYPE_SYSTEM, actd_stop_unit_async);
+ assert(ctx);
+
+ ctx->unit_state[0] = UNIT_ON;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 1;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_sync = actd_start_unit;
+ return test_sync_action(ctx);
}
-int test_unit_restart_async()
+int test_unit_stop(struct context *ctx)
{
- return test_async_action(G_BUS_TYPE_SYSTEM, actd_restart_unit_async);
+ assert(ctx);
+
+ ctx->unit_state[0] = UNIT_OFF;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 1;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_sync = actd_stop_unit;
+ return test_sync_action(ctx);
}
-int test_unit_start_session()
+int test_unit_restart(struct context *ctx)
{
- return actd_start_unit(G_BUS_TYPE_SESSION, TEST_SERVICE, -1);
+ assert(ctx);
+
+ ctx->unit_state[0] = UNIT_OFF;
+ ctx->unit_state[1] = UNIT_ON;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 2;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_sync = actd_restart_unit;
+ return test_sync_action(ctx);
}
-int test_unit_stop_session()
+void handler(int status, void *user_data, GError *err)
{
- return actd_stop_unit(G_BUS_TYPE_SESSION, TEST_SERVICE, -1);
+ struct context *ctx = (struct context *)user_data;
+ ctx->result = status;
+ if (status != UNIT_CONTROL_OK)
+ g_main_loop_quit(ctx->loop);
}
-int test_unit_restart_session()
+int test_async_action(struct context *ctx)
{
- return actd_restart_unit(G_BUS_TYPE_SESSION, TEST_SERVICE, -1);
+ assert(ctx);
+ assert(ctx->func_async);
+ int ret;
+
+ ctx->loop = g_main_loop_new(NULL, FALSE);
+ ctx->test_timeout = g_timeout_add_seconds(10, test_timeout, ctx);
+
+ ret = ctx->func_async(ctx->bus_type, TEST_SERVICE, handler, ctx, -1);
+ if (ret < 0)
+ return ret;
+
+ g_main_loop_run(ctx->loop);
+ g_main_loop_unref(ctx->loop);
+
+ return ret;
}
-int test_unit_start_async_session()
+int test_unit_start_async(struct context *ctx)
{
- return test_async_action(G_BUS_TYPE_SESSION, actd_start_unit_async);
+ assert(ctx);
+
+ ctx->unit_state[0] = UNIT_ON;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 1;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_async = actd_start_unit_async;
+ return test_async_action(ctx);
}
-int test_unit_stop_async_session()
+int test_unit_stop_async(struct context *ctx)
{
- return test_async_action(G_BUS_TYPE_SESSION, actd_stop_unit_async);
+ ctx->unit_state[0] = UNIT_OFF;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 1;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_async = actd_stop_unit_async;
+ return test_async_action(ctx);
}
-int test_unit_restart_async_session()
+int test_unit_restart_async(struct context *ctx)
{
- return test_async_action(G_BUS_TYPE_SESSION, actd_restart_unit_async);
+ ctx->unit_state[0] = UNIT_ON;
+ ctx->unit_state[1] = UNIT_OFF;
+ ctx->unit_state_pos = 0;
+ ctx->unit_state_cnt = 2;
+ ctx->result = UNIT_CONTROL_OK;
+ ctx->func_async = actd_restart_unit_async;
+ return test_async_action(ctx);
}
-int main()
+void test(GBusType bus_type, struct unit_test tests[])
{
- int i;
+ assert(bus_type == G_BUS_TYPE_SYSTEM || bus_type == G_BUS_TYPE_SESSION);
int ret;
+ struct context ctx = {};
- struct {
- const char *id;
- int (*func)(void);
- } tests[] = {
- {"UnitStart", test_unit_start},
- {"UnitStop", test_unit_stop},
- {"UnitRestart", test_unit_restart},
- {"UnitStartAsync", test_unit_start_async},
- {"UnitStopAsync", test_unit_stop_async},
- {"UnitRestartAsync", test_unit_restart_async},
- {"UnitStartSession", test_unit_start_session},
- {"UnitStopSession", test_unit_stop_session},
- {"UnitRestartSession", test_unit_restart_session},
- {"UnitStartAsynSessionc", test_unit_start_async_session},
- {"UnitStopAsyncSession", test_unit_stop_async_session},
- {"UnitRestartAsynSessionc", test_unit_restart_async_session},
- {NULL, NULL},
- };
+ ctx.bus = get_bus(bus_type);
+ if (!ctx.bus)
+ exit(EXIT_FAILURE);
+
+ ctx.bus_type = bus_type;
+
+ if (signal_subscribe(&ctx) == 0)
+ exit(EXIT_FAILURE);
- for (i = 0; tests[i].id; ++i) {
- ret = tests[i].func();
+ for (int i = 0; tests[i].id; ++i) {
+ ret = tests[i].func(&ctx);
if (ret < 0)
printf("[FAILED] %s (ret = %d)\n", tests[i].id, ret);
else
usleep(100000);
}
+ return;
+}
+
+int main()
+{
+ struct unit_test tests[] = {
+ {"UnitStart", test_unit_start},
+ {"UnitRestart", test_unit_restart},
+ {"UnitStop", test_unit_stop},
+ {"UnitStartAsync", test_unit_start_async},
+ {"UnitRestartAsync", test_unit_restart_async},
+ {"UnitStopAsync", test_unit_stop_async},
+ {NULL, NULL},
+ };
+
+ printf("bus type SYSTEM tests:\n");
+ test(G_BUS_TYPE_SYSTEM, tests);
+
+ printf("bus type SESSION tests:\n");
+ test(G_BUS_TYPE_SESSION, tests);
- return 0;
+ return EXIT_SUCCESS;
}