diff --git a/Makefile b/Makefile index 914c089..e52bb1f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ -include config.mk -LIB_ARCHIVE := $(BUILD_DIR)/lib$(LIB).a +LIB := $(BUILD_DIR)/$(LIB_FILENAME) SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find include -iname '*.h' @@ -17,38 +17,54 @@ OBJS_EXAMPLE := $(SRCS_EXAMPLE:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) -BIN_TEST := $(BUILD_DIR)/$(TEST_DIR)/runner +BINS_TEST := $(OBJS_TEST:%.c.o=%) BINS_EXAMPLE := $(OBJS_EXAMPLE:%.c.o=%) +TARGETS_TEST := $(BINS_TEST:%=test-%) +TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%) +TARGETS_EXAMPLE := $(BINS_EXAMPLE:%=example-%) + _CFLAGS := $(addprefix -I,$(INC_DIRS)) $(CFLAGS) -Wall -Wextra +.PHONY: all +all: lib + + # =====COMPILATION===== # Utility used by the CI to lint -.PHONY: lib -$(LIB_ARCHIVE): $(OBJS) - ar -rcs $@ $^ - .PHONY: objs objs: $(OBJS) +.PHONY: lib +lib: $(LIB) +$(LIB): $(OBJS) + ar -rcs $@ $(OBJS) + $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) -c $(_CFLAGS) $< -o $@ # =====TESTING===== .PHONY: test -test: $(BIN_TEST) - './$^' +test: $(TARGETS_TEST) .PHONY: test-mem -test-mem: $(BIN_TEST) - valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full './$^' +test-mem: $(TARGETS_MEM_TEST) + +.PHONY: $(TARGETS_TEST) +$(TARGETS_TEST): test-%: % + ./$^ + +.PHONY: $(TARGETS_MEM_TEST) +$(TARGETS_MEM_TEST): test-mem-%: % + valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./$^ .PHONY: build-test -build-test: $(BIN_TEST) +build-test: $(BINS_TEST) -$(BIN_TEST): $(OBJS_TEST) $(LIB_ARCHIVE) - $(CC) -o $@ $^ $(_LDFLAGS) +$(BINS_TEST): %: %.c.o $(LIB) + $(CC) \ + $^ -o $@ # Along with the include directory, each test includes $(TEST_DIR) (which # contains the acutest.h header file), and the src directory of the module it's @@ -64,8 +80,8 @@ $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c .PHONY: build-example build-example: $(BINS_EXAMPLE) -$(BINS_EXAMPLE): %: %.c.o $(LIB_ARCHIVE) - $(CC) $(LDFLAGS) \ +$(BINS_EXAMPLE): %: %.c.o $(LIB) + $(CC) \ $^ -o $@ # Example binaries link the resulting library @@ -76,22 +92,22 @@ $(BUILD_DIR)/$(EXAMPLE_DIR)/%.c.o: $(EXAMPLE_DIR)/%.c # =====MAINTENANCE===== .PHONY: lint lint: - @ clang-format -n --Werror \ - $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') \ - $(shell find '$(SRC_DIR)/_include/$(LIB)' -iname '*.h') \ - $(shell find 'include/$(LIB)' -iname '*.h') + clang-format -n --Werror \ + $(filter-out $(THIRDPARTY),$(SRCS)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H_INTERNAL)) .PHONY: fmt fmt: - @ clang-format -i \ - $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') \ - $(shell find '$(SRC_DIR)/_include/$(LIB)' -iname '*.h') \ - $(shell find 'include/$(LIB)' -iname '*.h') + clang-format -i \ + $(filter-out $(THIRDPARTY),$(SRCS)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H_INTERNAL)) .PHONY: check check: - @ mkdir -p $(BUILD_DIR)/cppcheck - @ cppcheck \ + mkdir -p $(BUILD_DIR)/cppcheck + cppcheck \ $(addprefix -I,$(INC_DIRS)) \ --cppcheck-build-dir=$(BUILD_DIR)/cppcheck \ --error-exitcode=1 \ @@ -100,7 +116,7 @@ check: --check-level=exhaustive \ --quiet \ -j$(shell nproc) \ - $(shell find '$(SRC_DIR)/$(LIB)' -iname '*.c') + $(filter-out $(THIRDPARTY),$(SRCS)) .PHONY: clean clean: diff --git a/config.mk b/config.mk index 96d73b9..4345382 100644 --- a/config.mk +++ b/config.mk @@ -1,9 +1,10 @@ -LIB = lnm +LIB_FILENAME = liblnm.a BUILD_DIR = build SRC_DIR = src TEST_DIR = test EXAMPLE_DIR = example +THIRDPARTY = src/picohttpparser.c include/picohttpparser.h PUB_INC_DIR = include INC_DIRS = $(PUB_INC_DIR) src/_include diff --git a/example/throughput.c b/example/throughput.c deleted file mode 100644 index b2704a8..0000000 --- a/example/throughput.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "lnm/http/res.h" -#include "lnm/log.h" -#include "lnm/http/loop.h" -#include "lnm/loop.h" - -lnm_err ctx_init(void **c_ctx, void *gctx) { - *c_ctx = NULL; - - return lnm_err_ok; -} - -void ctx_reset(void *c_ctx) {} -void ctx_free(void *c_ctx) {} - -lnm_err data_streamer(uint64_t *written, char *buf, - lnm_http_conn *conn, uint64_t offset, - uint64_t len) { - // Don't do anything, just let the application return random data stored in - // the read buffer. The goal is to benchmark the networking pipeline - *written = len; - - return lnm_err_ok; -} - -lnm_http_step_err step_fn(lnm_http_conn *conn) { - lnm_http_loop_ctx *ctx = conn->ctx; - uint64_t len = 1 << 30; - lnm_http_res_body_set_fn(&ctx->res, data_streamer, len); - - return lnm_http_step_err_done; -} - -int main() { - lnm_http_loop *hl; - - lnm_http_loop_init(&hl, NULL, ctx_init, - ctx_reset, - ctx_free); - - lnm_http_router *router; - lnm_http_router_init(&router); - - lnm_http_route *route; - lnm_http_router_add(&route, router, lnm_http_method_get, "/"); - lnm_http_route_step_append(route, step_fn, false); - - lnm_http_loop_router_set(hl, router); - - lnm_log_init_global(); - lnm_log_register_stdout(lnm_log_level_warning); - - printf("res = %i\n", lnm_http_loop_run(hl, 8080, 1, 0)); -} diff --git a/include/lnm/http/req.h b/include/lnm/http/req.h index 146fb81..1c32972 100644 --- a/include/lnm/http/req.h +++ b/include/lnm/http/req.h @@ -40,6 +40,7 @@ typedef struct lnm_http_req { struct { size_t o; size_t len; + regmatch_t groups[LNM_HTTP_MAX_REGEX_GROUPS]; } path; struct { size_t o; diff --git a/src/_include/picohttpparser.h b/include/picohttpparser.h similarity index 100% rename from src/_include/picohttpparser.h rename to include/picohttpparser.h diff --git a/src/lnm/http/lnm_http_consts.c b/src/http/lnm_http_consts.c similarity index 100% rename from src/lnm/http/lnm_http_consts.c rename to src/http/lnm_http_consts.c diff --git a/src/lnm/http/lnm_http_loop.c b/src/http/lnm_http_loop.c similarity index 100% rename from src/lnm/http/lnm_http_loop.c rename to src/http/lnm_http_loop.c diff --git a/src/lnm/http/lnm_http_loop_ctx.c b/src/http/lnm_http_loop_ctx.c similarity index 100% rename from src/lnm/http/lnm_http_loop_ctx.c rename to src/http/lnm_http_loop_ctx.c diff --git a/src/lnm/http/lnm_http_loop_process.c b/src/http/lnm_http_loop_process.c similarity index 99% rename from src/lnm/http/lnm_http_loop_process.c rename to src/http/lnm_http_loop_process.c index 4f6871f..ea690ce 100644 --- a/src/lnm/http/lnm_http_loop_process.c +++ b/src/http/lnm_http_loop_process.c @@ -8,6 +8,7 @@ #include "lnm/http/req.h" #include "lnm/log.h" #include "lnm/loop.h" +#include "lnm/loop_internal.h" static const char *section = "http"; diff --git a/src/lnm/http/lnm_http_loop_steps.c b/src/http/lnm_http_loop_steps.c similarity index 100% rename from src/lnm/http/lnm_http_loop_steps.c rename to src/http/lnm_http_loop_steps.c diff --git a/src/lnm/http/lnm_http_req.c b/src/http/lnm_http_req.c similarity index 100% rename from src/lnm/http/lnm_http_req.c rename to src/http/lnm_http_req.c diff --git a/src/lnm/http/lnm_http_res.c b/src/http/lnm_http_res.c similarity index 100% rename from src/lnm/http/lnm_http_res.c rename to src/http/lnm_http_res.c diff --git a/src/lnm/http/lnm_http_route.c b/src/http/lnm_http_route.c similarity index 100% rename from src/lnm/http/lnm_http_route.c rename to src/http/lnm_http_route.c diff --git a/src/lnm/http/lnm_http_router.c b/src/http/lnm_http_router.c similarity index 100% rename from src/lnm/http/lnm_http_router.c rename to src/http/lnm_http_router.c diff --git a/src/lnm/lnm_log.c b/src/lnm_log.c similarity index 97% rename from src/lnm/lnm_log.c rename to src/lnm_log.c index c27a876..15fb00f 100644 --- a/src/lnm/lnm_log.c +++ b/src/lnm_log.c @@ -65,9 +65,6 @@ void lnm_vlog(lnm_log_level level, const char *section, const char *fmt, continue; } - va_list aq; - va_copy(aq, ap); - switch (stream->type) { case lnm_logger_stream_type_file: fprintf(stream->ptr, "[%s][%s][%s] ", date_str, @@ -77,7 +74,7 @@ void lnm_vlog(lnm_log_level level, const char *section, const char *fmt, break; } - va_end(aq); + va_end(ap); } } diff --git a/src/lnm/lnm_utils.c b/src/lnm_utils.c similarity index 95% rename from src/lnm/lnm_utils.c rename to src/lnm_utils.c index 47989bf..849c31f 100644 --- a/src/lnm/lnm_utils.c +++ b/src/lnm_utils.c @@ -59,8 +59,8 @@ uint64_t lnm_digits(uint64_t num) { bool lnm_is_ascii(const char *s) { bool valid = true; - while (valid && (*s != '\0')) { - valid = *s >= 0; + while (valid && *s != '\0') { + valid = *s < 0; s++; } diff --git a/src/lnm/loop/lnm_loop.c b/src/loop/lnm_loop.c similarity index 100% rename from src/lnm/loop/lnm_loop.c rename to src/loop/lnm_loop.c diff --git a/src/lnm/loop/lnm_loop_conn.c b/src/loop/lnm_loop_conn.c similarity index 100% rename from src/lnm/loop/lnm_loop_conn.c rename to src/loop/lnm_loop_conn.c diff --git a/src/lnm/loop/lnm_loop_io.c b/src/loop/lnm_loop_io.c similarity index 100% rename from src/lnm/loop/lnm_loop_io.c rename to src/loop/lnm_loop_io.c diff --git a/src/lnm/loop/lnm_loop_worker.c b/src/loop/lnm_loop_worker.c similarity index 100% rename from src/lnm/loop/lnm_loop_worker.c rename to src/loop/lnm_loop_worker.c diff --git a/test/lnm/routing.c b/test/routing.c similarity index 94% rename from test/lnm/routing.c rename to test/routing.c index 921dc81..827d4c5 100644 --- a/test/lnm/routing.c +++ b/test/routing.c @@ -1,9 +1,8 @@ -#define TEST_NO_MAIN -#include "acutest.h" -#include "tests.h" - #include "lnm/common.h" #include "lnm/http/route.h" +#include "test.h" + +#include "lnm/http/loop.h" void test_routing_simple() { lnm_http_router *router; @@ -102,3 +101,11 @@ void test_routing_nest() { lnm_http_router_free(r2); } + +TEST_LIST = { + { "routing simple", test_routing_simple }, + { "routing star", test_routing_star }, + { "routing merge", test_routing_merge }, + { "routing nest", test_routing_nest }, + { NULL, NULL } +}; diff --git a/test/runner.c b/test/runner.c deleted file mode 100644 index 9fe31c0..0000000 --- a/test/runner.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "acutest.h" -#include "tests.h" - -TEST_LIST = { - { "routing simple", test_routing_simple }, - { "routing star", test_routing_star }, - { "routing merge", test_routing_merge }, - { "routing nest", test_routing_nest }, - { NULL, NULL } -}; diff --git a/test/acutest.h b/test/test.h similarity index 71% rename from test/acutest.h rename to test/test.h index 5f9cb19..9ab8f88 100644 --- a/test/acutest.h +++ b/test/test.h @@ -2,7 +2,7 @@ * Acutest -- Another C/C++ Unit Test facility * * - * Copyright 2013-2023 Martin Mitáš + * Copyright 2013-2020 Martin Mitas * Copyright 2019 Garrett D'Amore * * Permission is hereby granted, free of charge, to any person obtaining a @@ -28,19 +28,6 @@ #define ACUTEST_H -/* Try to auto-detect whether we need to disable C++ exception handling. - * If the detection fails, you may always define TEST_NO_EXCEPTIONS before - * including "acutest.h" manually. */ -#ifdef __cplusplus - #if (__cplusplus >= 199711L && !defined __cpp_exceptions) || \ - ((defined(__GNUC__) || defined(__clang__)) && !defined __EXCEPTIONS) - #ifndef TEST_NO_EXCEPTIONS - #define TEST_NO_EXCEPTIONS - #endif - #endif -#endif - - /************************ *** Public interface *** ************************/ @@ -94,10 +81,8 @@ * TEST_CHECK(ptr->member2 > 200); * } */ -#define TEST_CHECK_(cond,...) \ - acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__) -#define TEST_CHECK(cond) \ - acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond) +#define TEST_CHECK_(cond,...) acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__) +#define TEST_CHECK(cond) acutest_check_((cond), __FILE__, __LINE__, "%s", #cond) /* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the @@ -117,18 +102,17 @@ */ #define TEST_ASSERT_(cond,...) \ do { \ - if(!acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__)) \ + if(!acutest_check_((cond), __FILE__, __LINE__, __VA_ARGS__)) \ acutest_abort_(); \ } while(0) #define TEST_ASSERT(cond) \ do { \ - if(!acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond)) \ + if(!acutest_check_((cond), __FILE__, __LINE__, "%s", #cond)) \ acutest_abort_(); \ } while(0) #ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS /* Macros to verify that the code (the 1st argument) throws exception of given * type (the 2nd argument). (Note these macros are only available in C++.) * @@ -175,7 +159,6 @@ if(msg_ != NULL) \ acutest_message_("%s", msg_); \ } while(0) -#endif /* #ifndef TEST_NO_EXCEPTIONS */ #endif /* #ifdef __cplusplus */ @@ -205,7 +188,7 @@ * You may define another limit prior including "acutest.h" */ #ifndef TEST_CASE_MAXSIZE - #define TEST_CASE_MAXSIZE 64 +#define TEST_CASE_MAXSIZE 64 #endif @@ -237,7 +220,7 @@ * You may define another limit prior including "acutest.h" */ #ifndef TEST_MSG_MAXSIZE - #define TEST_MSG_MAXSIZE 1024 +#define TEST_MSG_MAXSIZE 1024 #endif @@ -258,21 +241,11 @@ * You may define another limit prior including "acutest.h" */ #ifndef TEST_DUMP_MAXSIZE - #define TEST_DUMP_MAXSIZE 1024 +#define TEST_DUMP_MAXSIZE 1024 #endif -/* Macros for marking the test as SKIPPED. - * Note it can only be used at the beginning of a test, before any other - * checking. - * - * Once used, the best practice is to return from the test routine as soon - * as possible. - */ -#define TEST_SKIP(...) acutest_skip_(__FILE__, __LINE__, __VA_ARGS__) - - -/* Common test initialisation/clean-up +/* Common test initialiation/clean-up * * In some test suites, it may be needed to perform some sort of the same * initialization and/or clean-up in all the tests. @@ -299,77 +272,42 @@ /* The unit test files should not rely on anything below. */ -#include - -/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */ -#if defined(__GNUC__) || defined(__clang__) - #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr)) -#else - #define ACUTEST_ATTRIBUTE_(attr) -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -enum acutest_state_ { - ACUTEST_STATE_INITIAL = -4, - ACUTEST_STATE_SELECTED = -3, - ACUTEST_STATE_NEEDTORUN = -2, - - /* By the end all tests should be in one of the following: */ - ACUTEST_STATE_EXCLUDED = -1, - ACUTEST_STATE_SUCCESS = 0, - ACUTEST_STATE_FAILED = 1, - ACUTEST_STATE_SKIPPED = 2 -}; - -int acutest_check_(int cond, const char* file, int line, const char* fmt, ...); -void acutest_case_(const char* fmt, ...); -void acutest_message_(const char* fmt, ...); -void acutest_dump_(const char* title, const void* addr, size_t size); -void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); -#ifdef __cplusplus - } /* extern "C" */ -#endif - -#ifndef TEST_NO_MAIN - #include #include #include +#include #include #include #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) - #define ACUTEST_UNIX_ 1 - #include - #include - #include - #include - #include - #include - #include +#define ACUTEST_UNIX_ 1 +#include +#include +#include +#include +#include +#include +#include - #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC - #define ACUTEST_HAS_POSIX_TIMER_ 1 - #endif +#if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC +#define ACUTEST_HAS_POSIX_TIMER_ 1 +#endif #endif #if defined(_gnu_linux_) || defined(__linux__) - #define ACUTEST_LINUX_ 1 - #include - #include +#define ACUTEST_LINUX_ 1 +#include +#include #endif #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) - #define ACUTEST_WIN_ 1 +#define ACUTEST_WIN_ 1 #include #include #endif #if defined(__APPLE__) - #define ACUTEST_MACOS_ +#define ACUTEST_MACOS_ #include #include #include @@ -378,26 +316,31 @@ void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); #endif #ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS - #include -#endif +#include #endif #ifdef __has_include - #if __has_include() - #include - #endif +#if __has_include() +#include +#endif +#endif + +/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */ +#if defined(__GNUC__) || defined(__clang__) +#define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr)) +#else +#define ACUTEST_ATTRIBUTE_(attr) #endif /* Note our global private identifiers end with '_' to mitigate risk of clash * with the unit tests implementation. */ #ifdef __cplusplus - extern "C" { +extern "C" { #endif #ifdef _MSC_VER - /* In the multi-platform code like ours, we cannot use the non-standard +/* In the multi-platform code like ours, we cannot use the non-standard * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of * standard sprintf(). Hence, lets disable the warning C4996. */ #pragma warning(push) @@ -411,32 +354,47 @@ struct acutest_test_ { }; struct acutest_test_data_ { - enum acutest_state_ state; + unsigned char flags; double duration; }; +enum { + ACUTEST_FLAG_RUN_ = 1 << 0, + ACUTEST_FLAG_SUCCESS_ = 1 << 1, + ACUTEST_FLAG_FAILURE_ = 1 << 2, +}; extern const struct acutest_test_ acutest_list_[]; +int acutest_check_(int cond, const char* file, int line, const char* fmt, ...); +void acutest_case_(const char* fmt, ...); +void acutest_message_(const char* fmt, ...); +void acutest_dump_(const char* title, const void* addr, size_t size); +void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); + + +#ifndef TEST_NO_MAIN static char* acutest_argv0_ = NULL; -static int acutest_list_size_ = 0; +static size_t acutest_list_size_ = 0; static struct acutest_test_data_* acutest_test_data_ = NULL; +static size_t acutest_count_ = 0; static int acutest_no_exec_ = -1; static int acutest_no_summary_ = 0; static int acutest_tap_ = 0; -static int acutest_exclude_mode_ = 0; +static int acutest_skip_mode_ = 0; static int acutest_worker_ = 0; static int acutest_worker_index_ = 0; static int acutest_cond_failed_ = 0; +static int acutest_was_aborted_ = 0; static FILE *acutest_xml_output_ = NULL; +static int acutest_stat_failed_units_ = 0; +static int acutest_stat_run_units_ = 0; + static const struct acutest_test_* acutest_current_test_ = NULL; static int acutest_current_index_ = 0; static char acutest_case_name_[TEST_CASE_MAXSIZE] = ""; -static int acutest_test_check_count_ = 0; -static int acutest_test_skip_count_ = 0; -static char acutest_test_skip_reason_[256] = ""; static int acutest_test_already_logged_ = 0; static int acutest_case_already_logged_ = 0; static int acutest_verbose_level_ = 2; @@ -447,18 +405,6 @@ static int acutest_timer_ = 0; static int acutest_abort_has_jmp_buf_ = 0; static jmp_buf acutest_abort_jmp_buf_; -static int -acutest_count_(enum acutest_state_ state) -{ - int i, n; - - for(i = 0, n = 0; i < acutest_list_size_; i++) { - if(acutest_test_data_[i].state == state) - n++; - } - - return n; -} static void acutest_cleanup_(void) @@ -473,9 +419,8 @@ acutest_exit_(int exit_code) exit(exit_code); } - #if defined ACUTEST_WIN_ - typedef LARGE_INTEGER acutest_timer_type_; +typedef LARGE_INTEGER acutest_timer_type_; static LARGE_INTEGER acutest_timer_freq_; static acutest_timer_type_ acutest_timer_start_; static acutest_timer_type_ acutest_timer_end_; @@ -506,40 +451,51 @@ acutest_exit_(int exit_code) printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); } #elif defined ACUTEST_HAS_POSIX_TIMER_ - static clockid_t acutest_timer_id_; - typedef struct timespec acutest_timer_type_; - static acutest_timer_type_ acutest_timer_start_; - static acutest_timer_type_ acutest_timer_end_; +static clockid_t acutest_timer_id_; +typedef struct timespec acutest_timer_type_; +static acutest_timer_type_ acutest_timer_start_; +static acutest_timer_type_ acutest_timer_end_; - static void - acutest_timer_init_(void) - { - if(acutest_timer_ == 1) - acutest_timer_id_ = CLOCK_MONOTONIC; - else if(acutest_timer_ == 2) - acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID; - } +static void +acutest_timer_init_(void) +{ + if(acutest_timer_ == 1) + acutest_timer_id_ = CLOCK_MONOTONIC; + else if(acutest_timer_ == 2) + acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID; +} - static void - acutest_timer_get_time_(struct timespec* ts) - { - clock_gettime(acutest_timer_id_, ts); - } +static void +acutest_timer_get_time_(struct timespec* ts) +{ + clock_gettime(acutest_timer_id_, ts); +} - static double - acutest_timer_diff_(struct timespec start, struct timespec end) - { - return (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1e9; - } +static double +acutest_timer_diff_(struct timespec start, struct timespec end) +{ + double endns; + double startns; - static void - acutest_timer_print_diff_(void) - { - printf("%.6lf secs", - acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); - } + endns = end.tv_sec; + endns *= 1e9; + endns += end.tv_nsec; + + startns = start.tv_sec; + startns *= 1e9; + startns += start.tv_nsec; + + return ((endns - startns)/ 1e9); +} + +static void +acutest_timer_print_diff_(void) +{ + printf("%.6lf secs", + acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); +} #else - typedef int acutest_timer_type_; +typedef int acutest_timer_type_; static acutest_timer_type_ acutest_timer_start_; static acutest_timer_type_ acutest_timer_end_; @@ -567,13 +523,11 @@ acutest_exit_(int exit_code) #endif #define ACUTEST_COLOR_DEFAULT_ 0 -#define ACUTEST_COLOR_RED_ 1 -#define ACUTEST_COLOR_GREEN_ 2 -#define ACUTEST_COLOR_YELLOW_ 3 -#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 10 -#define ACUTEST_COLOR_RED_INTENSIVE_ 11 -#define ACUTEST_COLOR_GREEN_INTENSIVE_ 12 -#define ACUTEST_COLOR_YELLOW_INTENSIVE_ 13 +#define ACUTEST_COLOR_GREEN_ 1 +#define ACUTEST_COLOR_RED_ 2 +#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 3 +#define ACUTEST_COLOR_GREEN_INTENSIVE_ 4 +#define ACUTEST_COLOR_RED_INTENSIVE_ 5 static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3)) acutest_colored_printf_(int color, const char* fmt, ...) @@ -595,12 +549,10 @@ acutest_colored_printf_(int color, const char* fmt, ...) { const char* col_str; switch(color) { - case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break; case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break; - case ACUTEST_COLOR_YELLOW_: col_str = "\033[0;33m"; break; - case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break; + case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break; case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break; - case ACUTEST_COLOR_YELLOW_INTENSIVE_: col_str = "\033[1;33m"; break; + case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break; case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break; default: col_str = "\033[0m"; break; } @@ -619,13 +571,11 @@ acutest_colored_printf_(int color, const char* fmt, ...) GetConsoleScreenBufferInfo(h, &info); switch(color) { - case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break; case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break; - case ACUTEST_COLOR_YELLOW_: attr = FOREGROUND_RED | FOREGROUND_GREEN; break; - case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break; case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; - case ACUTEST_COLOR_YELLOW_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; default: attr = 0; break; } if(attr != 0) @@ -640,35 +590,6 @@ acutest_colored_printf_(int color, const char* fmt, ...) #endif } -static const char* -acutest_basename_(const char* path) -{ - const char* name; - - name = strrchr(path, '/'); - if(name != NULL) - name++; - else - name = path; - -#ifdef ACUTEST_WIN_ - { - const char* alt_name; - - alt_name = strrchr(path, '\\'); - if(alt_name != NULL) - alt_name++; - else - alt_name = path; - - if(alt_name > name) - name = alt_name; - } -#endif - - return name; -} - static void acutest_begin_test_line_(const struct acutest_test_* test) { @@ -691,36 +612,26 @@ acutest_begin_test_line_(const struct acutest_test_* test) } static void -acutest_finish_test_line_(enum acutest_state_ state) +acutest_finish_test_line_(int result) { if(acutest_tap_) { - printf("%s %d - %s%s\n", - (state == ACUTEST_STATE_SUCCESS || state == ACUTEST_STATE_SKIPPED) ? "ok" : "not ok", - acutest_current_index_ + 1, - acutest_current_test_->name, - (state == ACUTEST_STATE_SKIPPED) ? " # SKIP" : ""); + const char* str = (result == 0) ? "ok" : "not ok"; - if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { + printf("%s %d - %s\n", str, acutest_current_index_ + 1, acutest_current_test_->name); + + if(result == 0 && acutest_timer_) { printf("# Duration: "); acutest_timer_print_diff_(); printf("\n"); } } else { - int color; - const char* str; - - switch(state) { - case ACUTEST_STATE_SUCCESS: color = ACUTEST_COLOR_GREEN_INTENSIVE_; str = "OK"; break; - case ACUTEST_STATE_SKIPPED: color = ACUTEST_COLOR_YELLOW_INTENSIVE_; str = "SKIPPED"; break; - case ACUTEST_STATE_FAILED: /* Fall through. */ - default: color = ACUTEST_COLOR_RED_INTENSIVE_; str = "FAILED"; break; - } - + int color = (result == 0) ? ACUTEST_COLOR_GREEN_INTENSIVE_ : ACUTEST_COLOR_RED_INTENSIVE_; + const char* str = (result == 0) ? "OK" : "FAILED"; printf("[ "); acutest_colored_printf_(color, "%s", str); printf(" ]"); - if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { + if(result == 0 && acutest_timer_) { printf(" "); acutest_timer_print_diff_(); } @@ -747,51 +658,6 @@ acutest_line_indent_(int level) printf("%.*s", n, spaces); } -void ACUTEST_ATTRIBUTE_(format (printf, 3, 4)) -acutest_skip_(const char* file, int line, const char* fmt, ...) -{ - va_list args; - size_t reason_len; - - va_start(args, fmt); - vsnprintf(acutest_test_skip_reason_, sizeof(acutest_test_skip_reason_), fmt, args); - va_end(args); - acutest_test_skip_reason_[sizeof(acutest_test_skip_reason_)-1] = '\0'; - - /* Remove final dot, if provided; that collides with our other logic. */ - reason_len = strlen(acutest_test_skip_reason_); - if(acutest_test_skip_reason_[reason_len-1] == '.') - acutest_test_skip_reason_[reason_len-1] = '\0'; - - if(acutest_test_check_count_ > 0) { - acutest_check_(0, file, line, "Cannot skip, already performed some checks"); - return; - } - - if(acutest_verbose_level_ >= 2) { - const char *result_str = "skipped"; - int result_color = ACUTEST_COLOR_YELLOW_; - - if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) - acutest_finish_test_line_(ACUTEST_STATE_SKIPPED); - acutest_test_already_logged_++; - - acutest_line_indent_(1); - - if(file != NULL) { - file = acutest_basename_(file); - printf("%s:%d: ", file, line); - } - - printf("%s... ", acutest_test_skip_reason_); - acutest_colored_printf_(result_color, "%s", result_str); - printf("\n"); - acutest_test_already_logged_++; - } - - acutest_test_skip_count_++; -} - int ACUTEST_ATTRIBUTE_(format (printf, 4, 5)) acutest_check_(int cond, const char* file, int line, const char* fmt, ...) { @@ -799,28 +665,19 @@ acutest_check_(int cond, const char* file, int line, const char* fmt, ...) int result_color; int verbose_level; - if(acutest_test_skip_count_) { - /* We've skipped the test. We shouldn't be here: The test implementation - * should have already return before. So lets suppress the following - * output. */ - cond = 1; - goto skip_check; - } - if(cond) { result_str = "ok"; result_color = ACUTEST_COLOR_GREEN_; verbose_level = 3; } else { if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) - acutest_finish_test_line_(ACUTEST_STATE_FAILED); - - acutest_test_failures_++; - acutest_test_already_logged_++; + acutest_finish_test_line_(-1); result_str = "failed"; result_color = ACUTEST_COLOR_RED_; verbose_level = 2; + acutest_test_failures_++; + acutest_test_already_logged_++; } if(acutest_verbose_level_ >= verbose_level) { @@ -835,8 +692,20 @@ acutest_check_(int cond, const char* file, int line, const char* fmt, ...) acutest_line_indent_(acutest_case_name_[0] ? 2 : 1); if(file != NULL) { - file = acutest_basename_(file); - printf("%s:%d: ", file, line); +#ifdef ACUTEST_WIN_ + const char* lastsep1 = strrchr(file, '\\'); + const char* lastsep2 = strrchr(file, '/'); + if(lastsep1 == NULL) + lastsep1 = file-1; + if(lastsep2 == NULL) + lastsep2 = file-1; + file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; +#else + const char* lastsep = strrchr(file, '/'); + if(lastsep != NULL) + file = lastsep+1; +#endif + printf("%s:%d: Check ", file, line); } va_start(args, fmt); @@ -849,9 +718,6 @@ acutest_check_(int cond, const char* file, int line, const char* fmt, ...) acutest_test_already_logged_++; } - acutest_test_check_count_++; - -skip_check: acutest_cond_failed_ = (cond == 0); return !acutest_cond_failed_; } @@ -1009,9 +875,7 @@ acutest_abort_(void) } else { if(acutest_current_test_ != NULL) acutest_fini_(acutest_current_test_->name); - fflush(stdout); - fflush(stderr); - acutest_exit_(ACUTEST_STATE_FAILED); + abort(); } } @@ -1025,6 +889,28 @@ acutest_list_names_(void) printf(" %s\n", test->name); } +static void +acutest_remember_(int i) +{ + if(acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_) + return; + + acutest_test_data_[i].flags |= ACUTEST_FLAG_RUN_; + acutest_count_++; +} + +static void +acutest_set_success_(int i, int success) +{ + acutest_test_data_[i].flags |= success ? ACUTEST_FLAG_SUCCESS_ : ACUTEST_FLAG_FAILURE_; +} + +static void +acutest_set_duration_(int i, double duration) +{ + acutest_test_data_[i].duration = duration; +} + static int acutest_name_contains_word_(const char* name, const char* pattern) { @@ -1049,15 +935,15 @@ acutest_name_contains_word_(const char* name, const char* pattern) } static int -acutest_select_(const char* pattern) +acutest_lookup_(const char* pattern) { int i; int n = 0; /* Try exact match. */ - for(i = 0; i < acutest_list_size_; i++) { + for(i = 0; i < (int) acutest_list_size_; i++) { if(strcmp(acutest_list_[i].name, pattern) == 0) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + acutest_remember_(i); n++; break; } @@ -1066,9 +952,9 @@ acutest_select_(const char* pattern) return n; /* Try word match. */ - for(i = 0; i < acutest_list_size_; i++) { + for(i = 0; i < (int) acutest_list_size_; i++) { if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + acutest_remember_(i); n++; } } @@ -1076,9 +962,9 @@ acutest_select_(const char* pattern) return n; /* Try relaxed match. */ - for(i = 0; i < acutest_list_size_; i++) { + for(i = 0; i < (int) acutest_list_size_; i++) { if(strstr(acutest_list_[i].name, pattern) != NULL) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + acutest_remember_(i); n++; } } @@ -1114,87 +1000,73 @@ acutest_error_(const char* fmt, ...) } /* Call directly the given test unit function. */ -static enum acutest_state_ +static int acutest_do_run_(const struct acutest_test_* test, int index) { - enum acutest_state_ state = ACUTEST_STATE_FAILED; + int status = -1; + acutest_was_aborted_ = 0; acutest_current_test_ = test; acutest_current_index_ = index; acutest_test_failures_ = 0; acutest_test_already_logged_ = 0; - acutest_test_check_count_ = 0; - acutest_test_skip_count_ = 0; acutest_cond_failed_ = 0; #ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS try { #endif -#endif - acutest_init_(test->name); - acutest_begin_test_line_(test); + acutest_init_(test->name); + acutest_begin_test_line_(test); - /* This is good to do in case the test unit crashes. */ - fflush(stdout); - fflush(stderr); + /* This is good to do in case the test unit crashes. */ + fflush(stdout); + fflush(stderr); - if(!acutest_worker_) { - acutest_abort_has_jmp_buf_ = 1; - if(setjmp(acutest_abort_jmp_buf_) != 0) - goto aborted; + if(!acutest_worker_) { + acutest_abort_has_jmp_buf_ = 1; + if(setjmp(acutest_abort_jmp_buf_) != 0) { + acutest_was_aborted_ = 1; + goto aborted; } + } - acutest_timer_get_time_(´st_timer_start_); - test->func(); + acutest_timer_get_time_(´st_timer_start_); + test->func(); + aborted: + acutest_abort_has_jmp_buf_ = 0; + acutest_timer_get_time_(´st_timer_end_); -aborted: - acutest_abort_has_jmp_buf_ = 0; - acutest_timer_get_time_(´st_timer_end_); + if(acutest_verbose_level_ >= 3) { + acutest_line_indent_(1); + if(acutest_test_failures_ == 0) { + acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: "); + printf("All conditions have passed.\n"); - if(acutest_test_failures_ > 0) - state = ACUTEST_STATE_FAILED; - else if(acutest_test_skip_count_ > 0) - state = ACUTEST_STATE_SKIPPED; - else - state = ACUTEST_STATE_SUCCESS; - - if(!acutest_test_already_logged_) - acutest_finish_test_line_(state); - - if(acutest_verbose_level_ >= 3) { - acutest_line_indent_(1); - switch(state) { - case ACUTEST_STATE_SUCCESS: - acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: "); - printf("All conditions have passed.\n"); - - if(acutest_timer_) { - acutest_line_indent_(1); - printf("Duration: "); - acutest_timer_print_diff_(); - printf("\n"); - } - break; - - case ACUTEST_STATE_SKIPPED: - acutest_colored_printf_(ACUTEST_COLOR_YELLOW_INTENSIVE_, "SKIPPED: "); - printf("%s.\n", acutest_test_skip_reason_); - break; - - default: - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); - printf("%d condition%s %s failed.\n", - acutest_test_failures_, - (acutest_test_failures_ == 1) ? "" : "s", - (acutest_test_failures_ == 1) ? "has" : "have"); - break; + if(acutest_timer_) { + acutest_line_indent_(1); + printf("Duration: "); + acutest_timer_print_diff_(); + printf("\n"); + } + } else { + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); + if(!acutest_was_aborted_) { + printf("%d condition%s %s failed.\n", + acutest_test_failures_, + (acutest_test_failures_ == 1) ? "" : "s", + (acutest_test_failures_ == 1) ? "has" : "have"); + } else { + printf("Aborted.\n"); } - printf("\n"); } + printf("\n"); + } else if(acutest_verbose_level_ >= 1 && acutest_test_failures_ == 0) { + acutest_finish_test_line_(0); + } + + status = (acutest_test_failures_ == 0) ? 0 : -1; #ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS } catch(std::exception& e) { const char* what = e.what(); acutest_check_(0, NULL, 0, "Threw std::exception"); @@ -1215,14 +1087,13 @@ aborted: printf("C++ exception.\n\n"); } } -#endif #endif acutest_fini_(test->name); acutest_case_(NULL); acutest_current_test_ = NULL; - return state; + return status; } /* Trigger the unit test. If possible (and not suppressed) it starts a child @@ -1231,7 +1102,7 @@ aborted: static void acutest_run_(const struct acutest_test_* test, int index, int master_index) { - enum acutest_state_ state = ACUTEST_STATE_FAILED; + int failed = 1; acutest_timer_type_ start, end; acutest_current_test_ = test; @@ -1252,16 +1123,21 @@ acutest_run_(const struct acutest_test_* test, int index, int master_index) pid = fork(); if(pid == (pid_t)-1) { acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno); + failed = 1; } else if(pid == 0) { /* Child: Do the test. */ acutest_worker_ = 1; - state = acutest_do_run_(test, index); - acutest_exit_((int) state); + failed = (acutest_do_run_(test, index) != 0); + acutest_exit_(failed ? 1 : 0); } else { /* Parent: Wait until child terminates and analyze its exit code. */ waitpid(pid, &exit_code, 0); if(WIFEXITED(exit_code)) { - state = (enum acutest_state_) WEXITSTATUS(exit_code); + switch(WEXITSTATUS(exit_code)) { + case 0: failed = 0; break; /* test has passed. */ + case 1: /* noop */ break; /* "normal" failure. */ + default: acutest_error_("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); + } } else if(WIFSIGNALED(exit_code)) { char tmp[32]; const char* signame; @@ -1274,7 +1150,7 @@ acutest_run_(const struct acutest_test_* test, int index, int master_index) case SIGSEGV: signame = "SIGSEGV"; break; case SIGILL: signame = "SIGILL"; break; case SIGTERM: signame = "SIGTERM"; break; - default: snprintf(tmp, sizeof(tmp), "signal %d", WTERMSIG(exit_code)); signame = tmp; break; + default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; } acutest_error_("Test interrupted by %s.", signame); } else { @@ -1291,7 +1167,7 @@ acutest_run_(const struct acutest_test_* test, int index, int master_index) /* Windows has no fork(). So we propagate all info into the child * through a command line arguments. */ - snprintf(buffer, sizeof(buffer), + _snprintf(buffer, sizeof(buffer)-1, "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", acutest_argv0_, index, acutest_timer_ ? "--time" : "", acutest_tap_ ? "--tap" : "", acutest_verbose_level_, @@ -1304,35 +1180,40 @@ acutest_run_(const struct acutest_test_* test, int index, int master_index) GetExitCodeProcess(processInfo.hProcess, &exitCode); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); - switch(exitCode) { - case 0: state = ACUTEST_STATE_SUCCESS; break; - case 1: state = ACUTEST_STATE_FAILED; break; - case 2: state = ACUTEST_STATE_SKIPPED; break; - case 3: acutest_error_("Aborted."); break; - case 0xC0000005: acutest_error_("Access violation."); break; - default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break; + failed = (exitCode != 0); + if(exitCode > 1) { + switch(exitCode) { + case 3: acutest_error_("Aborted."); break; + case 0xC0000005: acutest_error_("Access violation."); break; + default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break; + } } } else { acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError()); + failed = 1; } #else /* A platform where we don't know how to run child process. */ - state = acutest_do_run_(test, index); + failed = (acutest_do_run_(test, index) != 0); #endif } else { /* Child processes suppressed through --no-exec. */ - state = acutest_do_run_(test, index); + failed = (acutest_do_run_(test, index) != 0); } acutest_timer_get_time_(&end); acutest_current_test_ = NULL; - acutest_test_data_[master_index].state = state; - acutest_test_data_[master_index].duration = acutest_timer_diff_(start, end); + acutest_stat_run_units_++; + if(failed) + acutest_stat_failed_units_++; + + acutest_set_success_(master_index, !failed); + acutest_set_duration_(master_index, acutest_timer_diff_(start, end)); } #if defined(ACUTEST_WIN_) @@ -1369,8 +1250,8 @@ typedef struct acutest_test_CMDLINE_OPTION_ { static int acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options, - const char* arggroup, - int (*callback)(int /*optval*/, const char* /*arg*/)) + const char* arggroup, + int (*callback)(int /*optval*/, const char* /*arg*/)) { const ACUTEST_CMDLINE_OPTION_* opt; int i; @@ -1391,7 +1272,7 @@ acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options, badoptname[1] = arggroup[i]; badoptname[2] = '\0'; ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_), - badoptname); + badoptname); } if(ret != 0) @@ -1444,7 +1325,7 @@ acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** a if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { ret = callback(opt->id, argv[i]+2+len+1); } else { - snprintf(auxbuf, sizeof(auxbuf), "--%s", opt->longname); + sprintf(auxbuf, "--%s", opt->longname); ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf); } break; @@ -1488,7 +1369,7 @@ acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** a /* Strip any argument from the long option. */ char* assignment = strchr(badoptname, '='); if(assignment != NULL) { - size_t len = (size_t)(assignment - badoptname); + size_t len = assignment - badoptname; if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_) len = ACUTEST_CMDLINE_AUXBUF_SIZE_; strncpy(auxbuf, badoptname, len); @@ -1515,12 +1396,12 @@ acutest_help_(void) { printf("Usage: %s [options] [test...]\n", acutest_argv0_); printf("\n"); - printf("Run the specified unit tests; or if the option '--exclude' is used, run all\n"); + printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); printf("tests in the suite but those listed. By default, if no tests are specified\n"); printf("on the command line, all unit tests in the suite are run.\n"); printf("\n"); printf("Options:\n"); - printf(" -X, --exclude Execute all unit tests but the listed ones\n"); + printf(" -s, --skip Execute all unit tests but the listed ones\n"); printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); printf(" (WHEN is one of 'auto', 'always', 'never')\n"); printf(" -E, --no-exec Same as --exec=never\n"); @@ -1555,36 +1436,35 @@ acutest_help_(void) } static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = { - { 'X', "exclude", 'X', 0 }, - { 's', "skip", 'X', 0 }, /* kept for compatibility, use --exclude instead */ - { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 'E', "no-exec", 'E', 0 }, + { 's', "skip", 's', 0 }, + { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 'E', "no-exec", 'E', 0 }, #if defined ACUTEST_WIN_ - { 't', "time", 't', 0 }, + { 't', "time", 't', 0 }, { 0, "timer", 't', 0 }, /* kept for compatibility */ #elif defined ACUTEST_HAS_POSIX_TIMER_ - { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */ + { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */ #endif - { 0, "no-summary", 'S', 0 }, - { 0, "tap", 'T', 0 }, - { 'l', "list", 'l', 0 }, - { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 'q', "quiet", 'q', 0 }, - { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 0, "no-color", 'C', 0 }, - { 'h', "help", 'h', 0 }, - { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */ - { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, - { 0, NULL, 0, 0 } + { 0, "no-summary", 'S', 0 }, + { 0, "tap", 'T', 0 }, + { 'l', "list", 'l', 0 }, + { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 'q', "quiet", 'q', 0 }, + { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 0, "no-color", 'C', 0 }, + { 'h', "help", 'h', 0 }, + { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */ + { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, + { 0, NULL, 0, 0 } }; static int acutest_cmdline_callback_(int id, const char* arg) { switch(id) { - case 'X': - acutest_exclude_mode_ = 1; + case 's': + acutest_skip_mode_ = 1; break; case 'e': @@ -1609,10 +1489,10 @@ acutest_cmdline_callback_(int id, const char* arg) #if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_ if(arg == NULL || strcmp(arg, "real") == 0) { acutest_timer_ = 1; - #ifndef ACUTEST_WIN_ +#ifndef ACUTEST_WIN_ } else if(strcmp(arg, "cpu") == 0) { acutest_timer_ = 2; - #endif +#endif } else { fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg); fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); @@ -1678,7 +1558,7 @@ acutest_cmdline_callback_(int id, const char* arg) break; case 0: - if(acutest_select_(arg) == 0) { + if(acutest_lookup_(arg) == 0) { fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg); fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_); acutest_exit_(2); @@ -1707,114 +1587,100 @@ acutest_cmdline_callback_(int id, const char* arg) return 0; } -static int -acutest_under_debugger_(void) -{ + #ifdef ACUTEST_LINUX_ - /* Scan /proc/self/status for line "TracerPid: [PID]". If such line exists - * and the PID is non-zero, we're being debugged. */ - { - static const int OVERLAP = 32; - int fd; - char buf[512]; - size_t n_read; - pid_t tracer_pid = 0; +static int +acutest_is_tracer_present_(void) +{ + /* Must be large enough so the line 'TracerPid: ${PID}' can fit in. */ + static const int OVERLAP = 32; - /* Little trick so that we can treat the 1st line the same as any other - * and detect line start easily. */ - buf[0] = '\n'; - n_read = 1; + char buf[512]; + int tracer_present = 0; + int fd; + size_t n_read = 0; - fd = open("/proc/self/status", O_RDONLY); - if(fd != -1) { - while(1) { - static const char pattern[] = "\nTracerPid:"; - const char* field; + fd = open("/proc/self/status", O_RDONLY); + if(fd == -1) + return 0; - while(n_read < sizeof(buf) - 1) { - ssize_t n; + while(1) { + static const char pattern[] = "TracerPid:"; + const char* field; - n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read); - if(n <= 0) - break; - n_read += (size_t)n; - } - buf[n_read] = '\0'; + while(n_read < sizeof(buf) - 1) { + ssize_t n; - field = strstr(buf, pattern); - if(field != NULL && field < buf + sizeof(buf) - OVERLAP) { - tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); - break; - } + n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read); + if(n <= 0) + break; + n_read += n; + } + buf[n_read] = '\0'; - if(n_read == sizeof(buf) - 1) { - /* Move the tail with the potentially incomplete line we're - * be looking for to the beginning of the buffer. - * (The OVERLAP must be large enough so the searched line - * can fit in completely.) */ - memmove(buf, buf + sizeof(buf) - 1 - OVERLAP, OVERLAP); - n_read = OVERLAP; - } else { - break; - } - } + field = strstr(buf, pattern); + if(field != NULL && field < buf + sizeof(buf) - OVERLAP) { + pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); + tracer_present = (tracer_pid != 0); + break; + } - close(fd); - - if(tracer_pid != 0) - return 1; + if(n_read == sizeof(buf) - 1) { + /* Move the tail with the potentially incomplete line we're looking + * for to the beginning of the buffer. */ + memmove(buf, buf + sizeof(buf) - 1 - OVERLAP, OVERLAP); + n_read = OVERLAP; + } else { + break; } } + + close(fd); + return tracer_present; +} #endif #ifdef ACUTEST_MACOS_ - /* See https://developer.apple.com/library/archive/qa/qa1361/_index.html */ - { - int mib[4]; - struct kinfo_proc info; - size_t size; +static bool +acutest_AmIBeingDebugged(void) +{ + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; - size = sizeof(info); - info.kp_proc.p_flag = 0; - sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); - if(info.kp_proc.p_flag & P_TRACED) - return 1; - } -#endif + // Call sysctl. + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); -#ifdef ACUTEST_WIN_ - if(IsDebuggerPresent()) - return 1; -#endif - -#ifdef RUNNING_ON_VALGRIND - /* We treat Valgrind as a debugger of sorts. - * (Macro RUNNING_ON_VALGRIND is provided by , if available.) */ - if(RUNNING_ON_VALGRIND) - return 1; -#endif - - return 0; + // We're being debugged if the P_TRACED flag is set. + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); } +#endif int main(int argc, char** argv) { - int i, index; - int exit_code = 1; + int i; acutest_argv0_ = argv[0]; #if defined ACUTEST_UNIX_ acutest_colorize_ = isatty(STDOUT_FILENO); #elif defined ACUTEST_WIN_ - #if defined _BORLANDC_ + #if defined _BORLANDC_ acutest_colorize_ = isatty(_fileno(stdout)); #else acutest_colorize_ = _isatty(_fileno(stdout)); @@ -1847,39 +1713,37 @@ main(int argc, char** argv) #endif #endif - /* Determine what to run. */ - if(acutest_count_(ACUTEST_STATE_SELECTED) > 0) { - enum acutest_state_ if_selected; - enum acutest_state_ if_unselected; - - if(!acutest_exclude_mode_) { - if_selected = ACUTEST_STATE_NEEDTORUN; - if_unselected = ACUTEST_STATE_EXCLUDED; - } else { - if_selected = ACUTEST_STATE_EXCLUDED; - if_unselected = ACUTEST_STATE_NEEDTORUN; - } - - for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state == ACUTEST_STATE_SELECTED) - acutest_test_data_[i].state = if_selected; - else - acutest_test_data_[i].state = if_unselected; - } - } else { - /* By default, we want to run all tests. */ + /* By default, we want to run all tests. */ + if(acutest_count_ == 0) { for(i = 0; acutest_list_[i].func != NULL; i++) - acutest_test_data_[i].state = ACUTEST_STATE_NEEDTORUN; + acutest_remember_(i); } - /* By default, we want to suppress running tests as child processes if we - * run just one test, or if we're under debugger: Debugging tests is then - * so much easier. */ + /* Guess whether we want to run unit tests as child processes. */ if(acutest_no_exec_ < 0) { - if(acutest_count_(ACUTEST_STATE_NEEDTORUN) <= 1 || acutest_under_debugger_()) + acutest_no_exec_ = 0; + + if(acutest_count_ <= 1) { acutest_no_exec_ = 1; - else - acutest_no_exec_ = 0; + } else { +#ifdef ACUTEST_WIN_ + if(IsDebuggerPresent()) + acutest_no_exec_ = 1; +#endif +#ifdef ACUTEST_LINUX_ + if(acutest_is_tracer_present_()) + acutest_no_exec_ = 1; +#endif +#ifdef ACUTEST_MACOS_ + if(acutest_AmIBeingDebugged()) + acutest_no_exec_ = 1; +#endif +#ifdef RUNNING_ON_VALGRIND + /* RUNNING_ON_VALGRIND is provided by optionally included */ + if(RUNNING_ON_VALGRIND) + acutest_no_exec_ = 1; +#endif + } } if(acutest_tap_) { @@ -1893,38 +1757,37 @@ main(int argc, char** argv) acutest_no_summary_ = 1; if(!acutest_worker_) - printf("1..%d\n", acutest_count_(ACUTEST_STATE_NEEDTORUN)); + printf("1..%d\n", (int) acutest_count_); } - index = acutest_worker_index_; + int index = acutest_worker_index_; for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state == ACUTEST_STATE_NEEDTORUN) + int run = (acutest_test_data_[i].flags & ACUTEST_FLAG_RUN_); + if (acutest_skip_mode_) /* Run all tests except those listed. */ + run = !run; + if(run) acutest_run_(´st_list_[i], index++, i); } /* Write a summary */ if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) { - int n_run, n_success, n_failed ; - - n_run = acutest_list_size_ - acutest_count_(ACUTEST_STATE_EXCLUDED); - n_success = acutest_count_(ACUTEST_STATE_SUCCESS); - n_failed = acutest_count_(ACUTEST_STATE_FAILED); - if(acutest_verbose_level_ >= 3) { acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n"); - printf(" Count of run unit tests: %4d\n", n_run); - printf(" Count of successful unit tests: %4d\n", n_success); - printf(" Count of failed unit tests: %4d\n", n_failed); + printf(" Count of all unit tests: %4d\n", (int) acutest_list_size_); + printf(" Count of run unit tests: %4d\n", acutest_stat_run_units_); + printf(" Count of failed unit tests: %4d\n", acutest_stat_failed_units_); + printf(" Count of skipped unit tests: %4d\n", (int) acutest_list_size_ - acutest_stat_run_units_); } - if(n_failed == 0) { + if(acutest_stat_failed_units_ == 0) { acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:"); - printf(" No unit tests have failed.\n"); + printf(" All unit tests have passed.\n"); } else { acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:"); printf(" %d of %d unit tests %s failed.\n", - n_failed, n_run, (n_failed == 1) ? "has" : "have"); + acutest_stat_failed_units_, acutest_stat_run_units_, + (acutest_stat_failed_units_ == 1) ? "has" : "have"); } if(acutest_verbose_level_ >= 3) @@ -1932,63 +1795,45 @@ main(int argc, char** argv) } if (acutest_xml_output_) { - const char* suite_name = acutest_basename_(argv[0]); +#if defined ACUTEST_UNIX_ + char *suite_name = basename(argv[0]); +#elif defined ACUTEST_WIN_ + char suite_name[_MAX_FNAME]; + _splitpath(argv[0], NULL, NULL, suite_name, NULL); +#else + const char *suite_name = argv[0]; +#endif fprintf(acutest_xml_output_, "\n"); - fprintf(acutest_xml_output_, "\n", - suite_name, - (int)acutest_list_size_, - acutest_count_(ACUTEST_STATE_FAILED), - acutest_count_(ACUTEST_STATE_SKIPPED) + acutest_count_(ACUTEST_STATE_EXCLUDED)); + fprintf(acutest_xml_output_, "\n", + suite_name, (int)acutest_list_size_, acutest_stat_failed_units_, acutest_stat_failed_units_, + (int)acutest_list_size_ - acutest_stat_run_units_); for(i = 0; acutest_list_[i].func != NULL; i++) { struct acutest_test_data_ *details = ´st_test_data_[i]; - const char* str_state; fprintf(acutest_xml_output_, " \n", acutest_list_[i].name, details->duration); - - switch(details->state) { - case ACUTEST_STATE_SUCCESS: str_state = NULL; break; - case ACUTEST_STATE_EXCLUDED: /* Fall through. */ - case ACUTEST_STATE_SKIPPED: str_state = ""; break; - case ACUTEST_STATE_FAILED: /* Fall through. */ - default: str_state = ""; break; - } - - if(str_state != NULL) - fprintf(acutest_xml_output_, " %s\n", str_state); + if (details->flags & ACUTEST_FLAG_FAILURE_) + fprintf(acutest_xml_output_, " \n"); + if (!(details->flags & ACUTEST_FLAG_FAILURE_) && !(details->flags & ACUTEST_FLAG_SUCCESS_)) + fprintf(acutest_xml_output_, " \n"); fprintf(acutest_xml_output_, " \n"); } fprintf(acutest_xml_output_, "\n"); fclose(acutest_xml_output_); } - if(acutest_worker_ && acutest_count_(ACUTEST_STATE_EXCLUDED)+1 == acutest_list_size_) { - /* If we are the child process, we need to propagate the test state - * without any moderation. */ - for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state != ACUTEST_STATE_EXCLUDED) { - exit_code = (int) acutest_test_data_[i].state; - break; - } - } - } else { - if(acutest_count_(ACUTEST_STATE_FAILED) > 0) - exit_code = 1; - else - exit_code = 0; - } - acutest_cleanup_(); - return exit_code; + + return (acutest_stat_failed_units_ == 0) ? 0 : 1; } #endif /* #ifndef TEST_NO_MAIN */ #ifdef _MSC_VER - #pragma warning(pop) +#pragma warning(pop) #endif #ifdef __cplusplus - } /* extern "C" */ +} /* extern "C" */ #endif #endif /* #ifndef ACUTEST_H */ diff --git a/test/tests.h b/test/tests.h deleted file mode 100644 index f5ebcdc..0000000 --- a/test/tests.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef TEST_TESTS -#define TEST_TESTS - -void test_routing_simple(); -void test_routing_star(); -void test_routing_merge(); -void test_routing_nest(); - -#endif