add service activation acceptance test 48/200948/23
authorMaciej Slodczyk <m.slodczyk2@partner.samsung.com>
Wed, 6 Mar 2019 10:00:04 +0000 (11:00 +0100)
committerMaciej Slodczyk <m.slodczyk2@partner.samsung.com>
Mon, 25 Mar 2019 14:39:31 +0000 (15:39 +0100)
Change-Id: I9362c538576027bc6e5e7c0bd4c89f40525c4018
Signed-off-by: Maciej Slodczyk <m.slodczyk2@partner.samsung.com>
activationd/Makefile.am
activationd/configs/activationd-acceptance-test.service [new file with mode: 0644]
activationd/listeners/vconf.c
activationd/tests/activationd_acceptance_checker.c [new file with mode: 0644]
activationd/tests/activationd_acceptance_service.c [new file with mode: 0644]
activationd/tests/activationd_acceptance_test [new file with mode: 0755]
modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf [new file with mode: 0644]
modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf [new file with mode: 0644]
packaging/activationd.spec

index 8262ac615b6454d3e8143e38b3667e89d06e52f6..46e01350900115f63a97217299fdd7f5b92ee3b3 100644 (file)
@@ -30,6 +30,7 @@ AM_CFLAGS = \
        -rdynamic \
        $(GLIB_CFLAGS) \
        $(VCONF_CFLAGS) \
+       $(LIBSYSTEMD_CFLAGS) \
        -D"EPC_MODNAME_T=$(call name_fix,$(modname))"
 
 modulesdir = $(pkglibdir)/available-modules
@@ -40,10 +41,36 @@ modules_LTLIBRARIES = \
 
 vconf_key_changed_event_la_SOURCES = event_types/vconf_key_changed_event.c
 activation_dm_la_SOURCES = decision_makers/activation_dm.c
+activation_dm_config_DATA = ../modules.conf.d/activation_eh.conf.d/activation_eh.conf
+activation_dm_config_DATA += ../modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf
+activation_dm_configdir = $(modulesconfigdir)/activation_eh.conf.d
 vconf_listener_la_SOURCES = listeners/vconf.c
 vconf_listener_config_DATA = ../modules.conf.d/vconf_listener.conf.d/50-default.conf
+vconf_listener_config_DATA += ../modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf
 vconf_listener_configdir = $(modulesconfigdir)/vconf_listener.conf.d
 
+bin_PROGRAMS = activationd_acceptance_checker
+
+activationd_acceptance_checker_SOURCES = \
+       tests/activationd_acceptance_checker.c
+
+activationd_acceptance_checker_CFLAGS = $(AM_CFLAGS)
+activationd_acceptance_checker_LDFLAGS = $(VCONF_LIBS) $(LIBSYSTEMD_LIBS)
+
+bin_PROGRAMS += activationd_acceptance_service
+
+activationd_acceptance_service_SOURCES = \
+       tests/activationd_acceptance_service.c
+
+activationd_acceptance_service_CFLAGS = $(AM_CFLAGS)
+activationd_acceptance_service_LDFLAGS =
+
+bin_SCRIPTS = tests/activationd_acceptance_test
+
+unitdir = $(prefix)/lib/systemd/system
+unit_DATA = \
+                       configs/activationd-acceptance-test.service
+
 configdir = $(prefix)/lib/epc
 modulesconfigdir = $(configdir)/modules.conf.d
 
diff --git a/activationd/configs/activationd-acceptance-test.service b/activationd/configs/activationd-acceptance-test.service
new file mode 100644 (file)
index 0000000..8d905ba
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=activationd acceptance tests service
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/activationd_acceptance_service
index 0ab892533517c51810abd88dc8f79f6dd06b31ee..df639f7da6edac2613f548b346ea45ad741cb0f4 100644 (file)
@@ -213,7 +213,9 @@ static int add_key(struct vconf_listener *l, const char *key, keynode_t *node)
        }
 
        list_add_tail(&k->node, &l->keys);
-       vconf_notify_key_changed(key, on_vconf_key_changed, l);
+       if (vconf_notify_key_changed(key, on_vconf_key_changed, l) < 0)
+               log_error("vconf_notify_key_changed() failed (%s)", key);
+
        return 0;
 
 cleanup:
