unit tests: check real unit state after issuing a method call 00/209400/1
authorMaciej Slodczyk <m.slodczyk2@partner.samsung.com>
Fri, 5 Jul 2019 10:02:30 +0000 (12:02 +0200)
committerMaciej Slodczyk <m.slodczyk2@partner.samsung.com>
Fri, 5 Jul 2019 11:00:55 +0000 (13:00 +0200)
Change-Id: I532e2de9b2e91f05bfcdca6ab40408980ac98540
Signed-off-by: Maciej Slodczyk <m.slodczyk2@partner.samsung.com>
tests/unit_tests.c

index 818cae06992f1a6b5ede55f4d7b117c07e457425..9582b84b8309606c56ddb9afeb29229b22f62162 100644 (file)
 #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
@@ -124,6 +319,26 @@ int main()
 
                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;
 }