rewrite testing code to be a bit more durable and clean.

This commit is contained in:
2025-09-17 16:52:18 +02:00
parent e623a352d2
commit 47d34b3f7d
8 changed files with 218 additions and 304 deletions

View File

@@ -1,36 +0,0 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#include <stdint.h>
#include "../src/util/conf.h"
#include "../src/util/intdef.h"
#include "t_arith.h"
#include "t_conf.h"
#include "test.h"
testdat tests[] = {
{"ensure SAR", test_sar, NULL },
{"k=v", test_procbuf, &(struct test_procbuf){"key=val", "key", "val", 0} },
{"sometxt", test_procbuf, &(struct test_procbuf){"sometxt", "sometxt", "", CONF_ESYNTAX} },
{"comment", test_procbuf, &(struct test_procbuf){"# comment", "", "", CONF_ENODAT} },
{"empty", test_procbuf, &(struct test_procbuf){"", "", "", CONF_ENODAT} },
{"LF", test_procbuf, &(struct test_procbuf){"\n", "", "", CONF_ENODAT} },
{"CRLF", test_procbuf, &(struct test_procbuf){"\r\n", "", "", CONF_ENODAT} },
{"k=v (LF)", test_procbuf, &(struct test_procbuf){"k=v\na", "k", "v", 0} },
{"k=v (CRLF)", test_procbuf, &(struct test_procbuf){"k=v\r\na", "k", "v", 0} },
{"get", test_matchopt, &(struct test_matchopt){"key3", 2} },
{"invalid", test_matchopt, &(struct test_matchopt){"nono", -1} },
// NOTE: formatter is fucking with alignment making it use tabs @.@
{"i32", test_procval_int, &(struct test_procval_int){"42", 42, CONF_I32} },
{"i32_neg", test_procval_int, &(struct test_procval_int){"-42", (u32)-42, CONF_I32} },
{"u32_max", test_procval_int, &(struct test_procval_int){"4294967295", UINT32_MAX, CONF_U64} },
{"u64", test_procval_int, &(struct test_procval_int){"3141592653589793238", 3141592653589793238, CONF_U64}},
{"u8_overflow", test_procval_int, &(struct test_procval_int){"256", 255, CONF_U8} },
{"", test_procval_f32, NULL },
{"", test_procval_str, NULL },
{"", test_procval_str_predef, NULL },
{"", test_procval_fstr, NULL },
{"", test_procval_fstr_trunc, NULL },
{"", test_procval_eparse, NULL },
{"", test_getpat, NULL },
};

View File

@@ -1,19 +1,51 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>
#undef GLAD_GL_IMPLEMENTATION
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "dat.h" // contains the test data
#include "test.h"
#include "test_conf.h"
int main(void) {
// get test count
size_t n = sizeof(tests) / sizeof(tests[0]);
assert_true(-3 >> 5 == -1); // checking for arithmetic shift, rather than logical shift
assert_true(sizeof(u8) == 1);
assert_true(sizeof(u16) == 2);
assert_true(sizeof(u32) == 4);
assert_true(sizeof(u64) == 8);
assert_true(sizeof(f32) == 4);
assert_true(sizeof(f64) == 8);
test_conf_procbuf("key=val", "key", "val", 0);
test_conf_procbuf("sometxt", "sometxt", "", CONF_ESYNTAX);
test_conf_procbuf("# comment", "", "", CONF_ENODAT);
test_conf_procbuf("", "", "", CONF_ENODAT);
test_conf_procbuf("\n", "", "", CONF_ENODAT);
test_conf_procbuf("\r\n", "", "", CONF_ENODAT);
test_conf_procbuf("k=v\na", "k", "v", 0);
test_conf_procbuf("k=v\r\na", "k", "v", 0);
test_conf_getpat();
return exec_tests(tests, n);
struct conf_entry opts[] = {
{"key0", NULL, 0},
{"key1", NULL, 0},
{"key2", NULL, 0}
};
test_conf_matchopt(opts, 3, "key0", 0);
test_conf_matchopt(opts, 3, "key1", 1);
test_conf_matchopt(opts, 3, "key2", 2);
test_conf_matchopt(opts, 3, "key3", -1);
test_conf_procval_int("42", 42, CONF_I32);
test_conf_procval_int("-42", (u32)-42, CONF_I32);
test_conf_procval_int("4294967295", UINT32_MAX, CONF_U32);
test_conf_procval_int("9223372036854775807", INT64_MAX, CONF_I64);
test_conf_procval_int("256", 255, CONF_U8);
// TODO: write more tests for float parsing
test_conf_procval_f32("0.0", 0.0f);
test_conf_procval_f32("3.14159", 3.14159f);
test_conf_procval_f32("3.1428571428", 22.0f / 7.0f);
test_conf_procval_f32("-5.0", -5.0f);
/* return 1 if there were failed tests. */
printf("test results: (%u/%u)\n", test_okay, test_okay + test_fail);
return !!test_fail;
}

