diff --git a/test/dat.h b/test/dat.h deleted file mode 100644 index ec9427a..0000000 --- a/test/dat.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 2025 Quinn - * Licensed under the MIT Licence. See LICENSE for details */ -#include - -#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 }, -}; diff --git a/test/main.c b/test/main.c index 2a3e5b0..753fb52 100644 --- a/test/main.c +++ b/test/main.c @@ -1,19 +1,51 @@ /* Copyright (c) 2025 Quinn * Licensed under the MIT Licence. See LICENSE for details */ -#define GLAD_GL_IMPLEMENTATION -#include -#undef GLAD_GL_IMPLEMENTATION - -#include #include -#include -#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; } diff --git a/test/t_arith.h b/test/t_arith.h deleted file mode 100644 index ee526fc..0000000 --- a/test/t_arith.h +++ /dev/null @@ -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); -} diff --git a/test/t_conf.h b/test/t_conf.h deleted file mode 100644 index 58f5aa5..0000000 --- a/test/t_conf.h +++ /dev/null @@ -1,207 +0,0 @@ -/* Copyright (c) 2025 Quinn - * Licensed under the MIT Licence. See LICENSE for details */ -#pragma once -#include -#include -#include -#include - -#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; -} diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..4b062d6 --- /dev/null +++ b/test/test.c @@ -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; +} diff --git a/test/test.h b/test/test.h index 731a1ca..e7f0eec 100644 --- a/test/test.h +++ b/test/test.h @@ -3,46 +3,14 @@ #pragma once #include -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)) diff --git a/test/test_conf.c b/test/test_conf.c new file mode 100644 index 0000000..f9807f6 --- /dev/null +++ b/test/test_conf.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2025 Quinn + * Licensed under the MIT Licence. See LICENSE for details */ +#include "test_conf.h" + +#include +#include + +#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 +} diff --git a/test/test_conf.h b/test/test_conf.h new file mode 100644 index 0000000..daf77d0 --- /dev/null +++ b/test/test_conf.h @@ -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);