From 58e030de5d4ad86c8ee0d9342d3a1d862f37e406 Mon Sep 17 00:00:00 2001 From: o9000 Date: Tue, 19 Dec 2017 15:28:57 +0100 Subject: [PATCH] New unit testing skeleton --- .gitignore | 1 + CMakeLists.txt | 4 +- src/init.c | 4 ++ src/util/bool.h | 17 ++++++ src/util/colors.h | 10 +++ src/util/common.h | 7 +-- src/util/print.c | 83 +++++++++++++++++++++++++ src/util/print.h | 55 +++++++++++++++++ src/util/test.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/test.h | 72 ++++++++++++++++++++++ tint2.files | 8 +++ 11 files changed, 407 insertions(+), 7 deletions(-) create mode 100644 src/util/bool.h create mode 100644 src/util/colors.h create mode 100644 src/util/print.c create mode 100644 src/util/print.h create mode 100644 src/util/test.c create mode 100644 src/util/test.h diff --git a/.gitignore b/.gitignore index e44275d..c38ff86 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ version.h *.pyc *.todo packaging/make_ubuntu2.sh +test_*.log diff --git a/CMakeLists.txt b/CMakeLists.txt index 9da6ed7..4a927b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,9 @@ set( SOURCES src/config.c src/util/timer.c src/util/cache.c src/util/color.c + src/util/print.c src/util/gradient.c + src/util/test.c src/util/uevent.c src/util/window.c ) @@ -267,7 +269,7 @@ endif( RT_LIBRARY ) target_link_libraries( tint2 m ) add_dependencies( tint2 version ) -set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" ) +set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c11 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" ) set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" ) install( TARGETS tint2 DESTINATION bin ) diff --git a/src/init.c b/src/init.c index 00433e3..13d5671 100644 --- a/src/init.c +++ b/src/init.c @@ -17,6 +17,7 @@ #include "panel.h" #include "server.h" #include "signals.h" +#include "test.h" #include "tooltip.h" #include "tracing.h" #include "uevent.h" @@ -48,6 +49,9 @@ void handle_cli_arguments(int argc, char **argv) } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) { fprintf(stdout, "tint2 version %s\n", VERSION_STRING); exit(0); + } else if (strcmp(argv[i], "--test") == 0) { + run_all_tests(); + exit(0); } else if (strcmp(argv[i], "-c") == 0) { if (i + 1 < argc) { i++; diff --git a/src/util/bool.h b/src/util/bool.h new file mode 100644 index 0000000..dc86d4f --- /dev/null +++ b/src/util/bool.h @@ -0,0 +1,17 @@ +#ifndef BOOL_H +#define BOOL_H + +#ifndef bool +#define bool int +#define false 0 +#define true 1 +#endif + +#define SUCCESS true +#define FAILURE false + +#ifndef Status +typedef int Status; +#endif + +#endif diff --git a/src/util/colors.h b/src/util/colors.h new file mode 100644 index 0000000..f615dff --- /dev/null +++ b/src/util/colors.h @@ -0,0 +1,10 @@ +#ifndef COLORS_H +#define COLORS_H + +#define GREEN "\033[1;32m" +#define YELLOW "\033[1;33m" +#define RED "\033[1;31m" +#define BLUE "\033[1;34m" +#define RESET "\033[0m" + +#endif diff --git a/src/util/common.h b/src/util/common.h index c916288..c29cfb5 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -12,12 +12,7 @@ #include #include #include "area.h" - -#define GREEN "\033[1;32m" -#define YELLOW "\033[1;33m" -#define RED "\033[1;31m" -#define BLUE "\033[1;34m" -#define RESET "\033[0m" +#include "colors.h" #define MAX3(a, b, c) MAX(MAX(a, b), c) #define MIN3(a, b, c) MIN(MIN(a, b), c) diff --git a/src/util/print.c b/src/util/print.c new file mode 100644 index 0000000..25ae1bf --- /dev/null +++ b/src/util/print.c @@ -0,0 +1,83 @@ +#include + +#include "print.h" + +int print_uchar(unsigned char v) +{ + return printf("%u", v); +} + +int print_char(char v) +{ + return printf("%c", v); +} + +int print_short(short v) +{ + return printf("%d", v); +} + +int print_ushort(unsigned short v) +{ + return printf("%u", v); +} + +int print_int(int v) +{ + return printf("%d", v); +} + +int print_uint(unsigned v) +{ + return printf("%u", v); +} + +int print_long(long v) +{ + return printf("%ld", v); +} + +int print_ulong(unsigned long v) +{ + return printf("%lu", v); +} + +int print_long_long(long long v) +{ + return printf("%lld", v); +} + +int print_ulong_long(unsigned long long v) +{ + return printf("%llu", v); +} + +int print_float(float v) +{ + return printf("%f", (double)v); +} + +int print_double(double v) +{ + return printf("%f", v); +} + +int print_long_double(long double v) +{ + return printf("%Lf", v); +} + +int print_string(char *s) +{ + return printf("%s", s); +} + +int print_pointer(void *v) +{ + return printf("%p", v); +} + +int print_unknown() +{ + return printf("(variable of unknown type)"); +} diff --git a/src/util/print.h b/src/util/print.h new file mode 100644 index 0000000..52ac060 --- /dev/null +++ b/src/util/print.h @@ -0,0 +1,55 @@ +#ifndef PRINT_H +#define PRINT_H + +int print_uchar(unsigned char v); + +int print_char(char v); + +int print_short(short v); + +int print_ushort(unsigned short v); + +int print_int(int v); + +int print_uint(unsigned v); + +int print_long(long v); + +int print_ulong(unsigned long v); + +int print_long_long(long long v); + +int print_ulong_long(unsigned long long v); + +int print_float(float v); + +int print_double(double v); + +int print_long_double(long double v); + +int print_string(char *s); + +int print_pointer(void *v); + +int print_unknown(); + +#define print(x) \ + _Generic((x), \ + unsigned char: print_uchar, \ + char: print_char, \ + short int: print_short, \ + unsigned short int: print_ushort, \ + int: print_int, \ + unsigned int: print_uint, \ + long int: print_long, \ + unsigned long int: print_ulong, \ + long long int: print_long_long, \ + unsigned long long int: print_ulong_long, \ + float: print_float, \ + double: print_double, \ + long double: print_long_double, \ + char *: print_string, \ + void *: print_pointer, \ + default : print_unknown)(x) + +#endif diff --git a/src/util/test.c b/src/util/test.c new file mode 100644 index 0000000..7720764 --- /dev/null +++ b/src/util/test.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "colors.h" +#include "signals.h" +#include "test.h" + +typedef struct TestListItem { + Test *test; + const char *name; +} TestListItem; + +static GList *all_tests = NULL; + +void register_test_(Test *test, const char *name) +{ + TestListItem *item = (TestListItem *)calloc(sizeof(TestListItem), 1); + item->test = test; + item->name = name; + all_tests = g_list_append(all_tests, item); +} + +static char *test_log_name_from_test_name(const char *test_name) +{ + char *output_name = g_strdup_printf("test_%s.log", test_name); + char *result = strdup(output_name); + g_free(output_name); + return result; +} + +static void redirect_test_output(const char *test_name) +{ + char *output_name = test_log_name_from_test_name(test_name); + int fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + goto err; + if (dup2(fd, STDOUT_FILENO) == -1) + goto err; + if (dup2(fd, STDERR_FILENO) == -1) + goto err; + + close(fd); + free(output_name); + return; +err: + fprintf(stderr, "tint2: Could not redirect test output to file name: %s\n", output_name); + if (fd != -1) + close(fd); + free(output_name); +} + +__attribute__((noreturn)) +static void run_test_child(TestListItem *item) +{ + reset_signals(); + redirect_test_output(item->name); + bool result = true; + item->test(&result); + exit(result ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static FILE *open_test_log(const char *test_name) +{ + char *output_name = test_log_name_from_test_name(test_name); + FILE *log = fopen(output_name, "a"); + free(output_name); + return log; +} + +static Status run_test_parent(TestListItem *item, pid_t child) +{ + FILE *log = open_test_log(item->name); + if (child == -1) { + fprintf(log, "\n" "Test failed, fork failed\n"); + fclose(log); + return FAILURE; + } + + int child_status; + pid_t ret_pid = waitpid(child, &child_status, 0); + if (ret_pid != child) { + fprintf(log, "\n" "Test failed, waitpid failed\n"); + fclose(log); + return FAILURE; + } + if (WIFEXITED(child_status)) { + int exit_status = WEXITSTATUS(child_status); + if (exit_status == EXIT_SUCCESS) { + fprintf(log, "\n" "Test succeeded.\n"); + fclose(log); + return SUCCESS; + } else { + fprintf(log, "\n" "Test failed, exit status: %d.\n", exit_status); + fclose(log); + return FAILURE; + } + } else if (WIFSIGNALED(child_status)) { + int signal = WTERMSIG(child_status); + fprintf(log, "\n" "Test failed, child killed by signal: %d.\n", signal); + fclose(log); + return FAILURE; + } else { + fprintf(log, "\n" "Test failed, waitpid failed.\n"); + fclose(log); + return FAILURE; + } +} + +static Status run_test(TestListItem *item) +{ + pid_t pid = fork(); + if (pid == 0) + run_test_child(item); + return run_test_parent(item, pid); +} + +void run_all_tests() +{ + fprintf(stdout, BLUE "tint2: Running %d tests..." RESET "\n", g_list_length(all_tests)); + size_t count = 0, succeeded = 0, failed = 0; + for (GList *l = all_tests; l; l = l->next) { + TestListItem *item = (TestListItem *)l->data; + Status status = run_test(item); + count++; + fprintf(stdout, BLUE "tint2: Test " YELLOW "%s" BLUE ": ", item->name); + if (status == SUCCESS) { + fprintf(stdout, GREEN "succeeded" RESET "\n"); + succeeded++; + } else { + fprintf(stdout, RED "failed" RESET "\n"); + failed++; + } + } + if (failed == 0) + fprintf(stdout, BLUE "tint2: " GREEN "all %lu tests succeeded." RESET "\n", count); + else + fprintf(stdout, BLUE "tint2: " RED "%lu" BLUE " out of %lu tests " RED "failed." RESET "\n", failed, count); +} + +TEST(dummy_bad) { + int x = 2; + int y = 3; + ASSERT_EQUAL(x, y); +} diff --git a/src/util/test.h b/src/util/test.h new file mode 100644 index 0000000..4f3862e --- /dev/null +++ b/src/util/test.h @@ -0,0 +1,72 @@ +#ifndef TEST_H +#define TEST_H + +#include "bool.h" +#include "print.h" + +typedef void Test(Status *test_result_); + +void register_test_(Test *test, const char *name); + +#define TEST(name) \ + void test_##name(Status *test_result_); \ + __attribute__((constructor)) void test_register_##name() \ + { \ + register_test_(test_##name, #name); \ + } \ + void test_##name(Status *test_result_) + +void run_all_tests(); + +#define FAIL_TEST_ \ + *test_result_ = FAILURE; \ + return; + +#define ASSERT(value) \ + if (!(value)) { \ + FAIL_TEST_ \ + } + +#define ASSERT_EQUAL(a, b) \ + if (!(a == b)) { \ + printf("Assertion failed: %s == %s: ", #a, #b); \ + print(a); \ + printf(" != "); \ + print(b); \ + FAIL_TEST_ \ + } + +#define ASSERT_DIFFERENT(a, b) \ + if (a == b) { \ + printf("Assertion failed: %s != %s: ", #a, #b); \ + print(a); \ + printf(" == "); \ + print(b); \ + FAIL_TEST_ \ + } + + +#define ASSERT_STR_EQUAL(a, b) \ + if (strcmp(a, b) != 0) { \ + printf("Assertion failed: %s == %s: ", #a, #b); \ + print(a); \ + printf(" != "); \ + print(b); \ + FAIL_TEST_ \ + } + +#define ASSERT_STR_DIFFERENT(a, b) \ + if (strcmp(a, b) == 0) { \ + printf("Assertion failed: %s != %s: ", #a, #b); \ + print(a); \ + printf(" == "); \ + print(b); \ + FAIL_TEST_ \ + } + +#define ASSERT_TRUE(value) ASSERT_EQUAL(value, 1) +#define ASSERT_FALSE(value) ASSERT_EQUAL(value, 0) +#define ASSERT_NULL(value) ASSERT_EQUAL(value, NULL) +#define ASSERT_NON_NULL(value) ASSERT_DIFFERENT(value, NULL) + +#endif diff --git a/tint2.files b/tint2.files index aafafc8..5a714e2 100644 --- a/tint2.files +++ b/tint2.files @@ -237,3 +237,11 @@ src/signals.c src/signals.h src/tracing.c src/tracing.h +src/util/test.c +src/util/test.h +src/util/bool.h +src/util/colors.h +src/util/print.c +src/util/print.h +src/util/test.c +src/util/test.h