From dc635f86f30269a21e7440279a74265e3989ed4b Mon Sep 17 00:00:00 2001 From: Youngjae Cho Date: Fri, 1 Oct 2021 18:10:18 +0900 Subject: [PATCH] tests: add test running automatically on building If the test fails, the whole build will fail. Change-Id: Ibb88e60d13bcec09567630d051372fbcfbbb79da Signed-off-by: Youngjae Cho --- CMakeLists.txt | 2 + packaging/libsyscommon.spec | 1 + tests/CMakeLists.txt | 20 ++++ tests/libcommon/CMakeLists.txt | 16 +++ tests/libcommon/test-common.c | 208 +++++++++++++++++++++++++++++++++ tests/libcommon/test.conf | 8 ++ tests/test-main.c | 50 ++++++++ tests/test-main.h | 10 ++ tests/test-mock.c | 65 +++++++++++ tests/test-mock.h | 8 ++ 10 files changed, 388 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/libcommon/CMakeLists.txt create mode 100644 tests/libcommon/test-common.c create mode 100644 tests/libcommon/test.conf create mode 100644 tests/test-main.c create mode 100644 tests/test-main.h create mode 100644 tests/test-mock.c create mode 100644 tests/test-mock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 049a45b..bebdb2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,3 +68,5 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_I FOREACH(hfile ${HEADERS}) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${hfile} DESTINATION include/${PROJECT_NAME}) ENDFOREACH(hfile) + +ADD_SUBDIRECTORY(tests) diff --git a/packaging/libsyscommon.spec b/packaging/libsyscommon.spec index 80e7db4..4ec8ea2 100644 --- a/packaging/libsyscommon.spec +++ b/packaging/libsyscommon.spec @@ -11,6 +11,7 @@ BuildRequires: pkgconfig(glib-2.0) >= 2.44 BuildRequires: pkgconfig(gio-2.0) >= 2.44 BuildRequires: pkgconfig(gio-unix-2.0) BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(cmocka) BuildRequires: cmake Requires: /bin/cp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..bfd44ea --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +SET(TEST_DRIVER "test-driver") + +FILE(GLOB SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.c") + +SET(TEST_SUITE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +ADD_DEFINITIONS("-DTEST_SUITE_DIRECTORY=\"${TEST_SUITE_DIRECTORY}\"") + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(REQUIRED_PKGS REQUIRED + cmocka) + +ADD_EXECUTABLE(${TEST_DRIVER} ${SRCS}) +TARGET_LINK_LIBRARIES(${TEST_DRIVER} cmocka dl) +SET_TARGET_PROPERTIES(${TEST_DRIVER} PROPERTIES LINK_FLAGS "-Wl,--export-dynamic") + +ADD_CUSTOM_TARGET(run-test ALL "./${TEST_DRIVER}") +ADD_DEPENDENCIES(run-test ${TEST_DRIVER}) + +# add test suites +ADD_SUBDIRECTORY(libcommon) diff --git a/tests/libcommon/CMakeLists.txt b/tests/libcommon/CMakeLists.txt new file mode 100644 index 0000000..9fe27d3 --- /dev/null +++ b/tests/libcommon/CMakeLists.txt @@ -0,0 +1,16 @@ +SET(TEST_SUITE "test-driver-libcommon") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) + +FILE(GLOB SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*.c") +FILE(GLOB ORIG_SRCS "${CMAKE_SOURCE_DIR}/src/libcommon/*.c") + +ADD_LIBRARY(${TEST_SUITE} MODULE ${SRCS} ${ORIG_SRCS}) +SET_TARGET_PROPERTIES(${TEST_SUITE} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${TEST_SUITE_DIRECTORY}) +ADD_DEPENDENCIES(${TEST_DRIVER} ${TEST_SUITE}) + +SET(WRAP_FLAGS "${WRAP_FLAGS} -Wl,--wrap=open") +SET(WRAP_FLAGS "${WRAP_FLAGS} -Wl,--wrap=open64") +SET_TARGET_PROPERTIES(${TEST_SUITE} PROPERTIES LINK_FLAGS ${WRAP_FLAGS}) + +TARGET_LINK_LIBRARIES(${TEST_SUITE} cmocka glib-2.0) diff --git a/tests/libcommon/test-common.c b/tests/libcommon/test-common.c new file mode 100644 index 0000000..76e770c --- /dev/null +++ b/tests/libcommon/test-common.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "libcommon/file.h" +#include "libcommon/list.h" +#include "libcommon/ini-parser.h" +#include "../test-main.h" +#include "../test-mock.h" + +#define MOCK_FILE_PATH "testfile" +#define MOCK_FILE_LENGTH 100 + +static int setup(void **state) +{ + FILE *fp; + char cwd[128] = {0, }; + char path[256] = {0, }; + char str[MOCK_FILE_LENGTH]; + + memset(str, 't', MOCK_FILE_LENGTH); + + snprintf(path, sizeof(path), "%s/%s", getcwd(cwd, sizeof(cwd)), MOCK_FILE_PATH); + + fp = fopen(path, "w+"); + + if (!fp) { + printf("Failed to setup mockfile, %m\n"); + return -1; + } + + fwrite(str, 1, MOCK_FILE_LENGTH, fp); + fclose(fp); + + return 0; +} + +static int teardown(void **state) +{ + char cwd[128] = {0, }; + char path[256] = {0, }; + + snprintf(path, sizeof(path), "%s/%s", getcwd(cwd, sizeof(cwd)), MOCK_FILE_PATH); + + unlink(path); + + return 0; +} + +static void test_sys_get_str_p1(void **state) +{ + char buffer[MOCK_FILE_LENGTH * 2]; + int retval; + + /* remove possible null character */ + for (int i = 0; i < sizeof(buffer); ++i) + buffer[i] = 'x'; + + /* mocking open() syscall */ + mock_open(MOCK_FILE_PATH, O_RDONLY); + + /* this is example of how mock works */ + retval = sys_get_str("the pathname will be replaced to mocked one", + buffer, sizeof(buffer)); + + assert_int_equal(retval, 0); +} + +/* test small buffer */ +static void test_sys_get_str_n1(void **state) +{ + char buffer[MOCK_FILE_LENGTH / 2]; + int retval; + + /* remove possible null character */ + for (int i = 0; i < sizeof(buffer); ++i) + buffer[i] = 'x'; + + /* mocking open() syscall */ + mock_open(MOCK_FILE_PATH, O_RDONLY); + + /* this is example of how mock works */ + retval = sys_get_str("the pathname will be replaced to mocked one", + buffer, sizeof(buffer)); + + assert_int_equal(retval, -EIO); + assert_int_equal(buffer[0], 0); +} + +/* test no-exist file */ +static void test_sys_get_str_n2(void **state) +{ + char buffer[MOCK_FILE_LENGTH * 2]; + int retval; + + mock_open(MOCK_FILE_PATH, O_RDONLY); + + retval = sys_get_str("the pathname will be replaced to mocked one", + buffer, sizeof(buffer)); + + assert_int_equal(retval, -ENOENT); +} + +static void test_list_append_p(void **state) +{ + GList *head = NULL; + GList *elem, *elem2; + int i; + int value; + int length; + + for ( i = 0; i < 10; ++i) + SYS_G_LIST_APPEND(head, (intptr_t) i); + + length = SYS_G_LIST_LENGTH(head); + assert_int_equal(length, 10); + + i = 0; + SYS_G_LIST_FOREACH(head, elem, elem2) { + value = (int) elem2; + assert_int_equal(value, i++); + } +} + +static void test_list_prepend_p(void **state) +{ + GList *head = NULL; + GList *elem, *elem2; + int i; + int value; + int length; + + for ( i = 0; i < 10; ++i) + SYS_G_LIST_PREPEND(head, (intptr_t) i); + + length = SYS_G_LIST_LENGTH(head); + assert_int_equal(length, 10); + + i = 10; + SYS_G_LIST_FOREACH(head, elem, elem2) { + value = (int) elem2; + assert_int_equal(value, --i); + } +} + +static int check_user_data = 0x443; +static bool checked1; +static bool checked2; +static bool checked3; +static bool garbage; +static int test_parser(struct parse_result *result, void *data) +{ + assert_int_equal(data, check_user_data); + + if (MATCH(result->section, "Section1") && + MATCH(result->name, "Key1") && + MATCH(result->value, "Value1")) + checked1 = true; + else if (MATCH(result->section, "Section2") && + MATCH(result->name, "Key2") && + MATCH(result->value, "Value2")) + checked2 = true; + else if (MATCH(result->section, "Section2") && + MATCH(result->name, "Key3") && + MATCH(result->value, "Value3")) + checked3 = true; + else + garbage = true; + + return 0; +} + +static void test_config_parse_p(void **state) +{ + char cwd[128]; + char path[256]; + int retval; + + /* load predefined test.conf file */ + retval = snprintf(path, sizeof(path), "%s/libcommon/test.conf", getcwd(cwd, sizeof(cwd))); + if (retval >= sizeof(path)) + path[sizeof(path) - 1] = '\0'; + + config_parse(path, test_parser, (void *)(intptr_t) check_user_data); + + assert_true(checked1); + assert_true(checked2); + assert_true(checked3); + assert_false(garbage); +} + +int run_test_suite(void) +{ + const struct CMUnitTest testsuite[] = { + cmocka_unit_test_setup_teardown(test_sys_get_str_p1, setup, teardown), + cmocka_unit_test_setup_teardown(test_sys_get_str_n1, setup, teardown), + cmocka_unit_test(test_sys_get_str_n2), + cmocka_unit_test(test_list_append_p), + cmocka_unit_test(test_list_prepend_p), + cmocka_unit_test(test_config_parse_p), + }; + + return cmocka_run_group_tests(testsuite, NULL, NULL); +} diff --git a/tests/libcommon/test.conf b/tests/libcommon/test.conf new file mode 100644 index 0000000..c915ae1 --- /dev/null +++ b/tests/libcommon/test.conf @@ -0,0 +1,8 @@ +[Section1] +# this is comment +Key1=Value1 + +[Section2] +# this is comment +Key2=Value2 +Key3=Value3 diff --git a/tests/test-main.c b/tests/test-main.c new file mode 100644 index 0000000..0451498 --- /dev/null +++ b/tests/test-main.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#include "test-main.h" + +int main(int argc, char *argv[]) +{ + int fail = 0; + DIR *dir; + struct dirent *ent; + + dir = opendir(TEST_SUITE_DIRECTORY); + if (!dir) { + printf("Failed to opendir, %m\n"); + return 1; + } + + while ((ent = readdir(dir))) { + void *handle; + char sopath[512] = {0, }; + int (*run_test_suite) (void); + + if (!strstr(ent->d_name, ".so")) + continue; + + snprintf(sopath, sizeof(sopath), "%s/%s", TEST_SUITE_DIRECTORY, ent->d_name); + + handle = dlopen(sopath, RTLD_LAZY); + if (!handle) { + printf("Failed to dlopen, %s\n", dlerror()); + continue; + } + + run_test_suite = dlsym(handle, "run_test_suite"); + if (!run_test_suite) { + printf("Failed to dlsym, %s\n", dlerror()); + dlclose(handle); + continue; + } + + fail += run_test_suite(); + } + + closedir(dir); + + return fail; +} diff --git a/tests/test-main.h b/tests/test-main.h new file mode 100644 index 0000000..4b04a85 --- /dev/null +++ b/tests/test-main.h @@ -0,0 +1,10 @@ +#ifndef __TEST_MAIN_H__ +#define __TEST_MAIN_H__ + +#include +#include +#include +#include + +#endif //__TEST_MAIN_H__ + diff --git a/tests/test-mock.c b/tests/test-mock.c new file mode 100644 index 0000000..4e7f260 --- /dev/null +++ b/tests/test-mock.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +#include "test-main.h" +#include "test-mock.h" + +//static FILE *__fp = NULL; + +int __wrap_open(const char *pathname, int flags) +{ + const char *mock_pathname; + int mock_flags; + + mock_pathname = mock_ptr_type(char *); + if (mock_pathname == NULL) { + errno = -ENOENT; + return -1; + } + + mock_flags = mock_type(int); + + return open(mock_pathname, mock_flags); +} + +int __wrap_open64(const char *pathname, int flags) +{ + return __wrap_open(pathname, flags); +} + + +void mock_open(const char *pathname, int flags) +{ + will_return(__wrap_open, pathname); + will_return(__wrap_open, flags); +} + +/* +FILE *__wrap_fopen(const char *pathname, const char *mode) +{ + const char *mock_pathname = mock_ptr_type(char *); + const char *mock_mode = mock_ptr_type(char *); + + __fp = fopen(mock_pathname, mock_mode); + + return __fp; +} + +FILE *__wrap_fopen64(const char *pathname, const char *mode) +{ + return __wrap_fopen(pathname, mode); + +} + +int __wrap_fclose(FILE *stream) +{ + return 0; +} + +int __wrap_fclose64(FILE *stream) +{ + return __wrap_fclose(stream); +} +*/ diff --git a/tests/test-mock.h b/tests/test-mock.h new file mode 100644 index 0000000..22c7196 --- /dev/null +++ b/tests/test-mock.h @@ -0,0 +1,8 @@ +#ifndef __TEST_MOCK_H__ +#define __TEST_MOCK_H__ + +#include + +void mock_open(const char *pathname, int flags); + +#endif //__TEST_MOCK_H__ -- 2.34.1