View File

@@ -1,11 +0,0 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#pragma once
#include "test.h"
/* tests arithmetic shift for signed integers (rather than logical shift) */
int test_sar(void *dat) {
(void)dat;
return assert_true(-3 >> 5 == -1);
}

View File

@@ -1,207 +0,0 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#pragma once
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../src/util/conf.h"
#include "../src/util/intdef.h"
#include "test.h"
// Environment saver structure for conf_getpat tests
#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
struct test_getpat_envdat {
char *xdg_config_home;
char *home;
char *appdata;
char *userprofile;
};
/* save the current environment variables */
static void env_save(struct test_getpat_envdat *s) {
const char *tmp;
tmp = getenv("XDG_CONFIG_HOME");
s->xdg_config_home = tmp ? strdup(tmp) : NULL;
tmp = getenv("HOME");
s->home = tmp ? strdup(tmp) : NULL;
tmp = getenv("APPDATA");
s->appdata = tmp ? strdup(tmp) : NULL;
tmp = getenv("USERPROFILE");
s->userprofile = tmp ? strdup(tmp) : NULL;
}
/* restores the environment variables to what they were before */
static void env_restore(struct test_getpat_envdat *s) {
#ifdef _WIN32
if (s->xdg_config_home) _putenv_s("XDG_CONFIG_HOME", s->xdg_config_home);
else _putenv("XDG_CONFIG_HOME=");
if (s->home) _putenv_s("HOME", s->home);
else _putenv("HOME=");
if (s->appdata) _putenv_s("APPDATA", s->appdata);
else _putenv("APPDATA=");
if (s->userprofile) _putenv_s("USERPROFILE", s->userprofile);
else _putenv("USERPROFILE=");
#else
if (s->xdg_config_home) setenv("XDG_CONFIG_HOME", s->xdg_config_home, 1);
else unsetenv("XDG_CONFIG_HOME");
if (s->home) setenv("HOME", s->home, 1);
else unsetenv("HOME");
if (s->appdata) setenv("APPDATA", s->appdata, 1);
else unsetenv("APPDATA");
if (s->userprofile) setenv("USERPROFILE", s->userprofile, 1);
else unsetenv("USERPROFILE");
#endif
free(s->xdg_config_home);
free(s->home);
free(s->appdata);
free(s->userprofile);
}
#endif
/* check procbuf's functionality */
struct test_procbuf {
const char *in; // data in
const char *xkey; // expected key
const char *xval; // expected value
int xret; // expected return type
};
int test_procbuf(void *arg) {
struct test_procbuf *dat = arg;
size_t len = strlen(dat->in) + 1;
char k[len], v[len];
*k = '\0', *v = '\0';
return assert_true(conf_procbuf(dat->in, k, v, len) == dat->xret) ||
assert_true(!strcmp(k, dat->xkey)) ||
assert_true(!strcmp(v, dat->xval));
}
/* check matchopt functionality */
struct test_matchopt {
const char *key; // key to search for (key1, key2, key3)
int xidx; // expect index (<0 is NULL, may not be more than 2)
};
int test_matchopt(void *arg) {
struct test_matchopt *dat = arg;
struct conf_entry opts[] = {
{"key1", NULL, 0},
{"key2", NULL, 0},
{"key3", NULL, 0},
};
struct conf_entry *xopt = dat->xidx < 0 ? NULL : opts + dat->xidx;
return assert_true(conf_matchopt(opts, 3, dat->key) == xopt);
}
struct test_procval_int {
const char *val;
u64 xres;
u8 type;
};
int test_procval_int(void *arg) {
struct test_procval_int *dat = arg;
u64 out = 0;
return assert_true(!conf_procval(&(struct conf_entry){NULL, &out, dat->type}, dat->val)) ||
assert_true(out == dat->xres);
}
int test_procval_f32(void *arg) {
(void)arg;
f32 out;
return assert_true(!conf_procval(&(struct conf_entry){NULL, &out, CONF_F32}, "3.14159265")) ||
assert_true(fabsf(out - 3.14159265F) < 1e-6F);
}
int test_procval_str(void *arg) {
(void)arg;
char *out = NULL;
int ret = assert_true(!conf_procval(&(struct conf_entry){NULL, (void *)&out, CONF_STR}, "here comes the sowon")) ||
assert_false(strcmp("here comes the sowon", out));
free(out);
return ret;
}
int test_procval_str_predef(void *arg) {
(void)arg;
char *out = strdup("owo");
int ret = assert_true(!conf_procval(&(struct conf_entry){NULL, (void *)&out, CONF_STR}, "i leak if I don't free")) ||
assert_true(!strcmp("i leak if I don't free", out));
free(out);
return ret;
}
int test_procval_fstr(void *arg) {
(void)arg;
char buf[16];
struct conf_fstr str = {sizeof(buf), buf};
return assert_true(!conf_procval(&(struct conf_entry){NULL, &str, CONF_FSTR}, "hewwoo wowld")) ||
assert_true(!strcmp(str.out, "hewwoo wowld"));
}
int test_procval_fstr_trunc(void *arg) {
(void)arg;
char buf[8];
struct conf_fstr str = {sizeof(buf), buf};
return assert_true(!conf_procval(&(struct conf_entry){NULL, &str, CONF_FSTR}, "hewwooo wowld")) ||
assert_true(!strcmp(str.out, "hewwooo"));
}
int test_procval_eparse(void *arg) {
(void)arg;
i32 out;
return assert_true(conf_procval(&(struct conf_entry){NULL, &out, CONF_I32}, "owo") == CONF_EPARSE);
}
/* ensure paths are being set correctly */
int test_getpat(void *arg) {
(void)arg;
struct test_getpat_envdat envs;
env_save(&envs);
int ret = 0;
char *path = NULL;
#ifdef __linux__
setenv("XDG_CONFIG_HOME", "/test/config", 1);
path = conf_getpat("/app");
ret |= assert_true(path && !strcmp(path, "/test/config/app"));
free(path);
unsetenv("XDG_CONFIG_HOME");
setenv("HOME", "/test/home", 1);
path = conf_getpat("/app");
ret |= assert_true(path && !strcmp(path, "/test/home/.config/app"));
free(path);
unsetenv("HOME");
#elif defined(__APPLE__)
setenv("HOME", "/test/home", 1);
path = conf_getpat("/app");
ret |= assert_true(path && !strcmp(path, "/test/home/Library/Application Support/app"));
free(path);
unsetenv("HOME");
#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
_putenv("APPDATA=C:\\test\\appdata");
path = conf_getpat("\\app");
ret |= assert_true(path && !strcmp(path, "C:\\test\\appdata\\app"));
free(path);
_putenv("APPDATA=");
_putenv("USERPROFILE=C:\\test\\user");
path = conf_getpat("\\app");
ret |= assert_true(path && !strcmp(path, "C:\\test\\user\\AppData\\Roaming\\app"));
free(path);
_putenv("USERPROFILE=");
#endif
void *ptr;
ret |= assert_true(!(ptr = conf_getpat("anything")));
free(ptr);
env_restore(&envs);
return ret;
}

