From d5cf3acb7a45b7e46bf51dc6a66030d7bc986597 Mon Sep 17 00:00:00 2001 From: pommicket Date: Mon, 15 Sep 2025 18:54:37 -0400 Subject: Interpretation tests, various bugfixes --- CMakeLists.txt | 5 +- Makefile | 7 ++- examples/conf.pom | 5 +- examples/read_conf.c | 2 +- pom.c | 24 +++++--- tests/interpretation.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 1 + tests/test.h | 1 + 8 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 tests/interpretation.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f14c696..0972a9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,10 @@ endif() add_library(pom-static STATIC pom.c) add_library(pom SHARED pom.c) -add_executable(tests tests/errors.c tests/location.c tests/parsing.c tests/main.c) +add_executable(tests tests/errors.c tests/location.c tests/parsing.c tests/interpretation.c tests/main.c) target_include_directories(tests PRIVATE .) target_link_libraries(tests pom-static) +add_executable(read_conf examples/read_conf.c) +target_include_directories(read_conf PRIVATE .) +target_link_libraries(read_conf pom-static) diff --git a/Makefile b/Makefile index 8d8474b..65a7055 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ BUILD_DIR ?= $(PROFILE) __build: mkdir -p $(BUILD_DIR) P=`pwd` && cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(PROFILE) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 $$P - make -j16 -C $(BUILD_DIR) + $(MAKE) -C $(BUILD_DIR) -.PHONY: __build +test: __build + valgrind --exit-on-first-error=yes --error-exitcode=1 $(BUILD_DIR)/tests + +.PHONY: __build test diff --git a/examples/conf.pom b/examples/conf.pom index 016179a..6011308 100644 --- a/examples/conf.pom +++ b/examples/conf.pom @@ -1,4 +1 @@ -things = "ten, - great\,things, - i\\really, - love +things = \,,,76 diff --git a/examples/read_conf.c b/examples/read_conf.c index acc3eba..d96e190 100644 --- a/examples/read_conf.c +++ b/examples/read_conf.c @@ -2,7 +2,7 @@ #include #include -#include "pom.h" +#include int main(int argc, char **argv) { pom_error *error; diff --git a/pom.c b/pom.c index 2ca3ffa..772df4a 100644 --- a/pom.c +++ b/pom.c @@ -632,7 +632,7 @@ check_if_key_is_valid(struct parser *parser, const char *key) { bad = (0xfc001bffffffffffU >> c) & 1; } else if (c < 128) { // bitmask of disallowed ASCII characters 64-127 - bad = (0xfc0000017c000001U >> c) & 1; + bad = (0xf800000178000001 >> (c - 64)) & 1; } if (bad) { parser_error(parser, ERROR_KEY_INVALID_CHAR, c, c); @@ -1113,6 +1113,13 @@ static void libc_free(void *udata, void *ptr) { free(ptr); } +static void fix_settings(const pom_settings *in, pom_settings *out) { + *out = in ? *in : (pom_settings){0}; + if (!out->calloc) out->calloc = libc_calloc; + if (!out->realloc) out->realloc = libc_realloc; + if (!out->free) out->free = libc_free; +} + // make single pom_error out of all of parser's errors. static pom_error *parser_make_error(struct parser *parser) { // shouldn't realistically overflow given that we cut off at 1000 errors @@ -1160,10 +1167,7 @@ pom_load(const pom_settings *psettings, const char *filename, if (!read_func) fatal_error("%s called with NULL read function", __func__); pom_settings settings_data = {0}, *settings = &settings_data; - if (psettings) settings_data = *psettings; - if (!settings->calloc) settings->calloc = libc_calloc; - if (!settings->realloc) settings->realloc = libc_realloc; - if (!settings->free) settings->free = libc_free; + fix_settings(psettings, &settings_data); if (error) *error = NULL; // Start by allocating out-of-memory error, so we can just return // it if we run out of memory. @@ -1239,11 +1243,13 @@ read_file(void *file, char *buf, size_t len) { } pom_conf * -pom_load_file(const pom_settings *settings, const char *filename, FILE *file, pom_error **error) { +pom_load_file(const pom_settings *psettings, const char *filename, FILE *file, pom_error **error) { if (!filename) fatal_error("%s called with NULL file name", __func__); if (!file) fatal_error("%s called with NULL file", __func__); + pom_settings settings_data = {0}, *settings = &settings_data; + fix_settings(psettings, &settings_data); pom_conf *conf = pom_load(settings, filename, read_file, file, error); if (ferror(file)) { if (error) { @@ -1257,9 +1263,11 @@ pom_load_file(const pom_settings *settings, const char *filename, FILE *file, po } pom_conf * -pom_load_path(const pom_settings *settings, const char *path, pom_error **error) { +pom_load_path(const pom_settings *psettings, const char *path, pom_error **error) { if (!path) fatal_error("%s called with NULL file name", __func__); + pom_settings settings_data = {0}, *settings = &settings_data; + fix_settings(psettings, &settings_data); FILE *fp = fopen(path, "rb"); if (!fp) { if (error) { @@ -1869,7 +1877,7 @@ pom_conf_get_list(const pom_conf *conf, const char *key) { char *strings = (char *)(list + max_entries+1); const char *p = value_str; while (true) { - while (strchr(" \t\n", *p)) p++; + while (*p == ' ' || *p == '\t' || *p == '\n') p++; const char *end = p; char *out = *entry++ = strings; for (; *end; end++) { diff --git a/tests/interpretation.c b/tests/interpretation.c new file mode 100644 index 0000000..bdae290 --- /dev/null +++ b/tests/interpretation.c @@ -0,0 +1,163 @@ +#include "test.h" + +#include +#include +#include + +void test_interpretation(void) { + char **listing = list_dir("interpretation", ".pom"); + if (!listing) + return; + for (size_t l = 0; listing[l]; l++) { + const char *conf_path = listing[l]; + printf("Testing %s...\n",conf_path); + pom_error *error; + pom_conf *conf = pom_load_path(NULL, conf_path, &error); + if (error) { + test_fail("Error parsing %s: %s.", conf_path, + pom_error_to_string(error)); + free(error); + continue; + } + const pom_conf *bad = pom_conf_section(conf, "bad"); + const pom_conf *good = pom_conf_section(conf, "good"); + const char *key = NULL; + pom_key_iter *iter = NULL; + if (strstr(conf_path, "uint.pom")) { + uint64_t val, val2; + while ((key = pom_conf_next_key(bad, &iter))) { + if (!pom_conf_get_uint(bad, key, &val)) { + test_fail("Key %s should be rejected as a uint", key); + } + } + while ((key = pom_conf_next_key(good, &iter))) { + const pom_conf *section = pom_conf_section(good, key); + if (pom_conf_get_uint(section, "a", &val)) { + test_fail("Key %s.a should be parsed as a uint", key); + continue; + } + if (pom_conf_get_uint(section, "b", &val2)) { + test_fail("Key %s.a should be parsed as a uint", key); + continue; + } + if (val != val2) { + test_fail("Keys %s.a and %s.b disagree:\n" + "a: %" PRIu64 "\n" + "b: %" PRIu64, key, key, val, val2); + } + } + } else if (strstr(conf_path, "int.pom")) { + int64_t val, val2; + while ((key = pom_conf_next_key(bad, &iter))) { + if (!pom_conf_get_int(bad, key, &val)) { + test_fail("Key %s should be rejected as an int", key); + } + } + while ((key = pom_conf_next_key(good, &iter))) { + const pom_conf *section = pom_conf_section(good, key); + if (pom_conf_get_int(section, "a", &val)) { + test_fail("Key %s.a should be parsed as an int", key); + continue; + } + if (pom_conf_get_int(section, "b", &val2)) { + test_fail("Key %s.a should be parsed as an int", key); + continue; + } + if (val != val2) { + test_fail("Keys %s.a and %s.b disagree:\n" + "a: %" PRId64 "\n" + "b: %" PRId64, key, key, val, val2); + } + } + } else if (strstr(conf_path, "float.pom")) { + double val, val2; + while ((key = pom_conf_next_key(bad, &iter))) { + if (!pom_conf_get_float(bad, key, &val)) { + test_fail("Key %s should be rejected as a float", key); + } + } + while ((key = pom_conf_next_key(good, &iter))) { + const pom_conf *section = pom_conf_section(good, key); + if (pom_conf_get_float(section, "a", &val)) { + test_fail("Key %s.a should be parsed as a float", key); + continue; + } + if (pom_conf_get_float(section, "b", &val2)) { + test_fail("Key %s.a should be parsed as a float", key); + continue; + } + if (val != val2) { + test_fail("Keys %s.a and %s.b disagree:\n" + "a: %f\n" + "b: %f", key, key, val, val2); + } + } + } else if (strstr(conf_path, "bool.pom")) { + bool val, val2; + while ((key = pom_conf_next_key(bad, &iter))) { + if (!pom_conf_get_bool(bad, key, &val)) { + test_fail("Key %s should be rejected as a bool", key); + } + } + while ((key = pom_conf_next_key(good, &iter))) { + const pom_conf *section = pom_conf_section(good, key); + if (pom_conf_get_bool(section, "a", &val)) { + test_fail("Key %s.a should be parsed as a bool", key); + continue; + } + if (pom_conf_get_bool(section, "b", &val2)) { + test_fail("Key %s.a should be parsed as a bool", key); + continue; + } + if (val != val2) { + test_fail("Keys %s.a and %s.b disagree:\n" + "a: %d\n" + "b: %d", key, key, val, val2); + } + } + } else if (strstr(conf_path, "list.pom")) { + // TODO + while ((key = pom_conf_next_key(conf, &iter))) { + const pom_conf *section = pom_conf_section(conf, key); + char **list = pom_conf_get_list(section, "list"); + if (!list) { + test_fail("%s should have a subkey 'list'", key); + continue; + } + const char *sep = pom_conf_get(section, "sep"); + if (!sep) { + test_fail("%s should have a subkey 'sep'", key); + free(list); + continue; + } + size_t list_count, sep_count = 0; + for (list_count = 0; list[list_count]; list_count++); + for (size_t i = 0; sep[i]; i++) + sep_count += sep[i] == ';'; + if (list_count != sep_count) { + test_fail("List %s should have %zu elements (got %zu)", key, sep_count, list_count); + free(list); + continue; + } + for (size_t i = 0; list[i]; i++) { + const char *item = list[i]; + size_t sep_len = strcspn(sep, ";"); + if (strlen(item) != sep_len + || memcmp(item, sep, sep_len) != 0) { + test_fail("List %s element %zu is wrong:\n" + "expected: %.*s\n" + " got: %s\n", + key, i, (int)sep_len, sep, item); + } + sep += sep_len + 1; + } + free(list); + } + } else { + test_fail("Unrecognized test: %s",conf_path); + } + pom_conf_free(conf); + } + free_listing(listing); +// pom_conf *conf = pom_load_path("../tests"); +} diff --git a/tests/main.c b/tests/main.c index c34a79d..791fdc7 100644 --- a/tests/main.c +++ b/tests/main.c @@ -65,6 +65,7 @@ int main(int argc, char **argv) { test_parsing(); test_errors(); test_location(); + test_interpretation(); if (any_failure) { fprintf(stderr, "\x1b[1m\x1b[91mSome tests failed.\x1b[0m\n"); return EXIT_FAILURE; diff --git a/tests/test.h b/tests/test.h index 59807d1..c6109fb 100644 --- a/tests/test.h +++ b/tests/test.h @@ -16,5 +16,6 @@ void test_fail(PRINTF_FORMAT_STRING const char *, ...) ATTRIBUTE_PRINTF(1, 2); void test_parsing(void); void test_errors(void); void test_location(void); +void test_interpretation(void); char **list_dir(const char *dir, const char *suffix); void free_listing(char **listing); -- cgit v1.2.3