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 +