$(LN_S) $(notdir $<) $@)
endif
-check_PROGRAMS = test/test-init test/test-loaded \
- test/test-insmod test/test-rmmod test/test-rmmod2 \
- test/test-lookup test/test-path-from-name \
- test/test-get-dependencies test/test-mod-double-ref \
- test/test-blacklist test/test-elf test/test-probe \
- test/test-invalidate-config test/test-state
-
-TESTS = test/test-init test/test-loaded
-
-test_test_init_LDADD = libkmod/libkmod-private.la
-test_test_loaded_LDADD = libkmod/libkmod-private.la
-
-test_test_rmmod_LDADD = libkmod/libkmod-private.la
-test_test_rmmod2_LDADD = libkmod/libkmod-private.la
-test_test_insmod_LDADD = libkmod/libkmod-private.la
-test_test_lookup_LDADD = libkmod/libkmod-private.la
-test_test_path_from_name_LDADD = libkmod/libkmod-private.la
-test_test_get_dependencies_LDADD = libkmod/libkmod-private.la
-test_test_mod_double_ref_LDADD = libkmod/libkmod-private.la
-test_test_blacklist_LDADD = libkmod/libkmod-private.la
-test_test_elf_LDADD = libkmod/libkmod-private.la
-test_test_probe_LDADD = libkmod/libkmod-private.la
-test_test_invalidate_config_LDADD = libkmod/libkmod-private.la
-test_test_state_LDADD = libkmod/libkmod-private.la
+check_LTLIBRARIES = testsuite/libtestsuite.la
+testsuite_libtestsuite_la_SOURCES = testsuite/testsuite.c \
+ testsuite/testsuite.h
+testsuite_libtestsuite_la_DEPENDENCIES = testsuite/uname.so
DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc
--- /dev/null
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "testsuite.h"
+
+static const char *progname;
+int oneshot = 0;
+static const char options_short[] = "lhn";
+static const struct option options[] = {
+ { "list", no_argument, 0, 'l' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+};
+
+static void help(void)
+{
+ const struct option *itr;
+ const char *itr_short;
+
+ printf("Usage:\n"
+ "\t%s [options] <test>\n"
+ "Options:\n", basename(progname));
+
+ for (itr = options, itr_short = options_short;
+ itr->name != NULL; itr++, itr_short++)
+ printf("\t-%c, --%s\n", *itr_short, itr->name);
+}
+
+static void test_list(const struct test *tests[])
+{
+ size_t i;
+
+ printf("Available tests:\n");
+ for (i = 0; tests[i] != NULL; i++)
+ printf("\t%s, %s\n", tests[i]->name, tests[i]->description);
+}
+
+int test_init(int argc, char *const argv[], const struct test *tests[])
+{
+ progname = argv[0];
+
+ for (;;) {
+ int c, idx = 0;
+ c = getopt_long(argc, argv, options_short, options, &idx);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'l':
+ test_list(tests);
+ return 0;
+ case 'h':
+ help();
+ return 0;
+ case 'n':
+ oneshot = 1;
+ break;
+ case '?':
+ return -1;
+ default:
+ ERR("unexpected getopt_long() value %c\n", c);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+const struct test *test_find(const struct test *tests[], const char *name)
+{
+ size_t i;
+
+ for (i = 0; tests[i] != NULL; i++) {
+ if (strcmp(tests[i]->name, name) == 0)
+ return tests[i];
+ }
+
+ return NULL;
+}
+
+int test_spawn_test(const struct test *t)
+{
+ const char *const args[] = { progname, "-n", t->name, NULL };
+
+ execv(progname, (char *const *) args);
+
+ ERR("failed to spawn %s for %s: %m\n", progname, t->name);
+ return EXIT_FAILURE;
+}
+
+int test_spawn_prog(const char *prog, const char *args[])
+{
+ execv(prog, (char *const *) args);
+
+ ERR("failed to spawn %s ", prog);
+ return EXIT_FAILURE;
+}
+
+int test_run_spawned(const struct test *t)
+{
+ int err = t->func(t);
+ exit(err);
+}
+
+int test_run(const struct test *t)
+{
+ int err;
+ pid_t pid;
+
+ LOG("running %s, in forked context\n", t->name);
+
+ pid = fork();
+ if (pid < 0) {
+ ERR("could not fork(): %m\n");
+ LOG("FAILED: %s\n", t->name);
+ return EXIT_FAILURE;
+ }
+
+ if (pid > 0) {
+ do {
+ pid = wait(&err);
+ if (pid == -1) {
+ ERR("error waitpid(): %m\n");
+ return EXIT_FAILURE;
+ }
+ } while (!WIFEXITED(err) && !WIFSIGNALED(err));
+
+ if (err != 0)
+ ERR("error while running %s\n", t->name);
+
+ LOG("%s: %s\n", err == 0 ? "PASSED" : "FAILED", t->name);
+ return err;
+ }
+
+ /* kill child if parent dies */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+ test_run_spawned(t);
+}
--- /dev/null
+#ifndef _LIBKMOD_TESTSUITE_
+#define _LIBKMOD_TESTSUITE_
+
+#include <stdbool.h>
+#include <stdarg.h>
+
+struct test;
+typedef int (*testfunc)(const struct test *t);
+
+enum test_config {
+ TC_ROOTFS = 0,
+ TC_UNAME_R,
+ _TC_LAST,
+};
+
+struct test {
+ const char *name;
+ const char *description;
+ testfunc func;
+ const char *config[_TC_LAST];
+};
+
+
+const struct test *test_find(const struct test *tests[], const char *name);
+int test_init(int argc, char *const argv[], const struct test *tests[]);
+int test_spawn_test(const struct test *t);
+int test_spawn_prog(const char *prog, const char *args[]);
+
+int test_run(const struct test *t);
+__attribute__((noreturn)) int test_run_spawned(const struct test *t);
+
+extern int oneshot;
+
+#define TS_EXPORT __attribute__ ((visibility("default")))
+
+#define _LOG(prefix, fmt, ...) printf("TESTSUITE: " prefix fmt, ## __VA_ARGS__)
+#define LOG(fmt, ...) _LOG("", fmt, ## __VA_ARGS__)
+#define WARN(fmt, ...) _LOG("WARN: ", fmt, ## __VA_ARGS__)
+#define ERR(fmt, ...) _LOG("ERR: ", fmt, ## __VA_ARGS__)
+
+/* Test definitions */
+#define DEFINE_TEST(_name) \
+ struct test s_name = { \
+ .name = #_name, \
+ .func = _name, \
+ }
+
+#endif