#endif
#if !CPP
+# define BOOL_DEFINED 1
# if __STDC_VERSION__ >= 199901L
# include <stdbool.h>
# else
typedef pid_t CB_ProcHandle;
#endif
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_YELLOW "\x1b[33m"
+#define ANSI_COLOR_BLUE "\x1b[34m"
+#define ANSI_COLOR_MAGENTA "\x1b[35m"
+#define ANSI_COLOR_CYAN "\x1b[36m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
#define internal static
#ifndef _assert_break
# if OS_WINDOWS
# define _assert_break() __builtin_trap()
# endif
#endif
-#define Assert(COND) do { if (!(COND)) { _assert_break(); } } while (0)
+
+
+internal void cb_assertion_break(const char *condition,
+ const char *file, int32_t line) {
+ _assert_break();
+}
+
+typedef void (*cb_assertion_handler_fn)(const char *condition,
+ const char *file, int32_t line);
+internal cb_assertion_handler_fn cb_assertion_handler = cb_assertion_break;
+
+#define cb_assert(COND) \
+ do { \
+ if (!(COND)) { \
+ cb_assertion_handler(#COND, __FILE__, __LINE__); \
+ } \
+ } while (0)
+
#define StackPush(Head, Nodeptr) \
LLPushFrontCustom((Head), (Head), (Nodeptr), next)
#define StackPop(Head) (Head ? (Head = Head->next) : 0)
} \
(Dynarr)->Values = realloc((Dynarr)->Values, (Dynarr)->Capacity * \
sizeof((Dynarr)->Values[0])); \
- Assert((Dynarr)->Values); \
+ cb_assert((Dynarr)->Values); \
} while(0)
#define cb_dyn_push_custom(Dynarr, Node, Values, Count, Capacity) \
do { \
}
static void cb_print(CB_LogLevel level, const char *fmt, ...) {
-#define ANSI_COLOR_RED "\x1b[31m"
-#define ANSI_COLOR_GREEN "\x1b[32m"
-#define ANSI_COLOR_YELLOW "\x1b[33m"
-#define ANSI_COLOR_BLUE "\x1b[34m"
-#define ANSI_COLOR_MAGENTA "\x1b[35m"
-#define ANSI_COLOR_CYAN "\x1b[36m"
-#define ANSI_COLOR_RESET "\x1b[0m"
va_list args;
switch (level) {
case CB_LogLevel_Info: {
va_start(args, fmt);
printf("%s", _cb_format(fmt, args));
va_end(args);
-#undef ANSI_COLOR_RED
-#undef ANSI_COLOR_GREEN
-#undef ANSI_COLOR_YELLOW
-#undef ANSI_COLOR_BLUE
-#undef ANSI_COLOR_MAGENTA
-#undef ANSI_COLOR_CYAN
-#undef ANSI_COLOR_RESET
}
static char* cb_getenv(char *varname) {
CloseHandle(proc->handle);
#else
int32_t status = 0;
- Assert(waitpid(proc->handle, &status, 0) == proc->handle);
+ cb_assert(waitpid(proc->handle, &status, 0) == proc->handle);
if (WIFEXITED(status)) {
proc->status_code = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
}
internal void _cb_rebuild(int argc, char **argv, char *builder_src, ...) {
- Assert(argc >= 1);
+ cb_assert(argc >= 1);
char *exe_name = argv[0];
struct CB_PathList sources = {};
--- /dev/null
+#ifndef TESTS_H
+#define TESTS_H
+
+#include <setjmp.h>
+
+typedef void cb_test_func(void);
+
+struct CB_Test {
+ const char *name;
+ cb_test_func *function;
+};
+
+#if COMPILER_CL
+#pragma section(".CRT$XCU", read)
+typedef void cb_w32_test_func_init(void);
+#define CB_TEST(FUNC_NAME) \
+ static void FUNC_NAME(void); \
+ internal void register_##FUNC_NAME(void) { \
+ cb_test_register((struct CB_Test) { \
+ .function = &FUNC_NAME, \
+ .name = #FUNC_NAME, \
+ }); \
+ } \
+ __declspec(allocate(".CRT$XCU")) \
+ cb_w32_test_func_init *FUNC_NAME##_reg = register_##FUNC_NAME; \
+ static void FUNC_NAME(void)
+#else
+#define CB_TEST(FUNC_NAME) \
+ static void FUNC_NAME(void); \
+ __attribute__((constructor)) \
+ internal void register_##FUNC_NAME(void) { \
+ cb_test_register((struct CB_Test) { \
+ .function = &FUNC_NAME, \
+ .name = #FUNC_NAME, \
+ }); \
+ } \
+ static void FUNC_NAME(void)
+#endif
+
+static void cb_test_run(void);
+
+internal void cb_test_register(struct CB_Test test);
+internal void cb_assertion_test(const char *condition,
+ const char *file,
+ int32_t line);
+
+
+// ======================================================================
+// Implementations
+internal jmp_buf test_runner_jmpbuf = {0};
+
+internal struct {
+ struct CB_Test *tests;
+ size_t count;
+ size_t capacity;
+} cb_tests = {0};
+
+static void cb_test_run(void) {
+ int32_t test_passed_count = 0;
+ cb_assertion_handler = cb_assertion_test;
+ for (size_t i = 0; i < cb_tests.count; ++i) {
+ printf("test %s ... ", cb_tests.tests[i].name);
+ if (setjmp(test_runner_jmpbuf) == 0) {
+ cb_tests.tests[i].function();
+ printf(ANSI_COLOR_GREEN "ok\n" ANSI_COLOR_RESET);
+ test_passed_count += 1;
+ }
+ }
+ cb_assertion_handler = cb_assertion_break;
+ cb_println(CB_LogLevel_Info, "Summary: %d/%d tests passed",
+ test_passed_count, cb_tests.count);
+}
+
+internal void cb_test_register(struct CB_Test test) {
+ cb_dyn_push_custom(&cb_tests, test,
+ tests, count, capacity);
+}
+
+internal void cb_assertion_test(const char *condition,
+ const char *file, int32_t line) {
+ printf(ANSI_COLOR_RED "FAILED" ANSI_COLOR_RESET
+ ": `%s` (%s:%d)\n", condition, file, line);
+ longjmp(test_runner_jmpbuf, 1);
+}
+
+#endif