19
test/test.c Normal file
View File

@@ -0,0 +1,19 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#include "test.h"
#include "../src/util/intdef.h"
uint test_okay = 0;
uint test_fail = 0;
int test_process(int res, const char *restrict file, uint ln, const char *restrict function, const char *restrict expression) {
const char *status = res ?
"[\033[32;1m OK \033[0m]" :
"[\033[31;1m FAIL \033[0m]";
test_okay += !!res;
test_fail += !res;
printf("%s\t%s:%u-%s\t-> %s\n", status, file, ln, function, expression);
return res;
}

View File

@@ -3,46 +3,14 @@
#pragma once
#include <stdio.h>
const char *test_ctest;
size_t test_runs = 0;
#include "../src/util/intdef.h"
// evaluates the test
// returns 1 upon error
static inline int assert_helper(int cond, const char *restrict fname, unsigned ln, const char *restrict fnname, const char *restrict expr) {
test_runs++;
if (cond)
printf("[\033[32;1m OK \033[0m] %s %s -> %s:%u (%s)\n", test_ctest, fnname, fname, ln, expr);
else
printf("[\033[31;1m FAIL \033[0m] %s %s -> %s:%u (%s)\n", test_ctest, fnname, fname, ln, expr);
return !cond;
}
extern uint test_okay;
extern uint test_fail;
#define assert_true(expr) assert_helper(!!(expr), __FILE__, __LINE__, __func__, #expr) // evaluation expected to be true
#define assert_false(expr) assert_helper(!(expr), __FILE__, __LINE__, __func__, #expr) // evaluation expected to be false
/* Prints the result `res` of a test.
* Returns `res` back, for ease of chaining. */
int test_process(int res, const char *restrict file, uint ln, const char *restrict function, const char *restrict expression);
// contains the data for executing a single test
struct testdat {
const char *name; // test name
int (*test)(void *); // test, returns 0 upon success, non-zero upon failure
void *args; // arguments to the test
};
typedef struct testdat testdat;
// executes the tests, returns the amount of failed tests; >0: failure
static inline size_t exec_tests(testdat *dat, size_t ntests) {
size_t i;
size_t err = 0;
// perform tests and count the error state
for (i = 0; i < ntests; i++) {
test_ctest = dat[i].name;
err += !!(dat[i].test(dat[i].args));
}
// give final score
if (!err)
fprintf(stdout, "tests completed! (%zu/%zu)\n", test_runs - err, test_runs);
else
fprintf(stderr, " tests failed! (%zu/%zu)\n", test_runs - err, test_runs);
return err;
}
#define assert_true(expr) test_process((expr), __FILE__, __LINE__, __func__, #expr)
#define assert_false(expr) assert_true(!(expr))

