New unit testing skeleton
o9000 mrovi9000@gmail.com
11 files changed,
407 insertions(+),
7 deletions(-)
M
.gitignore
→
.gitignore
@@ -4,3 +4,4 @@ version.h
*.pyc *.todo packaging/make_ubuntu2.sh +test_*.log
M
CMakeLists.txt
→
CMakeLists.txt
@@ -146,7 +146,9 @@ src/util/strnatcmp.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 @@
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 )
M
src/init.c
→
src/init.c
@@ -17,6 +17,7 @@ #include "fps_distribution.h"
#include "panel.h" #include "server.h" #include "signals.h" +#include "test.h" #include "tooltip.h" #include "tracing.h" #include "uevent.h"@@ -47,6 +48,9 @@ print_usage();
exit(0); } 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) {
A
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
A
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
M
src/util/common.h
→
src/util/common.h
@@ -12,12 +12,7 @@ #include <glib.h>
#include <Imlib2.h> #include <pango/pangocairo.h> #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)
A
src/util/print.c
@@ -0,0 +1,83 @@
+#include <stdio.h> + +#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)"); +}
A
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
A
src/util/test.c
@@ -0,0 +1,153 @@
+#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <glib.h> + +#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); +}
A
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
M
tint2.files
→
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