From be95ee0fc5bd050ece97423aa2f7c3a356e46538 Mon Sep 17 00:00:00 2001 From: Joshua Potter Date: Thu, 23 Nov 2023 04:09:32 -0700 Subject: [PATCH] Add config tests. --- .gitignore | 1 + Makefile | 20 ++- include/config.h | 6 +- src/config.c | 9 +- test/runner.c | 12 ++ test/sput.h | 319 +++++++++++++++++++++++++++++++++++++++++++++ test/test_config.h | 27 ++++ 7 files changed, 384 insertions(+), 10 deletions(-) create mode 100644 test/runner.c create mode 100644 test/sput.h create mode 100644 test/test_config.h diff --git a/.gitignore b/.gitignore index 147e677..aa76474 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .direnv/ compile_commands.json spec +test/runner diff --git a/Makefile b/Makefile index 3bb155e..65ed709 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,17 @@ -COMMAND=clang -g -I include src/*.c main.c -o spec +BUILD=clang -g -I include src/*.c main.c -o spec -all: - @${COMMAND} +all: build bear -bear: - @bear -- ${COMMAND} +build: include/*.h src/*.c + @${BUILD} + +bear: include/*.h src/*.c + @bear -- ${BUILD} + +test: test/runner + $^ + +test/runner: include/*.h src/*.c test/*.c + clang -I include src/*.c test/*.c -o test/runner + +.PHONY: test diff --git a/include/config.h b/include/config.h index 0564375..fe4e563 100644 --- a/include/config.h +++ b/include/config.h @@ -17,7 +17,11 @@ enum ConfigError { INVALID_TARGET, }; -enum ConfigError load_config(const char *target, struct Config *config); +enum ConfigError load_config( + const char *root_dir, + const char *target, + struct Config *config +); void free_config(struct Config *config); diff --git a/src/config.c b/src/config.c index bbcbb07..2ec9ce8 100644 --- a/src/config.c +++ b/src/config.c @@ -3,10 +3,11 @@ #include "config.h" -const char *ENV_SPEC_ROOT_DIR = "SPEC_ROOT_DIR"; - -enum ConfigError load_config(const char *target, struct Config *config) { - const char *root_dir = getenv(ENV_SPEC_ROOT_DIR); +enum ConfigError load_config( + const char *root_dir, + const char *target, + struct Config *config +) { if (root_dir == 0) { return ENV_SPEC_ROOT_DIR_MISSING; } diff --git a/test/runner.c b/test/runner.c new file mode 100644 index 0000000..a4a7645 --- /dev/null +++ b/test/runner.c @@ -0,0 +1,12 @@ +#include "sput.h" +#include "test_config.h" + +int main(int argc, char *argv[]) { + sput_start_testing(); + + test_config_run(); + + sput_finish_testing(); + + return sput_get_return_value(); +} diff --git a/test/sput.h b/test/sput.h new file mode 100644 index 0000000..31d9e27 --- /dev/null +++ b/test/sput.h @@ -0,0 +1,319 @@ +/* + * sput - Simple, Portable Unit Testing Framework for C/C++ v1.4.0 + * + * http://www.use-strict.de/sput-unit-testing/ + * + * + * Copyright (C) 2011-2015 Lingua-Systems Software GmbH + * Copyright (C) 2016 Alex Linke + * + * All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef HAVE_SPUT_H +#define HAVE_SPUT_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include + + + /* =================================================================== + * definitions + * =================================================================== */ + +#define SPUT_VERSION_MAJOR 1 +#define SPUT_VERSION_MINOR 4 +#define SPUT_VERSION_PATCH 0 +#define SPUT_VERSION_STRING "1.4.0" + +#define SPUT_DEFAULT_SUITE_NAME "Unlabeled Suite" +#define SPUT_DEFAULT_CHECK_NAME "Unlabeled Check" + +#define SPUT_INITIALIZED 0x06 /* ACK */ + + + /* =================================================================== + * sput global variable + * =================================================================== */ + + static struct sput + { + FILE *out; + char initialized; + + struct sput_overall + { + unsigned long checks; + unsigned long suites; + unsigned long ok; + unsigned long nok; + } overall; + + struct sput_suite + { + const char *name; + unsigned long nr; + unsigned long checks; + unsigned long ok; + unsigned long nok; + } suite; + + struct sput_test + { + const char *name; + unsigned long nr; + } test; + + struct sput_check + { + const char *name; + const char *cond; + const char *type; + unsigned long line; + } check; + + struct sput_time + { + time_t start; + time_t end; + } time; + } __sput; + + + /* ================================================================== + * sput internal macros + * ================================================================== */ + +#define _sput_die_unless_initialized() \ + if (__sput.initialized != SPUT_INITIALIZED) \ + { \ + fputs("sput_start_testing() omitted\n", stderr); \ + exit(EXIT_FAILURE); \ + } + + +#define _sput_die_unless_suite_set() \ + if (! __sput.suite.name) \ + { \ + fputs("sput_enter_suite() omitted\n", __sput.out); \ + exit(EXIT_FAILURE); \ + } + + +#define _sput_die_unless_test_set() \ + if (! __sput.test.name) \ + { \ + fputs("sput_run_test() omitted\n", __sput.out); \ + exit(EXIT_FAILURE); \ + } + + +#define _sput_check_failed() \ + { \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + __sput.suite.nok++; \ + fprintf(__sput.out, \ + "[%lu:%lu] %s:#%lu \"%s\" FAIL\n" \ + "! Type: %s\n" \ + "! Condition: %s\n" \ + "! Line: %lu\n", \ + __sput.suite.nr, __sput.suite.checks, __sput.test.name, \ + __sput.test.nr, __sput.check.name, __sput.check.type, \ + __sput.check.cond, __sput.check.line); \ + } + + +#define _sput_check_succeeded() \ + { \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + __sput.suite.ok++; \ + fprintf(__sput.out, \ + "[%lu:%lu] %s:#%lu \"%s\" pass\n", \ + __sput.suite.nr, __sput.suite.checks, \ + __sput.test.name, \ + __sput.test.nr, \ + __sput.check.name); \ + } + + + /* ================================================================== + * user macros + * ================================================================== */ + +#define sput_start_testing() \ + do { \ + memset(&__sput, 0, sizeof(__sput)); \ + __sput.out = stdout; \ + __sput.time.start = time(NULL); \ + __sput.initialized = SPUT_INITIALIZED; \ + } while (0) + + +#define sput_leave_suite() \ + do { \ + float failpls = 0.0f; \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + failpls = __sput.suite.checks ? (float) \ + ((__sput.suite.nok * 100.0) / __sput.suite.checks) : \ + 0.0f; \ + fprintf(__sput.out, \ + "\n--> %lu check(s), %lu ok, %lu failed (%.2f%%)\n", \ + __sput.suite.checks, __sput.suite.ok, __sput.suite.nok, \ + failpls); \ + __sput.overall.checks += __sput.suite.checks; \ + __sput.overall.ok += __sput.suite.ok; \ + __sput.overall.nok += __sput.suite.nok; \ + memset(&__sput.suite, 0, sizeof(__sput.suite)); \ + } while (0) + + +#define sput_get_return_value() \ + (__sput.overall.nok > 0 ? EXIT_FAILURE : EXIT_SUCCESS) + + +#define sput_enter_suite(_name) \ + do { \ + _sput_die_unless_initialized(); \ + if (__sput.suite.name) \ + { \ + sput_leave_suite(); \ + } \ + __sput.suite.name = _name != NULL ? \ + _name : SPUT_DEFAULT_SUITE_NAME; \ + __sput.suite.nr = ++__sput.overall.suites; \ + fprintf(__sput.out, "\n== Entering suite #%lu, \"%s\" ==\n\n", \ + __sput.suite.nr, __sput.suite.name); \ + } while (0) + + +#define sput_finish_testing() \ + do { \ + float failpft = 0.0f; \ + _sput_die_unless_initialized(); \ + if (__sput.suite.name) \ + { \ + sput_leave_suite(); \ + } \ + failpft = __sput.overall.checks ? (float) \ + ((__sput.overall.nok * 100.0) / __sput.overall.checks) : \ + 0.0f; \ + __sput.time.end = time(NULL); \ + fprintf(__sput.out, \ + "\n==> %lu check(s) in %lu suite(s) finished after %.2f " \ + "second(s),\n" \ + " %lu succeeded, %lu failed (%.2f%%)\n" \ + "\n[%s]\n", \ + __sput.overall.checks, __sput.overall.suites, \ + difftime(__sput.time.end, __sput.time.start), \ + __sput.overall.ok, __sput.overall.nok, failpft, \ + (sput_get_return_value() == EXIT_SUCCESS) ? \ + "SUCCESS" : "FAILURE"); \ + } while (0) + + +#define sput_set_output_stream(_fp) \ + do { \ + __sput.out = _fp != NULL ? _fp : stdout; \ + } while (0) + + +#define sput_fail_if(_cond, _name) \ + do { \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + _sput_die_unless_test_set(); \ + __sput.check.name = _name != NULL ? \ + _name : SPUT_DEFAULT_CHECK_NAME; \ + __sput.check.line = __LINE__; \ + __sput.check.cond = #_cond; \ + __sput.check.type = "fail-if"; \ + __sput.test.nr++; \ + __sput.suite.checks++; \ + if ((_cond)) \ + { \ + _sput_check_failed(); \ + } \ + else \ + { \ + _sput_check_succeeded(); \ + } \ + } while (0) + + +#define sput_fail_unless(_cond, _name) \ + do { \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + _sput_die_unless_test_set(); \ + __sput.check.name = _name != NULL ? \ + _name : SPUT_DEFAULT_CHECK_NAME; \ + __sput.check.line = __LINE__; \ + __sput.check.cond = #_cond; \ + __sput.check.type = "fail-unless"; \ + __sput.test.nr++; \ + __sput.suite.checks++; \ + if (! (_cond)) \ + { \ + _sput_check_failed(); \ + } \ + else \ + { \ + _sput_check_succeeded(); \ + } \ + } while (0) + + +#define sput_run_test(_func) \ + do { \ + _sput_die_unless_initialized(); \ + _sput_die_unless_suite_set(); \ + memset(&__sput.test, 0, sizeof(__sput.test)); \ + __sput.test.name = #_func; \ + _func(); \ + } while (0) + + +#ifdef __cplusplus +} +#endif + + +#endif /* HAVE_SPUT_H */ + + +/* vim: set ft=c sts=4 sw=4 ts=4 ai et: */ diff --git a/test/test_config.h b/test/test_config.h new file mode 100644 index 0000000..1efdb18 --- /dev/null +++ b/test/test_config.h @@ -0,0 +1,27 @@ +#ifndef _SPEC_TEST_CONFIG +#define _SPEC_TEST_CONFIG + +#include "config.h" +#include "sput.h" + +static void test_load_config_root_dir_missing() { + struct Config config; + enum ConfigError retval = load_config(0, "target", &config); + + sput_fail_unless(retval == ENV_SPEC_ROOT_DIR_MISSING, "root_dir == 0"); +} + +static void test_load_config_root_dir_empty() { + struct Config config; + enum ConfigError retval = load_config("", "target", &config); + + sput_fail_unless(retval == ENV_SPEC_ROOT_DIR_EMPTY, "root_dir == \"\""); +} + +static void test_config_run() { + sput_enter_suite("load_config()"); + sput_run_test(test_load_config_root_dir_missing); + sput_run_test(test_load_config_root_dir_empty); +} + +#endif /* _SPEC_TEST_CONFIG */