130
test/test_conf.c Normal file
View File

@@ -0,0 +1,130 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#include "test_conf.h"
#include <math.h>
#include <string.h>
#include "../src/util/conf.h"
#include "test.h"
void test_conf_procbuf(const char *restrict buf, const char *restrict expect_key, const char *restrict expect_val, int expect_return) {
size_t len = strlen(buf) + 1;
char k[len], v[len];
*k = '\0', *v = '\0';
assert_true(conf_procbuf(buf, k, v, len) == expect_return) &&
assert_true(!strcmp(k, expect_key)) &&
assert_true(!strcmp(v, expect_val));
}
void test_conf_matchopt(struct conf_entry *opts, size_t optc, const char *restrict key, int expect_index) {
size_t idx = opts - conf_matchopt(opts, optc, key);
idx = (ssize_t)idx < 0 ? -(ssize_t)idx : idx;
int i = idx < optc ? idx : -1;
assert_true(i == expect_index);
}
void test_conf_procval_int(const char *val, u64 expect_value, int type) {
u8 out[sizeof(u64)] = {0};
assert_true(!conf_procval(&(struct conf_entry){NULL, out, type}, val));
assert_true(memcmp(out, &expect_value, sizeof(u64)) == 0);
}
void test_conf_procval_f32(const char *val, f32 expect_value) {
u8 out[4];
f32 result;
conf_procval(&(struct conf_entry){NULL, out, CONF_F32}, val);
memcpy(&result, out, 4);
assert_true(fabsf(expect_value - result) < 1e-9f);
}
void test_procval_str(void) {
char *out = NULL;
assert_true(!conf_procval(&(struct conf_entry){NULL, (void *)&out, CONF_STR}, "here comes the sowon")) &&
assert_false(strcmp("here comes the sowon", out));
free(out);
}
void test_procval_str_predef(void) {
char *out = strdup("owo");
assert_true(!conf_procval(&(struct conf_entry){NULL, (void *)&out, CONF_STR}, "i leak if I don't free")) &&
assert_true(!strcmp("i leak if I don't free", out));
free(out);
}
void test_procval_fstr(void) {
char buf[16];
struct conf_fstr str = {sizeof(buf), buf};
assert_true(!conf_procval(&(struct conf_entry){NULL, &str, CONF_FSTR}, "hewwoo wowld")) &&
assert_true(!strcmp(str.out, "hewwoo wowld"));
}
void test_procval_fstr_trunc(void) {
char buf[8];
struct conf_fstr str = {sizeof(buf), buf};
assert_true(!conf_procval(&(struct conf_entry){NULL, &str, CONF_FSTR}, "hewwooo wowld")) &&
assert_true(!strcmp(str.out, "hewwooo"));
}
void test_procval_eparse(void) {
i32 out;
assert_true(conf_procval(&(struct conf_entry){NULL, &out, CONF_I32}, "owo") == CONF_EPARSE);
}
void test_conf_getpat(void) {
char *path;
#if defined(__linux__)
/* test without setting environment variables. */
unsetenv("XDG_CONFIG_HOME");
unsetenv("HOME");
path = conf_getpat("/mypath");
assert_true(path == NULL);
free(path);
/* test with setting HOME. */
setenv("HOME", "/home/test", 1);
path = conf_getpat("/mypath");
assert_true(path != NULL && strcmp(path, "/home/test/.config/mypath") == 0);
free(path);
conf_getpat("/mypath");
/* test with setting XDG_CONFIG_HOME. */
setenv("XDG_CONFIG_HOME", "/etc/xdg", 1);
path = conf_getpat("/mypath");
assert_true(path != NULL && strcmp(path, "/etc/xdg/mypath") == 0);
free(path);
#elif defined(__APPLE__)
/* test without setting environment variables. */
unsetenv("HOME");
path = conf_getpat("/mypath");
assert_true(path == NULL);
free(path);
/* test with setting HOME. */
setenv("HOME", "/home/test", 1);
path = conf_getpat("/mypath");
assert_true(path != NULL && strcmp(path, "/home/test/Library/Application Support/mypath") == 0);
free(path);
#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
/* test without setting environment variables. */
_putenv("APPDATA=");
_putenv("USERPROFILE=");
path = conf_getpat("\\mypath");
assert_true(path == NULL);
free(path);
/* test with setting USERPROFILE */
_putenv("USERPROFILE=C:\\Users\\test");
path = conf_getpat("\\mypath");
assert_true(path != NULL && strcmp(path, "C:\\Users\\test\\mypath") == 0);
free(path);
/* test with setting APPDATA */
_putenv("APPDATA=C:\\Users\\test\\AppData\\Roaming");
path = conf_getpat("\\mypath");
assert_true(path != NULL && strcmp(path, "C:\\Users\\test\\AppData\\Roaming\\mypath") == 0);
free(path);
#else
assert_fail("unsupported operating system; no tests have been written.");
#endif
}

19
test/test_conf.h Normal file
View File

@@ -0,0 +1,19 @@
/* Copyright (c) 2025 Quinn
* Licensed under the MIT Licence. See LICENSE for details */
#pragma once
#include "../src/util/conf.h"
#include "../src/util/intdef.h"
void test_conf_procbuf(const char *restrict buf, const char *restrict expect_key, const char *restrict expect_val, int expect_return);
void test_conf_matchopt(struct conf_entry *restrict opts, size_t optc, const char *restrict key, int expect_index);
void test_conf_procval_int(const char *val, u64 expect_value, int type);
void test_conf_procval_f32(const char *val, f32 expect_value);
void test_conf_procval_fstr(const char *val, u64 expect_value, int type);
void test_conf_procval_str(const char *val, u64 expect_value, int type);
void test_procval_str(void);
void test_procval_str_predef(void);
void test_procval_fstr(void);
void test_procval_fstr_trunc(void);
void test_procval_eparse(void);
void test_conf_getpat(void);