diff --git a/activationd/tests/activationd_acceptance_checker.c b/activationd/tests/activationd_acceptance_checker.c
new file mode 100644 (file)
index 0000000..5d6295f
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * This file is part of epc.
+ *
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <systemd/sd-bus.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <vconf.h>
+
+#include "common.h"
+#include "systemd_dbus.h"
+
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define SERVICE_NAME "activationd-acceptance-test.service"
+#define VCONF_ACTIVATION_KEY "file/activationd/acceptance"
+
+enum unit_state {
+       UNKNOWN,
+       ACTIVE,
+       INACTIVE
+};
+
+struct action {
+       const char *action;
+       enum unit_state expected_states[2];
+       int state_count;
+       int current_state;
+};
+
+struct test_sequence {
+       struct action actions[3];
+       int current_action;
+       int ok;
+};
+
+
+struct test_data {
+       sd_bus *bus;
+       sd_event *loop;
+       struct test_sequence test;
+       enum unit_state state;
+       enum unit_state prev_state;
+       char *path;
+};
+
+static const char *from_unit_state(enum unit_state state)
+{
+       switch (state) {
+       case UNKNOWN:
+               return "unknown";
+       case ACTIVE:
+               return "active";
+       default:
+               return "inactive";
+       }
+}
+
+static enum unit_state to_unit_state(const char *state)
+{
+       if (!strncmp (state, "active", 6))
+               return ACTIVE;
+       else if (!strncmp (state, "inactive", 8) || !strncmp (state, "deactivating", 12))
+               return INACTIVE;
+       else
+               return UNKNOWN;
+}
+
+static int set_vconf_key(const char *val)
+{
+       keylist_t *kl = NULL;
+       kl = vconf_keylist_new();
+       if (!kl) {
+               fprintf(stderr, "error creating vconf keylists\n");
+               return -1;
+       }
+
+       vconf_keylist_add_str(kl, VCONF_ACTIVATION_KEY, val);
+       if (vconf_set(kl) < 0) {
+               fprintf(stderr, "error saving vconf key\n");
+               vconf_keylist_free(kl);
+               return -1;
+       }
+
+       vconf_keylist_free(kl);
+       return 0;
+}
+
+static int test_step(struct test_data *data)
+{
+       struct test_sequence *test = &data->test;
+       if (test->current_action == ARRAY_SIZE(test->actions)) {
+               sd_event_exit(data->loop, EXIT_SUCCESS);
+               test->ok = 1;
+               return 0;
+       }
+
+       if (set_vconf_key(test->actions[test->current_action].action))
+               return -1;
+       return 0;
+}
+
+static int check_test_result(struct test_data *data)
+{
+       struct action *a = &data->test.actions[data->test.current_action];
+       enum unit_state exp_state = a->expected_states[a->current_state];
+
+       if (data->test.ok == 1)
+               return 0;
+
+       if (data->state != exp_state) {
+               data->test.ok = 1;
+               fprintf(stderr, "test failed (%s instead of %s at step %d of %s)\n",
+                               from_unit_state(data->state),
+                               from_unit_state(exp_state),
+                               a->current_state,
+                               a->action);
+
+               sd_event_exit(data->loop, EXIT_FAILURE);
+               return -1;
+       }
+       a->current_state++;
+       if (a->current_state == a->state_count) {
+               data->test.current_action++;
+               if (test_step(data) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+
+static int check_unit_state(struct test_data *data, const char *state)
+{
+       enum unit_state new_state;
+
+       new_state = to_unit_state(state);
+       if (data->state != new_state) {
+               data->prev_state = data->state;
+               data->state = new_state;
+               check_test_result(data);
+       }
+       return 0;
+}
+
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+       int r;
+       struct test_data *data = (struct test_data*)userdata;
+       const char *iface;
+       const char *member;
+       const char *value = NULL;
+       const char *contents;
+
+       r = sd_bus_message_read(m, "s", &iface);
+       if (r < 0) {
+               fprintf(stderr, "Failed to parse PropertiesChanged\n");
+               return r;
+       }
+
+       r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+       if (r < 0)
+               return r;
+
+       while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+               char type;
+
+               r = sd_bus_message_read(m, "s", &member);
+               if (r < 0)
+                       return r;
+
+               r = sd_bus_message_peek_type(m, &type, &contents);
+               if (r < 0)
+                       return r;
+
+               r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+               if (r < 0)
+                       return r;
+
+               if (strcmp(member, "ActiveState") == 0) {
+                       r = sd_bus_message_read(m, "s", &value);
+                       if (r < 0)
+                               return r;
+               } else {
+                       r = sd_bus_message_skip(m, contents);
+                       if (r < 0)
+                               return r;
+               }
+
+               r = sd_bus_message_exit_container(m);
+               if (r < 0)
+                       return r;
+
+               r = sd_bus_message_exit_container(m);
+               if (r < 0)
+                       return r;
+       }
+
+       r = sd_bus_message_exit_container(m);
+       if (r < 0)
+               return r;
+
+       if (value)
+               check_unit_state(data, value);
+       return 0;
+}
+
+static void cleanup(struct test_data *data)
+{
+       free(data->path);
+       if (!data->bus)
+               return;
+       sd_bus_flush(data->bus);
+       sd_bus_unref(data->bus);
+}
+
+static int signal_handler(sd_event_source *s,
+               const struct signalfd_siginfo *si,
+               void *userdata)
+{
+       sd_event *loop = userdata;
+       sd_event_exit(loop, EXIT_FAILURE);
+       return 0;
+}
+
+static int start_test(sd_event_source *s, uint64_t usec, void *userdata)
+{
+       struct test_data *data = (struct test_data*)userdata;
+       return test_step(data);
+}
+
+static int timeout_handler(sd_event_source *s, uint64_t usec, void *userdata)
+{
+       struct test_data *data = (struct test_data*)userdata;
+       fprintf(stderr, "test timed out\n");
+       sd_event_exit(data->loop, EXIT_FAILURE);
+       return 0;
+}
+
+static int call_subscribe(struct test_data *data)
+{
+       __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
+       __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
+       int ret;
+
+       ret = sd_bus_call_method(data->bus,
+                       SYSTEMD_SERVICE,
+                       SYSTEMD_OBJ,
+                       SYSTEMD_MANAGER_INTERFACE,
+                       "Subscribe",
+                       &error,
+                       &reply,
+                       "");
+
+       if (ret < 0) {
+               fprintf(stderr, "Failed to issue Subscribe() call: %s\n", error.message);
+               return -1;
+       }
+       return 0;
+}
+
+static int call_load_unit(struct test_data *data)
+{
+       __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
+       __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
+       int ret;
+       const char *path = NULL;
+       char err[512];
+
+       ret = sd_bus_call_method(data->bus,
+                       SYSTEMD_SERVICE,
+                       SYSTEMD_OBJ,
+                       SYSTEMD_MANAGER_INTERFACE,
+                       "LoadUnit",
+                       &error,
+                       &reply,
+                       "s",
+                       SERVICE_NAME);
+
+       if (ret < 0) {
+               fprintf(stderr, "Failed to issue LoadUnit() call: %s\n", error.message);
+               return -1;
+       }
+
+       ret = sd_bus_message_read(reply, "o", &path);
+       if (ret < 0) {
+               if (strerror_r(-ret, err, sizeof err))
+                       fprintf(stderr, "Failed to read reply: %s\n", err);
+               return -1;
+       }
+
+       if (!path) {
+               fprintf(stderr, "ould not get object path\n");
+               return -1;
+       }
+       data->path = strdup(path);
+       return 0;
+}
+int main(int argc, char *argv[])
+{
+       struct test_data data = {
+               .bus = NULL,
+               .loop = NULL,
+               .state = UNKNOWN,
+               .prev_state = UNKNOWN,
+               .path = NULL,
+               .test = {
+                       .actions = {
+                               {"START", {ACTIVE, UNKNOWN}, 1, 0},
+                               {"RELOAD", {INACTIVE, ACTIVE}, 2, 0},
+                               {"STOP", {INACTIVE, UNKNOWN}, 1, 0}
+                       },
+                       .current_action = 0,
+                       .ok = 0
+               }
+       };
+
+       int r;
+       uint64_t now;
+       sigset_t ss;
+       char buf[512];
+       char err[512];
+
+       r = sd_bus_open_system(&data.bus);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to read reply: %s\n", err);
+               goto fail;
+       }
+
+       r = call_subscribe(&data);
+       if (r < 0)
+               goto fail;
+
+       r = call_load_unit(&data);
+       if (r < 0)
+               goto fail;
+
+       r = snprintf(buf, sizeof buf, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged', path='%s'", data.path);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to create match rule: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_bus_add_match(data.bus, NULL, buf, on_properties_changed, &data);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to add PropertiesChanged match: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_event_new(&data.loop);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to create main loop: %s\n", err);
+               goto fail;
+       }
+
+       sigemptyset(&ss);
+       sigaddset(&ss, SIGINT);
+       sigaddset(&ss, SIGTERM);
+       r = sigprocmask(SIG_BLOCK, &ss, NULL);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to change signal mask: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_event_add_signal(data.loop, NULL, SIGINT, signal_handler, data.loop);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to register SIGINT handler: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_event_add_signal(data.loop, NULL, SIGTERM, signal_handler, data.loop);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to register SIGTERM handler: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_bus_attach_event(data.bus, data.loop, 0);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to attach bus to event loop: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, 0, 0, start_test, &data);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
+               goto fail;
+       }
+
+       sd_event_now(data.loop, CLOCK_REALTIME, &now);
+
+       r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, now+10000000, 0, timeout_handler, &data);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
+               goto fail;
+       }
+
+       r = sd_event_loop(data.loop);
+       if (r < 0) {
+               if (strerror_r(-r, err, sizeof err))
+                       fprintf(stderr, "Failed to run main loop: %s\n", err);
+               goto fail;
+       }
+
+       cleanup(&data);
+       return r;
+
+fail:
+       cleanup(&data);
+       return EXIT_FAILURE;
+}
diff --git a/activationd/tests/activationd_acceptance_service.c b/activationd/tests/activationd_acceptance_service.c
new file mode 100644 (file)
index 0000000..5a4b946
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * This file is part of epc.
+ *
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+static void exit_handler(int signo)
+{
+       exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+       if (signal(SIGINT, exit_handler) == SIG_ERR ||
+                       signal(SIGQUIT, exit_handler) == SIG_ERR ||
+                       signal(SIGTERM, exit_handler) == SIG_ERR) {
+               perror("catching SIGNAL");
+               exit(EXIT_FAILURE);
+       }
+
+       printf("service started, do nothing...\n");
+       while (1)
+               sleep(10);
+
+       exit_handler(0);
+}
+
diff --git a/activationd/tests/activationd_acceptance_test b/activationd/tests/activationd_acceptance_test
new file mode 100755 (executable)
index 0000000..88fe1af
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+EPCD_PID=-1
+
+cleanup() {
+       systemctl stop activationd-acceptance-test
+       [ $EPCD_PID -ne -1 ] && kill $EPCD_PID > /dev/null 2>&1
+       vconftool unset  file/activationd/acceptance
+       exit 0
+}
+
+trap cleanup 0
+
+vconftool -f set -t string file/activationd/acceptance foo
+
+# assumes there's no epcd service running
+epcd > /dev/null 2>&1 &
+EPCD_PID=$!
+sleep 2
+
+/usr/bin/activationd_acceptance_checker
+
+if [ "$?" -eq "0" ]; then
+       echo "ok"
+else
+       echo "fail"
+fi
+
diff --git a/modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf b/modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf
new file mode 100644 (file)
index 0000000..ad35b77
--- /dev/null
@@ -0,0 +1,7 @@
+{
+       "rules":[
+       {"event":"vconf_key_changed", "key_name":"file/activationd/acceptance", "newval":"START", "action":"StartUnit", "target":"activationd-acceptance-test.service"},
+       {"event":"vconf_key_changed", "key_name":"file/activationd/acceptance", "newval":"STOP", "action":"StopUnit", "target":"activationd-acceptance-test.service"},
+       {"event":"vconf_key_changed", "key_name":"file/activationd/acceptance", "newval":"RELOAD", "action":"RestartUnit", "target":"activationd-acceptance-test.service"}
+       ]
+}
diff --git a/modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf b/modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf
new file mode 100644 (file)
index 0000000..06502b5
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "keys":[ "file/activationd/acceptance" ]
+}
index 7cbb1d1275814a55b286cfc7d8c715b29c2c9a37..e41d6384935743f6af95a5d452a2bb4905b10c82 100644 (file)
@@ -33,11 +33,11 @@ Group:      System/Monitoring
 %description -n event-processing-extra
 Event processing extra modules
 
-%package test-services
+%package -n activationd-test-services
 Summary:    activationd test services
 Group:      System/Monitoring
 
-%description test-services
+%description -n activationd-test-services
 Services used to test activationd functionality
 
 %package -n event-processing-devel
@@ -97,6 +97,8 @@ do
        echo %{enabled_moduledir}/${mod}.so >> extra-files;
 done
 
+echo /usr/lib/epc/service.conf.d/ >> activationd-files;
+
 for mod in vconf_listener \
        vconf_key_changed_event \
        activation_dm
@@ -116,6 +118,17 @@ done
 %files -f activationd-files
 %license COPYING
 %{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/50-default.conf
+%{_prefix}/lib/epc/modules.conf.d/activation_eh.conf.d/activation_eh.conf
+
+%files -n activationd-test-services
+%license COPYING
+%manifest %{name}.manifest
+%{_bindir}/activationd_acceptance_service
+%{_bindir}/activationd_acceptance_checker
+%{_bindir}/activationd_acceptance_test
+%{_unitdir}/activationd-acceptance-test.service
+%{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf
+%{_prefix}/lib/epc/modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf
 
 %files -n event-processing-core -f epc-files
 %license COPYING