From a53e41e31e3b0d4d007a321fd41b3ac0d0eee271 Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Tue, 12 Nov 2019 14:16:41 +0100 Subject: [PATCH] add cmocka based unit testing framework to the build process Contains a notifier test as a working example. Change-Id: I545406edad50763d9687abf4c418c2e6b0f94b0d Signed-off-by: Maciej Slodczyk --- CMakeLists.txt | 1 + packaging/resourced.spec | 1 + src/CMakeLists.txt | 24 ++++++++ src/common/macro.h | 11 ++++ tests/CMakeLists.txt | 28 +++++++++ tests/cmocka-core.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 tests/cmocka-core.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 94e406a..3555cdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,5 +55,6 @@ INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system. ADD_SUBDIRECTORY(src) IF(DEFINED RD_TESTS_PATH) + ENABLE_TESTING() ADD_SUBDIRECTORY(tests) ENDIF(DEFINED RD_TESTS_PATH) diff --git a/packaging/resourced.spec b/packaging/resourced.spec index ea2f1e1..7d9342d 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -43,6 +43,7 @@ BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(storage) BuildRequires: pkgconfig(libgum) BuildRequires: pkgconfig(capi-system-device) +BuildRequires: pkgconfig(cmocka) #only for data types BuildRequires: pkgconfig(tapi) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c54ad6..07d0828 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,6 +153,30 @@ ADD_EXECUTABLE(${RD_BINARY_NAME} ${RESOURCED_SOURCE_DIR}/init.c ${RESOURCED_SOURCE_DIR}/main.c ${SOURCES}) + +SET(SAVE_EXTRA_CFLAGS ${EXTRA_CFLAGS}) +SET(SAVE_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) +SET(SAVE_CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) +SET(EXTRA_CFLAGS "") +SET(EXTRA_CFLAGS_RELEASE "") +SET(CMAKE_C_FLAGS "") +SET(CMAKE_CXX_FLAGS "") +SET(CMAKE_CXX_FLAGS_RELEASE "") +SET(CMAKE_C_FLAGS_RELEASE "-D_UNIT_TEST -Wp,-U_FORTIFY_SOURCE -U_FORTIFY_SOURCE -O0 -g") +ADD_LIBRARY(resourced_shared_test STATIC + ${RESOURCED_INCLUDE_HEADERS} + ${RESOURCED_SHARED_SOURCES} + ${RESOURCED_SHARED_HEADERS} + ) + +SET_TARGET_PROPERTIES(resourced_shared_test + PROPERTIES COMPILE_FLAGS ${CMAKE_C_FLAGS_RELEASE}) +TARGET_LINK_LIBRARIES(resourced_shared_test ${RESOURCED_REQUIRE_PKGS_LDFLAGS}) + +SET(EXTRA_CFLAGS ${SAVE_EXTRA_CFLAGS}) +SET(CMAKE_C_FLAGS ${SAVE_CMAKE_C_FLAGS}) +SET(CMAKE_C_FLAGS_RELEASE ${SAVE_CMAKE_C_FLAGS_RELEASE}) + TARGET_LINK_LIBRARIES(${RD_BINARY_NAME} resourced-private-api ${RESOURCED_REQUIRE_PKGS_LDFLAGS} "-pie -ldl -lm -Wl,-rpath=${RD_PLUGIN_PATH}") SET_TARGET_PROPERTIES(${RD_BINARY_NAME} PROPERTIES COMPILE_FLAGS "-fvisibility=default") diff --git a/src/common/macro.h b/src/common/macro.h index f67a94d..c8e9a51 100644 --- a/src/common/macro.h +++ b/src/common/macro.h @@ -138,6 +138,7 @@ elem && ((node = elem->data) != NULL); \ elem = elem_next, elem_next = g_slist_next(elem), node = NULL) +#ifndef _UNIT_TEST #define MODULE_REGISTER(module) \ static void __attribute__ ((constructor)) module_init(void) \ { \ @@ -147,6 +148,16 @@ { \ remove_module(module); \ } +#else +/* module is declared in all modules as static struct and the resourced + * codebase is compiled with -Wall so we need to use 'module' pionter + * somehow to prevent from breaking the build process */ +#define MODULE_REGISTER(module) \ + static void __attribute__ ((constructor)) placeholder(void) \ + { \ + ((void)(module)); \ + } +#endif /* Overlap definition to consider three strings as a string */ #define STRING_FORMAT_SPECIFIER_WITH_MACRO(macro) "%"#macro"s" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f44c951..2576482 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,34 @@ TARGET_LINK_LIBRARIES(watchdog-test "${GLIB2_LDFLAGS}" "${GIO2_LDFLAGS}") +# run unit tests autimatically whenever building resourced +ADD_CUSTOM_TARGET(do-test ALL make test + WORKING_DIRECTORY ./ + USES_TERMINAL) + +# build unit test +ADD_EXECUTABLE(cmocka-core cmocka-core.c) + +PKG_CHECK_MODULES(CMOCKA REQUIRED cmocka) + +SET(EXTRA_CFLAGS "") +SET(RESOURCED_REQUIRE_PKGS_LDFLAGS "") +SET(CMAKE_C_FLAGS "") +SET(CMAKE_C_FLAGS_RELEASE "") +SET(CMAKE_LDFLAGS "") +SET(CMAKE_LDFLAGS_RELEASE "") + +SET(UNIT_TESTS_CFLAGS "-O0 -D_UNIT_TEST -D_GNU_SOURCE") + +SET_TARGET_PROPERTIES(cmocka-core PROPERTIES COMPILE_FLAGS + "-I${COMMON_SOURCE_DIR} -I/usr/include/dlog -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include ${UNIT_TESTS_CFLAGS}") +TARGET_LINK_LIBRARIES(cmocka-core resourced_shared_test cmocka dlog gio-2.0 gobject-2.0 glib-2.0 + "-Wl,--wrap=malloc,--wrap=free,--wrap=g_slist_append,--wrap=g_slist_remove,--wrap=strdup,--wrap=strndup -O0") + +# add unit test to test target +ADD_TEST(core cmocka-core) +ADD_DEPENDENCIES(do-test cmocka-core) + INSTALL(TARGETS watchdog-test DESTINATION ${RD_TESTS_PATH}) INSTALL(FILES run_tests.sh diff --git a/tests/cmocka-core.c b/tests/cmocka-core.c new file mode 100644 index 0000000..0793cae --- /dev/null +++ b/tests/cmocka-core.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "notifier.h" +#include "config-parser.h" + +typedef int (*noti_cb)(void *data); + +void *__real_malloc(size_t size); +void __real_free(void *ptr); +GSList *__real_g_slist_append(GSList *list, gpointer data); +GSList *__real_g_slist_remove(GSList *list, gconstpointer data); + +struct resourced_notifier { + enum notifier_type status; + noti_cb func; +}; + +struct resourced_notifier *notifier; + +int test_notifier_cb(void *data) +{ + check_expected_ptr(data); + return 0; +} + +void __wrap_free(void *ptr) +{ + bool check = mock_type(bool); + if (check) + check_expected_ptr(ptr); + __real_free(ptr); +} + +void *__wrap_malloc(size_t size) +{ + bool fake = mock_type(bool); + if (fake) + return NULL; + return __real_malloc(size); +} + +GSList *__wrap_g_slist_append(GSList *list, gpointer data) +{ + assert(data); + bool wrap_append = mock_type(bool); + if (!wrap_append) + return __real_g_slist_append(list, data); + + notifier = (struct resourced_notifier *)data; + + if (!notifier->func) + return NULL; + int t = mock_type(int); + void *f = mock_ptr_type(void *); + + assert(t == (int) notifier->status); + assert(f == (void*) notifier->func); + + (void) t; + (void) f; + + GSList *l = (GSList *)calloc(1, sizeof (GSList)); + assert(l); + l->data = data; + + return l; +} + +GSList *__wrap_g_slist_remove(GSList *list, gconstpointer data) +{ + bool wrap_remove = mock_type(bool); + if (!wrap_remove) + return __real_g_slist_remove(list, data); + + if (!list) + return NULL; + + struct resourced_notifier *n = (struct resourced_notifier *)data; + assert(n == notifier); + __real_free(list); + + (void) n; + return NULL; +} + +static void test_register_notifier(void **state) +{ + (void) state; /* unused */ + + void *fptr = test_notifier_cb; + assert_int_equal(register_notifier(1, NULL), -EINVAL); + + will_return(__wrap_malloc, true); + assert_int_equal(register_notifier(1, fptr), -ENOMEM); + will_return_maybe(__wrap_malloc, false); + + will_return(__wrap_g_slist_append, true); + will_return(__wrap_g_slist_append, 1); + will_return(__wrap_g_slist_append, cast_ptr_to_largest_integral_type(fptr)); + assert_int_equal(register_notifier(1, fptr), 0); + assert_int_equal(register_notifier(1, fptr), -EINVAL); +} + +static void test_notify(void **state) +{ + (void) state; /* unused */ + const char *d = "xoxo"; + expect_string(test_notifier_cb, data, d); + resourced_notify(1, (void *)d); +} + +static void test_unregister_notifier(void **state) +{ + (void) state; /* unused */ + void *fptr = test_notifier_cb; + assert_int_equal(unregister_notifier(1, NULL), -EINVAL); + + will_return(__wrap_free, true); + expect_value(__wrap_free, ptr, cast_ptr_to_largest_integral_type(notifier)); + will_return(__wrap_g_slist_remove, true); + assert_int_equal(unregister_notifier(1, fptr), 0); +} + +static int test_setup(void **state) +{ + will_return_maybe(__wrap_malloc, false); + return 0; +} + +int main(int argc, char* argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_register_notifier), + cmocka_unit_test(test_notify), + cmocka_unit_test(test_unregister_notifier), + + }; + return cmocka_run_group_tests(tests, test_setup, NULL); +} -- 2.7.4