From 575b04077b18ca810aa1974314785e32340d4bb6 Mon Sep 17 00:00:00 2001 From: Kazimierz Krosman Date: Mon, 19 Dec 2016 14:02:50 +0100 Subject: [PATCH] tests: Add test package for libdbuspolicy - again This reverts commit 376f38dafa0b7ee5eae3d8f0ba0333c84a79742d, which reverted commit bb7d32bb63f1d0fc463255000fbc773a119314a9. This is different from commit bb7d32bb63f1d0fc463255000fbc773a119314a9 in handling builds - it provides single spec file. Additionally, corrections for tests, mostly excluding initialization of the library from "test cases". Author: Kazimierz Krosman with some corrections by Adrian Szyndela Change-Id: I64a58818c5c7f692db38f8225bec1e7a11954de6 --- Makefile.am | 43 ++- configure.ac | 1 + packaging/libdbuspolicy.spec | 43 ++- src/cynara_prepare.sh | 8 + src/dbus_daemon.c | 236 +++++++++++++++ src/libdbuspolicy1.c | 7 + src/stest_common.c | 32 ++ src/stest_common.h | 22 ++ src/stest_cynara.c | 40 +++ src/stest_method_call.c | 80 +++++ src/stest_ownership.c | 59 ++++ src/stest_signal.c | 26 ++ src/test_runner.c | 692 +++++++++++++++++++++++++++++++++++++++++++ src/test_runner.h | 69 +++++ 14 files changed, 1347 insertions(+), 11 deletions(-) create mode 100644 src/cynara_prepare.sh create mode 100644 src/dbus_daemon.c create mode 100644 src/stest_common.c create mode 100644 src/stest_common.h create mode 100644 src/stest_cynara.c create mode 100644 src/stest_method_call.c create mode 100644 src/stest_ownership.c create mode 100644 src/stest_signal.c create mode 100644 src/test_runner.c create mode 100644 src/test_runner.h diff --git a/Makefile.am b/Makefile.am index 30eec82..c2e9409 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,7 +43,7 @@ LIBDBUSPOLICY1_AGE=0 pkginclude_HEADERS = src/dbuspolicy1/libdbuspolicy1.h lib_LTLIBRARIES = src/libdbuspolicy1.la -src_libdbuspolicy1_la_SOURCES =\ +COMMON_SRC =\ src/libdbuspolicy1-private.h \ src/libdbuspolicy1.c \ src/internal/internal.cpp \ @@ -51,7 +51,10 @@ src_libdbuspolicy1_la_SOURCES =\ src/internal/naive_policy_db.cpp \ src/internal/policy.cpp \ src/internal/xml_parser.cpp \ - src/internal/tslog.cpp \ + src/internal/tslog.cpp + +src_libdbuspolicy1_la_SOURCES =\ + $(COMMON_SRC) \ src/internal/cynara.cpp EXTRA_DIST += src/libdbuspolicy1.sym @@ -81,14 +84,7 @@ src_test_libdbuspolicy1_method_SOURCES = src/test-libdbuspolicy1-method.cpp noinst_LTLIBRARIES = src/libinternal.a src_libinternal_a_SOURCES =\ - src/libdbuspolicy1-private.h \ - src/internal/internal.cpp \ - src/libdbuspolicy1.c \ - src/internal/naive_policy_checker.cpp \ - src/internal/naive_policy_db.cpp \ - src/internal/policy.cpp \ - src/internal/xml_parser.cpp \ - src/internal/tslog.cpp \ + $(COMMON_SRC) \ src/internal/cynara_mockup.cpp src_test_libdbuspolicy1_ownership_LDADD = $(CYNARA_LIBS) \ @@ -100,6 +96,33 @@ src_test_libdbuspolicy1_signal_LDADD = $(CYNARA_LIBS) \ src_test_libdbuspolicy1_method_LDADD = $(CYNARA_LIBS) \ src/libinternal.a +if ENABLE_STANDALONE_TESTS +noinst_LTLIBRARIES += src/libinternalfortests.a +src_libinternalfortests_a_SOURCES =\ + $(COMMON_SRC) \ + src/internal/cynara.cpp + +runner_PROGRAMS = libdbuspolicy-tests +libdbuspolicy_tests_SOURCES = src/test_runner.c +runnerdir = ${libdir}/dbus-tests/runner/ + +alonetestdir = ${libdir}/dbus-tests/test-suites/libdbuspolicy-tests/ +alonetest_PROGRAMS = dbus_daemon stest_ownership stest_method_call stest_signal stest_cynara + +dbus_daemon_SOURCES = src/dbus_daemon.c +stest_ownership_SOURCES = src/stest_ownership.c src/stest_common.c +stest_method_call_SOURCES = src/stest_method_call.c src/stest_common.c +stest_signal_SOURCES = src/stest_signal.c src/stest_common.c +stest_cynara_SOURCES = src/stest_cynara.c src/stest_common.c + +stest_ownership_LDADD = src/libinternalfortests.a -lstdc++ $(CYNARA_LIBS) +stest_method_call_LDADD = src/libinternalfortests.a -lstdc++ $(CYNARA_LIBS) +stest_signal_LDADD = src/libinternalfortests.a -lstdc++ $(CYNARA_LIBS) +stest_cynara_LDADD = src/libinternalfortests.a -lstdc++ $(CYNARA_LIBS) + +all-tests:: $(alonetest_PROGRAMS) $(runner_PROGRAMS) +endif + if ENABLE_DOXYGEN CLEANFILES += documentation diff --git a/configure.ac b/configure.ac index 9c5aa73..6d6dbf4 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,7 @@ AS_IF([test "x$enable_debug" = "xyes"], [ AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests], [Add API function that allows to change credentials during execution]), [with_tests=yes], with_tests=no) +AM_CONDITIONAL([ENABLE_STANDALONE_TESTS], [test x$with_tests = xyes]) if test "x$with_tests" = "xyes"; then AC_DEFINE(LIBDBUSPOLICY_TESTS_API, 1, [Define if tests are enabled]) fi diff --git a/packaging/libdbuspolicy.spec b/packaging/libdbuspolicy.spec index f9e6e7e..0ec26d2 100644 --- a/packaging/libdbuspolicy.spec +++ b/packaging/libdbuspolicy.spec @@ -11,6 +11,9 @@ BuildRequires: pkgconfig(cynara-client) %package devel Summary: Helper library for fine-grained userspace policy handling-development package + +%package tests +Summary: Helper library for fine-grained userspace policy handling-development package Requires: %{name} = %{version} %description @@ -22,6 +25,8 @@ libdbuspolicy is a helper library for fine-grained userspace policy handling (with SMACK support). This package contains development files. +%description tests +This package contains contains integration tests for libdbuspolicy. %if 0%{?enable_doxygen:1} %package doc @@ -32,11 +37,18 @@ BuildRequires: doxygen Doxygen documentation for libdbuspolicy. %endif +%define testsdirname %{name}-tests +%define runnername %{testsdirname} +%define dbustestsdir %{_libdir}/dbus-tests + %prep %setup -q cp %{SOURCE1001} . %build +%reconfigure --libdir=%{_libdir} --prefix=/usr --enable-tests +make all-tests + %reconfigure --libdir=%{_libdir} --prefix=/usr \ %if 0%{?enable_doxygen:1} --enable-doxygen @@ -49,6 +61,30 @@ make check make DESTDIR=%{buildroot} install rm %{buildroot}%{_libdir}/libdbuspolicy1.la +%define testconfigsdir %{dbustestsdir}/configs/%{testsdirname} +%define btestconfigsdir %{buildroot}%{testconfigsdir} +mkdir -p %{btestconfigsdir} +libtool --mode=install install -m 0644 tests/system.conf %{btestconfigsdir} + +%define testconfigsystemddir %{testconfigsdir}/system.d +%define btestconfigsystemddir %{buildroot}%{testconfigsystemddir} +mkdir -p %{btestconfigsystemddir} +libtool --mode=install install -m 0644 tests/system.d/ownerships.test.conf %{btestconfigsystemddir} +libtool --mode=install install -m 0644 tests/system.d/signals.test.conf %{btestconfigsystemddir} +libtool --mode=install install -m 0644 tests/system.d/methods.test.conf %{btestconfigsystemddir} +libtool --mode=install install -m 0644 tests/system.d/cynara.test.conf %{btestconfigsystemddir} + +%define testsuitedir %{dbustestsdir}/test-suites/%{testsdirname} +%define btestsuitedir %{buildroot}%{testsuitedir} +mkdir -p %{btestsuitedir} +libtool --mode=install install -m 0755 src/cynara_prepare.sh %{btestsuitedir} +libtool --mode=install install -m 0755 stest_* dbus_daemon %{btestsuitedir} + +%define testrunnerdir %{dbustestsdir}/runner +%define btestrunnerdir %{buildroot}%{testrunnerdir} +mkdir -p %{btestrunnerdir} +libtool --mode=install install -m 0755 %{runnername} %{btestrunnerdir} + %post -p /sbin/ldconfig %postun -p /sbin/ldconfig @@ -60,7 +96,6 @@ rm %{buildroot}%{_libdir}/libdbuspolicy1.la %files devel %defattr(-,root,root) %{_includedir}/* - %{_libdir}/pkgconfig/* %{_libdir}/libdbuspolicy1.so %manifest %{name}.manifest @@ -71,3 +106,9 @@ rm %{buildroot}%{_libdir}/libdbuspolicy1.la %{_datadir}/doc/dbuspolicy/* %endif %changelog + +%files tests +%defattr(-,root,root) +%{testrunnerdir}/%{runnername} +%{testsuitedir}/* +%{testconfigsdir}/* diff --git a/src/cynara_prepare.sh b/src/cynara_prepare.sh new file mode 100644 index 0000000..acc2401 --- /dev/null +++ b/src/cynara_prepare.sh @@ -0,0 +1,8 @@ +#!/bin/sh +if [ -z "${1}" ]; then + cyad -e "MANIFESTS" -u 200 -c L -p t -r y + cyad -e "USER_TYPE_ADMIN" -u 200 -c L -p t -r y +else + cyad -s -k "MANIFESTS" -u 200 -c L -p t -t $1 + cyad -s -k "USER_TYPE_ADMIN" -u 200 -c L -p t -t $1 +fi diff --git a/src/dbus_daemon.c b/src/dbus_daemon.c new file mode 100644 index 0000000..058106e --- /dev/null +++ b/src/dbus_daemon.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kdbus.h" + +#define KDBUS_SYSTEM_BUS_PATH "/sys/fs/kdbus/0-system/bus" + +#define KDBUS_ALIGN8(l) (((l) + 7) & ~7) +#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) +#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) +#define KDBUS_ITEM_NEXT(item) \ + (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size)) +#define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ + iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size))) + +#define POOL_SIZE (16 * 1024 * 1024) + +#define err_r(_r, _msg) fprintf(stderr, "log: %d, %s, %s, %d, %s\n",(_r), (_msg), __func__, __LINE__, __FILE__) +#define err(_msg) err_r(errno, (_msg)) + +static uint8_t* pool; +static char* server_names[16]; +int ready_fd = 0; + +static inline int kdbus_cmd_hello(int bus_fd, struct kdbus_cmd_hello *cmd) +{ + int ret = ioctl(bus_fd, KDBUS_CMD_HELLO, cmd); + return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; +} + +static inline int kdbus_cmd_free(int conn_fd, struct kdbus_cmd_free *cmd) +{ + int ret = ioctl(conn_fd, KDBUS_CMD_FREE, cmd); + return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; +} + +static inline int kdbus_cmd_name_acquire(int conn_fd, struct kdbus_cmd *cmd) +{ + int ret = ioctl(conn_fd, KDBUS_CMD_NAME_ACQUIRE, cmd); + return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0; +} + +static void bus_poool_free_slice(int fd, uint64_t offset) +{ + struct kdbus_cmd_free cmd = { + .size = sizeof(cmd), + .offset = offset, + }; + int r; + r = kdbus_cmd_free(fd, &cmd); + if (r < 0) + err_r(r, "cannot free pool slice"); +} + +static int bus_open_connection(const char *name, + uint64_t recv_flags) +{ + struct kdbus_cmd_hello hello; + int r; + int fd; + + fd = open(name, O_RDWR | O_CLOEXEC); + if (fd < 0) { + r = err("cannot open bus"); + goto error; + } + + memset(&hello, 0, sizeof(hello)); + hello.size = sizeof(hello); + hello.attach_flags_send = _KDBUS_ATTACH_ALL; + hello.attach_flags_recv = recv_flags; + hello.pool_size = POOL_SIZE; + r = kdbus_cmd_hello(fd, &hello); + if (r < 0) { + err_r(r, "HELLO failed"); + goto error; + } + + bus_poool_free_slice(fd, hello.offset); + + /* + * Map the pool of the connection. Its size has been set in the + * command struct above. See kdbus.pool(7). + */ + pool = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); + if (pool == MAP_FAILED) { + r = err("cannot mmap pool"); + goto error; + } + + return fd; + +error: + return -1; +} + + + +static int bus_acquire_name(int fd, const char *name) +{ + struct kdbus_item *item; + struct kdbus_cmd *cmd; + size_t size; + int r; + + /* + * This function acquires a well-known name on the bus through the + * KDBUS_CMD_NAME_ACQUIRE ioctl. This ioctl takes an argument of type + * 'struct kdbus_cmd', which is assembled below. See kdbus.name(7). + */ + size = sizeof(*cmd); + size += KDBUS_ITEM_SIZE(strlen(name) + 1); + + cmd = alloca(size); + memset(cmd, 0, size); + cmd->size = size; + + /* + * The command requires an item of type KDBUS_ITEM_NAME, and its + * content must be a valid bus name. + */ + item = cmd->items; + item->type = KDBUS_ITEM_NAME; + item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; + strcpy(item->str, name); + + /* + * Employ the command on the connection owner file descriptor. + */ + r = kdbus_cmd_name_acquire(fd, cmd); + if (r < 0) + return err_r(r, "cannot acquire name"); + + return 0; +} + + +int parse_args(int argc, char** argv) +{ + char state = 0; + unsigned long int uid = 0; + unsigned long int gid = 0; + int i = 1; + int r = 0; + int fdl = -1; + int count = 0; + + while (i < argc) + { + if (argv[i][0] == '-') + state = argv[i][1]; + else { + switch (state) { + + case 'n': + server_names[count++] = argv[i]; + break; + + case 'u': + uid = strtoul(argv[i],NULL,10); + r = setuid((uid_t)uid); + if (r< 0) { + fprintf(stderr, "Cannot set uid"); + exit(-3245); + } + break; + + case 'g': + gid = strtoul(argv[i],NULL,10); + r = setgid((gid_t)gid); + if (r< 0) { + fprintf(stderr, "Cannot set gid"); + exit(-3245); + } + break; + + case 'l': + fdl = open("/proc/self/attr/current", 0, S_IWUSR); + if (fdl < 0) + { + fprintf(stderr,"Cannot open /proc/self/attr/current\n"); + exit(-345); + } + + r = write(fdl, argv[i], strlen(argv[i])); + if (r < 0) + { + fprintf(stderr, "Cannot write to /proc/self/attr/current\n"); + close(fdl); + exit(-345); + } + close(fdl); + break; + case 'q': + ready_fd = strtol(argv[i], NULL, 10); + break; + } + } + i++; + } + return 0; +} + +int main(int argc, char* argv[]) { + int j = 0; + int fd; + parse_args(argc, argv); + fd = bus_open_connection(KDBUS_SYSTEM_BUS_PATH, KDBUS_ATTACH_PIDS); + if (fd < 0) { + return -1; + } + + while(server_names[j] != '\0') + { + if (bus_acquire_name(fd, server_names[j])) + return -1; + j++; + } + + close(ready_fd); + + for(;;); + return 0; +} diff --git a/src/libdbuspolicy1.c b/src/libdbuspolicy1.c index f507f25..b37b1ac 100644 --- a/src/libdbuspolicy1.c +++ b/src/libdbuspolicy1.c @@ -51,6 +51,13 @@ #define UID_INVALID ((uid_t) -1) #define GID_INVALID ((gid_t) -1) +#ifdef LIBDBUSPOLICY_TESTS_API +#undef SYSTEM_BUS_CONF_FILE_PRIMARY +#undef SESSION_BUS_CONF_FILE_PRIMARY +#define SYSTEM_BUS_CONF_FILE_PRIMARY "/usr/lib/dbus-tests/configs/libdbuspolicy-tests/system.conf" +#define SESSION_BUS_CONF_FILE_PRIMARY "/usr/lib/dbus-tests/configs/libdbuspolicy-tests/session.conf" +#endif + /** A process ID */ typedef unsigned long dbus_pid_t; /** A user ID */ diff --git a/src/stest_common.c b/src/stest_common.c new file mode 100644 index 0000000..6938047 --- /dev/null +++ b/src/stest_common.c @@ -0,0 +1,32 @@ +/* Author: Kazimierz Krosman */ +#include +#include +#include + +uint64_t test_cases_mask = ~(0ULL); +int test_count = 0; + +void assert_printf(int res, int tc, const char* value, int line, const char* file) +{ + if (res) { + printf("[r][%d]1\n", tc); + } else { + printf("[r][%d]0\n", tc); + fprintf(stderr, "[r][%d]0<-w=%s, line=%d, file=%s\n", tc, value, line, file); + } +} + +void prepare_mask(int argc, char* argv[]) +{ + if (argc > 1) + { + int i = 1; + test_cases_mask = 0; + for (;i < argc; i++) { + int val = strtol(argv[i], NULL, 10); + if (val >= 0) + test_cases_mask |= (1< + +#define tassert(v) \ + do { \ + if (test_cases_mask & (1 << test_count)) \ + { \ + int ___r = (v); \ + assert_printf(___r, test_count, #v, __LINE__, __FILE__); \ + } \ + test_count++; \ + } while (0) + +enum { + false, + true +}; + +extern uint64_t test_cases_mask; +extern int test_count; +void prepare_mask(int argc, char* argv[]); +void assert_printf(int res, int tc, const char* value, int line, const char* file); diff --git a/src/stest_cynara.c b/src/stest_cynara.c new file mode 100644 index 0000000..7e1df99 --- /dev/null +++ b/src/stest_cynara.c @@ -0,0 +1,40 @@ +/* Author: Kazimierz Krosman */ +#include +#include +#include +#include +#include +#include +#include "stest_common.h" + +#define NEGATIVE_MATCH "negative" +#define COUNT 1000 + +void __dbuspolicy1_change_creds(void* configuration, uid_t uid, gid_t gid,const char* label); + +int main(int argc, char* argv[]) +{ + void* c = NULL; + int desired_result = 1; + int i; + + assert(argc>0); + + if (!strncmp(argv[0], NEGATIVE_MATCH, sizeof(NEGATIVE_MATCH)-1)) + desired_result = 0; + + printf("---desired result: %d %s\n\n", desired_result, argv[0]); + c = dbuspolicy1_init("/sys/fs/kdbus/0-system/bus"); + assert(c != NULL); + + __dbuspolicy1_change_creds(c,200,0,"L"); + + for (i = 0; i < COUNT; i++) { + int result = dbuspolicy1_check_in(c, "cynara.destination", "cynara.sender", + "L", 200, 0, NULL, "cynara.interface", "cynara.method", + DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0); + assert(result == desired_result); + } + printf("[r][0]1\n"); + return 0; +} diff --git a/src/stest_method_call.c b/src/stest_method_call.c new file mode 100644 index 0000000..d1bf8e1 --- /dev/null +++ b/src/stest_method_call.c @@ -0,0 +1,80 @@ +/* Author: Kazimierz Krosman */ +#include +#include +#include +#include +#include "stest_common.h" + +void __dbuspolicy1_change_creds(void* configuration, uid_t uid, gid_t gid,const char* label); + +int main(int argc, char* argv[]) +{ + void* c = NULL; + + prepare_mask(argc,argv); + + c = dbuspolicy1_init("/sys/fs/kdbus/0-system/bus"); + assert(c != NULL); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test2", "org.test.test3", NULL, "org.test.Itest1", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test2", "org.test.test3", NULL, 0, 0, "", "org.test.Itest1", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test2", "org.test.test3", "", "org.test.Itest1", "DontDoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test2", "org.test.test3", "", 0, 0, "", "org.test.Itest1", "DontDoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test2", "org.test.test3", "", "org.test.Itest1", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test2", "org.test.test3", "", 0, 0, "", "org.test.Itest1", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test2", "org.test.test3", "", "org.test.Itest2", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test2", "org.test.test3", "", 0, 0, "", "org.test.Itest2", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test3", "org.test.test2", "", "org.test.Itest3", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test3", "org.test.test2", "", 5001, 100, "", "org.test.Itest3", "NotKnown", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test3", "org.test.test2", "", "org.test.Itest3", "DontDoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test3", "org.test.test2", "", 5001, 100, "", "org.test.Itest3", "DontDoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == false); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test3", "org.test.test2", "", "org.test.Itest3", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test3", "org.test.test2", "", 5001, 100, "" ,"org.test.Itest3", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test3", "org.test.test2", "", "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test3", "org.test.test2", "", 5001, 100, NULL, "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test9", "org.test.test10", "", "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test9", "org.test.test10", "", 5001, 100, NULL, "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "org.test.test10", "org.test.test9", "", "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,5001,100,NULL); + tassert(dbuspolicy1_check_in(c, "org.test.test10", "org.test.test9", "", 0, 0, NULL, "org.test.Itest4", "DoIt", DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL, "", 0, 0) == true); + + return 0; +} diff --git a/src/stest_ownership.c b/src/stest_ownership.c new file mode 100644 index 0000000..9172500 --- /dev/null +++ b/src/stest_ownership.c @@ -0,0 +1,59 @@ +/* Author: Kazimierz Krosman */ +#include +#include +#include +#include +#include "stest_common.h" + +void __dbuspolicy1_change_creds(void* configuration, uid_t uid, gid_t gid,const char* label); + +int main(int argc, char* argv[]) +{ + void* c = NULL; + + prepare_mask(argc,argv); + + c = dbuspolicy1_init("/sys/fs/kdbus/0-system/bus"); + assert(c != NULL); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(dbuspolicy1_can_own(c, "org.test.test1")); + + __dbuspolicy1_change_creds(c, 5009, 0, NULL); + tassert(dbuspolicy1_can_own(c, "org.test.test1")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test2")); + + __dbuspolicy1_change_creds(c, 5009, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test2")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test3")); + + __dbuspolicy1_change_creds(c, 5009, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test3")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test4")); + + __dbuspolicy1_change_creds(c, 5009, 0, NULL); + tassert(dbuspolicy1_can_own(c, "org.test.test4")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(!dbuspolicy1_can_own(c, "org.test.test5")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(dbuspolicy1_can_own(c, "org.test.test6")); + + __dbuspolicy1_change_creds(c, 0, 0, NULL); + tassert(dbuspolicy1_can_own(c, "org.test.test7")); + + tassert(dbuspolicy1_can_own(c, "a.b.c")); + tassert(dbuspolicy1_can_own(c, "a.b")); + tassert(!dbuspolicy1_can_own(c, "c")); + tassert(!dbuspolicy1_can_own(c, "a.c")); + tassert(!dbuspolicy1_can_own(c, "b.c")); + + return 0; +} diff --git a/src/stest_signal.c b/src/stest_signal.c new file mode 100644 index 0000000..5fbef7c --- /dev/null +++ b/src/stest_signal.c @@ -0,0 +1,26 @@ +/* Author: Kazimierz Krosman */ +#include +#include +#include +#include +#include "stest_common.h" + +void __dbuspolicy1_change_creds(void* configuration, uid_t uid, gid_t gid,const char* label); + +int main(int argc, char* argv[]) +{ + void* c = NULL; + + prepare_mask(argc,argv); + + c = dbuspolicy1_init("/sys/fs/kdbus/0-system/bus"); + assert(c != NULL); + + __dbuspolicy1_change_creds(c,0,0,NULL); + tassert(dbuspolicy1_check_out(c, "", "bli.bla.blubb test.test1 test.tes3", "", "/an/object/path", "", DBUSPOLICY_MESSAGE_TYPE_SIGNAL, "", 0, 0) == true); + + __dbuspolicy1_change_creds(c,5010,0,NULL); + tassert(dbuspolicy1_check_out(c, "", "bli.bla.blubb", "", "/an/object/path", "", DBUSPOLICY_MESSAGE_TYPE_SIGNAL, "", 0, 0) == false); + + return 0; +} diff --git a/src/test_runner.c b/src/test_runner.c new file mode 100644 index 0000000..33517a0 --- /dev/null +++ b/src/test_runner.c @@ -0,0 +1,692 @@ +/* This file contains test-runner for libdbuspolicy + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * Author: Kazimierz Krosman + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_runner.h" + +enum { + PIPE_READ, + PIPE_WRITE, +}; + +/*****************CUSTOM SECTION***********************************/ +/*custom function that parse stdout of binary and result of binary*/ +void parse_test_state(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option); +void parse_one_test_one_binary(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option); +char** prepare_args_for_binary(const struct binary* b, const char* test_name); +int daemon_init(void); +int daemon_clear(void); +int cynara_init(void); +int cynara_clear(void); +int cynara_n_init(void); +int cynara_n_clear(void); + +static struct test_case ownership_test_cases[] = { + {"0", "Simple check for ownership in default context"}, + {"1", "Simple check for ownership in default context"}, + {"2", "Ownership check in user context"}, + {"3", "Ownership check in user context (negative test)"}, + {"4", "Ownership check in mandatory context with user context (context priority check)"}, + {"5", "Ownership check in mandatory context with user context (for another user)"}, + {"6", "Ownership check for not root user context (negative test)"}, + {"7", "Ownership check for not root user context"}, + {"8", "Ownership check for group context"}, + {"9", "Ownership check for all context (priority check)"}, + {"10", "Ownership check for all context (priority check)"}, + {"11", "Ownership check for own_prefix case (1)"}, + {"12", "Ownership check for own_prefix case (2)"}, + {"13", "Ownership check for own_prefix case (negative test)"}, + {"14", "Ownership check for own_prefix case (negative test)"}, + {"15", "Ownership check for own_prefix case (negative test)"}, + {NULL, NULL} +}; + +static struct test_case method_call_test_cases[] = { + {"0", "check_out test"}, + {"1", "check_in test"}, + {"2", "check_out test"}, + {"3", "check_in test"}, + {"4", "check_out test"}, + {"5", "check_in test"}, + {"6", "check_out test"}, + {"7", "check_in test"}, + {"8", "check_out test"}, + {"9", "check_in test"}, + {"10", "check_out test"}, + {"11", "check_in test"}, + {"12", "check_out test"}, + {"13", "check_in test"}, + {"14", "check_out test"}, + {"15", "check_in test"}, + {"16", "check_out test"}, + {"17", "check_in test"}, + {"18", "check_out test"}, + {"19", "check_in test"}, + {NULL, NULL} +}; + +static struct test_case signal_test_cases[] = { + {"0", "Check out for signal"}, + {"1", "Check out for signal (negative)"}, + {NULL, NULL} +}; + +static struct test_case cynara_test_cases[] = { + {"0", "Check cynara for result"}, + {NULL, NULL} +}; + +static struct test_case negative_cynara_test_cases[] = { + {"0", "Check cynara for result (negative test)"}, + {NULL, NULL} +}; + +/* This table is used to start binaries */ +struct binary tests[] = { +/*path, name, TC_table, timeout in us, prepare_args_handler, parse_function_handler, init_handler, clean_handler*/ + {TEST_PATH "stest_ownership", "ownership", ownership_test_cases, 1000*1000, prepare_args_for_binary, parse_test_state, daemon_init, daemon_clear}, + {TEST_PATH "stest_method_call", "method_call", method_call_test_cases, 1000*1000, prepare_args_for_binary, parse_test_state, daemon_init, daemon_clear}, + {TEST_PATH "stest_signal", "signal", signal_test_cases, 1000*1000, prepare_args_for_binary, parse_test_state, daemon_init, daemon_clear}, + {TEST_PATH "stest_cynara", "cynara", cynara_test_cases, 1000*1000, prepare_args_for_binary, parse_test_state, cynara_init, cynara_clear}, + {TEST_PATH "stest_cynara", "negative_cynara", negative_cynara_test_cases, 1000*1000, prepare_args_for_binary, parse_test_state, cynara_n_init, cynara_n_clear}, +}; + + +static const char result_pattern[] = "[r][%0]%1"; +static struct state { + unsigned int i; // index of input buffer + unsigned int j; // index in results[n] + unsigned int n; //index in results buffer (0 or 1) + char results[2][MAX_COMMENT]; +} g_state; + +static void sm_reset(void) +{ + memset(&g_state, 0, sizeof (g_state)); +} + +static int sm_update(char* buffer, int i) +{ + int l = strlen(buffer) + 1; + while (i < l && g_state.i < sizeof(result_pattern) - 1) { + if (result_pattern[g_state.i] == '%') { + g_state.n = result_pattern[g_state.i+1] - '0'; + if (g_state.n > 1) { + sm_reset(); + i--; + } else if (isalnum(buffer[i])) { + g_state.results[g_state.n][g_state.j++] = buffer[i]; + } else if (buffer[i] == result_pattern[g_state.i+2] || buffer[i] == '\n') { + g_state.results[g_state.n][g_state.j] = 0; + g_state.i += 3; + g_state.j = 0; + if (g_state.n == 1) + return i; + } else { + g_state.i = 0; + g_state.j = 0; + } + } else if (result_pattern[g_state.i] == buffer[i]) { + g_state.i++; + } else { + sm_reset(); + } + i++; + } + + if (g_state.i >= sizeof(result_pattern) - 1) { + g_state.results[g_state.n][g_state.j] = 0; + g_state.i += 3; + g_state.j = 0; + if (g_state.n == 1) + return i; + } + + return 0; +} + +static const char* sm_get_result(int i) +{ + return g_state.results[i]; +} + +static pid_t daemon_pids[4] ={0}; +static int daemons_pipe[4][2]; +static char* daemon_argv1[] = {TEST_PATH "dbus_daemon", "-q", NULL, "-n","org.test.test3", NULL}; +static char* daemon_argv2[] = {TEST_PATH "dbus_daemon", "-q", NULL,"-n", "org.test.test2", "-g", "100", "-u", "5001", NULL}; +static char* daemon_argv3[] = {TEST_PATH "dbus_daemon", "-q", NULL,"-n", "org.test.test12", "org.test.test9", NULL}; +static char* daemon_argv4[] = {TEST_PATH "dbus_daemon", "-q", NULL, "-n", "org.test.test13", "org.test.test10", "org.test.test11", "-g", "100", "-u", "5001", NULL}; +static char** daemons[] = { daemon_argv1, daemon_argv2, daemon_argv3, daemon_argv4}; + +int daemon_init(void) +{ + int i = 0; + int ready_proc = 0; + struct timeval tv; + fd_set rfds; + int nfds = -1; + int proc_num = sizeof(daemons)/sizeof(daemons[0]); + + for (;i < proc_num;i++) { + if (pipe(daemons_pipe[i]) < 0) { + perror("cannot create pipe"); + goto error1; + } + + daemon_pids[i] = fork(); + if (!daemon_pids[i]) { + char fdd[15]; + sprintf(fdd,"%d",daemons_pipe[i][PIPE_WRITE]); + daemons[i][2] = fdd; + close (daemons_pipe[i][PIPE_READ]); + (void)execv(daemons[i][0],daemons[i]); + exit(0); + } else if (daemon_pids[i] > 0){ + close (daemons_pipe[i][PIPE_WRITE]); + } else + goto error1; + } + + tv.tv_sec = 1; + tv.tv_usec = 0; + while(1) { + FD_ZERO(&rfds); + for (i = 0; i < proc_num;i++) { + if (daemons_pipe[i][PIPE_READ] > -1) + FD_SET(daemons_pipe[i][PIPE_READ], &rfds); + } + + if (ready_proc >= proc_num) + break; + + nfds = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); + if (nfds > 0) { + for (i = 0; i < proc_num;i++) { + if (daemons_pipe[i][PIPE_READ] > -1 && FD_ISSET(daemons_pipe[i][PIPE_READ], &rfds)) { + ++ready_proc; + close (daemons_pipe[i][PIPE_READ]); + daemons_pipe[i][PIPE_READ] = -1; + } + } + } else + goto error1; + } + + return 1; +error1: + daemon_clear(); + return 0; +} + +int daemon_clear(void) +{ + unsigned int i = 0; + for (;i < sizeof(daemons)/sizeof(daemons[0]);i++) { + if (daemon_pids[i]) + kill(daemon_pids[i], SIGKILL); + } + return 0; +} + +static int call_script(char* path, char* argv[]) +{ + int status; + int r; + pid_t pid = fork(); + + if (pid < 0) + return 0; + else if (!pid) { + (void)execv(path, argv); + exit(-1); + } + + r = waitpid(pid, &status, 0); + if (r > 0) { + if (WIFEXITED(status)) + return !(WEXITSTATUS(status)); + else if (WIFCONTINUED(status)) + kill(pid, SIGKILL); + } + + /* fail */ + return 0; +} + +int cynara_n_init(void) +{ + char* argv[] = {CYNARA_PATH, "DENY", NULL}; + return call_script(argv[0],argv); +} + +int cynara_n_clear(void) +{ + return cynara_clear(); +} + +int cynara_init(void) +{ + char* argv[] = {CYNARA_PATH, "ALLOW", NULL}; + return call_script(argv[0],argv); +} + +int cynara_clear(void) +{ + char* argv[] = {CYNARA_PATH, NULL}; + return call_script(argv[0],argv); +} + +static char* args[3]; +char** prepare_args_for_binary(const struct binary* b, const char* test_name) +{ + args[0] = (char*)b->name; + if (!test_name[0]) + args[1] = NULL; + else { + args[1] = (char*)test_name; + args[2] = NULL; + } + return args; +} + + +/* + * Example of test_result detector function implementation + */ +void parse_test_state(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option) +{ + char test_id[MAX_COMMENT]; + + switch(state_change) { + case INIT_TEST: + sm_reset(); + break; + case NEW_STDOUT: + { + int k = 0; + buffer[state_option] = 0; + get_test_id(test_id, b, test_name); + fprintf(stderr, "[stdout][%s]%s\n",test_id, buffer); + while ( (k = sm_update(buffer, k))) { + const char* t_name = sm_get_result(0); + const char* result_code = sm_get_result(1); + bool is_successful = result_code[0] == '1' && result_code[1] == 0; + const char* result_text = is_successful ? "PASS" : "FAIL"; + + get_test_id(test_id, b, t_name); + add_test_result(test_id, result_text, "", is_successful); + sm_reset(); + } + } + break; + case NEW_STDERR: + buffer[state_option] = 0; + get_test_id(test_id, b, test_name); + fprintf(stderr, "[stderr][%s]%s\n",test_id, buffer); + break; + case RESULT_CODE: + if (state_option != 0) + add_test_result(get_test_id(test_id, b, test_name), "FAIL", "Test exited with error code", 0); + break; + case RESULT_SIGNAL: + add_test_result(get_test_id(test_id, b, test_name), "FAIL", "Finished by SIGNAL", 0); + break; + case RESULT_TIMEOUT: + add_test_result(get_test_id(test_id, b, test_name), "FAIL", "Test TIMEOUT", 0); + break; + } +} + +/****************************END of CUSTOM FUNCTIONS************************/ + + +static struct option long_options[] = { + {"list", no_argument, 0, 'l'}, + {"run", required_argument, 0, 'r'}, + {"description", required_argument, 0, 'd'}, + {0, 0, 0, 0 } +}; + +static int stdin_pipe[2]; +static int stdout_pipe[2]; +static int stderr_pipe[2]; +static struct test_result test_results[MAX_TC_NUM]; +static int test_results_i; +static char buffer[MAX_BUFFER]; +static const char* requested_tc[MAX_TC_NUM]; + +char* get_test_id(char* dest, const struct binary* b, const char* test_name) +{ + int len = strlen(b->name); + memcpy(dest, b->name, len); + memcpy(dest + len, test_name, strlen(test_name)+1); + return dest; +} + +static void print_description(const char* name, const char* description) +{ + printf("%s;%s\n",name, description); +} + +static void print_list(const char* test_name) +{ + unsigned int i; + char full_name[MAX_COMMENT]; + for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) { + int j = 0; + int l = strlen(tests[i].name); + memcpy(full_name, tests[i].name, l+1); + if (test_name && strncmp(test_name, full_name, l) != 0) + continue; + + while (tests[i].test_cases[j].name) { + memcpy(full_name + l, tests[i].test_cases[j].name, strlen(tests[i].test_cases[j].name) + 1); + if (!test_name || strcmp(full_name, test_name) == 0) + print_description(full_name,tests[i].test_cases[j].description); + j++; + } + } +} + + +static void stop_binary(const struct binary* b, pid_t pid, const char* test_name) +{ + int status = 0; + int res = 0; + res = waitpid(pid, &status, 0); + if (res == 0) { + //timeouted + kill(pid, SIGKILL); + res = waitpid(pid, &status, WNOHANG); + b->parse(b, test_name, buffer, RESULT_TIMEOUT, res); + } else if (res < 0) { + //errno check + kill(pid, SIGKILL); + res = waitpid(pid, &status, WNOHANG); + b->parse(b, test_name, buffer, RESULT_ERROR, res); + } else if (res > 0) { + if (WIFEXITED(status)) { + b->parse(b, test_name, buffer, RESULT_CODE, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + b->parse(b, test_name, buffer, RESULT_SIGNAL, WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + b->parse(b, test_name, buffer, RESULT_SIGNAL, WSTOPSIG(status)); + } else if (WIFCONTINUED(status)) { + kill(pid, SIGKILL); + b->parse(b, test_name, buffer, RESULT_SIGNAL, -1); + } + } +} + +static void parse_output_with_timeout(const struct binary* b, pid_t pid, const char* test_name) +{ + struct timeval tv; + fd_set rfds; + int nfds; + int res; + tv.tv_sec = b->timeout/(1000*1000); + tv.tv_usec = (b->timeout-tv.tv_sec*1000*1000); + while (1) { + FD_ZERO(&rfds); + FD_SET(stdout_pipe[PIPE_READ], &rfds); + FD_SET(stderr_pipe[PIPE_READ], &rfds); + nfds = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); + if (nfds == -1) { + if (errno != EINTR) + break; + } else if (nfds > 0) { + if (FD_ISSET(stdout_pipe[PIPE_READ], &rfds)) { + res = read(stdout_pipe[PIPE_READ], buffer, MAX_BUFFER-1); + if (res == 0 || (res < 0 && errno != EINTR)) + break; + else if (res >=0) + b->parse(b, test_name, buffer, NEW_STDOUT, res); + } + + if (FD_ISSET(stderr_pipe[PIPE_READ], &rfds)) { + res = read(stderr_pipe[PIPE_READ], buffer, MAX_BUFFER-1); + if (res == 0 || (res < 0 && errno != EINTR)) + break; + b->parse(b, test_name, buffer, NEW_STDERR, res); + } + } else { + //timeout + break; + } + } + stop_binary(b, pid, test_name); +} + +static int create_child(const char* path, char* const arguments[]) +{ + int child; + int nResult; + if (pipe(stdin_pipe) < 0) { + perror("allocating pipe for child input redirect failed"); + goto error1; + } + + if (pipe(stdout_pipe) < 0) { + perror("allocating pipe for child output redirect failed"); + goto error2; + } + + if (pipe(stderr_pipe) < 0) { + perror("allocating pipe for child output redirect failed"); + goto error3; + } + + child = fork(); + if (!child) { + char ld_path[512]; + sprintf(ld_path, "/usr/lib/dbus-tests/lib/libdbuspolicy-tests/:"); + // redirect stdin + if (dup2(stdin_pipe[PIPE_READ], STDIN_FILENO) == -1) { + perror("redirecting stdin failed"); + return -1; + } + + // redirect stdout + if (dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO) == -1) { + perror("redirecting stdout failed"); + return -1; + } + + // redirect stderr + if (dup2(stderr_pipe[PIPE_WRITE], STDERR_FILENO) == -1) { + perror("redirecting stderr failed"); + return -1; + } + + // all these are for use by parent only + close(stdin_pipe[PIPE_READ]); + close(stdin_pipe[PIPE_WRITE]); + close(stdout_pipe[PIPE_READ]); + close(stdout_pipe[PIPE_WRITE]); + close(stderr_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_WRITE]); + + char* ld_path_b = getenv("LD_LIBRARY_PATH"); + if (ld_path_b != NULL) + memcpy(ld_path + strlen(ld_path), ld_path_b, strlen(ld_path_b)+1); + setenv("LD_LIBRARY_PATH", ld_path, 1); + // run child process image + nResult = execv(path, arguments); + + // if we get here at all, an error occurred, but we are in the child + // process, so just exit + perror("exec of the child process failed"); + exit(nResult); + } else if (child > 0) { + // parent continues here + + // close unused file descriptors, these are for child only + close(stdin_pipe[PIPE_READ]); + close(stdout_pipe[PIPE_WRITE]); + close(stderr_pipe[PIPE_WRITE]); + } else { + // failed to create child + goto error4; + } + + return child; + +error4: + close(stderr_pipe[PIPE_READ]); + close(stderr_pipe[PIPE_WRITE]); +error3: + close(stdout_pipe[PIPE_READ]); + close(stdout_pipe[PIPE_WRITE]); +error2: + close(stdin_pipe[PIPE_READ]); + close(stdin_pipe[PIPE_WRITE]); +error1: + return -1; +} + +static void run_test(const struct binary* b, const char* test_name) +{ + int res = -1; + char** arg; + char test_id[MAX_COMMENT]; + + assert(b); + assert(b->name); + assert(b->path); + assert(test_name); + + arg = b->prepare_args(b, test_name); + + if (b->init) + if (!b->init()) { + add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Internal error: Cannot init test", 0); + return; + } + + res = create_child(b->path, arg); + if (res > 0) + parse_output_with_timeout(b, res, test_name); + else + add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Internal error: Cannot start test", 0); + + if (b->clean) + b->clean(); +} + +static void parse_run_test(const char* tc) { + unsigned int i = 0; + for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) { + int len = strlen(tests[i].name); + if (strncmp(tc, tests[i].name, len) == 0) { + if (tc[len] == '*' || tc[len] == '\0') + run_test(&tests[i], ""); + else + run_test(&tests[i], tc + len); + } + } +} + +static int parse_option(int argc, char* argv[]) +{ + int ch = 0; + int c = 0; + while ((ch = getopt_long(argc, argv, "lr:d:", long_options, NULL)) != -1) { + switch (ch) { + case 'l': + print_list(NULL); + return 1; + case 'r': + if (c >= MAX_TC_NUM - 1) //NULL at the end + return 0; + + if (optarg) + requested_tc[c++] = optarg; + + break; + case 'd': + print_list(optarg); + return 1; + } + } + return 0; +} + +//output_new/fail/success +void add_test_result(const char* test_id, const char* result, const char* comment, int res) +{ + test_results[test_results_i].is_positive = res; + strcpy(test_results[test_results_i].result, result); + strcpy(test_results[test_results_i].comment, comment); + strcpy(test_results[test_results_i++].name, test_id); +} + +static void prepare_results(void) +{ + //todo +} + +static void print_results() +{ + int i = 0; + for (i = 0; i < test_results_i; i++) + { + printf("%s;%s;%s\n", test_results[i].name, test_results[i].result, test_results[i].comment); + } +} + +int main(int argc, char* argv[]) +{ + unsigned int i; + signal(SIGPIPE, SIG_IGN); + if (parse_option(argc, argv)) + return 0; + + prepare_results(); + + if (!requested_tc[0]) { + for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) + run_test(&tests[i], ""); + } else { + i = 0; + while(requested_tc[i]) { + parse_run_test(requested_tc[i]); + i++; + } + } + + print_results(); + return 0; +} diff --git a/src/test_runner.h b/src/test_runner.h new file mode 100644 index 0000000..3c79a99 --- /dev/null +++ b/src/test_runner.h @@ -0,0 +1,69 @@ +/* This file is part of test-runner (see template.c) + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * Author: Kazimierz Krosman + * + * 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. +*/ + +#ifndef TEST_RUNNER_H +#define TEST_RUNNER_H +#include + +#define TC_NAME "libdbuspolicy-tests" +#define TEST_PATH "/usr/lib/dbus-tests/test-suites/libdbuspolicy-tests/" +#define CYNARA_PATH TEST_PATH "cynara_prepare.sh" +#define MAX_TC_NUM 1024 +#define MAX_BUFFER (64*1024) +#define MAX_COMMENT 1024 + +enum { + INIT_TEST, + NEW_STDOUT, + NEW_STDERR, + RESULT_CODE, + RESULT_SIGNAL, + RESULT_ERROR, + RESULT_TIMEOUT +}; + +struct test_result { + bool is_positive; + char comment[MAX_COMMENT]; + char result[MAX_COMMENT]; + char name[MAX_COMMENT]; +}; + +struct test_case { + const char* name; + const char* description; +}; + +struct binary { + const char* path; + const char* name; + /* can be filled by asking binary */ + struct test_case* test_cases; + int timeout; + + char** (*prepare_args) (const struct binary* b, const char* test_name); + void (*parse) (const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option); + int (*init)(void); + int (*clean)(void); +}; + +char* get_test_id(char* dest, const struct binary* b, const char* test_name); +void add_test_result(const char* test_id, const char* result, const char* comment, int res); + + +#endif /* TEST_RUNNER_H */ -- 2.7.4