src/cx/test.h

changeset 766
e59b76889f00
child 767
d31f4d4075dc
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/cx/test.h	Wed Dec 20 16:46:14 2023 +0100
     1.3 @@ -0,0 +1,326 @@
     1.4 +/*
     1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 + *
     1.7 + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
     1.8 + *
     1.9 + * Redistribution and use in source and binary forms, with or without
    1.10 + * modification, are permitted provided that the following conditions are met:
    1.11 + *
    1.12 + *   1. Redistributions of source code must retain the above copyright
    1.13 + *      notice, this list of conditions and the following disclaimer.
    1.14 + *
    1.15 + *   2. Redistributions in binary form must reproduce the above copyright
    1.16 + *      notice, this list of conditions and the following disclaimer in the
    1.17 + *      documentation and/or other materials provided with the distribution.
    1.18 + *
    1.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    1.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    1.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    1.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    1.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    1.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    1.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    1.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    1.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    1.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    1.29 + * POSSIBILITY OF SUCH DAMAGE.
    1.30 + */
    1.31 + 
    1.32 +/**
    1.33 + * @file: test.h
    1.34 + * 
    1.35 + * UCX Test Framework.
    1.36 + * 
    1.37 + * Usage of this test framework:
    1.38 + *
    1.39 + * **** IN HEADER FILE: ****
    1.40 + *
    1.41 + * <pre>
    1.42 + * CX_TEST(function_name);
    1.43 + * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
    1.44 + * </pre>
    1.45 + *
    1.46 + * **** IN SOURCE FILE: ****
    1.47 + * <pre>
    1.48 + * CX_TEST_SUBROUTINE(subroutine_name, paramlist) {
    1.49 + *   // tests with CX_TEST_ASSERT()
    1.50 + * }
    1.51 + * 
    1.52 + * CX_TEST(function_name) {
    1.53 + *   // memory allocation and other stuff here
    1.54 + *   #CX_TEST_BEGIN
    1.55 + *   // tests with CX_TEST_ASSERT() and/or
    1.56 + *   // calls with CX_TEST_CALL_SUBROUTINE() here
    1.57 + *   #CX_TEST_END
    1.58 + *   // cleanup of memory here
    1.59 + * }
    1.60 + * </pre>
    1.61 + *
    1.62 + * @remark if a test fails, execution continues at the
    1.63 + * #CX_TEST_END macro! So make sure every necessary cleanup happens afterwards.
    1.64 + * 
    1.65 + * @attention Do not call own functions within a test, that use
    1.66 + * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE().
    1.67 + *
    1.68 + * @author Mike Becker
    1.69 + * @author Olaf Wintermann
    1.70 + *
    1.71 + */
    1.72 +
    1.73 +#ifndef UCX_TEST_H
    1.74 +#define	UCX_TEST_H
    1.75 +
    1.76 +#include <stdlib.h>
    1.77 +#include <stdio.h>
    1.78 +#include <string.h>
    1.79 +#include <setjmp.h>
    1.80 +
    1.81 +#ifdef	__cplusplus
    1.82 +extern "C" {
    1.83 +#endif
    1.84 +
    1.85 +#ifndef __FUNCTION__
    1.86 +/**
    1.87 + * Alias for the <code>__func__</code> preprocessor macro.
    1.88 + * Some compilers use <code>__func__</code> and others use __FUNCTION__.
    1.89 + * We use __FUNCTION__ so we define it for those compilers which use
    1.90 + * <code>__func__</code>.
    1.91 + */
    1.92 +#define __FUNCTION__ __func__
    1.93 +#endif
    1.94 +
    1.95 +#ifndef UCX_COMMON_H
    1.96 +/**
    1.97 + * Function pointer compatible with fwrite-like functions.
    1.98 + */
    1.99 +typedef size_t (*cx_write_func)(
   1.100 +        void const *,
   1.101 +        size_t,
   1.102 +        size_t,
   1.103 +        void *
   1.104 +);
   1.105 +#endif // UCX_COMMON_H
   1.106 +
   1.107 +/** Type for the CxTestSuite. */
   1.108 +typedef struct CxTestSuite CxTestSuite;
   1.109 +
   1.110 +/** Pointer to a test function. */
   1.111 +typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func);
   1.112 +
   1.113 +/** Type for the internal list of test cases. */
   1.114 +typedef struct CxTestSet CxTestSet;
   1.115 +
   1.116 +/** Structure for the internal list of test cases. */
   1.117 +struct CxTestSet {
   1.118 +    
   1.119 +    /** Test case. */
   1.120 +    CxTest test;
   1.121 +    
   1.122 +    /** Pointer to the next list element. */
   1.123 +    CxTestSet *next;
   1.124 +};
   1.125 +
   1.126 +/**
   1.127 + * A test suite containing multiple test cases.
   1.128 + */
   1.129 +struct CxTestSuite {
   1.130 +    
   1.131 +    /** The number of successful tests after the suite has been run. */
   1.132 +    unsigned int success;
   1.133 +    
   1.134 +    /** The number of failed tests after the suite has been run. */
   1.135 +    unsigned int failure;
   1.136 +
   1.137 +    /** The optional name of this test suite. */
   1.138 +    char const *name;
   1.139 +    
   1.140 +    /**
   1.141 +     * Internal list of test cases.
   1.142 +     * Use cx_test_register() to add tests to this list.
   1.143 +     */
   1.144 +    CxTestSet *tests;
   1.145 +};
   1.146 +
   1.147 +/**
   1.148 + * Creates a new test suite.
   1.149 + * @param name optional name of the suite
   1.150 + * @return a new test suite
   1.151 + */
   1.152 +static inline CxTestSuite* cx_test_suite_new(char const *name) {
   1.153 +    CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
   1.154 +    if (suite != NULL) {
   1.155 +        suite->name = name;
   1.156 +        suite->success = 0;
   1.157 +        suite->failure = 0;
   1.158 +        suite->tests = NULL;
   1.159 +    }
   1.160 +
   1.161 +    return suite;
   1.162 +}
   1.163 +
   1.164 +/**
   1.165 + * Destroys a test suite.
   1.166 + * @param suite the test suite to destroy
   1.167 + */
   1.168 +static inline void cx_test_suite_free(CxTestSuite* suite) {
   1.169 +    CxTestSet *l = suite->tests;
   1.170 +    while (l != NULL) {
   1.171 +        CxTestSet *e = l;
   1.172 +        l = l->next;
   1.173 +        free(e);
   1.174 +    }
   1.175 +    free(suite);
   1.176 +}
   1.177 +
   1.178 +/**
   1.179 + * Registers a test function with the specified test suite.
   1.180 + * 
   1.181 + * @param suite the suite, the test function shall be added to
   1.182 + * @param test the test function to register
   1.183 + * @return zero on success or non-zero on failure
   1.184 + */
   1.185 +static inline int cx_test_register(CxTestSuite* suite, CxTest test) {
   1.186 +    CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
   1.187 +    if (t) {
   1.188 +        t->test = test;
   1.189 +        t->next = NULL;
   1.190 +        if (suite->tests == NULL) {
   1.191 +            suite->tests = t;
   1.192 +        } else {
   1.193 +            CxTestSet *last = suite->tests;
   1.194 +            while (last->next) {
   1.195 +                last = last->next;
   1.196 +            }
   1.197 +            last->next = t;
   1.198 +        }
   1.199 +        return 0;
   1.200 +    } else {
   1.201 +        return 1;
   1.202 +    }
   1.203 +}
   1.204 +
   1.205 +/**
   1.206 + * Runs a test suite and writes the test log to the specified stream.
   1.207 + * @param suite the test suite to run
   1.208 + * @param out_target the target buffer or file to write the output to
   1.209 + * @param out_writer the write function writing to \p out_target
   1.210 + */
   1.211 +static inline void cx_test_run(CxTestSuite *suite,
   1.212 +                               void *out_target, cx_write_func out_writer) {
   1.213 +    if (suite->name == NULL) {
   1.214 +        out_writer("*** Test Suite ***\n", 1, 19, out_target);
   1.215 +    } else {
   1.216 +        out_writer("*** Test Suite : ", 1, 17, out_target);
   1.217 +        out_writer(suite->name, 1, strlen(suite->name), out_target);
   1.218 +        out_writer(" ***\n", 1, 5, out_target);
   1.219 +    }
   1.220 +    suite->success = 0;
   1.221 +    suite->failure = 0;
   1.222 +    for (CxTestSet *elem = suite->tests; elem; elem = elem->next) {
   1.223 +        elem->test(suite, out_target, out_writer);
   1.224 +    }
   1.225 +    out_writer("\nAll test completed.\n", 1, 21, out_target);
   1.226 +    char total[80];
   1.227 +    int len = snprintf(
   1.228 +            total, 80,
   1.229 +            "  Total:   %u\n  Success: %u\n  Failure: %u\n",
   1.230 +            suite->success + suite->failure, suite->success, suite->failure
   1.231 +    );
   1.232 +    out_writer(total, 1, len, out_target);
   1.233 +}
   1.234 +
   1.235 +/**
   1.236 + * Runs a test suite and writes the test log to the specified FILE stream.
   1.237 + * @param suite the test suite to run
   1.238 + * @param file the target file to write the output to
   1.239 + */
   1.240 +#define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite)
   1.241 +
   1.242 +/**
   1.243 + * Runs a test suite and writes the test log to stdout.
   1.244 + * @param suite the test suite to run
   1.245 + */
   1.246 +#define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout)
   1.247 +
   1.248 +/**
   1.249 + * Macro for a #CxTest function header.
   1.250 + * 
   1.251 + * Use this macro to declare and/or define a #CxTest function.
   1.252 + * 
   1.253 + * @param name the name of the test function
   1.254 + */
   1.255 +#define CX_TEST(name) void name(CxTestSuite* _suite_,void *_output_, cx_write_func _writefnc_)
   1.256 +
   1.257 +/**
   1.258 + * Marks the begin of a test.
   1.259 + * <b>Note:</b> Any CX_TEST_ASSERT() calls must be performed <b>after</b>
   1.260 + * #CX_TEST_BEGIN.
   1.261 + * 
   1.262 + * @see #CX_TEST_END
   1.263 + */
   1.264 +#define CX_TEST_BEGIN _writefnc_("Running ", 1, 8, _output_);\
   1.265 +        _writefnc_(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
   1.266 +        _writefnc_("... ", 1, 4, _output_);\
   1.267 +        jmp_buf _env_; \
   1.268 +        if (!setjmp(_env_)) {
   1.269 +
   1.270 +/**
   1.271 + * Checks a test assertion.
   1.272 + * If the assertion is correct, the test carries on. If the assertion is not
   1.273 + * correct, the specified message (terminated by a dot and a line break) is
   1.274 + * written to the test suites output stream.
   1.275 + * @param condition the condition to check
   1.276 + * @param message the message that shall be printed out on failure
   1.277 + */
   1.278 +#define CX_TEST_ASSERT(condition,message) if (!(condition)) { \
   1.279 +        _writefnc_(message".\n", 1, 2+strlen(message), _output_); \
   1.280 +        _suite_->failure++; \
   1.281 +        longjmp(_env_, 1);\
   1.282 +    }
   1.283 +
   1.284 +/**
   1.285 + * Macro for a test subroutine function header.
   1.286 + * 
   1.287 + * Use this to declare and/or define a subroutine that can be called by using
   1.288 + * CX_TEST_CALL_SUBROUTINE().
   1.289 + * 
   1.290 + * @param name the name of the subroutine
   1.291 + * @param ... the parameter list
   1.292 + * 
   1.293 + * @see CX_TEST_CALL_SUBROUTINE()
   1.294 + */
   1.295 +#define CX_TEST_SUBROUTINE(name,...) void name(CxTestSuite* _suite_,\
   1.296 +        void *_output_, jmp_buf _env_, __VA_ARGS__)
   1.297 +
   1.298 +/**
   1.299 + * Macro for calling a test subroutine.
   1.300 + * 
   1.301 + * Subroutines declared with CX_TEST_SUBROUTINE() can be called by using this
   1.302 + * macro.
   1.303 + * 
   1.304 + * <b>Note:</b> You may <b>only</b> call subroutines within a #CX_TEST_BEGIN-
   1.305 + * #CX_TEST_END-block.
   1.306 + * 
   1.307 + * @param name the name of the subroutine
   1.308 + * @param ... the argument list
   1.309 + * 
   1.310 + * @see CX_TEST_SUBROUTINE()
   1.311 + */
   1.312 +#define CX_TEST_CALL_SUBROUTINE(name,...) \
   1.313 +        name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__);
   1.314 +
   1.315 +/**
   1.316 + * Marks the end of a test.
   1.317 + * <b>Note:</b> Any CX_TEST_ASSERT() calls must be performed <b>before</b>
   1.318 + * #CX_TEST_END.
   1.319 + * 
   1.320 + * @see #CX_TEST_BEGIN
   1.321 + */
   1.322 +#define CX_TEST_END _writefnc_("success.\n", 1, 9, _output_); _suite_->success++;}
   1.323 +
   1.324 +#ifdef	__cplusplus
   1.325 +}
   1.326 +#endif
   1.327 +
   1.328 +#endif	/* UCX_TEST_H */
   1.329 +

mercurial