picom/subprojects/test.h/test.h

244 lines
10 KiB
C

// SPDX-License-Identifier: MIT
#pragma once
#ifdef UNIT_TEST
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#define USE_SYSCTL_FOR_ARGS 1
// clang-format off
#include <sys/types.h>
#include <sys/sysctl.h>
// clang-format on
#include <unistd.h> // getpid
#endif
struct test_file_metadata;
struct test_failure {
bool present;
const char *message;
const char *file;
int line;
bool owned;
};
struct test_case_metadata {
void (*fn)(struct test_case_metadata *, struct test_file_metadata *);
struct test_failure failure;
const char *name;
struct test_case_metadata *next;
};
struct test_file_metadata {
bool registered;
const char *name;
struct test_file_metadata *next;
struct test_case_metadata *tests;
};
struct test_file_metadata __attribute__((weak)) * test_file_head;
#define SET_FAILURE(_message, _owned) \
metadata->failure = (struct test_failure) { \
.message = _message, .file = __FILE__, .line = __LINE__, \
.present = true, .owned = _owned, \
}
#define TEST_EQUAL(a, b) \
do { \
if ((a) != (b)) { \
SET_FAILURE(#a " != " #b, false); \
return; \
} \
} while (0)
#define TEST_NOTEQUAL(a, b) \
do { \
if ((a) == (b)) { \
SET_FAILURE(#a " == " #b, false); \
return; \
} \
} while (0)
#define TEST_TRUE(a) \
do { \
if (!(a)) { \
SET_FAILURE(#a " is not true", false); \
return; \
} \
} while (0)
#define TEST_STREQUAL(a, b) \
do { \
if (strcmp(a, b) != 0) { \
const char *test_strequal__part2 = " != " #b; \
size_t test_strequal__len = \
strlen(a) + strlen(test_strequal__part2) + 3; \
char *test_strequal__buf = malloc(test_strequal__len); \
snprintf(test_strequal__buf, test_strequal__len, "\"%s\"%s", a, \
test_strequal__part2); \
SET_FAILURE(test_strequal__buf, true); \
return; \
} \
} while (0)
#define TEST_STRNEQUAL(a, b, len) \
do { \
if (strncmp(a, b, len) != 0) { \
const char *test_strnequal__part2 = " != " #b; \
size_t test_strnequal__len2 = \
len + strlen(test_strnequal__part2) + 3; \
char *test_strnequal__buf = malloc(test_strnequal__len2); \
snprintf(test_strnequal__buf, test_strnequal__len2, \
"\"%.*s\"%s", (int)len, a, test_strnequal__part2); \
SET_FAILURE(test_strnequal__buf, true); \
return; \
} \
} while (0)
#define TEST_STREQUAL3(str, expected, len) \
do { \
if (len != strlen(expected) || strncmp(str, expected, len) != 0) { \
const char *test_strequal3__part2 = " != " #expected; \
size_t test_strequal3__len2 = \
len + strlen(test_strequal3__part2) + 3; \
char *test_strequal3__buf = malloc(test_strequal3__len2); \
snprintf(test_strequal3__buf, test_strequal3__len2, \
"\"%.*s\"%s", (int)len, str, test_strequal3__part2); \
SET_FAILURE(test_strequal3__buf, true); \
return; \
} \
} while (0)
#define TEST_CASE(_name) \
static void __test_h_##_name(struct test_case_metadata *, \
struct test_file_metadata *); \
static struct test_file_metadata __test_h_file; \
static struct test_case_metadata __test_h_meta_##_name = { \
.name = #_name, \
.fn = __test_h_##_name, \
}; \
static void __attribute__((constructor(101))) __test_h_##_name##_register(void) { \
__test_h_meta_##_name.next = __test_h_file.tests; \
__test_h_file.tests = &__test_h_meta_##_name; \
if (!__test_h_file.registered) { \
__test_h_file.name = __FILE__; \
__test_h_file.next = test_file_head; \
test_file_head = &__test_h_file; \
__test_h_file.registered = true; \
} \
} \
static void __test_h_##_name( \
struct test_case_metadata *metadata __attribute__((unused)), \
struct test_file_metadata *file_metadata __attribute__((unused)))
extern void __attribute__((weak)) (*test_h_unittest_setup)(void);
/// Run defined tests, return true if all tests succeeds
/// @param[out] tests_run if not NULL, set to whether tests were run
static inline void __attribute__((constructor(102))) run_tests(void) {
bool should_run = false;
#ifdef USE_SYSCTL_FOR_ARGS
int mib[] = {
CTL_KERN,
#if defined(__NetBSD__) || defined(__OpenBSD__)
KERN_PROC_ARGS,
getpid(),
KERN_PROC_ARGV,
#else
KERN_PROC,
KERN_PROC_ARGS,
getpid(),
#endif
};
char *arg = NULL;
size_t arglen;
sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &arglen, NULL, 0);
arg = malloc(arglen);
sysctl(mib, sizeof(mib) / sizeof(mib[0]), arg, &arglen, NULL, 0);
#else
FILE *cmdlinef = fopen("/proc/self/cmdline", "r");
char *arg = NULL;
int arglen;
fscanf(cmdlinef, "%ms%n", &arg, &arglen);
fclose(cmdlinef);
#endif
for (char *pos = arg; pos < arg + arglen; pos += strlen(pos) + 1) {
if (strcmp(pos, "--unittest") == 0) {
should_run = true;
break;
}
}
free(arg);
if (!should_run) {
return;
}
if (&test_h_unittest_setup) {
test_h_unittest_setup();
}
struct test_file_metadata *i = test_file_head;
int failed = 0, success = 0;
while (i) {
fprintf(stderr, "Running tests from %s:\n", i->name);
struct test_case_metadata *j = i->tests;
while (j) {
fprintf(stderr, "\t%s ... ", j->name);
j->failure.present = false;
j->fn(j, i);
if (j->failure.present) {
fprintf(stderr, "failed (%s at %s:%d)\n", j->failure.message,
j->failure.file, j->failure.line);
if (j->failure.owned) {
free((char *)j->failure.message);
j->failure.message = NULL;
}
failed++;
} else {
fprintf(stderr, "passed\n");
success++;
}
j = j->next;
}
fprintf(stderr, "\n");
i = i->next;
}
int total = failed + success;
fprintf(stderr, "Test results: passed %d/%d, failed %d/%d\n", success, total,
failed, total);
exit(failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
#else
#include <stdbool.h>
#define TEST_CASE(name) static void __attribute__((unused)) __test_h_##name(void)
#define TEST_EQUAL(a, b) \
(void)(a); \
(void)(b)
#define TEST_NOTEQUAL(a, b) \
(void)(a); \
(void)(b)
#define TEST_TRUE(a) (void)(a)
#define TEST_STREQUAL(a, b) \
(void)(a); \
(void)(b)
#define TEST_STRNEQUAL(a, b, len) \
(void)(a); \
(void)(b); \
(void)(len)
#define TEST_STREQUAL3(str, expected, len) \
(void)(str); \
(void)(expected); \
(void)(len)
#endif