Path: blob/main/sys/contrib/openzfs/tests/unit/munit.c
289319 views
// SPDX-License-Identifier: MIT1/* µnit Testing Framework2* Copyright (c) 2013-2018 Evan Nemerson <[email protected]>3*4* Permission is hereby granted, free of charge, to any person5* obtaining a copy of this software and associated documentation6* files (the "Software"), to deal in the Software without7* restriction, including without limitation the rights to use, copy,8* modify, merge, publish, distribute, sublicense, and/or sell copies9* of the Software, and to permit persons to whom the Software is10* furnished to do so, subject to the following conditions:11*12* The above copyright notice and this permission notice shall be13* included in all copies or substantial portions of the Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,16* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF17* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND18* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS19* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN20* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN21* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE22* SOFTWARE.23*/2425/*** Configuration ***/2627/* This is just where the output from the test goes. It's really just28* meant to let you choose stdout or stderr, but if anyone really want29* to direct it to a file let me know, it would be fairly easy to30* support. */31#if !defined(MUNIT_OUTPUT_FILE)32# define MUNIT_OUTPUT_FILE stdout33#endif3435/* This is a bit more useful; it tells µnit how to format the seconds in36* timed tests. If your tests run for longer you might want to reduce37* it, and if your computer is really fast and your tests are tiny you38* can increase it. */39#if !defined(MUNIT_TEST_TIME_FORMAT)40# define MUNIT_TEST_TIME_FORMAT "0.8f"41#endif4243/* If you have long test names you might want to consider bumping44* this. The result information takes 43 characters. */45#if !defined(MUNIT_TEST_NAME_LEN)46# define MUNIT_TEST_NAME_LEN 3747#endif4849/* If you don't like the timing information, you can disable it by50* defining MUNIT_DISABLE_TIMING. */51#if !defined(MUNIT_DISABLE_TIMING)52# define MUNIT_ENABLE_TIMING53#endif5455/* OpenZFS: claim no strerror_r, causing munit to use its own internal56* fallback. There are two version of strerror_r (XSI and GNU), subtly57* different, and some glibc versions have warn_unused_result set on the58* prototype. munit is not prepared for this variance, so better just to59* let it do its own thing. -- robn, 2026-05-21 */60#if !defined(MUNIT_NO_STRERROR_R)61# define MUNIT_NO_STRERROR_R62#endif6364/*** End configuration ***/6566#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L)67# undef _POSIX_C_SOURCE68#endif69#if !defined(_POSIX_C_SOURCE)70# define _POSIX_C_SOURCE 200809L71#endif7273/* Solaris freaks out if you try to use a POSIX or SUS standard without74* the "right" C standard. */75#if defined(_XOPEN_SOURCE)76# undef _XOPEN_SOURCE77#endif7879#if defined(__STDC_VERSION__)80# if __STDC_VERSION__ >= 201112L81# define _XOPEN_SOURCE 70082# elif __STDC_VERSION__ >= 199901L83# define _XOPEN_SOURCE 60084# endif85#endif8687/* Because, according to Microsoft, POSIX is deprecated. You've got88* to appreciate the chutzpah. */89#if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE)90# define _CRT_NONSTDC_NO_DEPRECATE91#endif9293#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)94# include <stdbool.h>95#elif defined(_WIN32)96/* https://msdn.microsoft.com/en-us/library/tf4dy80a.aspx */97#endif9899#include <limits.h>100#include <time.h>101#include <errno.h>102#include <string.h>103#include <stdlib.h>104#include <stdio.h>105#include <stdarg.h>106#include <setjmp.h>107108#if !defined(MUNIT_NO_NL_LANGINFO) && !defined(_WIN32)109# define MUNIT_NL_LANGINFO110# include <locale.h>111# include <langinfo.h>112# include <strings.h>113#endif114115#if !defined(_WIN32)116# include <unistd.h>117# include <sys/types.h>118# include <sys/wait.h>119#else120# include <windows.h>121# include <io.h>122# include <fcntl.h>123# if !defined(STDERR_FILENO)124# define STDERR_FILENO _fileno(stderr)125# endif126#endif127128#include "munit.h"129130#define MUNIT_STRINGIFY(x) #x131#define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x)132133#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || \134defined(__IBMCPP__)135# define MUNIT_THREAD_LOCAL __thread136#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) || \137defined(_Thread_local)138# define MUNIT_THREAD_LOCAL _Thread_local139#elif defined(_WIN32)140# define MUNIT_THREAD_LOCAL __declspec(thread)141#endif142143/* MSVC 12.0 will emit a warning at /W4 for code like 'do { ... }144* while (0)', or 'do { ... } while (1)'. I'm pretty sure nobody145* at Microsoft compiles with /W4. */146#if defined(_MSC_VER) && (_MSC_VER <= 1800)147# pragma warning(disable : 4127)148#endif149150#if defined(_WIN32) || defined(__EMSCRIPTEN__)151# define MUNIT_NO_FORK152#endif153154#if defined(__EMSCRIPTEN__)155# define MUNIT_NO_BUFFER156#endif157158/*** Logging ***/159160static MunitLogLevel munit_log_level_visible = MUNIT_LOG_INFO;161static MunitLogLevel munit_log_level_fatal = MUNIT_LOG_ERROR;162163#if defined(MUNIT_THREAD_LOCAL)164static MUNIT_THREAD_LOCAL munit_bool munit_error_jmp_buf_valid = 0;165static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf;166#endif167168/* At certain warning levels, mingw will trigger warnings about169* suggesting the format attribute, which we've explicity *not* set170* because it will then choke on our attempts to use the MS-specific171* I64 modifier for size_t (which we have to use since MSVC doesn't172* support the C99 z modifier). */173174#if defined(__MINGW32__) || defined(__MINGW64__)175# pragma GCC diagnostic push176# pragma GCC diagnostic ignored "-Wsuggest-attribute=format"177#endif178179MUNIT_PRINTF(5, 0)180static void munit_logf_exv(MunitLogLevel level, FILE *fp, const char *filename,181int line, const char *format, va_list ap) {182if (level < munit_log_level_visible)183return;184185switch (level) {186case MUNIT_LOG_DEBUG:187fputs("Debug", fp);188break;189case MUNIT_LOG_INFO:190fputs("Info", fp);191break;192case MUNIT_LOG_WARNING:193fputs("Warning", fp);194break;195case MUNIT_LOG_ERROR:196fputs("Error", fp);197break;198default:199munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)",200level);201return;202}203204fputs(": ", fp);205if (filename != NULL)206fprintf(fp, "%s:%d: ", filename, line);207vfprintf(fp, format, ap);208fputc('\n', fp);209}210211MUNIT_PRINTF(3, 4)212static void munit_logf_internal(MunitLogLevel level, FILE *fp,213const char *format, ...) {214va_list ap;215216va_start(ap, format);217munit_logf_exv(level, fp, NULL, 0, format, ap);218va_end(ap);219}220221static void munit_log_internal(MunitLogLevel level, FILE *fp,222const char *message) {223munit_logf_internal(level, fp, "%s", message);224}225226void munit_logf_ex(MunitLogLevel level, const char *filename, int line,227const char *format, ...) {228va_list ap;229230va_start(ap, format);231munit_logf_exv(level, stderr, filename, line, format, ap);232va_end(ap);233234if (level >= munit_log_level_fatal) {235#if defined(MUNIT_THREAD_LOCAL)236if (munit_error_jmp_buf_valid)237longjmp(munit_error_jmp_buf, 1);238#endif239abort();240}241}242243void munit_errorf_ex(const char *filename, int line, const char *format, ...) {244va_list ap;245246va_start(ap, format);247munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap);248va_end(ap);249250#if defined(MUNIT_THREAD_LOCAL)251if (munit_error_jmp_buf_valid)252longjmp(munit_error_jmp_buf, 1);253#endif254abort();255}256257#if defined(__MINGW32__) || defined(__MINGW64__)258# pragma GCC diagnostic pop259#endif260261#if !defined(MUNIT_STRERROR_LEN)262# define MUNIT_STRERROR_LEN 80263#endif264265static void munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) {266#if defined(MUNIT_NO_STRERROR_R) || \267(defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API))268munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno);269#else270char munit_error_str[MUNIT_STRERROR_LEN];271munit_error_str[0] = '\0';272273# if !defined(_WIN32)274strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN);275# else276strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno);277# endif278279munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno);280#endif281}282283/*** Memory allocation ***/284285void *munit_malloc_ex(const char *filename, int line, size_t size) {286void *ptr;287288if (size == 0)289return NULL;290291ptr = calloc(1, size);292if (MUNIT_UNLIKELY(ptr == NULL)) {293munit_logf_ex(MUNIT_LOG_ERROR, filename, line,294"Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size);295}296297return ptr;298}299300/*** Timer code ***/301302#if defined(MUNIT_ENABLE_TIMING)303304# define psnip_uint64_t munit_uint64_t305# define psnip_uint32_t munit_uint32_t306307/* Code copied from portable-snippets308* <https://github.com/nemequ/portable-snippets/>. If you need to309* change something, please do it there so we can keep the code in310* sync. */311312/* Clocks (v1)313* Portable Snippets - https://gitub.com/nemequ/portable-snippets314* Created by Evan Nemerson <[email protected]>315*316* To the extent possible under law, the authors have waived all317* copyright and related or neighboring rights to this code. For318* details, see the Creative Commons Zero 1.0 Universal license at319* https://creativecommons.org/publicdomain/zero/1.0/320*/321322# if !defined(PSNIP_CLOCK_H)323# define PSNIP_CLOCK_H324325# if !defined(psnip_uint64_t)326# include "../exact-int/exact-int.h"327# endif328329# if !defined(PSNIP_CLOCK_STATIC_INLINE)330# if defined(__GNUC__)331# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))332# else333# define PSNIP_CLOCK__COMPILER_ATTRIBUTES334# endif335336# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static337# endif338339enum PsnipClockType {340/* This clock provides the current time, in units since 1970-01-01341* 00:00:00 UTC not including leap seconds. In other words, UNIX342* time. Keep in mind that this clock doesn't account for leap343* seconds, and can go backwards (think NTP adjustments). */344PSNIP_CLOCK_TYPE_WALL = 1,345/* The CPU time is a clock which increases only when the current346* process is active (i.e., it doesn't increment while blocking on347* I/O). */348PSNIP_CLOCK_TYPE_CPU = 2,349/* Monotonic time is always running (unlike CPU time), but it only350ever moves forward unless you reboot the system. Things like NTP351adjustments have no effect on this clock. */352PSNIP_CLOCK_TYPE_MONOTONIC = 3353};354355struct PsnipClockTimespec {356psnip_uint64_t seconds;357psnip_uint64_t nanoseconds;358};359360/* Methods we support: */361362# define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1363# define PSNIP_CLOCK_METHOD_TIME 2364# define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3365# define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4366# define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5367# define PSNIP_CLOCK_METHOD_CLOCK 6368# define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7369# define PSNIP_CLOCK_METHOD_GETRUSAGE 8370# define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9371# define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10372373# include <assert.h>374375# if defined(HEDLEY_UNREACHABLE)376# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()377# else378# define PSNIP_CLOCK_UNREACHABLE() assert(0)379# endif380381/* Choose an implementation */382383/* #undef PSNIP_CLOCK_WALL_METHOD */384/* #undef PSNIP_CLOCK_CPU_METHOD */385/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */386387/* We want to be able to detect the libc implementation, so we include388<limits.h> (<features.h> isn't available everywhere). */389390# if defined(__unix__) || defined(__unix) || defined(__linux__)391# include <limits.h>392# include <unistd.h>393# endif394395# if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)396/* These are known to work without librt. If you know of others397* please let us know so we can add them. */398# if (defined(__GLIBC__) && \399(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \400(defined(__FreeBSD__))401# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME402# elif !defined(PSNIP_CLOCK_NO_LIBRT)403# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME404# endif405# endif406407# if defined(_WIN32)408# if !defined(PSNIP_CLOCK_CPU_METHOD)409# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES410# endif411# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)412# define PSNIP_CLOCK_MONOTONIC_METHOD \413PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER414# endif415# endif416417# if defined(__MACH__) && !defined(__gnu_hurd__)418# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)419# define PSNIP_CLOCK_MONOTONIC_METHOD \420PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME421# endif422# endif423424# if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)425# include <time.h>426# if !defined(PSNIP_CLOCK_WALL_METHOD)427# if defined(CLOCK_REALTIME_PRECISE)428# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME429# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE430# elif !defined(__sun)431# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME432# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME433# endif434# endif435# if !defined(PSNIP_CLOCK_CPU_METHOD)436# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)437# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME438# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID439# elif defined(CLOCK_VIRTUAL)440# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME441# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL442# endif443# endif444# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)445# if defined(CLOCK_MONOTONIC_RAW)446# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME447# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC448# elif defined(CLOCK_MONOTONIC_PRECISE)449# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME450# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE451# elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)452# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME453# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC454# endif455# endif456# endif457458# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)459# if !defined(PSNIP_CLOCK_WALL_METHOD)460# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY461# endif462# endif463464# if !defined(PSNIP_CLOCK_WALL_METHOD)465# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME466# endif467468# if !defined(PSNIP_CLOCK_CPU_METHOD)469# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK470# endif471472/* Primarily here for testing. */473# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \474defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)475# error No monotonic clock found.476# endif477478/* Implementations */479480# if (defined(PSNIP_CLOCK_CPU_METHOD) && \481(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \482(defined(PSNIP_CLOCK_WALL_METHOD) && \483(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \484(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \485(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \486(defined(PSNIP_CLOCK_CPU_METHOD) && \487(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \488(defined(PSNIP_CLOCK_WALL_METHOD) && \489(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \490(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \491(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \492(defined(PSNIP_CLOCK_CPU_METHOD) && \493(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \494(defined(PSNIP_CLOCK_WALL_METHOD) && \495(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \496(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \497(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))498# include <time.h>499# endif500501# if (defined(PSNIP_CLOCK_CPU_METHOD) && \502(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \503(defined(PSNIP_CLOCK_WALL_METHOD) && \504(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \505(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \506(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))507# include <sys/time.h>508# endif509510# if (defined(PSNIP_CLOCK_CPU_METHOD) && \511(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \512(defined(PSNIP_CLOCK_WALL_METHOD) && \513(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \514(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \515(PSNIP_CLOCK_MONOTONIC_METHOD == \516PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \517(defined(PSNIP_CLOCK_CPU_METHOD) && \518(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \519(defined(PSNIP_CLOCK_WALL_METHOD) && \520(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \521(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \522(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))523# include <windows.h>524# endif525526# if (defined(PSNIP_CLOCK_CPU_METHOD) && \527(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \528(defined(PSNIP_CLOCK_WALL_METHOD) && \529(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \530(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \531(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))532# include <sys/time.h>533# include <sys/resource.h>534# endif535536# if (defined(PSNIP_CLOCK_CPU_METHOD) && \537(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \538(defined(PSNIP_CLOCK_WALL_METHOD) && \539(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \540(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \541(PSNIP_CLOCK_MONOTONIC_METHOD == \542PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))543# include <CoreServices/CoreServices.h>544# include <mach/mach.h>545# include <mach/mach_time.h>546# endif547548/*** Implementations ***/549550# define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t)(1000000000ULL))551552# if (defined(PSNIP_CLOCK_CPU_METHOD) && \553(PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \554(defined(PSNIP_CLOCK_WALL_METHOD) && \555(PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \556(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \557(PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))558PSNIP_CLOCK__FUNCTION psnip_uint32_t559psnip_clock__clock_getres(clockid_t clk_id) {560struct timespec res;561int r;562563r = clock_getres(clk_id, &res);564if (r != 0)565return 0;566567return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC /568(psnip_uint64_t)res.tv_nsec);569}570571PSNIP_CLOCK__FUNCTION int572psnip_clock__clock_gettime(clockid_t clk_id, struct PsnipClockTimespec *res) {573struct timespec ts;574575if (clock_gettime(clk_id, &ts) != 0)576return -10;577578res->seconds = (psnip_uint64_t)(ts.tv_sec);579res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec);580581return 0;582}583# endif584585PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_wall_get_precision(void) {586# if !defined(PSNIP_CLOCK_WALL_METHOD)587return 0;588# elif defined(PSNIP_CLOCK_WALL_METHOD) && \589PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME590return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);591# elif defined(PSNIP_CLOCK_WALL_METHOD) && \592PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY593return 1000000;594# elif defined(PSNIP_CLOCK_WALL_METHOD) && \595PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME596return 1;597# else598return 0;599# endif600}601602PSNIP_CLOCK__FUNCTION int603psnip_clock_wall_get_time(struct PsnipClockTimespec *res) {604# if !defined(PSNIP_CLOCK_WALL_METHOD)605(void)res;606607return -2;608# elif defined(PSNIP_CLOCK_WALL_METHOD) && \609PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME610return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);611# elif defined(PSNIP_CLOCK_WALL_METHOD) && \612PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME613res->seconds = time(NULL);614res->nanoseconds = 0;615# elif defined(PSNIP_CLOCK_WALL_METHOD) && \616PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY617struct timeval tv;618619if (gettimeofday(&tv, NULL) != 0)620return -6;621622res->seconds = (psnip_uint64_t)tv.tv_sec;623res->nanoseconds = (psnip_uint64_t)tv.tv_usec * 1000;624# else625(void)res;626627return -2;628# endif629630return 0;631}632633PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_cpu_get_precision(void) {634# if !defined(PSNIP_CLOCK_CPU_METHOD)635return 0;636# elif defined(PSNIP_CLOCK_CPU_METHOD) && \637PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME638return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);639# elif defined(PSNIP_CLOCK_CPU_METHOD) && \640PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK641return CLOCKS_PER_SEC;642# elif defined(PSNIP_CLOCK_CPU_METHOD) && \643PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES644return PSNIP_CLOCK_NSEC_PER_SEC / 100;645# else646return 0;647# endif648}649650PSNIP_CLOCK__FUNCTION int651psnip_clock_cpu_get_time(struct PsnipClockTimespec *res) {652# if !defined(PSNIP_CLOCK_CPU_METHOD)653(void)res;654return -2;655# elif defined(PSNIP_CLOCK_CPU_METHOD) && \656PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME657return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);658# elif defined(PSNIP_CLOCK_CPU_METHOD) && \659PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK660clock_t t = clock();661if (t == ((clock_t)-1))662return -5;663res->seconds = t / CLOCKS_PER_SEC;664res->nanoseconds =665(t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);666# elif defined(PSNIP_CLOCK_CPU_METHOD) && \667PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES668FILETIME CreationTime, ExitTime, KernelTime, UserTime;669LARGE_INTEGER date, adjust;670671if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime,672&KernelTime, &UserTime))673return -7;674675/* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */676date.HighPart = (LONG)UserTime.dwHighDateTime;677date.LowPart = UserTime.dwLowDateTime;678adjust.QuadPart = 11644473600000 * 10000;679date.QuadPart -= adjust.QuadPart;680681res->seconds = (psnip_uint64_t)(date.QuadPart / 10000000);682res->nanoseconds = (psnip_uint64_t)(date.QuadPart % 10000000) *683(PSNIP_CLOCK_NSEC_PER_SEC / 100);684# elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE685struct rusage usage;686if (getrusage(RUSAGE_SELF, &usage) != 0)687return -8;688689res->seconds = usage.ru_utime.tv_sec;690res->nanoseconds = tv.tv_usec * 1000;691# else692(void)res;693return -2;694# endif695696return 0;697}698699PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_monotonic_get_precision(void) {700# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)701return 0;702# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \703PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME704return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);705# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \706PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME707static mach_timebase_info_data_t tbi = {7080,709};710if (tbi.denom == 0)711mach_timebase_info(&tbi);712return (psnip_uint32_t)(tbi.numer / tbi.denom);713# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \714PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64715return 1000;716# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \717PSNIP_CLOCK_MONOTONIC_METHOD == \718PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER719LARGE_INTEGER Frequency;720QueryPerformanceFrequency(&Frequency);721return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)722? PSNIP_CLOCK_NSEC_PER_SEC723: Frequency.QuadPart);724# else725return 0;726# endif727}728729PSNIP_CLOCK__FUNCTION int730psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) {731# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)732(void)res;733return -2;734# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \735PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME736return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);737# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \738PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME739psnip_uint64_t nsec = mach_absolute_time();740static mach_timebase_info_data_t tbi = {7410,742};743if (tbi.denom == 0)744mach_timebase_info(&tbi);745nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom);746res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;747res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;748# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \749PSNIP_CLOCK_MONOTONIC_METHOD == \750PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER751LARGE_INTEGER t, f;752if (QueryPerformanceCounter(&t) == 0)753return -12;754755QueryPerformanceFrequency(&f);756res->seconds = (psnip_uint64_t)(t.QuadPart / f.QuadPart);757res->nanoseconds = (psnip_uint64_t)(t.QuadPart % f.QuadPart);758if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)759res->nanoseconds /= (psnip_uint64_t)f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;760else761res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / (psnip_uint64_t)f.QuadPart;762# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \763PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64764const ULONGLONG msec = GetTickCount64();765res->seconds = msec / 1000;766res->nanoseconds = sec % 1000;767# else768return -2;769# endif770771return 0;772}773774/* Returns the number of ticks per second for the specified clock.775* For example, a clock with millisecond precision would return 1000,776* and a clock with 1 second (such as the time() function) would777* return 1.778*779* If the requested clock isn't available, it will return 0.780* Hopefully this will be rare, but if it happens to you please let us781* know so we can work on finding a way to support your system.782*783* Note that different clocks on the same system often have a784* different precisions.785*/786PSNIP_CLOCK__FUNCTION psnip_uint32_t787psnip_clock_get_precision(enum PsnipClockType clock_type) {788switch (clock_type) {789case PSNIP_CLOCK_TYPE_MONOTONIC:790return psnip_clock_monotonic_get_precision();791case PSNIP_CLOCK_TYPE_CPU:792return psnip_clock_cpu_get_precision();793case PSNIP_CLOCK_TYPE_WALL:794return psnip_clock_wall_get_precision();795}796797PSNIP_CLOCK_UNREACHABLE();798return 0;799}800801/* Set the provided timespec to the requested time. Returns 0 on802* success, or a negative value on failure. */803PSNIP_CLOCK__FUNCTION int psnip_clock_get_time(enum PsnipClockType clock_type,804struct PsnipClockTimespec *res) {805assert(res != NULL);806807switch (clock_type) {808case PSNIP_CLOCK_TYPE_MONOTONIC:809return psnip_clock_monotonic_get_time(res);810case PSNIP_CLOCK_TYPE_CPU:811return psnip_clock_cpu_get_time(res);812case PSNIP_CLOCK_TYPE_WALL:813return psnip_clock_wall_get_time(res);814}815816return -1;817}818819# endif /* !defined(PSNIP_CLOCK_H) */820821static psnip_uint64_t munit_clock_get_elapsed(struct PsnipClockTimespec *start,822struct PsnipClockTimespec *end) {823psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC;824if (end->nanoseconds < start->nanoseconds) {825return r - (start->nanoseconds - end->nanoseconds);826}827828return r + (end->nanoseconds - start->nanoseconds);829}830831#else832# include <time.h>833#endif /* defined(MUNIT_ENABLE_TIMING) */834835/*** PRNG stuff ***/836837/* This is (unless I screwed up, which is entirely possible) the838* version of PCG with 32-bit state. It was chosen because it has a839* small enough state that we should reliably be able to use CAS840* instead of requiring a lock for thread-safety.841*842* If I did screw up, I probably will not bother changing it unless843* there is a significant bias. It's really not important this be844* particularly strong, as long as it is fairly random it's much more845* important that it be reproducible, so bug reports have a better846* chance of being reproducible. */847848#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \849!defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) && \850(!defined(__GNUC_MINOR__) || (__GNUC__ > 4) || \851(__GNUC__ == 4 && __GNUC_MINOR__ > 8))852# define HAVE_STDATOMIC853#elif defined(__clang__)854# if __has_extension(c_atomic)855# define HAVE_CLANG_ATOMICS856# endif857#endif858859/* Workaround for http://llvm.org/bugs/show_bug.cgi?id=26911 */860#if defined(__clang__) && defined(_WIN32)861# undef HAVE_STDATOMIC862# if defined(__c2__)863# undef HAVE_CLANG_ATOMICS864# endif865#endif866867#if defined(_OPENMP)868# define ATOMIC_UINT32_T uint32_t869#elif defined(HAVE_STDATOMIC)870# include <stdatomic.h>871# define ATOMIC_UINT32_T _Atomic uint32_t872#elif defined(HAVE_CLANG_ATOMICS)873# define ATOMIC_UINT32_T _Atomic uint32_t874#elif defined(_WIN32)875# define ATOMIC_UINT32_T volatile LONG876#else877# define ATOMIC_UINT32_T volatile uint32_t878#endif879880static ATOMIC_UINT32_T munit_rand_state = 42;881882#if defined(_OPENMP)883static inline void munit_atomic_store(ATOMIC_UINT32_T *dest,884ATOMIC_UINT32_T value) {885# pragma omp critical(munit_atomics)886*dest = value;887}888889static inline uint32_t munit_atomic_load(ATOMIC_UINT32_T *src) {890int ret;891# pragma omp critical(munit_atomics)892ret = *src;893return ret;894}895896static inline uint32_t munit_atomic_cas(ATOMIC_UINT32_T *dest,897ATOMIC_UINT32_T *expected,898ATOMIC_UINT32_T desired) {899munit_bool ret;900901# pragma omp critical(munit_atomics)902{903if (*dest == *expected) {904*dest = desired;905ret = 1;906} else {907ret = 0;908}909}910911return ret;912}913#elif defined(HAVE_STDATOMIC)914# define munit_atomic_store(dest, value) atomic_store(dest, value)915# define munit_atomic_load(src) atomic_load(src)916# define munit_atomic_cas(dest, expected, value) \917atomic_compare_exchange_weak(dest, expected, value)918#elif defined(HAVE_CLANG_ATOMICS)919# define munit_atomic_store(dest, value) \920__c11_atomic_store(dest, value, __ATOMIC_SEQ_CST)921# define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST)922# define munit_atomic_cas(dest, expected, value) \923__c11_atomic_compare_exchange_weak(dest, expected, value, \924__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)925#elif defined(__GNUC__) && (__GNUC__ > 4) || \926(__GNUC__ == 4 && __GNUC_MINOR__ >= 7)927# define munit_atomic_store(dest, value) \928__atomic_store_n(dest, value, __ATOMIC_SEQ_CST)929# define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST)930# define munit_atomic_cas(dest, expected, value) \931__atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST, \932__ATOMIC_SEQ_CST)933#elif defined(__GNUC__) && (__GNUC__ >= 4)934# define munit_atomic_store(dest, value) \935do { \936*(dest) = (value); \937} while (0)938# define munit_atomic_load(src) (*(src))939# define munit_atomic_cas(dest, expected, value) \940__sync_bool_compare_and_swap(dest, *expected, value)941#elif defined(_WIN32) /* Untested */942# define munit_atomic_store(dest, value) \943do { \944*(dest) = (value); \945} while (0)946# define munit_atomic_load(src) (*(src))947# define munit_atomic_cas(dest, expected, value) \948InterlockedCompareExchange((dest), (value), *(expected))949#else950# warning No atomic implementation, PRNG will not be thread-safe951# define munit_atomic_store(dest, value) \952do { \953*(dest) = (value); \954} while (0)955# define munit_atomic_load(src) (*(src))956static inline munit_bool munit_atomic_cas(ATOMIC_UINT32_T *dest,957ATOMIC_UINT32_T *expected,958ATOMIC_UINT32_T desired) {959if (*dest == *expected) {960*dest = desired;961return 1;962} else {963return 0;964}965}966#endif967968#define MUNIT_PRNG_MULTIPLIER (747796405U)969#define MUNIT_PRNG_INCREMENT (1729U)970971static munit_uint32_t munit_rand_next_state(munit_uint32_t state) {972return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT;973}974975static munit_uint32_t munit_rand_from_state(munit_uint32_t state) {976munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U);977res ^= res >> 22;978return res;979}980981void munit_rand_seed(munit_uint32_t seed) {982munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);983munit_atomic_store(&munit_rand_state, state);984}985986static munit_uint32_t munit_rand_generate_seed(void) {987munit_uint32_t seed, state;988#if defined(MUNIT_ENABLE_TIMING)989struct PsnipClockTimespec wc = {9900,991};992993psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc);994seed = (munit_uint32_t)wc.nanoseconds;995#else996seed = (munit_uint32_t)time(NULL);997#endif998999state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);1000return munit_rand_from_state(state);1001}10021003static munit_uint32_t munit_rand_state_uint32(munit_uint32_t *state) {1004const munit_uint32_t old = *state;1005*state = munit_rand_next_state(old);1006return munit_rand_from_state(old);1007}10081009munit_uint32_t munit_rand_uint32(void) {1010munit_uint32_t old, state;10111012do {1013old = munit_atomic_load(&munit_rand_state);1014state = munit_rand_next_state(old);1015} while (!munit_atomic_cas(&munit_rand_state, &old, state));10161017return munit_rand_from_state(old);1018}10191020static void munit_rand_state_memory(munit_uint32_t *state, size_t size,1021munit_uint8_t *data) {1022size_t members_remaining = size / sizeof(munit_uint32_t);1023size_t bytes_remaining = size % sizeof(munit_uint32_t);1024munit_uint8_t *b = data;1025munit_uint32_t rv;1026while (members_remaining-- > 0) {1027rv = munit_rand_state_uint32(state);1028memcpy(b, &rv, sizeof(munit_uint32_t));1029b += sizeof(munit_uint32_t);1030}1031if (bytes_remaining != 0) {1032rv = munit_rand_state_uint32(state);1033memcpy(b, &rv, bytes_remaining);1034}1035}10361037void munit_rand_memory(size_t size, munit_uint8_t *data) {1038munit_uint32_t old, state;10391040do {1041state = old = munit_atomic_load(&munit_rand_state);1042munit_rand_state_memory(&state, size, data);1043} while (!munit_atomic_cas(&munit_rand_state, &old, state));1044}10451046static munit_uint32_t munit_rand_state_at_most(munit_uint32_t *state,1047munit_uint32_t salt,1048munit_uint32_t max) {1049/* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same1050* as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not1051* to avoid compiler warnings.1052*/1053const munit_uint32_t min = (~max + 1U) % max;1054munit_uint32_t x;10551056if (max == (~((munit_uint32_t)0U)))1057return munit_rand_state_uint32(state) ^ salt;10581059max++;10601061do {1062x = munit_rand_state_uint32(state) ^ salt;1063} while (x < min);10641065return x % max;1066}10671068static munit_uint32_t munit_rand_at_most(munit_uint32_t salt,1069munit_uint32_t max) {1070munit_uint32_t old, state;1071munit_uint32_t retval;10721073do {1074state = old = munit_atomic_load(&munit_rand_state);1075retval = munit_rand_state_at_most(&state, salt, max);1076} while (!munit_atomic_cas(&munit_rand_state, &old, state));10771078return retval;1079}10801081int munit_rand_int_range(int min, int max) {1082munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min;10831084if (min > max)1085return munit_rand_int_range(max, min);10861087if (range > (~((munit_uint32_t)0U)))1088range = (~((munit_uint32_t)0U));10891090return min + (int)munit_rand_at_most(0, (munit_uint32_t)range);1091}10921093double munit_rand_double(void) {1094munit_uint32_t old, state;1095double retval = 0.0;10961097do {1098state = old = munit_atomic_load(&munit_rand_state);10991100/* See http://mumble.net/~campbell/tmp/random_real.c for how to do1101* this right. Patches welcome if you feel that this is too1102* biased. */1103retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t)0U)) + 1.0);1104} while (!munit_atomic_cas(&munit_rand_state, &old, state));11051106return retval;1107}11081109/*** Test suite handling ***/11101111typedef struct {1112unsigned int successful;1113unsigned int skipped;1114unsigned int failed;1115unsigned int errored;1116#if defined(MUNIT_ENABLE_TIMING)1117munit_uint64_t cpu_clock;1118munit_uint64_t wall_clock;1119#endif1120} MunitReport;11211122typedef struct {1123const char *prefix;1124const MunitSuite *suite;1125const char **tests;1126munit_uint32_t seed;1127unsigned int iterations;1128MunitParameter *parameters;1129munit_bool single_parameter_mode;1130void *user_data;1131MunitReport report;1132munit_bool colorize;1133munit_bool fork;1134munit_bool show_stderr;1135munit_bool fatal_failures;1136} MunitTestRunner;11371138const char *munit_parameters_get(const MunitParameter params[],1139const char *key) {1140const MunitParameter *param;11411142for (param = params; param != NULL && param->name != NULL; param++)1143if (strcmp(param->name, key) == 0)1144return param->value;1145return NULL;1146}11471148#if defined(MUNIT_ENABLE_TIMING)1149static void munit_print_time(FILE *fp, munit_uint64_t nanoseconds) {1150fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT,1151((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC));1152}1153#endif11541155/* Add a paramter to an array of parameters. */1156static MunitResult munit_parameters_add(size_t *params_size,1157MunitParameter **params, char *name,1158char *value) {1159*params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2));1160if (*params == NULL)1161return MUNIT_ERROR;11621163(*params)[*params_size].name = name;1164(*params)[*params_size].value = value;1165(*params_size)++;1166(*params)[*params_size].name = NULL;1167(*params)[*params_size].value = NULL;11681169return MUNIT_OK;1170}11711172/* Concatenate two strings, but just return one of the components1173* unaltered if the other is NULL or "". */1174static char *munit_maybe_concat(size_t *len, char *prefix, char *suffix) {1175char *res;1176size_t res_l;1177const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0;1178const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0;1179if (prefix_l == 0 && suffix_l == 0) {1180res = NULL;1181res_l = 0;1182} else if (prefix_l == 0 && suffix_l != 0) {1183res = suffix;1184res_l = suffix_l;1185} else if (prefix_l != 0 && suffix_l == 0) {1186res = prefix;1187res_l = prefix_l;1188} else {1189res_l = prefix_l + suffix_l;1190res = malloc(res_l + 1);1191memcpy(res, prefix, prefix_l);1192memcpy(res + prefix_l, suffix, suffix_l);1193res[res_l] = 0;1194}11951196if (len != NULL)1197*len = res_l;11981199return res;1200}12011202/* Possbily free a string returned by munit_maybe_concat. */1203static void munit_maybe_free_concat(char *s, const char *prefix,1204const char *suffix) {1205if (prefix != s && suffix != s)1206free(s);1207}12081209/* Cheap string hash function, just used to salt the PRNG. */1210static munit_uint32_t munit_str_hash(const char *name) {1211const char *p;1212munit_uint32_t h = 5381U;12131214for (p = name; *p != '\0'; p++)1215h = (munit_uint32_t)(h << 5) + h + (munit_uint32_t)*p;12161217return h;1218}12191220static void munit_splice(int from, int to) {1221munit_uint8_t buf[1024];1222#if !defined(_WIN32)1223ssize_t len;1224ssize_t bytes_written;1225ssize_t write_res;1226#else1227int len;1228int bytes_written;1229int write_res;1230#endif1231do {1232len = read(from, buf, sizeof(buf));1233if (len > 0) {1234bytes_written = 0;1235do {1236write_res = write(to, buf + bytes_written,1237#if !defined(_WIN32)1238(size_t)1239#else1240(unsigned int)1241#endif1242(len - bytes_written));1243if (write_res < 0)1244break;1245bytes_written += write_res;1246} while (bytes_written < len);1247} else1248break;1249} while (1);1250}12511252/* This is the part that should be handled in the child process */1253static MunitResult munit_test_runner_exec(MunitTestRunner *runner,1254const MunitTest *test,1255const MunitParameter params[],1256MunitReport *report) {1257unsigned int iterations = runner->iterations;1258MunitResult result = MUNIT_FAIL;1259#if defined(MUNIT_ENABLE_TIMING)1260struct PsnipClockTimespec wall_clock_begin =1261{12620,1263},1264wall_clock_end = {12650,1266};1267struct PsnipClockTimespec cpu_clock_begin =1268{12690,1270},1271cpu_clock_end = {12720,1273};1274#endif1275unsigned int i = 0;12761277if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) ==1278MUNIT_TEST_OPTION_SINGLE_ITERATION)1279iterations = 1;1280else if (iterations == 0)1281iterations = runner->suite->iterations;12821283munit_rand_seed(runner->seed);12841285do {1286void *data = (test->setup == NULL) ? runner->user_data1287: test->setup(params, runner->user_data);12881289#if defined(MUNIT_ENABLE_TIMING)1290psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin);1291psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin);1292#endif12931294result = test->test(params, data);12951296#if defined(MUNIT_ENABLE_TIMING)1297psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end);1298psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end);1299#endif13001301if (test->tear_down != NULL)1302test->tear_down(data);13031304if (MUNIT_LIKELY(result == MUNIT_OK)) {1305report->successful++;1306#if defined(MUNIT_ENABLE_TIMING)1307report->wall_clock +=1308munit_clock_get_elapsed(&wall_clock_begin, &wall_clock_end);1309report->cpu_clock +=1310munit_clock_get_elapsed(&cpu_clock_begin, &cpu_clock_end);1311#endif1312} else {1313switch ((int)result) {1314case MUNIT_SKIP:1315report->skipped++;1316break;1317case MUNIT_FAIL:1318report->failed++;1319break;1320case MUNIT_ERROR:1321report->errored++;1322break;1323default:1324break;1325}1326break;1327}1328} while (++i < iterations);13291330return result;1331}13321333#if defined(MUNIT_EMOTICON)1334# define MUNIT_RESULT_STRING_OK ":)"1335# define MUNIT_RESULT_STRING_SKIP ":|"1336# define MUNIT_RESULT_STRING_FAIL ":("1337# define MUNIT_RESULT_STRING_ERROR ":o"1338# define MUNIT_RESULT_STRING_TODO ":/"1339#else1340# define MUNIT_RESULT_STRING_OK "OK "1341# define MUNIT_RESULT_STRING_SKIP "SKIP "1342# define MUNIT_RESULT_STRING_FAIL "FAIL "1343# define MUNIT_RESULT_STRING_ERROR "ERROR"1344# define MUNIT_RESULT_STRING_TODO "TODO "1345#endif13461347static void munit_test_runner_print_color(const MunitTestRunner *runner,1348const char *string, char color) {1349if (runner->colorize)1350fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string);1351else1352fputs(string, MUNIT_OUTPUT_FILE);1353}13541355#if !defined(MUNIT_NO_BUFFER)1356static int munit_replace_stderr(FILE *stderr_buf) {1357if (stderr_buf != NULL) {1358const int orig_stderr = dup(STDERR_FILENO);13591360int errfd = fileno(stderr_buf);1361if (MUNIT_UNLIKELY(errfd == -1)) {1362exit(EXIT_FAILURE);1363}13641365dup2(errfd, STDERR_FILENO);13661367return orig_stderr;1368}13691370return -1;1371}13721373static void munit_restore_stderr(int orig_stderr) {1374if (orig_stderr != -1) {1375dup2(orig_stderr, STDERR_FILENO);1376close(orig_stderr);1377}1378}1379#endif /* !defined(MUNIT_NO_BUFFER) */13801381/* Run a test with the specified parameters. */1382static void1383munit_test_runner_run_test_with_params(MunitTestRunner *runner,1384const MunitTest *test,1385const MunitParameter params[]) {1386MunitResult result = MUNIT_OK;1387MunitReport report = {0, 0, 0, 0,1388#if defined(MUNIT_ENABLE_TIMING)13890, 01390#endif1391};1392unsigned int output_l;1393munit_bool first;1394const MunitParameter *param;1395FILE *stderr_buf;1396#if !defined(MUNIT_NO_FORK)1397int pipefd[2];1398pid_t fork_pid;1399ssize_t bytes_written = 0;1400ssize_t write_res;1401ssize_t bytes_read = 0;1402ssize_t read_res;1403int status = 0;1404pid_t changed_pid;1405#endif14061407if (params != NULL) {1408output_l = 2;1409fputs(" ", MUNIT_OUTPUT_FILE);1410first = 1;1411for (param = params; param != NULL && param->name != NULL; param++) {1412if (!first) {1413fputs(", ", MUNIT_OUTPUT_FILE);1414output_l += 2;1415} else {1416first = 0;1417}14181419output_l += (unsigned int)fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name,1420param->value);1421}1422while (output_l++ < MUNIT_TEST_NAME_LEN) {1423fputc(' ', MUNIT_OUTPUT_FILE);1424}1425}14261427fflush(MUNIT_OUTPUT_FILE);14281429stderr_buf = NULL;1430#if !defined(_WIN32) || defined(__MINGW32__)1431stderr_buf = tmpfile();1432#else1433tmpfile_s(&stderr_buf);1434#endif1435if (stderr_buf == NULL) {1436munit_log_errno(MUNIT_LOG_ERROR, stderr,1437"unable to create buffer for stderr");1438result = MUNIT_ERROR;1439goto print_result;1440}14411442#if !defined(MUNIT_NO_FORK)1443if (runner->fork) {1444pipefd[0] = -1;1445pipefd[1] = -1;1446if (pipe(pipefd) != 0) {1447munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe");1448result = MUNIT_ERROR;1449goto print_result;1450}14511452fork_pid = fork();1453if (fork_pid == 0) {1454int orig_stderr;14551456close(pipefd[0]);14571458orig_stderr = munit_replace_stderr(stderr_buf);1459munit_test_runner_exec(runner, test, params, &report);14601461/* Note that we don't restore stderr. This is so we can buffer1462* things written to stderr later on (such as by1463* asan/tsan/ubsan, valgrind, etc.) */1464close(orig_stderr);14651466do {1467write_res =1468write(pipefd[1], ((munit_uint8_t *)(&report)) + bytes_written,1469sizeof(report) - (size_t)bytes_written);1470if (write_res < 0) {1471if (stderr_buf != NULL) {1472munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe");1473}1474exit(EXIT_FAILURE);1475}1476bytes_written += write_res;1477} while ((size_t)bytes_written < sizeof(report));14781479if (stderr_buf != NULL)1480fclose(stderr_buf);1481close(pipefd[1]);14821483exit(EXIT_SUCCESS);1484} else if (fork_pid == -1) {1485close(pipefd[0]);1486close(pipefd[1]);1487if (stderr_buf != NULL) {1488munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork");1489}1490report.errored++;1491result = MUNIT_ERROR;1492} else {1493close(pipefd[1]);1494do {1495read_res = read(pipefd[0], ((munit_uint8_t *)(&report)) + bytes_read,1496sizeof(report) - (size_t)bytes_read);1497if (read_res < 1)1498break;1499bytes_read += read_res;1500} while (bytes_read < (ssize_t)sizeof(report));15011502changed_pid = waitpid(fork_pid, &status, 0);15031504if (MUNIT_LIKELY(changed_pid == fork_pid) &&1505MUNIT_LIKELY(WIFEXITED(status))) {1506if (bytes_read != sizeof(report)) {1507munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,1508"child exited unexpectedly with status %d",1509WEXITSTATUS(status));1510report.errored++;1511} else if (WEXITSTATUS(status) != EXIT_SUCCESS) {1512munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,1513"child exited with status %d",1514WEXITSTATUS(status));1515report.errored++;1516}1517} else {1518if (WIFSIGNALED(status)) {1519# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)1520munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,1521"child killed by signal %d (%s)",1522WTERMSIG(status), strsignal(WTERMSIG(status)));1523# else1524munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,1525"child killed by signal %d", WTERMSIG(status));1526# endif1527} else if (WIFSTOPPED(status)) {1528munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,1529"child stopped by signal %d", WSTOPSIG(status));1530}1531report.errored++;1532}15331534close(pipefd[0]);1535waitpid(fork_pid, NULL, 0);1536}1537} else1538#endif1539{1540#if !defined(MUNIT_NO_BUFFER)1541const volatile int orig_stderr = munit_replace_stderr(stderr_buf);1542#endif15431544#if defined(MUNIT_THREAD_LOCAL)1545if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) {1546result = MUNIT_FAIL;1547report.failed++;1548} else {1549munit_error_jmp_buf_valid = 1;1550result = munit_test_runner_exec(runner, test, params, &report);1551}1552#else1553result = munit_test_runner_exec(runner, test, params, &report);1554#endif15551556#if !defined(MUNIT_NO_BUFFER)1557munit_restore_stderr(orig_stderr);1558#endif15591560/* Here just so that the label is used on Windows and we don't get1561* a warning */1562goto print_result;1563}15641565print_result:15661567fputs("[ ", MUNIT_OUTPUT_FILE);1568if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) {1569if (report.failed != 0 || report.errored != 0 || report.skipped != 0) {1570munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3');1571result = MUNIT_OK;1572} else {1573munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');1574if (MUNIT_LIKELY(stderr_buf != NULL))1575munit_log_internal(MUNIT_LOG_ERROR, stderr_buf,1576"Test marked TODO, but was successful.");1577runner->report.failed++;1578result = MUNIT_ERROR;1579}1580} else if (report.failed > 0) {1581munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1');1582runner->report.failed++;1583result = MUNIT_FAIL;1584} else if (report.errored > 0) {1585munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');1586runner->report.errored++;1587result = MUNIT_ERROR;1588} else if (report.skipped > 0) {1589munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3');1590runner->report.skipped++;1591result = MUNIT_SKIP;1592} else if (report.successful > 1) {1593munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');1594#if defined(MUNIT_ENABLE_TIMING)1595fputs(" ] [ ", MUNIT_OUTPUT_FILE);1596munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful);1597fputs(" / ", MUNIT_OUTPUT_FILE);1598munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful);1599fprintf(MUNIT_OUTPUT_FILE,1600" CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ",1601"");1602munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);1603fputs(" / ", MUNIT_OUTPUT_FILE);1604munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);1605fputs(" CPU", MUNIT_OUTPUT_FILE);1606#endif1607runner->report.successful++;1608result = MUNIT_OK;1609} else if (report.successful > 0) {1610munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');1611#if defined(MUNIT_ENABLE_TIMING)1612fputs(" ] [ ", MUNIT_OUTPUT_FILE);1613munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);1614fputs(" / ", MUNIT_OUTPUT_FILE);1615munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);1616fputs(" CPU", MUNIT_OUTPUT_FILE);1617#endif1618runner->report.successful++;1619result = MUNIT_OK;1620}1621fputs(" ]\n", MUNIT_OUTPUT_FILE);16221623if (stderr_buf != NULL) {1624if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) {1625fflush(MUNIT_OUTPUT_FILE);16261627rewind(stderr_buf);1628munit_splice(fileno(stderr_buf), STDERR_FILENO);16291630fflush(stderr);1631}16321633fclose(stderr_buf);1634}1635}16361637static void munit_test_runner_run_test_wild(MunitTestRunner *runner,1638const MunitTest *test,1639const char *test_name,1640MunitParameter *params,1641MunitParameter *p) {1642const MunitParameterEnum *pe;1643char **values;1644MunitParameter *next;16451646for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {1647if (p->name == pe->name)1648break;1649}16501651if (pe == NULL)1652return;16531654for (values = pe->values; *values != NULL; values++) {1655next = p + 1;1656p->value = *values;1657if (next->name == NULL) {1658munit_test_runner_run_test_with_params(runner, test, params);1659} else {1660munit_test_runner_run_test_wild(runner, test, test_name, params, next);1661}1662if (runner->fatal_failures &&1663(runner->report.failed != 0 || runner->report.errored != 0))1664break;1665}1666}16671668/* Run a single test, with every combination of parameters1669* requested. */1670static void munit_test_runner_run_test(MunitTestRunner *runner,1671const MunitTest *test,1672const char *prefix) {1673char *test_name =1674munit_maybe_concat(NULL, (char *)prefix, (char *)test->name);1675/* The array of parameters to pass to1676* munit_test_runner_run_test_with_params */1677MunitParameter *params = NULL;1678size_t params_l = 0;1679/* Wildcard parameters are parameters which have possible values1680* specified in the test, but no specific value was passed to the1681* CLI. That means we want to run the test once for every1682* possible combination of parameter values or, if --single was1683* passed to the CLI, a single time with a random set of1684* parameters. */1685MunitParameter *wild_params = NULL;1686size_t wild_params_l = 0;1687const MunitParameterEnum *pe;1688const MunitParameter *cli_p;1689munit_bool filled;1690unsigned int possible;1691char **vals;1692size_t first_wild;1693const MunitParameter *wp;1694int pidx;16951696munit_rand_seed(runner->seed);16971698fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s",1699test_name);17001701if (test->parameters == NULL) {1702/* No parameters. Simple, nice. */1703munit_test_runner_run_test_with_params(runner, test, NULL);1704} else {1705fputc('\n', MUNIT_OUTPUT_FILE);17061707for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {1708/* Did we received a value for this parameter from the CLI? */1709filled = 0;1710for (cli_p = runner->parameters; cli_p != NULL && cli_p->name != NULL;1711cli_p++) {1712if (strcmp(cli_p->name, pe->name) == 0) {1713if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name,1714cli_p->value) != MUNIT_OK))1715goto cleanup;1716filled = 1;1717break;1718}1719}1720if (filled)1721continue;17221723/* Nothing from CLI, is the enum NULL/empty? We're not a1724* fuzzer… */1725if (pe->values == NULL || pe->values[0] == NULL)1726continue;17271728/* If --single was passed to the CLI, choose a value from the1729* list of possibilities randomly. */1730if (runner->single_parameter_mode) {1731possible = 0;1732for (vals = pe->values; *vals != NULL; vals++)1733possible++;1734/* We want the tests to be reproducible, even if you're only1735* running a single test, but we don't want every test with1736* the same number of parameters to choose the same parameter1737* number, so use the test name as a primitive salt. */1738pidx = (int)munit_rand_at_most(munit_str_hash(test_name), possible - 1);1739if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name,1740pe->values[pidx]) != MUNIT_OK))1741goto cleanup;1742} else {1743/* We want to try every permutation. Put in a placeholder1744* entry, we'll iterate through them later. */1745if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params,1746pe->name, NULL) != MUNIT_OK))1747goto cleanup;1748}1749}17501751if (wild_params_l != 0) {1752first_wild = params_l;1753for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) {1754for (pe = test->parameters;1755pe != NULL && pe->name != NULL && pe->values != NULL; pe++) {1756if (strcmp(wp->name, pe->name) == 0) {1757if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms,1758pe->name,1759pe->values[0]) != MUNIT_OK))1760goto cleanup;1761}1762}1763}17641765munit_test_runner_run_test_wild(runner, test, test_name, params,1766params + first_wild);1767} else {1768munit_test_runner_run_test_with_params(runner, test, params);1769}17701771cleanup:1772free(params);1773free(wild_params);1774}17751776munit_maybe_free_concat(test_name, prefix, test->name);1777}17781779/* Recurse through the suite and run all the tests. If a list of1780* tests to run was provied on the command line, run only those1781* tests. */1782static void munit_test_runner_run_suite(MunitTestRunner *runner,1783const MunitSuite *suite,1784const char *prefix) {1785size_t pre_l;1786char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);1787const MunitTest *test;1788const char **test_name;1789const MunitSuite *child_suite;17901791/* Run the tests. */1792for (test = suite->tests; test != NULL && test->test != NULL; test++) {1793if (runner->tests != NULL) { /* Specific tests were requested on the CLI */1794for (test_name = runner->tests; test_name != NULL && *test_name != NULL;1795test_name++) {1796if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) &&1797strncmp(test->name, *test_name + pre_l,1798strlen(*test_name + pre_l)) == 0) {1799munit_test_runner_run_test(runner, test, pre);1800if (runner->fatal_failures &&1801(runner->report.failed != 0 || runner->report.errored != 0))1802goto cleanup;1803}1804}1805} else { /* Run all tests */1806munit_test_runner_run_test(runner, test, pre);1807}1808}18091810if (runner->fatal_failures &&1811(runner->report.failed != 0 || runner->report.errored != 0))1812goto cleanup;18131814/* Run any child suites. */1815for (child_suite = suite->suites;1816child_suite != NULL && child_suite->prefix != NULL; child_suite++) {1817munit_test_runner_run_suite(runner, child_suite, pre);1818}18191820cleanup:18211822munit_maybe_free_concat(pre, prefix, suite->prefix);1823}18241825static void munit_test_runner_run(MunitTestRunner *runner) {1826munit_test_runner_run_suite(runner, runner->suite, NULL);1827}18281829static void munit_print_help(int argc, char *const *argv, void *user_data,1830const MunitArgument arguments[]) {1831const MunitArgument *arg;1832(void)argc;18331834printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]);1835puts(1836" --seed SEED\n"1837" Value used to seed the PRNG. Must be a 32-bit integer in "1838"decimal\n"1839" notation with no separators (commas, decimals, spaces, "1840"etc.), or\n"1841" hexidecimal prefixed by \"0x\".\n"1842" --iterations N\n"1843" Run each test N times. 0 means the default number.\n"1844" --param name value\n"1845" A parameter key/value pair which will be passed to any test "1846"with\n"1847" takes a parameter of that name. If not provided, the test "1848"will be\n"1849" run once for each possible parameter value.\n"1850" --list Write a list of all available tests.\n"1851" --list-params\n"1852" Write a list of all available tests and their possible "1853"parameters.\n"1854" --single Run each parameterized test in a single configuration "1855"instead of\n"1856" every possible combination\n"1857" --log-visible debug|info|warning|error\n"1858" --log-fatal debug|info|warning|error\n"1859" Set the level at which messages of different severities are "1860"visible,\n"1861" or cause the test to terminate.\n"1862#if !defined(MUNIT_NO_FORK)1863" --no-fork Do not execute tests in a child process. If this option is "1864"supplied\n"1865" and a test crashes (including by failing an assertion), no "1866"further\n"1867" tests will be performed.\n"1868#endif1869" --fatal-failures\n"1870" Stop executing tests as soon as a failure is found.\n"1871" --show-stderr\n"1872" Show data written to stderr by the tests, even if the test "1873"succeeds.\n"1874" --color auto|always|never\n"1875" Colorize (or don't) the output.\n"1876/* 123456789012345678901234567890123456789012345678901234567890123456789012345678901877*/1878" --help Print this help message and exit.\n");1879#if defined(MUNIT_NL_LANGINFO)1880setlocale(LC_ALL, "");1881fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit",1882stdout);1883#else1884puts("munit");1885#endif1886printf(" %d.%d.%d\n"1887"Full documentation at: https://nemequ.github.io/munit/\n",1888(MUNIT_CURRENT_VERSION >> 16) & 0xff,1889(MUNIT_CURRENT_VERSION >> 8) & 0xff,1890(MUNIT_CURRENT_VERSION >> 0) & 0xff);1891for (arg = arguments; arg != NULL && arg->name != NULL; arg++)1892arg->write_help(arg, user_data);1893}18941895static const MunitArgument *1896munit_arguments_find(const MunitArgument arguments[], const char *name) {1897const MunitArgument *arg;18981899for (arg = arguments; arg != NULL && arg->name != NULL; arg++)1900if (strcmp(arg->name, name) == 0)1901return arg;19021903return NULL;1904}19051906static void munit_suite_list_tests(const MunitSuite *suite,1907munit_bool show_params, const char *prefix) {1908size_t pre_l;1909char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);1910const MunitTest *test;1911const MunitParameterEnum *params;1912munit_bool first;1913char **val;1914const MunitSuite *child_suite;19151916for (test = suite->tests; test != NULL && test->name != NULL; test++) {1917if (pre != NULL)1918fputs(pre, stdout);1919puts(test->name);19201921if (show_params) {1922for (params = test->parameters; params != NULL && params->name != NULL;1923params++) {1924fprintf(stdout, " - %s: ", params->name);1925if (params->values == NULL) {1926puts("Any");1927} else {1928first = 1;1929for (val = params->values; *val != NULL; val++) {1930if (!first) {1931fputs(", ", stdout);1932} else {1933first = 0;1934}1935fputs(*val, stdout);1936}1937putc('\n', stdout);1938}1939}1940}1941}19421943for (child_suite = suite->suites;1944child_suite != NULL && child_suite->prefix != NULL; child_suite++) {1945munit_suite_list_tests(child_suite, show_params, pre);1946}19471948munit_maybe_free_concat(pre, prefix, suite->prefix);1949}19501951static munit_bool munit_stream_supports_ansi(FILE *stream) {1952#if !defined(_WIN32)1953return isatty(fileno(stream));1954#else19551956# if !defined(__MINGW32__)1957size_t ansicon_size = 0;1958# endif19591960if (isatty(fileno(stream))) {1961# if !defined(__MINGW32__)1962getenv_s(&ansicon_size, NULL, 0, "ANSICON");1963return ansicon_size != 0;1964# else1965return getenv("ANSICON") != NULL;1966# endif1967}1968return 0;1969#endif1970}19711972int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc,1973char *const *argv,1974const MunitArgument arguments[]) {1975int result = EXIT_FAILURE;1976MunitTestRunner runner;1977size_t parameters_size = 0;1978size_t tests_size = 0;1979int arg;19801981char *envptr;1982unsigned long ts;1983char *endptr;1984unsigned long long iterations;1985MunitLogLevel level;1986const MunitArgument *argument;1987const char **runner_tests;1988unsigned int tests_run;1989unsigned int tests_total;19901991runner.prefix = NULL;1992runner.suite = NULL;1993runner.tests = NULL;1994runner.seed = 0;1995runner.iterations = 0;1996runner.parameters = NULL;1997runner.single_parameter_mode = 0;1998runner.user_data = NULL;19992000runner.report.successful = 0;2001runner.report.skipped = 0;2002runner.report.failed = 0;2003runner.report.errored = 0;2004#if defined(MUNIT_ENABLE_TIMING)2005runner.report.cpu_clock = 0;2006runner.report.wall_clock = 0;2007#endif20082009runner.colorize = 0;2010#if !defined(_WIN32)2011runner.fork = 1;2012#else2013runner.fork = 0;2014#endif2015runner.show_stderr = 0;2016runner.fatal_failures = 0;2017runner.suite = suite;2018runner.user_data = user_data;2019runner.seed = munit_rand_generate_seed();2020runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);20212022for (arg = 1; arg < argc; arg++) {2023if (strncmp("--", argv[arg], 2) == 0) {2024if (strcmp("seed", argv[arg] + 2) == 0) {2025if (arg + 1 >= argc) {2026munit_logf_internal(MUNIT_LOG_ERROR, stderr,2027"%s requires an argument", argv[arg]);2028goto cleanup;2029}20302031envptr = argv[arg + 1];2032ts = strtoul(argv[arg + 1], &envptr, 0);2033if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) {2034munit_logf_internal(MUNIT_LOG_ERROR, stderr,2035"invalid value ('%s') passed to %s",2036argv[arg + 1], argv[arg]);2037goto cleanup;2038}2039runner.seed = (munit_uint32_t)ts;20402041arg++;2042} else if (strcmp("iterations", argv[arg] + 2) == 0) {2043if (arg + 1 >= argc) {2044munit_logf_internal(MUNIT_LOG_ERROR, stderr,2045"%s requires an argument", argv[arg]);2046goto cleanup;2047}20482049endptr = argv[arg + 1];2050iterations = strtoul(argv[arg + 1], &endptr, 0);2051if (*endptr != '\0' || iterations > UINT_MAX) {2052munit_logf_internal(MUNIT_LOG_ERROR, stderr,2053"invalid value ('%s') passed to %s",2054argv[arg + 1], argv[arg]);2055goto cleanup;2056}20572058runner.iterations = (unsigned int)iterations;20592060arg++;2061} else if (strcmp("param", argv[arg] + 2) == 0) {2062if (arg + 2 >= argc) {2063munit_logf_internal(MUNIT_LOG_ERROR, stderr,2064"%s requires two arguments", argv[arg]);2065goto cleanup;2066}20672068runner.parameters = realloc(runner.parameters, sizeof(MunitParameter) *2069(parameters_size + 2));2070if (runner.parameters == NULL) {2071munit_log_internal(MUNIT_LOG_ERROR, stderr,2072"failed to allocate memory");2073goto cleanup;2074}2075runner.parameters[parameters_size].name = (char *)argv[arg + 1];2076runner.parameters[parameters_size].value = (char *)argv[arg + 2];2077parameters_size++;2078runner.parameters[parameters_size].name = NULL;2079runner.parameters[parameters_size].value = NULL;2080arg += 2;2081} else if (strcmp("color", argv[arg] + 2) == 0) {2082if (arg + 1 >= argc) {2083munit_logf_internal(MUNIT_LOG_ERROR, stderr,2084"%s requires an argument", argv[arg]);2085goto cleanup;2086}20872088if (strcmp(argv[arg + 1], "always") == 0)2089runner.colorize = 1;2090else if (strcmp(argv[arg + 1], "never") == 0)2091runner.colorize = 0;2092else if (strcmp(argv[arg + 1], "auto") == 0)2093runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);2094else {2095munit_logf_internal(MUNIT_LOG_ERROR, stderr,2096"invalid value ('%s') passed to %s",2097argv[arg + 1], argv[arg]);2098goto cleanup;2099}21002101arg++;2102} else if (strcmp("help", argv[arg] + 2) == 0) {2103munit_print_help(argc, argv, user_data, arguments);2104result = EXIT_SUCCESS;2105goto cleanup;2106} else if (strcmp("single", argv[arg] + 2) == 0) {2107runner.single_parameter_mode = 1;2108} else if (strcmp("show-stderr", argv[arg] + 2) == 0) {2109runner.show_stderr = 1;2110#if !defined(_WIN32)2111} else if (strcmp("no-fork", argv[arg] + 2) == 0) {2112runner.fork = 0;2113#endif2114} else if (strcmp("fatal-failures", argv[arg] + 2) == 0) {2115runner.fatal_failures = 1;2116} else if (strcmp("log-visible", argv[arg] + 2) == 0 ||2117strcmp("log-fatal", argv[arg] + 2) == 0) {2118if (arg + 1 >= argc) {2119munit_logf_internal(MUNIT_LOG_ERROR, stderr,2120"%s requires an argument", argv[arg]);2121goto cleanup;2122}21232124if (strcmp(argv[arg + 1], "debug") == 0)2125level = MUNIT_LOG_DEBUG;2126else if (strcmp(argv[arg + 1], "info") == 0)2127level = MUNIT_LOG_INFO;2128else if (strcmp(argv[arg + 1], "warning") == 0)2129level = MUNIT_LOG_WARNING;2130else if (strcmp(argv[arg + 1], "error") == 0)2131level = MUNIT_LOG_ERROR;2132else {2133munit_logf_internal(MUNIT_LOG_ERROR, stderr,2134"invalid value ('%s') passed to %s",2135argv[arg + 1], argv[arg]);2136goto cleanup;2137}21382139if (strcmp("log-visible", argv[arg] + 2) == 0)2140munit_log_level_visible = level;2141else2142munit_log_level_fatal = level;21432144arg++;2145} else if (strcmp("list", argv[arg] + 2) == 0) {2146munit_suite_list_tests(suite, 0, NULL);2147result = EXIT_SUCCESS;2148goto cleanup;2149} else if (strcmp("list-params", argv[arg] + 2) == 0) {2150munit_suite_list_tests(suite, 1, NULL);2151result = EXIT_SUCCESS;2152goto cleanup;2153} else {2154argument = munit_arguments_find(arguments, argv[arg] + 2);2155if (argument == NULL) {2156munit_logf_internal(MUNIT_LOG_ERROR, stderr,2157"unknown argument ('%s')", argv[arg]);2158goto cleanup;2159}21602161if (!argument->parse_argument(suite, user_data, &arg, argc, argv))2162goto cleanup;2163}2164} else {2165runner_tests =2166realloc((void *)runner.tests, sizeof(char *) * (tests_size + 2));2167if (runner_tests == NULL) {2168munit_log_internal(MUNIT_LOG_ERROR, stderr,2169"failed to allocate memory");2170goto cleanup;2171}2172runner.tests = runner_tests;2173runner.tests[tests_size++] = argv[arg];2174runner.tests[tests_size] = NULL;2175}2176}21772178fflush(stderr);2179fprintf(MUNIT_OUTPUT_FILE,2180"Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed);21812182munit_test_runner_run(&runner);21832184tests_run =2185runner.report.successful + runner.report.failed + runner.report.errored;2186tests_total = tests_run + runner.report.skipped;2187if (tests_run == 0) {2188fprintf(stderr, "No tests run, %d (100%%) skipped.\n",2189runner.report.skipped);2190} else {2191fprintf(MUNIT_OUTPUT_FILE,2192"%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n",2193runner.report.successful, tests_run,2194(((double)runner.report.successful) / ((double)tests_run)) * 100.0,2195runner.report.skipped,2196(((double)runner.report.skipped) / ((double)tests_total)) * 100.0);2197}21982199if (runner.report.failed == 0 && runner.report.errored == 0) {2200result = EXIT_SUCCESS;2201}22022203cleanup:2204free(runner.parameters);2205free((void *)runner.tests);22062207return result;2208}22092210int munit_suite_main(const MunitSuite *suite, void *user_data, int argc,2211char *const *argv) {2212return munit_suite_main_custom(suite, user_data, argc, argv, NULL);2213}22142215static uint8_t hexchars[] = "0123456789abcdef";22162217static uint8_t *hexdump_addr(uint8_t *dest, size_t addr) {2218size_t i;2219uint8_t a;22202221for (i = 0; i < 4; ++i) {2222a = (addr >> (3 - i) * 8) & 0xff;22232224*dest++ = hexchars[a >> 4];2225*dest++ = hexchars[a & 0xf];2226}22272228return dest;2229}22302231static uint8_t *asciidump(uint8_t *dest, const uint8_t *data, size_t datalen) {2232size_t i;22332234*dest++ = '|';22352236for (i = 0; i < datalen; ++i) {2237if (0x20 <= data[i] && data[i] <= 0x7e) {2238*dest++ = data[i];2239} else {2240*dest++ = '.';2241}2242}22432244*dest++ = '|';22452246return dest;2247}22482249static uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) {2250size_t i;22512252for (i = 0; i < datalen; ++i) {2253*dest++ = hexchars[data[i] >> 4];2254*dest++ = hexchars[data[i] & 0xf];2255*dest++ = ' ';2256}22572258for (; i < 8; ++i) {2259*dest++ = ' ';2260*dest++ = ' ';2261*dest++ = ' ';2262}22632264return dest;2265}22662267static uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) {2268dest = hexdump8(dest, data, datalen < 8 ? datalen : 8);2269*dest++ = ' ';22702271if (datalen < 8) {2272data = NULL;2273datalen = 0;2274} else {2275data += 8;2276datalen -= 8;2277}22782279dest = hexdump8(dest, data, datalen);2280*dest++ = ' ';22812282return dest;2283}22842285static uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen,2286size_t addr) {2287dest = hexdump_addr(dest, addr);2288*dest++ = ' ';2289*dest++ = ' ';22902291dest = hexdump16(dest, data, datalen);22922293dest = asciidump(dest, data, datalen);22942295return dest;2296}22972298int munit_hexdump(FILE *fp, const void *data, size_t datalen) {2299size_t offset = 0, n, len;2300uint8_t buf[128], *p;2301const uint8_t *s;2302int repeated = 0;23032304if (datalen == 0) {2305return 0;2306}23072308for (; offset < datalen; offset += 16) {2309n = datalen - offset;2310s = (const uint8_t *)data + offset;23112312if (n >= 16) {2313n = 16;23142315if (offset > 0) {2316if (memcmp(s - 16, s, 16) == 0) {2317if (repeated) {2318continue;2319}23202321repeated = 1;23222323if (fwrite("*\n", 1, 2, fp) < 2) {2324return -1;2325}23262327continue;2328}23292330repeated = 0;2331}2332}23332334p = hexdump_line(buf, s, n, offset);2335*p++ = '\n';23362337len = (size_t)(p - buf);23382339if (fwrite(buf, 1, len, fp) < len) {2340return -1;2341}2342}23432344p = hexdump_addr(buf, datalen);2345*p++ = '\n';23462347len = (size_t)(p - buf);23482349if (fwrite(buf, 1, len, fp) < len) {2350return -1;2351}23522353return 0;2354}23552356int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b,2357size_t blen) {2358size_t offset = 0, k, i, len, ncomp, maxlen, adoff = 0;2359uint8_t buf[128], *p;2360const uint8_t mk[2] = {'-', '+'};2361struct datasource {2362const uint8_t *data;2363size_t datalen;2364const uint8_t *s;2365size_t n;2366} ds[] = {{a, alen, NULL, 0}, {b, blen, NULL, 0}}, *dp;23672368maxlen = alen < blen ? blen : alen;23692370for (; offset < maxlen; offset += 16) {2371for (k = 0; k < 2; ++k) {2372dp = &ds[k];23732374if (offset < dp->datalen) {2375dp->s = (const uint8_t *)dp->data + offset;2376dp->n = dp->datalen - offset;23772378if (dp->n > 16) {2379dp->n = 16;2380}2381} else {2382dp->s = NULL;2383dp->n = 0;2384}2385}23862387if (ds[0].n == ds[1].n && memcmp(ds[0].s, ds[1].s, ds[0].n) == 0) {2388continue;2389}23902391for (k = 0; k < 2; ++k) {2392dp = &ds[k];23932394if (!dp->n) {2395continue;2396}23972398p = buf;2399*p++ = mk[k];2400*p++ = mk[k];2401*p++ = mk[k];2402*p++ = mk[k];24032404p = hexdump_line(p, dp->s, dp->n, offset);2405*p++ = '\n';24062407len = (size_t)(p - buf);24082409if (fwrite(buf, 1, len, fp) < len) {2410return -1;2411}2412}24132414if (!ds[0].n || !ds[1].n) {2415continue;2416}24172418ncomp = ds[0].n < ds[1].n ? ds[0].n : ds[1].n;24192420p = buf + 4 + 10;24212422memset(buf, ' ', 4 + 78);24232424for (i = 0; i < ncomp; ++i) {2425if (ds[0].s[i] == ds[1].s[i]) {2426*p++ = ' ';2427*p++ = ' ';2428} else {2429adoff = 4 + 10 + 51 + i;2430*(buf + adoff) = '^';24312432*p++ = '^';2433*p++ = '^';2434}24352436*p++ = ' ';24372438if (i == 7) {2439*p++ = ' ';2440}2441}24422443if (adoff) {2444len = adoff + 1;2445} else {2446len = (size_t)(p - buf);2447}24482449buf[len++] = '\n';24502451if (fwrite(buf, 1, len, fp) < len) {2452return -1;2453}2454}24552456return 0;2457}245824592460