xref: /freebsd/sys/contrib/openzfs/tests/unit/munit.c (revision d9497217456002b0ddad3cd319570d0b098daa29)
1*d9497217SMartin Matuska // SPDX-License-Identifier: MIT
2*d9497217SMartin Matuska /* µnit Testing Framework
3*d9497217SMartin Matuska  * Copyright (c) 2013-2018 Evan Nemerson <evan@nemerson.com>
4*d9497217SMartin Matuska  *
5*d9497217SMartin Matuska  * Permission is hereby granted, free of charge, to any person
6*d9497217SMartin Matuska  * obtaining a copy of this software and associated documentation
7*d9497217SMartin Matuska  * files (the "Software"), to deal in the Software without
8*d9497217SMartin Matuska  * restriction, including without limitation the rights to use, copy,
9*d9497217SMartin Matuska  * modify, merge, publish, distribute, sublicense, and/or sell copies
10*d9497217SMartin Matuska  * of the Software, and to permit persons to whom the Software is
11*d9497217SMartin Matuska  * furnished to do so, subject to the following conditions:
12*d9497217SMartin Matuska  *
13*d9497217SMartin Matuska  * The above copyright notice and this permission notice shall be
14*d9497217SMartin Matuska  * included in all copies or substantial portions of the Software.
15*d9497217SMartin Matuska  *
16*d9497217SMartin Matuska  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*d9497217SMartin Matuska  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*d9497217SMartin Matuska  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19*d9497217SMartin Matuska  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20*d9497217SMartin Matuska  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21*d9497217SMartin Matuska  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22*d9497217SMartin Matuska  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23*d9497217SMartin Matuska  * SOFTWARE.
24*d9497217SMartin Matuska  */
25*d9497217SMartin Matuska 
26*d9497217SMartin Matuska /*** Configuration ***/
27*d9497217SMartin Matuska 
28*d9497217SMartin Matuska /* This is just where the output from the test goes.  It's really just
29*d9497217SMartin Matuska  * meant to let you choose stdout or stderr, but if anyone really want
30*d9497217SMartin Matuska  * to direct it to a file let me know, it would be fairly easy to
31*d9497217SMartin Matuska  * support. */
32*d9497217SMartin Matuska #if !defined(MUNIT_OUTPUT_FILE)
33*d9497217SMartin Matuska #  define MUNIT_OUTPUT_FILE stdout
34*d9497217SMartin Matuska #endif
35*d9497217SMartin Matuska 
36*d9497217SMartin Matuska /* This is a bit more useful; it tells µnit how to format the seconds in
37*d9497217SMartin Matuska  * timed tests.  If your tests run for longer you might want to reduce
38*d9497217SMartin Matuska  * it, and if your computer is really fast and your tests are tiny you
39*d9497217SMartin Matuska  * can increase it. */
40*d9497217SMartin Matuska #if !defined(MUNIT_TEST_TIME_FORMAT)
41*d9497217SMartin Matuska #  define MUNIT_TEST_TIME_FORMAT "0.8f"
42*d9497217SMartin Matuska #endif
43*d9497217SMartin Matuska 
44*d9497217SMartin Matuska /* If you have long test names you might want to consider bumping
45*d9497217SMartin Matuska  * this.  The result information takes 43 characters. */
46*d9497217SMartin Matuska #if !defined(MUNIT_TEST_NAME_LEN)
47*d9497217SMartin Matuska #  define MUNIT_TEST_NAME_LEN 37
48*d9497217SMartin Matuska #endif
49*d9497217SMartin Matuska 
50*d9497217SMartin Matuska /* If you don't like the timing information, you can disable it by
51*d9497217SMartin Matuska  * defining MUNIT_DISABLE_TIMING. */
52*d9497217SMartin Matuska #if !defined(MUNIT_DISABLE_TIMING)
53*d9497217SMartin Matuska #  define MUNIT_ENABLE_TIMING
54*d9497217SMartin Matuska #endif
55*d9497217SMartin Matuska 
56*d9497217SMartin Matuska /* OpenZFS: claim no strerror_r, causing munit to use its own internal
57*d9497217SMartin Matuska  * fallback. There are two version of strerror_r (XSI and GNU), subtly
58*d9497217SMartin Matuska  * different, and some glibc versions have warn_unused_result set on the
59*d9497217SMartin Matuska  * prototype. munit is not prepared for this variance, so better just to
60*d9497217SMartin Matuska  * let it do its own thing. -- robn, 2026-05-21 */
61*d9497217SMartin Matuska #if !defined(MUNIT_NO_STRERROR_R)
62*d9497217SMartin Matuska #  define MUNIT_NO_STRERROR_R
63*d9497217SMartin Matuska #endif
64*d9497217SMartin Matuska 
65*d9497217SMartin Matuska /*** End configuration ***/
66*d9497217SMartin Matuska 
67*d9497217SMartin Matuska #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L)
68*d9497217SMartin Matuska #  undef _POSIX_C_SOURCE
69*d9497217SMartin Matuska #endif
70*d9497217SMartin Matuska #if !defined(_POSIX_C_SOURCE)
71*d9497217SMartin Matuska #  define _POSIX_C_SOURCE 200809L
72*d9497217SMartin Matuska #endif
73*d9497217SMartin Matuska 
74*d9497217SMartin Matuska /* Solaris freaks out if you try to use a POSIX or SUS standard without
75*d9497217SMartin Matuska  * the "right" C standard. */
76*d9497217SMartin Matuska #if defined(_XOPEN_SOURCE)
77*d9497217SMartin Matuska #  undef _XOPEN_SOURCE
78*d9497217SMartin Matuska #endif
79*d9497217SMartin Matuska 
80*d9497217SMartin Matuska #if defined(__STDC_VERSION__)
81*d9497217SMartin Matuska #  if __STDC_VERSION__ >= 201112L
82*d9497217SMartin Matuska #    define _XOPEN_SOURCE 700
83*d9497217SMartin Matuska #  elif __STDC_VERSION__ >= 199901L
84*d9497217SMartin Matuska #    define _XOPEN_SOURCE 600
85*d9497217SMartin Matuska #  endif
86*d9497217SMartin Matuska #endif
87*d9497217SMartin Matuska 
88*d9497217SMartin Matuska /* Because, according to Microsoft, POSIX is deprecated.  You've got
89*d9497217SMartin Matuska  * to appreciate the chutzpah. */
90*d9497217SMartin Matuska #if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE)
91*d9497217SMartin Matuska #  define _CRT_NONSTDC_NO_DEPRECATE
92*d9497217SMartin Matuska #endif
93*d9497217SMartin Matuska 
94*d9497217SMartin Matuska #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
95*d9497217SMartin Matuska #  include <stdbool.h>
96*d9497217SMartin Matuska #elif defined(_WIN32)
97*d9497217SMartin Matuska /* https://msdn.microsoft.com/en-us/library/tf4dy80a.aspx */
98*d9497217SMartin Matuska #endif
99*d9497217SMartin Matuska 
100*d9497217SMartin Matuska #include <limits.h>
101*d9497217SMartin Matuska #include <time.h>
102*d9497217SMartin Matuska #include <errno.h>
103*d9497217SMartin Matuska #include <string.h>
104*d9497217SMartin Matuska #include <stdlib.h>
105*d9497217SMartin Matuska #include <stdio.h>
106*d9497217SMartin Matuska #include <stdarg.h>
107*d9497217SMartin Matuska #include <setjmp.h>
108*d9497217SMartin Matuska 
109*d9497217SMartin Matuska #if !defined(MUNIT_NO_NL_LANGINFO) && !defined(_WIN32)
110*d9497217SMartin Matuska #  define MUNIT_NL_LANGINFO
111*d9497217SMartin Matuska #  include <locale.h>
112*d9497217SMartin Matuska #  include <langinfo.h>
113*d9497217SMartin Matuska #  include <strings.h>
114*d9497217SMartin Matuska #endif
115*d9497217SMartin Matuska 
116*d9497217SMartin Matuska #if !defined(_WIN32)
117*d9497217SMartin Matuska #  include <unistd.h>
118*d9497217SMartin Matuska #  include <sys/types.h>
119*d9497217SMartin Matuska #  include <sys/wait.h>
120*d9497217SMartin Matuska #else
121*d9497217SMartin Matuska #  include <windows.h>
122*d9497217SMartin Matuska #  include <io.h>
123*d9497217SMartin Matuska #  include <fcntl.h>
124*d9497217SMartin Matuska #  if !defined(STDERR_FILENO)
125*d9497217SMartin Matuska #    define STDERR_FILENO _fileno(stderr)
126*d9497217SMartin Matuska #  endif
127*d9497217SMartin Matuska #endif
128*d9497217SMartin Matuska 
129*d9497217SMartin Matuska #include "munit.h"
130*d9497217SMartin Matuska 
131*d9497217SMartin Matuska #define MUNIT_STRINGIFY(x) #x
132*d9497217SMartin Matuska #define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x)
133*d9497217SMartin Matuska 
134*d9497217SMartin Matuska #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) ||  \
135*d9497217SMartin Matuska   defined(__IBMCPP__)
136*d9497217SMartin Matuska #  define MUNIT_THREAD_LOCAL __thread
137*d9497217SMartin Matuska #elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) ||          \
138*d9497217SMartin Matuska   defined(_Thread_local)
139*d9497217SMartin Matuska #  define MUNIT_THREAD_LOCAL _Thread_local
140*d9497217SMartin Matuska #elif defined(_WIN32)
141*d9497217SMartin Matuska #  define MUNIT_THREAD_LOCAL __declspec(thread)
142*d9497217SMartin Matuska #endif
143*d9497217SMartin Matuska 
144*d9497217SMartin Matuska /* MSVC 12.0 will emit a warning at /W4 for code like 'do { ... }
145*d9497217SMartin Matuska  * while (0)', or 'do { ... } while (1)'.  I'm pretty sure nobody
146*d9497217SMartin Matuska  * at Microsoft compiles with /W4. */
147*d9497217SMartin Matuska #if defined(_MSC_VER) && (_MSC_VER <= 1800)
148*d9497217SMartin Matuska #  pragma warning(disable : 4127)
149*d9497217SMartin Matuska #endif
150*d9497217SMartin Matuska 
151*d9497217SMartin Matuska #if defined(_WIN32) || defined(__EMSCRIPTEN__)
152*d9497217SMartin Matuska #  define MUNIT_NO_FORK
153*d9497217SMartin Matuska #endif
154*d9497217SMartin Matuska 
155*d9497217SMartin Matuska #if defined(__EMSCRIPTEN__)
156*d9497217SMartin Matuska #  define MUNIT_NO_BUFFER
157*d9497217SMartin Matuska #endif
158*d9497217SMartin Matuska 
159*d9497217SMartin Matuska /*** Logging ***/
160*d9497217SMartin Matuska 
161*d9497217SMartin Matuska static MunitLogLevel munit_log_level_visible = MUNIT_LOG_INFO;
162*d9497217SMartin Matuska static MunitLogLevel munit_log_level_fatal = MUNIT_LOG_ERROR;
163*d9497217SMartin Matuska 
164*d9497217SMartin Matuska #if defined(MUNIT_THREAD_LOCAL)
165*d9497217SMartin Matuska static MUNIT_THREAD_LOCAL munit_bool munit_error_jmp_buf_valid = 0;
166*d9497217SMartin Matuska static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf;
167*d9497217SMartin Matuska #endif
168*d9497217SMartin Matuska 
169*d9497217SMartin Matuska /* At certain warning levels, mingw will trigger warnings about
170*d9497217SMartin Matuska  * suggesting the format attribute, which we've explicity *not* set
171*d9497217SMartin Matuska  * because it will then choke on our attempts to use the MS-specific
172*d9497217SMartin Matuska  * I64 modifier for size_t (which we have to use since MSVC doesn't
173*d9497217SMartin Matuska  * support the C99 z modifier). */
174*d9497217SMartin Matuska 
175*d9497217SMartin Matuska #if defined(__MINGW32__) || defined(__MINGW64__)
176*d9497217SMartin Matuska #  pragma GCC diagnostic push
177*d9497217SMartin Matuska #  pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
178*d9497217SMartin Matuska #endif
179*d9497217SMartin Matuska 
180*d9497217SMartin Matuska MUNIT_PRINTF(5, 0)
munit_logf_exv(MunitLogLevel level,FILE * fp,const char * filename,int line,const char * format,va_list ap)181*d9497217SMartin Matuska static void munit_logf_exv(MunitLogLevel level, FILE *fp, const char *filename,
182*d9497217SMartin Matuska                            int line, const char *format, va_list ap) {
183*d9497217SMartin Matuska   if (level < munit_log_level_visible)
184*d9497217SMartin Matuska     return;
185*d9497217SMartin Matuska 
186*d9497217SMartin Matuska   switch (level) {
187*d9497217SMartin Matuska   case MUNIT_LOG_DEBUG:
188*d9497217SMartin Matuska     fputs("Debug", fp);
189*d9497217SMartin Matuska     break;
190*d9497217SMartin Matuska   case MUNIT_LOG_INFO:
191*d9497217SMartin Matuska     fputs("Info", fp);
192*d9497217SMartin Matuska     break;
193*d9497217SMartin Matuska   case MUNIT_LOG_WARNING:
194*d9497217SMartin Matuska     fputs("Warning", fp);
195*d9497217SMartin Matuska     break;
196*d9497217SMartin Matuska   case MUNIT_LOG_ERROR:
197*d9497217SMartin Matuska     fputs("Error", fp);
198*d9497217SMartin Matuska     break;
199*d9497217SMartin Matuska   default:
200*d9497217SMartin Matuska     munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)",
201*d9497217SMartin Matuska                   level);
202*d9497217SMartin Matuska     return;
203*d9497217SMartin Matuska   }
204*d9497217SMartin Matuska 
205*d9497217SMartin Matuska   fputs(": ", fp);
206*d9497217SMartin Matuska   if (filename != NULL)
207*d9497217SMartin Matuska     fprintf(fp, "%s:%d: ", filename, line);
208*d9497217SMartin Matuska   vfprintf(fp, format, ap);
209*d9497217SMartin Matuska   fputc('\n', fp);
210*d9497217SMartin Matuska }
211*d9497217SMartin Matuska 
212*d9497217SMartin Matuska MUNIT_PRINTF(3, 4)
munit_logf_internal(MunitLogLevel level,FILE * fp,const char * format,...)213*d9497217SMartin Matuska static void munit_logf_internal(MunitLogLevel level, FILE *fp,
214*d9497217SMartin Matuska                                 const char *format, ...) {
215*d9497217SMartin Matuska   va_list ap;
216*d9497217SMartin Matuska 
217*d9497217SMartin Matuska   va_start(ap, format);
218*d9497217SMartin Matuska   munit_logf_exv(level, fp, NULL, 0, format, ap);
219*d9497217SMartin Matuska   va_end(ap);
220*d9497217SMartin Matuska }
221*d9497217SMartin Matuska 
munit_log_internal(MunitLogLevel level,FILE * fp,const char * message)222*d9497217SMartin Matuska static void munit_log_internal(MunitLogLevel level, FILE *fp,
223*d9497217SMartin Matuska                                const char *message) {
224*d9497217SMartin Matuska   munit_logf_internal(level, fp, "%s", message);
225*d9497217SMartin Matuska }
226*d9497217SMartin Matuska 
munit_logf_ex(MunitLogLevel level,const char * filename,int line,const char * format,...)227*d9497217SMartin Matuska void munit_logf_ex(MunitLogLevel level, const char *filename, int line,
228*d9497217SMartin Matuska                    const char *format, ...) {
229*d9497217SMartin Matuska   va_list ap;
230*d9497217SMartin Matuska 
231*d9497217SMartin Matuska   va_start(ap, format);
232*d9497217SMartin Matuska   munit_logf_exv(level, stderr, filename, line, format, ap);
233*d9497217SMartin Matuska   va_end(ap);
234*d9497217SMartin Matuska 
235*d9497217SMartin Matuska   if (level >= munit_log_level_fatal) {
236*d9497217SMartin Matuska #if defined(MUNIT_THREAD_LOCAL)
237*d9497217SMartin Matuska     if (munit_error_jmp_buf_valid)
238*d9497217SMartin Matuska       longjmp(munit_error_jmp_buf, 1);
239*d9497217SMartin Matuska #endif
240*d9497217SMartin Matuska     abort();
241*d9497217SMartin Matuska   }
242*d9497217SMartin Matuska }
243*d9497217SMartin Matuska 
munit_errorf_ex(const char * filename,int line,const char * format,...)244*d9497217SMartin Matuska void munit_errorf_ex(const char *filename, int line, const char *format, ...) {
245*d9497217SMartin Matuska   va_list ap;
246*d9497217SMartin Matuska 
247*d9497217SMartin Matuska   va_start(ap, format);
248*d9497217SMartin Matuska   munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap);
249*d9497217SMartin Matuska   va_end(ap);
250*d9497217SMartin Matuska 
251*d9497217SMartin Matuska #if defined(MUNIT_THREAD_LOCAL)
252*d9497217SMartin Matuska   if (munit_error_jmp_buf_valid)
253*d9497217SMartin Matuska     longjmp(munit_error_jmp_buf, 1);
254*d9497217SMartin Matuska #endif
255*d9497217SMartin Matuska   abort();
256*d9497217SMartin Matuska }
257*d9497217SMartin Matuska 
258*d9497217SMartin Matuska #if defined(__MINGW32__) || defined(__MINGW64__)
259*d9497217SMartin Matuska #  pragma GCC diagnostic pop
260*d9497217SMartin Matuska #endif
261*d9497217SMartin Matuska 
262*d9497217SMartin Matuska #if !defined(MUNIT_STRERROR_LEN)
263*d9497217SMartin Matuska #  define MUNIT_STRERROR_LEN 80
264*d9497217SMartin Matuska #endif
265*d9497217SMartin Matuska 
munit_log_errno(MunitLogLevel level,FILE * fp,const char * msg)266*d9497217SMartin Matuska static void munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) {
267*d9497217SMartin Matuska #if defined(MUNIT_NO_STRERROR_R) ||                                            \
268*d9497217SMartin Matuska   (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API))
269*d9497217SMartin Matuska   munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno);
270*d9497217SMartin Matuska #else
271*d9497217SMartin Matuska   char munit_error_str[MUNIT_STRERROR_LEN];
272*d9497217SMartin Matuska   munit_error_str[0] = '\0';
273*d9497217SMartin Matuska 
274*d9497217SMartin Matuska #  if !defined(_WIN32)
275*d9497217SMartin Matuska   strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN);
276*d9497217SMartin Matuska #  else
277*d9497217SMartin Matuska   strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno);
278*d9497217SMartin Matuska #  endif
279*d9497217SMartin Matuska 
280*d9497217SMartin Matuska   munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno);
281*d9497217SMartin Matuska #endif
282*d9497217SMartin Matuska }
283*d9497217SMartin Matuska 
284*d9497217SMartin Matuska /*** Memory allocation ***/
285*d9497217SMartin Matuska 
munit_malloc_ex(const char * filename,int line,size_t size)286*d9497217SMartin Matuska void *munit_malloc_ex(const char *filename, int line, size_t size) {
287*d9497217SMartin Matuska   void *ptr;
288*d9497217SMartin Matuska 
289*d9497217SMartin Matuska   if (size == 0)
290*d9497217SMartin Matuska     return NULL;
291*d9497217SMartin Matuska 
292*d9497217SMartin Matuska   ptr = calloc(1, size);
293*d9497217SMartin Matuska   if (MUNIT_UNLIKELY(ptr == NULL)) {
294*d9497217SMartin Matuska     munit_logf_ex(MUNIT_LOG_ERROR, filename, line,
295*d9497217SMartin Matuska                   "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size);
296*d9497217SMartin Matuska   }
297*d9497217SMartin Matuska 
298*d9497217SMartin Matuska   return ptr;
299*d9497217SMartin Matuska }
300*d9497217SMartin Matuska 
301*d9497217SMartin Matuska /*** Timer code ***/
302*d9497217SMartin Matuska 
303*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
304*d9497217SMartin Matuska 
305*d9497217SMartin Matuska #  define psnip_uint64_t munit_uint64_t
306*d9497217SMartin Matuska #  define psnip_uint32_t munit_uint32_t
307*d9497217SMartin Matuska 
308*d9497217SMartin Matuska /* Code copied from portable-snippets
309*d9497217SMartin Matuska  * <https://github.com/nemequ/portable-snippets/>.  If you need to
310*d9497217SMartin Matuska  * change something, please do it there so we can keep the code in
311*d9497217SMartin Matuska  * sync. */
312*d9497217SMartin Matuska 
313*d9497217SMartin Matuska /* Clocks (v1)
314*d9497217SMartin Matuska  * Portable Snippets - https://gitub.com/nemequ/portable-snippets
315*d9497217SMartin Matuska  * Created by Evan Nemerson <evan@nemerson.com>
316*d9497217SMartin Matuska  *
317*d9497217SMartin Matuska  *   To the extent possible under law, the authors have waived all
318*d9497217SMartin Matuska  *   copyright and related or neighboring rights to this code.  For
319*d9497217SMartin Matuska  *   details, see the Creative Commons Zero 1.0 Universal license at
320*d9497217SMartin Matuska  *   https://creativecommons.org/publicdomain/zero/1.0/
321*d9497217SMartin Matuska  */
322*d9497217SMartin Matuska 
323*d9497217SMartin Matuska #  if !defined(PSNIP_CLOCK_H)
324*d9497217SMartin Matuska #    define PSNIP_CLOCK_H
325*d9497217SMartin Matuska 
326*d9497217SMartin Matuska #    if !defined(psnip_uint64_t)
327*d9497217SMartin Matuska #      include "../exact-int/exact-int.h"
328*d9497217SMartin Matuska #    endif
329*d9497217SMartin Matuska 
330*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_STATIC_INLINE)
331*d9497217SMartin Matuska #      if defined(__GNUC__)
332*d9497217SMartin Matuska #        define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))
333*d9497217SMartin Matuska #      else
334*d9497217SMartin Matuska #        define PSNIP_CLOCK__COMPILER_ATTRIBUTES
335*d9497217SMartin Matuska #      endif
336*d9497217SMartin Matuska 
337*d9497217SMartin Matuska #      define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static
338*d9497217SMartin Matuska #    endif
339*d9497217SMartin Matuska 
340*d9497217SMartin Matuska enum PsnipClockType {
341*d9497217SMartin Matuska   /* This clock provides the current time, in units since 1970-01-01
342*d9497217SMartin Matuska    * 00:00:00 UTC not including leap seconds.  In other words, UNIX
343*d9497217SMartin Matuska    * time.  Keep in mind that this clock doesn't account for leap
344*d9497217SMartin Matuska    * seconds, and can go backwards (think NTP adjustments). */
345*d9497217SMartin Matuska   PSNIP_CLOCK_TYPE_WALL = 1,
346*d9497217SMartin Matuska   /* The CPU time is a clock which increases only when the current
347*d9497217SMartin Matuska    * process is active (i.e., it doesn't increment while blocking on
348*d9497217SMartin Matuska    * I/O). */
349*d9497217SMartin Matuska   PSNIP_CLOCK_TYPE_CPU = 2,
350*d9497217SMartin Matuska   /* Monotonic time is always running (unlike CPU time), but it only
351*d9497217SMartin Matuska      ever moves forward unless you reboot the system.  Things like NTP
352*d9497217SMartin Matuska      adjustments have no effect on this clock. */
353*d9497217SMartin Matuska   PSNIP_CLOCK_TYPE_MONOTONIC = 3
354*d9497217SMartin Matuska };
355*d9497217SMartin Matuska 
356*d9497217SMartin Matuska struct PsnipClockTimespec {
357*d9497217SMartin Matuska   psnip_uint64_t seconds;
358*d9497217SMartin Matuska   psnip_uint64_t nanoseconds;
359*d9497217SMartin Matuska };
360*d9497217SMartin Matuska 
361*d9497217SMartin Matuska /* Methods we support: */
362*d9497217SMartin Matuska 
363*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1
364*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_TIME 2
365*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3
366*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4
367*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5
368*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_CLOCK 6
369*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7
370*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_GETRUSAGE 8
371*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9
372*d9497217SMartin Matuska #    define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10
373*d9497217SMartin Matuska 
374*d9497217SMartin Matuska #    include <assert.h>
375*d9497217SMartin Matuska 
376*d9497217SMartin Matuska #    if defined(HEDLEY_UNREACHABLE)
377*d9497217SMartin Matuska #      define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()
378*d9497217SMartin Matuska #    else
379*d9497217SMartin Matuska #      define PSNIP_CLOCK_UNREACHABLE() assert(0)
380*d9497217SMartin Matuska #    endif
381*d9497217SMartin Matuska 
382*d9497217SMartin Matuska /* Choose an implementation */
383*d9497217SMartin Matuska 
384*d9497217SMartin Matuska /* #undef PSNIP_CLOCK_WALL_METHOD */
385*d9497217SMartin Matuska /* #undef PSNIP_CLOCK_CPU_METHOD */
386*d9497217SMartin Matuska /* #undef PSNIP_CLOCK_MONOTONIC_METHOD */
387*d9497217SMartin Matuska 
388*d9497217SMartin Matuska /* We want to be able to detect the libc implementation, so we include
389*d9497217SMartin Matuska    <limits.h> (<features.h> isn't available everywhere). */
390*d9497217SMartin Matuska 
391*d9497217SMartin Matuska #    if defined(__unix__) || defined(__unix) || defined(__linux__)
392*d9497217SMartin Matuska #      include <limits.h>
393*d9497217SMartin Matuska #      include <unistd.h>
394*d9497217SMartin Matuska #    endif
395*d9497217SMartin Matuska 
396*d9497217SMartin Matuska #    if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
397*d9497217SMartin Matuska /* These are known to work without librt.  If you know of others
398*d9497217SMartin Matuska  * please let us know so we can add them. */
399*d9497217SMartin Matuska #      if (defined(__GLIBC__) &&                                               \
400*d9497217SMartin Matuska            (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) ||    \
401*d9497217SMartin Matuska         (defined(__FreeBSD__))
402*d9497217SMartin Matuska #        define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
403*d9497217SMartin Matuska #      elif !defined(PSNIP_CLOCK_NO_LIBRT)
404*d9497217SMartin Matuska #        define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
405*d9497217SMartin Matuska #      endif
406*d9497217SMartin Matuska #    endif
407*d9497217SMartin Matuska 
408*d9497217SMartin Matuska #    if defined(_WIN32)
409*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_CPU_METHOD)
410*d9497217SMartin Matuska #        define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES
411*d9497217SMartin Matuska #      endif
412*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
413*d9497217SMartin Matuska #        define PSNIP_CLOCK_MONOTONIC_METHOD                                   \
414*d9497217SMartin Matuska           PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
415*d9497217SMartin Matuska #      endif
416*d9497217SMartin Matuska #    endif
417*d9497217SMartin Matuska 
418*d9497217SMartin Matuska #    if defined(__MACH__) && !defined(__gnu_hurd__)
419*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
420*d9497217SMartin Matuska #        define PSNIP_CLOCK_MONOTONIC_METHOD                                   \
421*d9497217SMartin Matuska           PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
422*d9497217SMartin Matuska #      endif
423*d9497217SMartin Matuska #    endif
424*d9497217SMartin Matuska 
425*d9497217SMartin Matuska #    if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)
426*d9497217SMartin Matuska #      include <time.h>
427*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_WALL_METHOD)
428*d9497217SMartin Matuska #        if defined(CLOCK_REALTIME_PRECISE)
429*d9497217SMartin Matuska #          define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
430*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE
431*d9497217SMartin Matuska #        elif !defined(__sun)
432*d9497217SMartin Matuska #          define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
433*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME
434*d9497217SMartin Matuska #        endif
435*d9497217SMartin Matuska #      endif
436*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_CPU_METHOD)
437*d9497217SMartin Matuska #        if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)
438*d9497217SMartin Matuska #          define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
439*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID
440*d9497217SMartin Matuska #        elif defined(CLOCK_VIRTUAL)
441*d9497217SMartin Matuska #          define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
442*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL
443*d9497217SMartin Matuska #        endif
444*d9497217SMartin Matuska #      endif
445*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
446*d9497217SMartin Matuska #        if defined(CLOCK_MONOTONIC_RAW)
447*d9497217SMartin Matuska #          define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
448*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
449*d9497217SMartin Matuska #        elif defined(CLOCK_MONOTONIC_PRECISE)
450*d9497217SMartin Matuska #          define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
451*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE
452*d9497217SMartin Matuska #        elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
453*d9497217SMartin Matuska #          define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
454*d9497217SMartin Matuska #          define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
455*d9497217SMartin Matuska #        endif
456*d9497217SMartin Matuska #      endif
457*d9497217SMartin Matuska #    endif
458*d9497217SMartin Matuska 
459*d9497217SMartin Matuska #    if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
460*d9497217SMartin Matuska #      if !defined(PSNIP_CLOCK_WALL_METHOD)
461*d9497217SMartin Matuska #        define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY
462*d9497217SMartin Matuska #      endif
463*d9497217SMartin Matuska #    endif
464*d9497217SMartin Matuska 
465*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_WALL_METHOD)
466*d9497217SMartin Matuska #      define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME
467*d9497217SMartin Matuska #    endif
468*d9497217SMartin Matuska 
469*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_CPU_METHOD)
470*d9497217SMartin Matuska #      define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK
471*d9497217SMartin Matuska #    endif
472*d9497217SMartin Matuska 
473*d9497217SMartin Matuska /* Primarily here for testing. */
474*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                              \
475*d9497217SMartin Matuska       defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)
476*d9497217SMartin Matuska #      error No monotonic clock found.
477*d9497217SMartin Matuska #    endif
478*d9497217SMartin Matuska 
479*d9497217SMartin Matuska /* Implementations */
480*d9497217SMartin Matuska 
481*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
482*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) ||      \
483*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
484*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) ||       \
485*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
486*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) ||  \
487*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_CPU_METHOD) &&                                      \
488*d9497217SMartin Matuska        (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) ||                \
489*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
490*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) ||               \
491*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
492*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) ||          \
493*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_CPU_METHOD) &&                                      \
494*d9497217SMartin Matuska        (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) ||                 \
495*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
496*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) ||                \
497*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
498*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))
499*d9497217SMartin Matuska #      include <time.h>
500*d9497217SMartin Matuska #    endif
501*d9497217SMartin Matuska 
502*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
503*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) ||       \
504*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
505*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) ||        \
506*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
507*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))
508*d9497217SMartin Matuska #      include <sys/time.h>
509*d9497217SMartin Matuska #    endif
510*d9497217SMartin Matuska 
511*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
512*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) ||    \
513*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
514*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) ||     \
515*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
516*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD ==                                        \
517*d9497217SMartin Matuska         PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) ||                                \
518*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_CPU_METHOD) &&                                      \
519*d9497217SMartin Matuska        (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) ||       \
520*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
521*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) ||      \
522*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
523*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))
524*d9497217SMartin Matuska #      include <windows.h>
525*d9497217SMartin Matuska #    endif
526*d9497217SMartin Matuska 
527*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
528*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) ||          \
529*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
530*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) ||           \
531*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
532*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))
533*d9497217SMartin Matuska #      include <sys/time.h>
534*d9497217SMartin Matuska #      include <sys/resource.h>
535*d9497217SMartin Matuska #    endif
536*d9497217SMartin Matuska 
537*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
538*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
539*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
540*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) ||  \
541*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
542*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD ==                                        \
543*d9497217SMartin Matuska         PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))
544*d9497217SMartin Matuska #      include <CoreServices/CoreServices.h>
545*d9497217SMartin Matuska #      include <mach/mach.h>
546*d9497217SMartin Matuska #      include <mach/mach_time.h>
547*d9497217SMartin Matuska #    endif
548*d9497217SMartin Matuska 
549*d9497217SMartin Matuska /*** Implementations ***/
550*d9497217SMartin Matuska 
551*d9497217SMartin Matuska #    define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t)(1000000000ULL))
552*d9497217SMartin Matuska 
553*d9497217SMartin Matuska #    if (defined(PSNIP_CLOCK_CPU_METHOD) &&                                    \
554*d9497217SMartin Matuska          (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) ||      \
555*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_WALL_METHOD) &&                                     \
556*d9497217SMartin Matuska        (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) ||       \
557*d9497217SMartin Matuska       (defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                                \
558*d9497217SMartin Matuska        (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))
559*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock__clock_getres(clockid_t clk_id)560*d9497217SMartin Matuska psnip_clock__clock_getres(clockid_t clk_id) {
561*d9497217SMartin Matuska   struct timespec res;
562*d9497217SMartin Matuska   int r;
563*d9497217SMartin Matuska 
564*d9497217SMartin Matuska   r = clock_getres(clk_id, &res);
565*d9497217SMartin Matuska   if (r != 0)
566*d9497217SMartin Matuska     return 0;
567*d9497217SMartin Matuska 
568*d9497217SMartin Matuska   return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC /
569*d9497217SMartin Matuska                           (psnip_uint64_t)res.tv_nsec);
570*d9497217SMartin Matuska }
571*d9497217SMartin Matuska 
572*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION int
psnip_clock__clock_gettime(clockid_t clk_id,struct PsnipClockTimespec * res)573*d9497217SMartin Matuska psnip_clock__clock_gettime(clockid_t clk_id, struct PsnipClockTimespec *res) {
574*d9497217SMartin Matuska   struct timespec ts;
575*d9497217SMartin Matuska 
576*d9497217SMartin Matuska   if (clock_gettime(clk_id, &ts) != 0)
577*d9497217SMartin Matuska     return -10;
578*d9497217SMartin Matuska 
579*d9497217SMartin Matuska   res->seconds = (psnip_uint64_t)(ts.tv_sec);
580*d9497217SMartin Matuska   res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec);
581*d9497217SMartin Matuska 
582*d9497217SMartin Matuska   return 0;
583*d9497217SMartin Matuska }
584*d9497217SMartin Matuska #    endif
585*d9497217SMartin Matuska 
psnip_clock_wall_get_precision(void)586*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_wall_get_precision(void) {
587*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_WALL_METHOD)
588*d9497217SMartin Matuska   return 0;
589*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
590*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
591*d9497217SMartin Matuska   return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);
592*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
593*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
594*d9497217SMartin Matuska   return 1000000;
595*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
596*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
597*d9497217SMartin Matuska   return 1;
598*d9497217SMartin Matuska #    else
599*d9497217SMartin Matuska   return 0;
600*d9497217SMartin Matuska #    endif
601*d9497217SMartin Matuska }
602*d9497217SMartin Matuska 
603*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION int
psnip_clock_wall_get_time(struct PsnipClockTimespec * res)604*d9497217SMartin Matuska psnip_clock_wall_get_time(struct PsnipClockTimespec *res) {
605*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_WALL_METHOD)
606*d9497217SMartin Matuska   (void)res;
607*d9497217SMartin Matuska 
608*d9497217SMartin Matuska   return -2;
609*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
610*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
611*d9497217SMartin Matuska   return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);
612*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
613*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
614*d9497217SMartin Matuska   res->seconds = time(NULL);
615*d9497217SMartin Matuska   res->nanoseconds = 0;
616*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_WALL_METHOD) &&                                  \
617*d9497217SMartin Matuska       PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
618*d9497217SMartin Matuska   struct timeval tv;
619*d9497217SMartin Matuska 
620*d9497217SMartin Matuska   if (gettimeofday(&tv, NULL) != 0)
621*d9497217SMartin Matuska     return -6;
622*d9497217SMartin Matuska 
623*d9497217SMartin Matuska   res->seconds = (psnip_uint64_t)tv.tv_sec;
624*d9497217SMartin Matuska   res->nanoseconds = (psnip_uint64_t)tv.tv_usec * 1000;
625*d9497217SMartin Matuska #    else
626*d9497217SMartin Matuska   (void)res;
627*d9497217SMartin Matuska 
628*d9497217SMartin Matuska   return -2;
629*d9497217SMartin Matuska #    endif
630*d9497217SMartin Matuska 
631*d9497217SMartin Matuska   return 0;
632*d9497217SMartin Matuska }
633*d9497217SMartin Matuska 
psnip_clock_cpu_get_precision(void)634*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_cpu_get_precision(void) {
635*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_CPU_METHOD)
636*d9497217SMartin Matuska   return 0;
637*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
638*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
639*d9497217SMartin Matuska   return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);
640*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
641*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
642*d9497217SMartin Matuska   return CLOCKS_PER_SEC;
643*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
644*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
645*d9497217SMartin Matuska   return PSNIP_CLOCK_NSEC_PER_SEC / 100;
646*d9497217SMartin Matuska #    else
647*d9497217SMartin Matuska   return 0;
648*d9497217SMartin Matuska #    endif
649*d9497217SMartin Matuska }
650*d9497217SMartin Matuska 
651*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION int
psnip_clock_cpu_get_time(struct PsnipClockTimespec * res)652*d9497217SMartin Matuska psnip_clock_cpu_get_time(struct PsnipClockTimespec *res) {
653*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_CPU_METHOD)
654*d9497217SMartin Matuska   (void)res;
655*d9497217SMartin Matuska   return -2;
656*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
657*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
658*d9497217SMartin Matuska   return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);
659*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
660*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
661*d9497217SMartin Matuska   clock_t t = clock();
662*d9497217SMartin Matuska   if (t == ((clock_t)-1))
663*d9497217SMartin Matuska     return -5;
664*d9497217SMartin Matuska   res->seconds = t / CLOCKS_PER_SEC;
665*d9497217SMartin Matuska   res->nanoseconds =
666*d9497217SMartin Matuska     (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);
667*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_CPU_METHOD) &&                                   \
668*d9497217SMartin Matuska       PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
669*d9497217SMartin Matuska   FILETIME CreationTime, ExitTime, KernelTime, UserTime;
670*d9497217SMartin Matuska   LARGE_INTEGER date, adjust;
671*d9497217SMartin Matuska 
672*d9497217SMartin Matuska   if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime,
673*d9497217SMartin Matuska                        &KernelTime, &UserTime))
674*d9497217SMartin Matuska     return -7;
675*d9497217SMartin Matuska 
676*d9497217SMartin Matuska   /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */
677*d9497217SMartin Matuska   date.HighPart = (LONG)UserTime.dwHighDateTime;
678*d9497217SMartin Matuska   date.LowPart = UserTime.dwLowDateTime;
679*d9497217SMartin Matuska   adjust.QuadPart = 11644473600000 * 10000;
680*d9497217SMartin Matuska   date.QuadPart -= adjust.QuadPart;
681*d9497217SMartin Matuska 
682*d9497217SMartin Matuska   res->seconds = (psnip_uint64_t)(date.QuadPart / 10000000);
683*d9497217SMartin Matuska   res->nanoseconds = (psnip_uint64_t)(date.QuadPart % 10000000) *
684*d9497217SMartin Matuska                      (PSNIP_CLOCK_NSEC_PER_SEC / 100);
685*d9497217SMartin Matuska #    elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE
686*d9497217SMartin Matuska   struct rusage usage;
687*d9497217SMartin Matuska   if (getrusage(RUSAGE_SELF, &usage) != 0)
688*d9497217SMartin Matuska     return -8;
689*d9497217SMartin Matuska 
690*d9497217SMartin Matuska   res->seconds = usage.ru_utime.tv_sec;
691*d9497217SMartin Matuska   res->nanoseconds = tv.tv_usec * 1000;
692*d9497217SMartin Matuska #    else
693*d9497217SMartin Matuska   (void)res;
694*d9497217SMartin Matuska   return -2;
695*d9497217SMartin Matuska #    endif
696*d9497217SMartin Matuska 
697*d9497217SMartin Matuska   return 0;
698*d9497217SMartin Matuska }
699*d9497217SMartin Matuska 
psnip_clock_monotonic_get_precision(void)700*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_monotonic_get_precision(void) {
701*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
702*d9497217SMartin Matuska   return 0;
703*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
704*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
705*d9497217SMartin Matuska   return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);
706*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
707*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
708*d9497217SMartin Matuska   static mach_timebase_info_data_t tbi = {
709*d9497217SMartin Matuska     0,
710*d9497217SMartin Matuska   };
711*d9497217SMartin Matuska   if (tbi.denom == 0)
712*d9497217SMartin Matuska     mach_timebase_info(&tbi);
713*d9497217SMartin Matuska   return (psnip_uint32_t)(tbi.numer / tbi.denom);
714*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
715*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
716*d9497217SMartin Matuska   return 1000;
717*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
718*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD ==                                          \
719*d9497217SMartin Matuska         PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
720*d9497217SMartin Matuska   LARGE_INTEGER Frequency;
721*d9497217SMartin Matuska   QueryPerformanceFrequency(&Frequency);
722*d9497217SMartin Matuska   return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
723*d9497217SMartin Matuska                             ? PSNIP_CLOCK_NSEC_PER_SEC
724*d9497217SMartin Matuska                             : Frequency.QuadPart);
725*d9497217SMartin Matuska #    else
726*d9497217SMartin Matuska   return 0;
727*d9497217SMartin Matuska #    endif
728*d9497217SMartin Matuska }
729*d9497217SMartin Matuska 
730*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION int
psnip_clock_monotonic_get_time(struct PsnipClockTimespec * res)731*d9497217SMartin Matuska psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) {
732*d9497217SMartin Matuska #    if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
733*d9497217SMartin Matuska   (void)res;
734*d9497217SMartin Matuska   return -2;
735*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
736*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
737*d9497217SMartin Matuska   return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);
738*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
739*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
740*d9497217SMartin Matuska   psnip_uint64_t nsec = mach_absolute_time();
741*d9497217SMartin Matuska   static mach_timebase_info_data_t tbi = {
742*d9497217SMartin Matuska     0,
743*d9497217SMartin Matuska   };
744*d9497217SMartin Matuska   if (tbi.denom == 0)
745*d9497217SMartin Matuska     mach_timebase_info(&tbi);
746*d9497217SMartin Matuska   nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom);
747*d9497217SMartin Matuska   res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;
748*d9497217SMartin Matuska   res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;
749*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
750*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD ==                                          \
751*d9497217SMartin Matuska         PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
752*d9497217SMartin Matuska   LARGE_INTEGER t, f;
753*d9497217SMartin Matuska   if (QueryPerformanceCounter(&t) == 0)
754*d9497217SMartin Matuska     return -12;
755*d9497217SMartin Matuska 
756*d9497217SMartin Matuska   QueryPerformanceFrequency(&f);
757*d9497217SMartin Matuska   res->seconds = (psnip_uint64_t)(t.QuadPart / f.QuadPart);
758*d9497217SMartin Matuska   res->nanoseconds = (psnip_uint64_t)(t.QuadPart % f.QuadPart);
759*d9497217SMartin Matuska   if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
760*d9497217SMartin Matuska     res->nanoseconds /= (psnip_uint64_t)f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;
761*d9497217SMartin Matuska   else
762*d9497217SMartin Matuska     res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / (psnip_uint64_t)f.QuadPart;
763*d9497217SMartin Matuska #    elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) &&                             \
764*d9497217SMartin Matuska       PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
765*d9497217SMartin Matuska   const ULONGLONG msec = GetTickCount64();
766*d9497217SMartin Matuska   res->seconds = msec / 1000;
767*d9497217SMartin Matuska   res->nanoseconds = sec % 1000;
768*d9497217SMartin Matuska #    else
769*d9497217SMartin Matuska   return -2;
770*d9497217SMartin Matuska #    endif
771*d9497217SMartin Matuska 
772*d9497217SMartin Matuska   return 0;
773*d9497217SMartin Matuska }
774*d9497217SMartin Matuska 
775*d9497217SMartin Matuska /* Returns the number of ticks per second for the specified clock.
776*d9497217SMartin Matuska  * For example, a clock with millisecond precision would return 1000,
777*d9497217SMartin Matuska  * and a clock with 1 second (such as the time() function) would
778*d9497217SMartin Matuska  * return 1.
779*d9497217SMartin Matuska  *
780*d9497217SMartin Matuska  * If the requested clock isn't available, it will return 0.
781*d9497217SMartin Matuska  * Hopefully this will be rare, but if it happens to you please let us
782*d9497217SMartin Matuska  * know so we can work on finding a way to support your system.
783*d9497217SMartin Matuska  *
784*d9497217SMartin Matuska  * Note that different clocks on the same system often have a
785*d9497217SMartin Matuska  * different precisions.
786*d9497217SMartin Matuska  */
787*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION psnip_uint32_t
psnip_clock_get_precision(enum PsnipClockType clock_type)788*d9497217SMartin Matuska psnip_clock_get_precision(enum PsnipClockType clock_type) {
789*d9497217SMartin Matuska   switch (clock_type) {
790*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_MONOTONIC:
791*d9497217SMartin Matuska     return psnip_clock_monotonic_get_precision();
792*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_CPU:
793*d9497217SMartin Matuska     return psnip_clock_cpu_get_precision();
794*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_WALL:
795*d9497217SMartin Matuska     return psnip_clock_wall_get_precision();
796*d9497217SMartin Matuska   }
797*d9497217SMartin Matuska 
798*d9497217SMartin Matuska   PSNIP_CLOCK_UNREACHABLE();
799*d9497217SMartin Matuska   return 0;
800*d9497217SMartin Matuska }
801*d9497217SMartin Matuska 
802*d9497217SMartin Matuska /* Set the provided timespec to the requested time.  Returns 0 on
803*d9497217SMartin Matuska  * success, or a negative value on failure. */
psnip_clock_get_time(enum PsnipClockType clock_type,struct PsnipClockTimespec * res)804*d9497217SMartin Matuska PSNIP_CLOCK__FUNCTION int psnip_clock_get_time(enum PsnipClockType clock_type,
805*d9497217SMartin Matuska                                                struct PsnipClockTimespec *res) {
806*d9497217SMartin Matuska   assert(res != NULL);
807*d9497217SMartin Matuska 
808*d9497217SMartin Matuska   switch (clock_type) {
809*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_MONOTONIC:
810*d9497217SMartin Matuska     return psnip_clock_monotonic_get_time(res);
811*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_CPU:
812*d9497217SMartin Matuska     return psnip_clock_cpu_get_time(res);
813*d9497217SMartin Matuska   case PSNIP_CLOCK_TYPE_WALL:
814*d9497217SMartin Matuska     return psnip_clock_wall_get_time(res);
815*d9497217SMartin Matuska   }
816*d9497217SMartin Matuska 
817*d9497217SMartin Matuska   return -1;
818*d9497217SMartin Matuska }
819*d9497217SMartin Matuska 
820*d9497217SMartin Matuska #  endif /* !defined(PSNIP_CLOCK_H) */
821*d9497217SMartin Matuska 
munit_clock_get_elapsed(struct PsnipClockTimespec * start,struct PsnipClockTimespec * end)822*d9497217SMartin Matuska static psnip_uint64_t munit_clock_get_elapsed(struct PsnipClockTimespec *start,
823*d9497217SMartin Matuska                                               struct PsnipClockTimespec *end) {
824*d9497217SMartin Matuska   psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC;
825*d9497217SMartin Matuska   if (end->nanoseconds < start->nanoseconds) {
826*d9497217SMartin Matuska     return r - (start->nanoseconds - end->nanoseconds);
827*d9497217SMartin Matuska   }
828*d9497217SMartin Matuska 
829*d9497217SMartin Matuska   return r + (end->nanoseconds - start->nanoseconds);
830*d9497217SMartin Matuska }
831*d9497217SMartin Matuska 
832*d9497217SMartin Matuska #else
833*d9497217SMartin Matuska #  include <time.h>
834*d9497217SMartin Matuska #endif /* defined(MUNIT_ENABLE_TIMING) */
835*d9497217SMartin Matuska 
836*d9497217SMartin Matuska /*** PRNG stuff ***/
837*d9497217SMartin Matuska 
838*d9497217SMartin Matuska /* This is (unless I screwed up, which is entirely possible) the
839*d9497217SMartin Matuska  * version of PCG with 32-bit state.  It was chosen because it has a
840*d9497217SMartin Matuska  * small enough state that we should reliably be able to use CAS
841*d9497217SMartin Matuska  * instead of requiring a lock for thread-safety.
842*d9497217SMartin Matuska  *
843*d9497217SMartin Matuska  * If I did screw up, I probably will not bother changing it unless
844*d9497217SMartin Matuska  * there is a significant bias.  It's really not important this be
845*d9497217SMartin Matuska  * particularly strong, as long as it is fairly random it's much more
846*d9497217SMartin Matuska  * important that it be reproducible, so bug reports have a better
847*d9497217SMartin Matuska  * chance of being reproducible. */
848*d9497217SMartin Matuska 
849*d9497217SMartin Matuska #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) &&              \
850*d9497217SMartin Matuska   !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) &&                 \
851*d9497217SMartin Matuska   (!defined(__GNUC_MINOR__) || (__GNUC__ > 4) ||                               \
852*d9497217SMartin Matuska    (__GNUC__ == 4 && __GNUC_MINOR__ > 8))
853*d9497217SMartin Matuska #  define HAVE_STDATOMIC
854*d9497217SMartin Matuska #elif defined(__clang__)
855*d9497217SMartin Matuska #  if __has_extension(c_atomic)
856*d9497217SMartin Matuska #    define HAVE_CLANG_ATOMICS
857*d9497217SMartin Matuska #  endif
858*d9497217SMartin Matuska #endif
859*d9497217SMartin Matuska 
860*d9497217SMartin Matuska /* Workaround for http://llvm.org/bugs/show_bug.cgi?id=26911 */
861*d9497217SMartin Matuska #if defined(__clang__) && defined(_WIN32)
862*d9497217SMartin Matuska #  undef HAVE_STDATOMIC
863*d9497217SMartin Matuska #  if defined(__c2__)
864*d9497217SMartin Matuska #    undef HAVE_CLANG_ATOMICS
865*d9497217SMartin Matuska #  endif
866*d9497217SMartin Matuska #endif
867*d9497217SMartin Matuska 
868*d9497217SMartin Matuska #if defined(_OPENMP)
869*d9497217SMartin Matuska #  define ATOMIC_UINT32_T uint32_t
870*d9497217SMartin Matuska #elif defined(HAVE_STDATOMIC)
871*d9497217SMartin Matuska #  include <stdatomic.h>
872*d9497217SMartin Matuska #  define ATOMIC_UINT32_T _Atomic uint32_t
873*d9497217SMartin Matuska #elif defined(HAVE_CLANG_ATOMICS)
874*d9497217SMartin Matuska #  define ATOMIC_UINT32_T _Atomic uint32_t
875*d9497217SMartin Matuska #elif defined(_WIN32)
876*d9497217SMartin Matuska #  define ATOMIC_UINT32_T volatile LONG
877*d9497217SMartin Matuska #else
878*d9497217SMartin Matuska #  define ATOMIC_UINT32_T volatile uint32_t
879*d9497217SMartin Matuska #endif
880*d9497217SMartin Matuska 
881*d9497217SMartin Matuska static ATOMIC_UINT32_T munit_rand_state = 42;
882*d9497217SMartin Matuska 
883*d9497217SMartin Matuska #if defined(_OPENMP)
munit_atomic_store(ATOMIC_UINT32_T * dest,ATOMIC_UINT32_T value)884*d9497217SMartin Matuska static inline void munit_atomic_store(ATOMIC_UINT32_T *dest,
885*d9497217SMartin Matuska                                       ATOMIC_UINT32_T value) {
886*d9497217SMartin Matuska #  pragma omp critical(munit_atomics)
887*d9497217SMartin Matuska   *dest = value;
888*d9497217SMartin Matuska }
889*d9497217SMartin Matuska 
munit_atomic_load(ATOMIC_UINT32_T * src)890*d9497217SMartin Matuska static inline uint32_t munit_atomic_load(ATOMIC_UINT32_T *src) {
891*d9497217SMartin Matuska   int ret;
892*d9497217SMartin Matuska #  pragma omp critical(munit_atomics)
893*d9497217SMartin Matuska   ret = *src;
894*d9497217SMartin Matuska   return ret;
895*d9497217SMartin Matuska }
896*d9497217SMartin Matuska 
munit_atomic_cas(ATOMIC_UINT32_T * dest,ATOMIC_UINT32_T * expected,ATOMIC_UINT32_T desired)897*d9497217SMartin Matuska static inline uint32_t munit_atomic_cas(ATOMIC_UINT32_T *dest,
898*d9497217SMartin Matuska                                         ATOMIC_UINT32_T *expected,
899*d9497217SMartin Matuska                                         ATOMIC_UINT32_T desired) {
900*d9497217SMartin Matuska   munit_bool ret;
901*d9497217SMartin Matuska 
902*d9497217SMartin Matuska #  pragma omp critical(munit_atomics)
903*d9497217SMartin Matuska   {
904*d9497217SMartin Matuska     if (*dest == *expected) {
905*d9497217SMartin Matuska       *dest = desired;
906*d9497217SMartin Matuska       ret = 1;
907*d9497217SMartin Matuska     } else {
908*d9497217SMartin Matuska       ret = 0;
909*d9497217SMartin Matuska     }
910*d9497217SMartin Matuska   }
911*d9497217SMartin Matuska 
912*d9497217SMartin Matuska   return ret;
913*d9497217SMartin Matuska }
914*d9497217SMartin Matuska #elif defined(HAVE_STDATOMIC)
915*d9497217SMartin Matuska #  define munit_atomic_store(dest, value) atomic_store(dest, value)
916*d9497217SMartin Matuska #  define munit_atomic_load(src) atomic_load(src)
917*d9497217SMartin Matuska #  define munit_atomic_cas(dest, expected, value)                              \
918*d9497217SMartin Matuska     atomic_compare_exchange_weak(dest, expected, value)
919*d9497217SMartin Matuska #elif defined(HAVE_CLANG_ATOMICS)
920*d9497217SMartin Matuska #  define munit_atomic_store(dest, value)                                      \
921*d9497217SMartin Matuska     __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST)
922*d9497217SMartin Matuska #  define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST)
923*d9497217SMartin Matuska #  define munit_atomic_cas(dest, expected, value)                              \
924*d9497217SMartin Matuska     __c11_atomic_compare_exchange_weak(dest, expected, value,                  \
925*d9497217SMartin Matuska                                        __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
926*d9497217SMartin Matuska #elif defined(__GNUC__) && (__GNUC__ > 4) ||                                   \
927*d9497217SMartin Matuska   (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
928*d9497217SMartin Matuska #  define munit_atomic_store(dest, value)                                      \
929*d9497217SMartin Matuska     __atomic_store_n(dest, value, __ATOMIC_SEQ_CST)
930*d9497217SMartin Matuska #  define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST)
931*d9497217SMartin Matuska #  define munit_atomic_cas(dest, expected, value)                              \
932*d9497217SMartin Matuska     __atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST,    \
933*d9497217SMartin Matuska                                 __ATOMIC_SEQ_CST)
934*d9497217SMartin Matuska #elif defined(__GNUC__) && (__GNUC__ >= 4)
935*d9497217SMartin Matuska #  define munit_atomic_store(dest, value)                                      \
936*d9497217SMartin Matuska     do {                                                                       \
937*d9497217SMartin Matuska       *(dest) = (value);                                                       \
938*d9497217SMartin Matuska     } while (0)
939*d9497217SMartin Matuska #  define munit_atomic_load(src) (*(src))
940*d9497217SMartin Matuska #  define munit_atomic_cas(dest, expected, value)                              \
941*d9497217SMartin Matuska     __sync_bool_compare_and_swap(dest, *expected, value)
942*d9497217SMartin Matuska #elif defined(_WIN32) /* Untested */
943*d9497217SMartin Matuska #  define munit_atomic_store(dest, value)                                      \
944*d9497217SMartin Matuska     do {                                                                       \
945*d9497217SMartin Matuska       *(dest) = (value);                                                       \
946*d9497217SMartin Matuska     } while (0)
947*d9497217SMartin Matuska #  define munit_atomic_load(src) (*(src))
948*d9497217SMartin Matuska #  define munit_atomic_cas(dest, expected, value)                              \
949*d9497217SMartin Matuska     InterlockedCompareExchange((dest), (value), *(expected))
950*d9497217SMartin Matuska #else
951*d9497217SMartin Matuska #  warning No atomic implementation, PRNG will not be thread-safe
952*d9497217SMartin Matuska #  define munit_atomic_store(dest, value)                                      \
953*d9497217SMartin Matuska     do {                                                                       \
954*d9497217SMartin Matuska       *(dest) = (value);                                                       \
955*d9497217SMartin Matuska     } while (0)
956*d9497217SMartin Matuska #  define munit_atomic_load(src) (*(src))
munit_atomic_cas(ATOMIC_UINT32_T * dest,ATOMIC_UINT32_T * expected,ATOMIC_UINT32_T desired)957*d9497217SMartin Matuska static inline munit_bool munit_atomic_cas(ATOMIC_UINT32_T *dest,
958*d9497217SMartin Matuska                                           ATOMIC_UINT32_T *expected,
959*d9497217SMartin Matuska                                           ATOMIC_UINT32_T desired) {
960*d9497217SMartin Matuska   if (*dest == *expected) {
961*d9497217SMartin Matuska     *dest = desired;
962*d9497217SMartin Matuska     return 1;
963*d9497217SMartin Matuska   } else {
964*d9497217SMartin Matuska     return 0;
965*d9497217SMartin Matuska   }
966*d9497217SMartin Matuska }
967*d9497217SMartin Matuska #endif
968*d9497217SMartin Matuska 
969*d9497217SMartin Matuska #define MUNIT_PRNG_MULTIPLIER (747796405U)
970*d9497217SMartin Matuska #define MUNIT_PRNG_INCREMENT (1729U)
971*d9497217SMartin Matuska 
munit_rand_next_state(munit_uint32_t state)972*d9497217SMartin Matuska static munit_uint32_t munit_rand_next_state(munit_uint32_t state) {
973*d9497217SMartin Matuska   return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT;
974*d9497217SMartin Matuska }
975*d9497217SMartin Matuska 
munit_rand_from_state(munit_uint32_t state)976*d9497217SMartin Matuska static munit_uint32_t munit_rand_from_state(munit_uint32_t state) {
977*d9497217SMartin Matuska   munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U);
978*d9497217SMartin Matuska   res ^= res >> 22;
979*d9497217SMartin Matuska   return res;
980*d9497217SMartin Matuska }
981*d9497217SMartin Matuska 
munit_rand_seed(munit_uint32_t seed)982*d9497217SMartin Matuska void munit_rand_seed(munit_uint32_t seed) {
983*d9497217SMartin Matuska   munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);
984*d9497217SMartin Matuska   munit_atomic_store(&munit_rand_state, state);
985*d9497217SMartin Matuska }
986*d9497217SMartin Matuska 
munit_rand_generate_seed(void)987*d9497217SMartin Matuska static munit_uint32_t munit_rand_generate_seed(void) {
988*d9497217SMartin Matuska   munit_uint32_t seed, state;
989*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
990*d9497217SMartin Matuska   struct PsnipClockTimespec wc = {
991*d9497217SMartin Matuska     0,
992*d9497217SMartin Matuska   };
993*d9497217SMartin Matuska 
994*d9497217SMartin Matuska   psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc);
995*d9497217SMartin Matuska   seed = (munit_uint32_t)wc.nanoseconds;
996*d9497217SMartin Matuska #else
997*d9497217SMartin Matuska   seed = (munit_uint32_t)time(NULL);
998*d9497217SMartin Matuska #endif
999*d9497217SMartin Matuska 
1000*d9497217SMartin Matuska   state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);
1001*d9497217SMartin Matuska   return munit_rand_from_state(state);
1002*d9497217SMartin Matuska }
1003*d9497217SMartin Matuska 
munit_rand_state_uint32(munit_uint32_t * state)1004*d9497217SMartin Matuska static munit_uint32_t munit_rand_state_uint32(munit_uint32_t *state) {
1005*d9497217SMartin Matuska   const munit_uint32_t old = *state;
1006*d9497217SMartin Matuska   *state = munit_rand_next_state(old);
1007*d9497217SMartin Matuska   return munit_rand_from_state(old);
1008*d9497217SMartin Matuska }
1009*d9497217SMartin Matuska 
munit_rand_uint32(void)1010*d9497217SMartin Matuska munit_uint32_t munit_rand_uint32(void) {
1011*d9497217SMartin Matuska   munit_uint32_t old, state;
1012*d9497217SMartin Matuska 
1013*d9497217SMartin Matuska   do {
1014*d9497217SMartin Matuska     old = munit_atomic_load(&munit_rand_state);
1015*d9497217SMartin Matuska     state = munit_rand_next_state(old);
1016*d9497217SMartin Matuska   } while (!munit_atomic_cas(&munit_rand_state, &old, state));
1017*d9497217SMartin Matuska 
1018*d9497217SMartin Matuska   return munit_rand_from_state(old);
1019*d9497217SMartin Matuska }
1020*d9497217SMartin Matuska 
munit_rand_state_memory(munit_uint32_t * state,size_t size,munit_uint8_t * data)1021*d9497217SMartin Matuska static void munit_rand_state_memory(munit_uint32_t *state, size_t size,
1022*d9497217SMartin Matuska                                     munit_uint8_t *data) {
1023*d9497217SMartin Matuska   size_t members_remaining = size / sizeof(munit_uint32_t);
1024*d9497217SMartin Matuska   size_t bytes_remaining = size % sizeof(munit_uint32_t);
1025*d9497217SMartin Matuska   munit_uint8_t *b = data;
1026*d9497217SMartin Matuska   munit_uint32_t rv;
1027*d9497217SMartin Matuska   while (members_remaining-- > 0) {
1028*d9497217SMartin Matuska     rv = munit_rand_state_uint32(state);
1029*d9497217SMartin Matuska     memcpy(b, &rv, sizeof(munit_uint32_t));
1030*d9497217SMartin Matuska     b += sizeof(munit_uint32_t);
1031*d9497217SMartin Matuska   }
1032*d9497217SMartin Matuska   if (bytes_remaining != 0) {
1033*d9497217SMartin Matuska     rv = munit_rand_state_uint32(state);
1034*d9497217SMartin Matuska     memcpy(b, &rv, bytes_remaining);
1035*d9497217SMartin Matuska   }
1036*d9497217SMartin Matuska }
1037*d9497217SMartin Matuska 
munit_rand_memory(size_t size,munit_uint8_t * data)1038*d9497217SMartin Matuska void munit_rand_memory(size_t size, munit_uint8_t *data) {
1039*d9497217SMartin Matuska   munit_uint32_t old, state;
1040*d9497217SMartin Matuska 
1041*d9497217SMartin Matuska   do {
1042*d9497217SMartin Matuska     state = old = munit_atomic_load(&munit_rand_state);
1043*d9497217SMartin Matuska     munit_rand_state_memory(&state, size, data);
1044*d9497217SMartin Matuska   } while (!munit_atomic_cas(&munit_rand_state, &old, state));
1045*d9497217SMartin Matuska }
1046*d9497217SMartin Matuska 
munit_rand_state_at_most(munit_uint32_t * state,munit_uint32_t salt,munit_uint32_t max)1047*d9497217SMartin Matuska static munit_uint32_t munit_rand_state_at_most(munit_uint32_t *state,
1048*d9497217SMartin Matuska                                                munit_uint32_t salt,
1049*d9497217SMartin Matuska                                                munit_uint32_t max) {
1050*d9497217SMartin Matuska   /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same
1051*d9497217SMartin Matuska    * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not
1052*d9497217SMartin Matuska    * to avoid compiler warnings.
1053*d9497217SMartin Matuska    */
1054*d9497217SMartin Matuska   const munit_uint32_t min = (~max + 1U) % max;
1055*d9497217SMartin Matuska   munit_uint32_t x;
1056*d9497217SMartin Matuska 
1057*d9497217SMartin Matuska   if (max == (~((munit_uint32_t)0U)))
1058*d9497217SMartin Matuska     return munit_rand_state_uint32(state) ^ salt;
1059*d9497217SMartin Matuska 
1060*d9497217SMartin Matuska   max++;
1061*d9497217SMartin Matuska 
1062*d9497217SMartin Matuska   do {
1063*d9497217SMartin Matuska     x = munit_rand_state_uint32(state) ^ salt;
1064*d9497217SMartin Matuska   } while (x < min);
1065*d9497217SMartin Matuska 
1066*d9497217SMartin Matuska   return x % max;
1067*d9497217SMartin Matuska }
1068*d9497217SMartin Matuska 
munit_rand_at_most(munit_uint32_t salt,munit_uint32_t max)1069*d9497217SMartin Matuska static munit_uint32_t munit_rand_at_most(munit_uint32_t salt,
1070*d9497217SMartin Matuska                                          munit_uint32_t max) {
1071*d9497217SMartin Matuska   munit_uint32_t old, state;
1072*d9497217SMartin Matuska   munit_uint32_t retval;
1073*d9497217SMartin Matuska 
1074*d9497217SMartin Matuska   do {
1075*d9497217SMartin Matuska     state = old = munit_atomic_load(&munit_rand_state);
1076*d9497217SMartin Matuska     retval = munit_rand_state_at_most(&state, salt, max);
1077*d9497217SMartin Matuska   } while (!munit_atomic_cas(&munit_rand_state, &old, state));
1078*d9497217SMartin Matuska 
1079*d9497217SMartin Matuska   return retval;
1080*d9497217SMartin Matuska }
1081*d9497217SMartin Matuska 
munit_rand_int_range(int min,int max)1082*d9497217SMartin Matuska int munit_rand_int_range(int min, int max) {
1083*d9497217SMartin Matuska   munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min;
1084*d9497217SMartin Matuska 
1085*d9497217SMartin Matuska   if (min > max)
1086*d9497217SMartin Matuska     return munit_rand_int_range(max, min);
1087*d9497217SMartin Matuska 
1088*d9497217SMartin Matuska   if (range > (~((munit_uint32_t)0U)))
1089*d9497217SMartin Matuska     range = (~((munit_uint32_t)0U));
1090*d9497217SMartin Matuska 
1091*d9497217SMartin Matuska   return min + (int)munit_rand_at_most(0, (munit_uint32_t)range);
1092*d9497217SMartin Matuska }
1093*d9497217SMartin Matuska 
munit_rand_double(void)1094*d9497217SMartin Matuska double munit_rand_double(void) {
1095*d9497217SMartin Matuska   munit_uint32_t old, state;
1096*d9497217SMartin Matuska   double retval = 0.0;
1097*d9497217SMartin Matuska 
1098*d9497217SMartin Matuska   do {
1099*d9497217SMartin Matuska     state = old = munit_atomic_load(&munit_rand_state);
1100*d9497217SMartin Matuska 
1101*d9497217SMartin Matuska     /* See http://mumble.net/~campbell/tmp/random_real.c for how to do
1102*d9497217SMartin Matuska      * this right.  Patches welcome if you feel that this is too
1103*d9497217SMartin Matuska      * biased. */
1104*d9497217SMartin Matuska     retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t)0U)) + 1.0);
1105*d9497217SMartin Matuska   } while (!munit_atomic_cas(&munit_rand_state, &old, state));
1106*d9497217SMartin Matuska 
1107*d9497217SMartin Matuska   return retval;
1108*d9497217SMartin Matuska }
1109*d9497217SMartin Matuska 
1110*d9497217SMartin Matuska /*** Test suite handling ***/
1111*d9497217SMartin Matuska 
1112*d9497217SMartin Matuska typedef struct {
1113*d9497217SMartin Matuska   unsigned int successful;
1114*d9497217SMartin Matuska   unsigned int skipped;
1115*d9497217SMartin Matuska   unsigned int failed;
1116*d9497217SMartin Matuska   unsigned int errored;
1117*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1118*d9497217SMartin Matuska   munit_uint64_t cpu_clock;
1119*d9497217SMartin Matuska   munit_uint64_t wall_clock;
1120*d9497217SMartin Matuska #endif
1121*d9497217SMartin Matuska } MunitReport;
1122*d9497217SMartin Matuska 
1123*d9497217SMartin Matuska typedef struct {
1124*d9497217SMartin Matuska   const char *prefix;
1125*d9497217SMartin Matuska   const MunitSuite *suite;
1126*d9497217SMartin Matuska   const char **tests;
1127*d9497217SMartin Matuska   munit_uint32_t seed;
1128*d9497217SMartin Matuska   unsigned int iterations;
1129*d9497217SMartin Matuska   MunitParameter *parameters;
1130*d9497217SMartin Matuska   munit_bool single_parameter_mode;
1131*d9497217SMartin Matuska   void *user_data;
1132*d9497217SMartin Matuska   MunitReport report;
1133*d9497217SMartin Matuska   munit_bool colorize;
1134*d9497217SMartin Matuska   munit_bool fork;
1135*d9497217SMartin Matuska   munit_bool show_stderr;
1136*d9497217SMartin Matuska   munit_bool fatal_failures;
1137*d9497217SMartin Matuska } MunitTestRunner;
1138*d9497217SMartin Matuska 
munit_parameters_get(const MunitParameter params[],const char * key)1139*d9497217SMartin Matuska const char *munit_parameters_get(const MunitParameter params[],
1140*d9497217SMartin Matuska                                  const char *key) {
1141*d9497217SMartin Matuska   const MunitParameter *param;
1142*d9497217SMartin Matuska 
1143*d9497217SMartin Matuska   for (param = params; param != NULL && param->name != NULL; param++)
1144*d9497217SMartin Matuska     if (strcmp(param->name, key) == 0)
1145*d9497217SMartin Matuska       return param->value;
1146*d9497217SMartin Matuska   return NULL;
1147*d9497217SMartin Matuska }
1148*d9497217SMartin Matuska 
1149*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
munit_print_time(FILE * fp,munit_uint64_t nanoseconds)1150*d9497217SMartin Matuska static void munit_print_time(FILE *fp, munit_uint64_t nanoseconds) {
1151*d9497217SMartin Matuska   fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT,
1152*d9497217SMartin Matuska           ((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC));
1153*d9497217SMartin Matuska }
1154*d9497217SMartin Matuska #endif
1155*d9497217SMartin Matuska 
1156*d9497217SMartin Matuska /* Add a paramter to an array of parameters. */
munit_parameters_add(size_t * params_size,MunitParameter ** params,char * name,char * value)1157*d9497217SMartin Matuska static MunitResult munit_parameters_add(size_t *params_size,
1158*d9497217SMartin Matuska                                         MunitParameter **params, char *name,
1159*d9497217SMartin Matuska                                         char *value) {
1160*d9497217SMartin Matuska   *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2));
1161*d9497217SMartin Matuska   if (*params == NULL)
1162*d9497217SMartin Matuska     return MUNIT_ERROR;
1163*d9497217SMartin Matuska 
1164*d9497217SMartin Matuska   (*params)[*params_size].name = name;
1165*d9497217SMartin Matuska   (*params)[*params_size].value = value;
1166*d9497217SMartin Matuska   (*params_size)++;
1167*d9497217SMartin Matuska   (*params)[*params_size].name = NULL;
1168*d9497217SMartin Matuska   (*params)[*params_size].value = NULL;
1169*d9497217SMartin Matuska 
1170*d9497217SMartin Matuska   return MUNIT_OK;
1171*d9497217SMartin Matuska }
1172*d9497217SMartin Matuska 
1173*d9497217SMartin Matuska /* Concatenate two strings, but just return one of the components
1174*d9497217SMartin Matuska  * unaltered if the other is NULL or "". */
munit_maybe_concat(size_t * len,char * prefix,char * suffix)1175*d9497217SMartin Matuska static char *munit_maybe_concat(size_t *len, char *prefix, char *suffix) {
1176*d9497217SMartin Matuska   char *res;
1177*d9497217SMartin Matuska   size_t res_l;
1178*d9497217SMartin Matuska   const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0;
1179*d9497217SMartin Matuska   const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0;
1180*d9497217SMartin Matuska   if (prefix_l == 0 && suffix_l == 0) {
1181*d9497217SMartin Matuska     res = NULL;
1182*d9497217SMartin Matuska     res_l = 0;
1183*d9497217SMartin Matuska   } else if (prefix_l == 0 && suffix_l != 0) {
1184*d9497217SMartin Matuska     res = suffix;
1185*d9497217SMartin Matuska     res_l = suffix_l;
1186*d9497217SMartin Matuska   } else if (prefix_l != 0 && suffix_l == 0) {
1187*d9497217SMartin Matuska     res = prefix;
1188*d9497217SMartin Matuska     res_l = prefix_l;
1189*d9497217SMartin Matuska   } else {
1190*d9497217SMartin Matuska     res_l = prefix_l + suffix_l;
1191*d9497217SMartin Matuska     res = malloc(res_l + 1);
1192*d9497217SMartin Matuska     memcpy(res, prefix, prefix_l);
1193*d9497217SMartin Matuska     memcpy(res + prefix_l, suffix, suffix_l);
1194*d9497217SMartin Matuska     res[res_l] = 0;
1195*d9497217SMartin Matuska   }
1196*d9497217SMartin Matuska 
1197*d9497217SMartin Matuska   if (len != NULL)
1198*d9497217SMartin Matuska     *len = res_l;
1199*d9497217SMartin Matuska 
1200*d9497217SMartin Matuska   return res;
1201*d9497217SMartin Matuska }
1202*d9497217SMartin Matuska 
1203*d9497217SMartin Matuska /* Possbily free a string returned by munit_maybe_concat. */
munit_maybe_free_concat(char * s,const char * prefix,const char * suffix)1204*d9497217SMartin Matuska static void munit_maybe_free_concat(char *s, const char *prefix,
1205*d9497217SMartin Matuska                                     const char *suffix) {
1206*d9497217SMartin Matuska   if (prefix != s && suffix != s)
1207*d9497217SMartin Matuska     free(s);
1208*d9497217SMartin Matuska }
1209*d9497217SMartin Matuska 
1210*d9497217SMartin Matuska /* Cheap string hash function, just used to salt the PRNG. */
munit_str_hash(const char * name)1211*d9497217SMartin Matuska static munit_uint32_t munit_str_hash(const char *name) {
1212*d9497217SMartin Matuska   const char *p;
1213*d9497217SMartin Matuska   munit_uint32_t h = 5381U;
1214*d9497217SMartin Matuska 
1215*d9497217SMartin Matuska   for (p = name; *p != '\0'; p++)
1216*d9497217SMartin Matuska     h = (munit_uint32_t)(h << 5) + h + (munit_uint32_t)*p;
1217*d9497217SMartin Matuska 
1218*d9497217SMartin Matuska   return h;
1219*d9497217SMartin Matuska }
1220*d9497217SMartin Matuska 
munit_splice(int from,int to)1221*d9497217SMartin Matuska static void munit_splice(int from, int to) {
1222*d9497217SMartin Matuska   munit_uint8_t buf[1024];
1223*d9497217SMartin Matuska #if !defined(_WIN32)
1224*d9497217SMartin Matuska   ssize_t len;
1225*d9497217SMartin Matuska   ssize_t bytes_written;
1226*d9497217SMartin Matuska   ssize_t write_res;
1227*d9497217SMartin Matuska #else
1228*d9497217SMartin Matuska   int len;
1229*d9497217SMartin Matuska   int bytes_written;
1230*d9497217SMartin Matuska   int write_res;
1231*d9497217SMartin Matuska #endif
1232*d9497217SMartin Matuska   do {
1233*d9497217SMartin Matuska     len = read(from, buf, sizeof(buf));
1234*d9497217SMartin Matuska     if (len > 0) {
1235*d9497217SMartin Matuska       bytes_written = 0;
1236*d9497217SMartin Matuska       do {
1237*d9497217SMartin Matuska         write_res = write(to, buf + bytes_written,
1238*d9497217SMartin Matuska #if !defined(_WIN32)
1239*d9497217SMartin Matuska                           (size_t)
1240*d9497217SMartin Matuska #else
1241*d9497217SMartin Matuska                           (unsigned int)
1242*d9497217SMartin Matuska #endif
1243*d9497217SMartin Matuska                             (len - bytes_written));
1244*d9497217SMartin Matuska         if (write_res < 0)
1245*d9497217SMartin Matuska           break;
1246*d9497217SMartin Matuska         bytes_written += write_res;
1247*d9497217SMartin Matuska       } while (bytes_written < len);
1248*d9497217SMartin Matuska     } else
1249*d9497217SMartin Matuska       break;
1250*d9497217SMartin Matuska   } while (1);
1251*d9497217SMartin Matuska }
1252*d9497217SMartin Matuska 
1253*d9497217SMartin Matuska /* This is the part that should be handled in the child process */
munit_test_runner_exec(MunitTestRunner * runner,const MunitTest * test,const MunitParameter params[],MunitReport * report)1254*d9497217SMartin Matuska static MunitResult munit_test_runner_exec(MunitTestRunner *runner,
1255*d9497217SMartin Matuska                                           const MunitTest *test,
1256*d9497217SMartin Matuska                                           const MunitParameter params[],
1257*d9497217SMartin Matuska                                           MunitReport *report) {
1258*d9497217SMartin Matuska   unsigned int iterations = runner->iterations;
1259*d9497217SMartin Matuska   MunitResult result = MUNIT_FAIL;
1260*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1261*d9497217SMartin Matuska   struct PsnipClockTimespec wall_clock_begin =
1262*d9497217SMartin Matuska                               {
1263*d9497217SMartin Matuska                                 0,
1264*d9497217SMartin Matuska                               },
1265*d9497217SMartin Matuska                             wall_clock_end = {
1266*d9497217SMartin Matuska                               0,
1267*d9497217SMartin Matuska                             };
1268*d9497217SMartin Matuska   struct PsnipClockTimespec cpu_clock_begin =
1269*d9497217SMartin Matuska                               {
1270*d9497217SMartin Matuska                                 0,
1271*d9497217SMartin Matuska                               },
1272*d9497217SMartin Matuska                             cpu_clock_end = {
1273*d9497217SMartin Matuska                               0,
1274*d9497217SMartin Matuska                             };
1275*d9497217SMartin Matuska #endif
1276*d9497217SMartin Matuska   unsigned int i = 0;
1277*d9497217SMartin Matuska 
1278*d9497217SMartin Matuska   if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) ==
1279*d9497217SMartin Matuska       MUNIT_TEST_OPTION_SINGLE_ITERATION)
1280*d9497217SMartin Matuska     iterations = 1;
1281*d9497217SMartin Matuska   else if (iterations == 0)
1282*d9497217SMartin Matuska     iterations = runner->suite->iterations;
1283*d9497217SMartin Matuska 
1284*d9497217SMartin Matuska   munit_rand_seed(runner->seed);
1285*d9497217SMartin Matuska 
1286*d9497217SMartin Matuska   do {
1287*d9497217SMartin Matuska     void *data = (test->setup == NULL) ? runner->user_data
1288*d9497217SMartin Matuska                                        : test->setup(params, runner->user_data);
1289*d9497217SMartin Matuska 
1290*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1291*d9497217SMartin Matuska     psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin);
1292*d9497217SMartin Matuska     psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin);
1293*d9497217SMartin Matuska #endif
1294*d9497217SMartin Matuska 
1295*d9497217SMartin Matuska     result = test->test(params, data);
1296*d9497217SMartin Matuska 
1297*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1298*d9497217SMartin Matuska     psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end);
1299*d9497217SMartin Matuska     psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end);
1300*d9497217SMartin Matuska #endif
1301*d9497217SMartin Matuska 
1302*d9497217SMartin Matuska     if (test->tear_down != NULL)
1303*d9497217SMartin Matuska       test->tear_down(data);
1304*d9497217SMartin Matuska 
1305*d9497217SMartin Matuska     if (MUNIT_LIKELY(result == MUNIT_OK)) {
1306*d9497217SMartin Matuska       report->successful++;
1307*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1308*d9497217SMartin Matuska       report->wall_clock +=
1309*d9497217SMartin Matuska         munit_clock_get_elapsed(&wall_clock_begin, &wall_clock_end);
1310*d9497217SMartin Matuska       report->cpu_clock +=
1311*d9497217SMartin Matuska         munit_clock_get_elapsed(&cpu_clock_begin, &cpu_clock_end);
1312*d9497217SMartin Matuska #endif
1313*d9497217SMartin Matuska     } else {
1314*d9497217SMartin Matuska       switch ((int)result) {
1315*d9497217SMartin Matuska       case MUNIT_SKIP:
1316*d9497217SMartin Matuska         report->skipped++;
1317*d9497217SMartin Matuska         break;
1318*d9497217SMartin Matuska       case MUNIT_FAIL:
1319*d9497217SMartin Matuska         report->failed++;
1320*d9497217SMartin Matuska         break;
1321*d9497217SMartin Matuska       case MUNIT_ERROR:
1322*d9497217SMartin Matuska         report->errored++;
1323*d9497217SMartin Matuska         break;
1324*d9497217SMartin Matuska       default:
1325*d9497217SMartin Matuska         break;
1326*d9497217SMartin Matuska       }
1327*d9497217SMartin Matuska       break;
1328*d9497217SMartin Matuska     }
1329*d9497217SMartin Matuska   } while (++i < iterations);
1330*d9497217SMartin Matuska 
1331*d9497217SMartin Matuska   return result;
1332*d9497217SMartin Matuska }
1333*d9497217SMartin Matuska 
1334*d9497217SMartin Matuska #if defined(MUNIT_EMOTICON)
1335*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_OK ":)"
1336*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_SKIP ":|"
1337*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_FAIL ":("
1338*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_ERROR ":o"
1339*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_TODO ":/"
1340*d9497217SMartin Matuska #else
1341*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_OK "OK   "
1342*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_SKIP "SKIP "
1343*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_FAIL "FAIL "
1344*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_ERROR "ERROR"
1345*d9497217SMartin Matuska #  define MUNIT_RESULT_STRING_TODO "TODO "
1346*d9497217SMartin Matuska #endif
1347*d9497217SMartin Matuska 
munit_test_runner_print_color(const MunitTestRunner * runner,const char * string,char color)1348*d9497217SMartin Matuska static void munit_test_runner_print_color(const MunitTestRunner *runner,
1349*d9497217SMartin Matuska                                           const char *string, char color) {
1350*d9497217SMartin Matuska   if (runner->colorize)
1351*d9497217SMartin Matuska     fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string);
1352*d9497217SMartin Matuska   else
1353*d9497217SMartin Matuska     fputs(string, MUNIT_OUTPUT_FILE);
1354*d9497217SMartin Matuska }
1355*d9497217SMartin Matuska 
1356*d9497217SMartin Matuska #if !defined(MUNIT_NO_BUFFER)
munit_replace_stderr(FILE * stderr_buf)1357*d9497217SMartin Matuska static int munit_replace_stderr(FILE *stderr_buf) {
1358*d9497217SMartin Matuska   if (stderr_buf != NULL) {
1359*d9497217SMartin Matuska     const int orig_stderr = dup(STDERR_FILENO);
1360*d9497217SMartin Matuska 
1361*d9497217SMartin Matuska     int errfd = fileno(stderr_buf);
1362*d9497217SMartin Matuska     if (MUNIT_UNLIKELY(errfd == -1)) {
1363*d9497217SMartin Matuska       exit(EXIT_FAILURE);
1364*d9497217SMartin Matuska     }
1365*d9497217SMartin Matuska 
1366*d9497217SMartin Matuska     dup2(errfd, STDERR_FILENO);
1367*d9497217SMartin Matuska 
1368*d9497217SMartin Matuska     return orig_stderr;
1369*d9497217SMartin Matuska   }
1370*d9497217SMartin Matuska 
1371*d9497217SMartin Matuska   return -1;
1372*d9497217SMartin Matuska }
1373*d9497217SMartin Matuska 
munit_restore_stderr(int orig_stderr)1374*d9497217SMartin Matuska static void munit_restore_stderr(int orig_stderr) {
1375*d9497217SMartin Matuska   if (orig_stderr != -1) {
1376*d9497217SMartin Matuska     dup2(orig_stderr, STDERR_FILENO);
1377*d9497217SMartin Matuska     close(orig_stderr);
1378*d9497217SMartin Matuska   }
1379*d9497217SMartin Matuska }
1380*d9497217SMartin Matuska #endif /* !defined(MUNIT_NO_BUFFER) */
1381*d9497217SMartin Matuska 
1382*d9497217SMartin Matuska /* Run a test with the specified parameters. */
1383*d9497217SMartin Matuska static void
munit_test_runner_run_test_with_params(MunitTestRunner * runner,const MunitTest * test,const MunitParameter params[])1384*d9497217SMartin Matuska munit_test_runner_run_test_with_params(MunitTestRunner *runner,
1385*d9497217SMartin Matuska                                        const MunitTest *test,
1386*d9497217SMartin Matuska                                        const MunitParameter params[]) {
1387*d9497217SMartin Matuska   MunitResult result = MUNIT_OK;
1388*d9497217SMartin Matuska   MunitReport report = {0, 0, 0, 0,
1389*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1390*d9497217SMartin Matuska                         0, 0
1391*d9497217SMartin Matuska #endif
1392*d9497217SMartin Matuska   };
1393*d9497217SMartin Matuska   unsigned int output_l;
1394*d9497217SMartin Matuska   munit_bool first;
1395*d9497217SMartin Matuska   const MunitParameter *param;
1396*d9497217SMartin Matuska   FILE *stderr_buf;
1397*d9497217SMartin Matuska #if !defined(MUNIT_NO_FORK)
1398*d9497217SMartin Matuska   int pipefd[2];
1399*d9497217SMartin Matuska   pid_t fork_pid;
1400*d9497217SMartin Matuska   ssize_t bytes_written = 0;
1401*d9497217SMartin Matuska   ssize_t write_res;
1402*d9497217SMartin Matuska   ssize_t bytes_read = 0;
1403*d9497217SMartin Matuska   ssize_t read_res;
1404*d9497217SMartin Matuska   int status = 0;
1405*d9497217SMartin Matuska   pid_t changed_pid;
1406*d9497217SMartin Matuska #endif
1407*d9497217SMartin Matuska 
1408*d9497217SMartin Matuska   if (params != NULL) {
1409*d9497217SMartin Matuska     output_l = 2;
1410*d9497217SMartin Matuska     fputs("  ", MUNIT_OUTPUT_FILE);
1411*d9497217SMartin Matuska     first = 1;
1412*d9497217SMartin Matuska     for (param = params; param != NULL && param->name != NULL; param++) {
1413*d9497217SMartin Matuska       if (!first) {
1414*d9497217SMartin Matuska         fputs(", ", MUNIT_OUTPUT_FILE);
1415*d9497217SMartin Matuska         output_l += 2;
1416*d9497217SMartin Matuska       } else {
1417*d9497217SMartin Matuska         first = 0;
1418*d9497217SMartin Matuska       }
1419*d9497217SMartin Matuska 
1420*d9497217SMartin Matuska       output_l += (unsigned int)fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name,
1421*d9497217SMartin Matuska                                         param->value);
1422*d9497217SMartin Matuska     }
1423*d9497217SMartin Matuska     while (output_l++ < MUNIT_TEST_NAME_LEN) {
1424*d9497217SMartin Matuska       fputc(' ', MUNIT_OUTPUT_FILE);
1425*d9497217SMartin Matuska     }
1426*d9497217SMartin Matuska   }
1427*d9497217SMartin Matuska 
1428*d9497217SMartin Matuska   fflush(MUNIT_OUTPUT_FILE);
1429*d9497217SMartin Matuska 
1430*d9497217SMartin Matuska   stderr_buf = NULL;
1431*d9497217SMartin Matuska #if !defined(_WIN32) || defined(__MINGW32__)
1432*d9497217SMartin Matuska   stderr_buf = tmpfile();
1433*d9497217SMartin Matuska #else
1434*d9497217SMartin Matuska   tmpfile_s(&stderr_buf);
1435*d9497217SMartin Matuska #endif
1436*d9497217SMartin Matuska   if (stderr_buf == NULL) {
1437*d9497217SMartin Matuska     munit_log_errno(MUNIT_LOG_ERROR, stderr,
1438*d9497217SMartin Matuska                     "unable to create buffer for stderr");
1439*d9497217SMartin Matuska     result = MUNIT_ERROR;
1440*d9497217SMartin Matuska     goto print_result;
1441*d9497217SMartin Matuska   }
1442*d9497217SMartin Matuska 
1443*d9497217SMartin Matuska #if !defined(MUNIT_NO_FORK)
1444*d9497217SMartin Matuska   if (runner->fork) {
1445*d9497217SMartin Matuska     pipefd[0] = -1;
1446*d9497217SMartin Matuska     pipefd[1] = -1;
1447*d9497217SMartin Matuska     if (pipe(pipefd) != 0) {
1448*d9497217SMartin Matuska       munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe");
1449*d9497217SMartin Matuska       result = MUNIT_ERROR;
1450*d9497217SMartin Matuska       goto print_result;
1451*d9497217SMartin Matuska     }
1452*d9497217SMartin Matuska 
1453*d9497217SMartin Matuska     fork_pid = fork();
1454*d9497217SMartin Matuska     if (fork_pid == 0) {
1455*d9497217SMartin Matuska       int orig_stderr;
1456*d9497217SMartin Matuska 
1457*d9497217SMartin Matuska       close(pipefd[0]);
1458*d9497217SMartin Matuska 
1459*d9497217SMartin Matuska       orig_stderr = munit_replace_stderr(stderr_buf);
1460*d9497217SMartin Matuska       munit_test_runner_exec(runner, test, params, &report);
1461*d9497217SMartin Matuska 
1462*d9497217SMartin Matuska       /* Note that we don't restore stderr.  This is so we can buffer
1463*d9497217SMartin Matuska        * things written to stderr later on (such as by
1464*d9497217SMartin Matuska        * asan/tsan/ubsan, valgrind, etc.) */
1465*d9497217SMartin Matuska       close(orig_stderr);
1466*d9497217SMartin Matuska 
1467*d9497217SMartin Matuska       do {
1468*d9497217SMartin Matuska         write_res =
1469*d9497217SMartin Matuska           write(pipefd[1], ((munit_uint8_t *)(&report)) + bytes_written,
1470*d9497217SMartin Matuska                 sizeof(report) - (size_t)bytes_written);
1471*d9497217SMartin Matuska         if (write_res < 0) {
1472*d9497217SMartin Matuska           if (stderr_buf != NULL) {
1473*d9497217SMartin Matuska             munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe");
1474*d9497217SMartin Matuska           }
1475*d9497217SMartin Matuska           exit(EXIT_FAILURE);
1476*d9497217SMartin Matuska         }
1477*d9497217SMartin Matuska         bytes_written += write_res;
1478*d9497217SMartin Matuska       } while ((size_t)bytes_written < sizeof(report));
1479*d9497217SMartin Matuska 
1480*d9497217SMartin Matuska       if (stderr_buf != NULL)
1481*d9497217SMartin Matuska         fclose(stderr_buf);
1482*d9497217SMartin Matuska       close(pipefd[1]);
1483*d9497217SMartin Matuska 
1484*d9497217SMartin Matuska       exit(EXIT_SUCCESS);
1485*d9497217SMartin Matuska     } else if (fork_pid == -1) {
1486*d9497217SMartin Matuska       close(pipefd[0]);
1487*d9497217SMartin Matuska       close(pipefd[1]);
1488*d9497217SMartin Matuska       if (stderr_buf != NULL) {
1489*d9497217SMartin Matuska         munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork");
1490*d9497217SMartin Matuska       }
1491*d9497217SMartin Matuska       report.errored++;
1492*d9497217SMartin Matuska       result = MUNIT_ERROR;
1493*d9497217SMartin Matuska     } else {
1494*d9497217SMartin Matuska       close(pipefd[1]);
1495*d9497217SMartin Matuska       do {
1496*d9497217SMartin Matuska         read_res = read(pipefd[0], ((munit_uint8_t *)(&report)) + bytes_read,
1497*d9497217SMartin Matuska                         sizeof(report) - (size_t)bytes_read);
1498*d9497217SMartin Matuska         if (read_res < 1)
1499*d9497217SMartin Matuska           break;
1500*d9497217SMartin Matuska         bytes_read += read_res;
1501*d9497217SMartin Matuska       } while (bytes_read < (ssize_t)sizeof(report));
1502*d9497217SMartin Matuska 
1503*d9497217SMartin Matuska       changed_pid = waitpid(fork_pid, &status, 0);
1504*d9497217SMartin Matuska 
1505*d9497217SMartin Matuska       if (MUNIT_LIKELY(changed_pid == fork_pid) &&
1506*d9497217SMartin Matuska           MUNIT_LIKELY(WIFEXITED(status))) {
1507*d9497217SMartin Matuska         if (bytes_read != sizeof(report)) {
1508*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
1509*d9497217SMartin Matuska                               "child exited unexpectedly with status %d",
1510*d9497217SMartin Matuska                               WEXITSTATUS(status));
1511*d9497217SMartin Matuska           report.errored++;
1512*d9497217SMartin Matuska         } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
1513*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
1514*d9497217SMartin Matuska                               "child exited with status %d",
1515*d9497217SMartin Matuska                               WEXITSTATUS(status));
1516*d9497217SMartin Matuska           report.errored++;
1517*d9497217SMartin Matuska         }
1518*d9497217SMartin Matuska       } else {
1519*d9497217SMartin Matuska         if (WIFSIGNALED(status)) {
1520*d9497217SMartin Matuska #  if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)
1521*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
1522*d9497217SMartin Matuska                               "child killed by signal %d (%s)",
1523*d9497217SMartin Matuska                               WTERMSIG(status), strsignal(WTERMSIG(status)));
1524*d9497217SMartin Matuska #  else
1525*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
1526*d9497217SMartin Matuska                               "child killed by signal %d", WTERMSIG(status));
1527*d9497217SMartin Matuska #  endif
1528*d9497217SMartin Matuska         } else if (WIFSTOPPED(status)) {
1529*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
1530*d9497217SMartin Matuska                               "child stopped by signal %d", WSTOPSIG(status));
1531*d9497217SMartin Matuska         }
1532*d9497217SMartin Matuska         report.errored++;
1533*d9497217SMartin Matuska       }
1534*d9497217SMartin Matuska 
1535*d9497217SMartin Matuska       close(pipefd[0]);
1536*d9497217SMartin Matuska       waitpid(fork_pid, NULL, 0);
1537*d9497217SMartin Matuska     }
1538*d9497217SMartin Matuska   } else
1539*d9497217SMartin Matuska #endif
1540*d9497217SMartin Matuska   {
1541*d9497217SMartin Matuska #if !defined(MUNIT_NO_BUFFER)
1542*d9497217SMartin Matuska     const volatile int orig_stderr = munit_replace_stderr(stderr_buf);
1543*d9497217SMartin Matuska #endif
1544*d9497217SMartin Matuska 
1545*d9497217SMartin Matuska #if defined(MUNIT_THREAD_LOCAL)
1546*d9497217SMartin Matuska     if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) {
1547*d9497217SMartin Matuska       result = MUNIT_FAIL;
1548*d9497217SMartin Matuska       report.failed++;
1549*d9497217SMartin Matuska     } else {
1550*d9497217SMartin Matuska       munit_error_jmp_buf_valid = 1;
1551*d9497217SMartin Matuska       result = munit_test_runner_exec(runner, test, params, &report);
1552*d9497217SMartin Matuska     }
1553*d9497217SMartin Matuska #else
1554*d9497217SMartin Matuska     result = munit_test_runner_exec(runner, test, params, &report);
1555*d9497217SMartin Matuska #endif
1556*d9497217SMartin Matuska 
1557*d9497217SMartin Matuska #if !defined(MUNIT_NO_BUFFER)
1558*d9497217SMartin Matuska     munit_restore_stderr(orig_stderr);
1559*d9497217SMartin Matuska #endif
1560*d9497217SMartin Matuska 
1561*d9497217SMartin Matuska     /* Here just so that the label is used on Windows and we don't get
1562*d9497217SMartin Matuska      * a warning */
1563*d9497217SMartin Matuska     goto print_result;
1564*d9497217SMartin Matuska   }
1565*d9497217SMartin Matuska 
1566*d9497217SMartin Matuska print_result:
1567*d9497217SMartin Matuska 
1568*d9497217SMartin Matuska   fputs("[ ", MUNIT_OUTPUT_FILE);
1569*d9497217SMartin Matuska   if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) {
1570*d9497217SMartin Matuska     if (report.failed != 0 || report.errored != 0 || report.skipped != 0) {
1571*d9497217SMartin Matuska       munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3');
1572*d9497217SMartin Matuska       result = MUNIT_OK;
1573*d9497217SMartin Matuska     } else {
1574*d9497217SMartin Matuska       munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');
1575*d9497217SMartin Matuska       if (MUNIT_LIKELY(stderr_buf != NULL))
1576*d9497217SMartin Matuska         munit_log_internal(MUNIT_LOG_ERROR, stderr_buf,
1577*d9497217SMartin Matuska                            "Test marked TODO, but was successful.");
1578*d9497217SMartin Matuska       runner->report.failed++;
1579*d9497217SMartin Matuska       result = MUNIT_ERROR;
1580*d9497217SMartin Matuska     }
1581*d9497217SMartin Matuska   } else if (report.failed > 0) {
1582*d9497217SMartin Matuska     munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1');
1583*d9497217SMartin Matuska     runner->report.failed++;
1584*d9497217SMartin Matuska     result = MUNIT_FAIL;
1585*d9497217SMartin Matuska   } else if (report.errored > 0) {
1586*d9497217SMartin Matuska     munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');
1587*d9497217SMartin Matuska     runner->report.errored++;
1588*d9497217SMartin Matuska     result = MUNIT_ERROR;
1589*d9497217SMartin Matuska   } else if (report.skipped > 0) {
1590*d9497217SMartin Matuska     munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3');
1591*d9497217SMartin Matuska     runner->report.skipped++;
1592*d9497217SMartin Matuska     result = MUNIT_SKIP;
1593*d9497217SMartin Matuska   } else if (report.successful > 1) {
1594*d9497217SMartin Matuska     munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');
1595*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1596*d9497217SMartin Matuska     fputs(" ] [ ", MUNIT_OUTPUT_FILE);
1597*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful);
1598*d9497217SMartin Matuska     fputs(" / ", MUNIT_OUTPUT_FILE);
1599*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful);
1600*d9497217SMartin Matuska     fprintf(MUNIT_OUTPUT_FILE,
1601*d9497217SMartin Matuska             " CPU ]\n  %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ",
1602*d9497217SMartin Matuska             "");
1603*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);
1604*d9497217SMartin Matuska     fputs(" / ", MUNIT_OUTPUT_FILE);
1605*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);
1606*d9497217SMartin Matuska     fputs(" CPU", MUNIT_OUTPUT_FILE);
1607*d9497217SMartin Matuska #endif
1608*d9497217SMartin Matuska     runner->report.successful++;
1609*d9497217SMartin Matuska     result = MUNIT_OK;
1610*d9497217SMartin Matuska   } else if (report.successful > 0) {
1611*d9497217SMartin Matuska     munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');
1612*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
1613*d9497217SMartin Matuska     fputs(" ] [ ", MUNIT_OUTPUT_FILE);
1614*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);
1615*d9497217SMartin Matuska     fputs(" / ", MUNIT_OUTPUT_FILE);
1616*d9497217SMartin Matuska     munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);
1617*d9497217SMartin Matuska     fputs(" CPU", MUNIT_OUTPUT_FILE);
1618*d9497217SMartin Matuska #endif
1619*d9497217SMartin Matuska     runner->report.successful++;
1620*d9497217SMartin Matuska     result = MUNIT_OK;
1621*d9497217SMartin Matuska   }
1622*d9497217SMartin Matuska   fputs(" ]\n", MUNIT_OUTPUT_FILE);
1623*d9497217SMartin Matuska 
1624*d9497217SMartin Matuska   if (stderr_buf != NULL) {
1625*d9497217SMartin Matuska     if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) {
1626*d9497217SMartin Matuska       fflush(MUNIT_OUTPUT_FILE);
1627*d9497217SMartin Matuska 
1628*d9497217SMartin Matuska       rewind(stderr_buf);
1629*d9497217SMartin Matuska       munit_splice(fileno(stderr_buf), STDERR_FILENO);
1630*d9497217SMartin Matuska 
1631*d9497217SMartin Matuska       fflush(stderr);
1632*d9497217SMartin Matuska     }
1633*d9497217SMartin Matuska 
1634*d9497217SMartin Matuska     fclose(stderr_buf);
1635*d9497217SMartin Matuska   }
1636*d9497217SMartin Matuska }
1637*d9497217SMartin Matuska 
munit_test_runner_run_test_wild(MunitTestRunner * runner,const MunitTest * test,const char * test_name,MunitParameter * params,MunitParameter * p)1638*d9497217SMartin Matuska static void munit_test_runner_run_test_wild(MunitTestRunner *runner,
1639*d9497217SMartin Matuska                                             const MunitTest *test,
1640*d9497217SMartin Matuska                                             const char *test_name,
1641*d9497217SMartin Matuska                                             MunitParameter *params,
1642*d9497217SMartin Matuska                                             MunitParameter *p) {
1643*d9497217SMartin Matuska   const MunitParameterEnum *pe;
1644*d9497217SMartin Matuska   char **values;
1645*d9497217SMartin Matuska   MunitParameter *next;
1646*d9497217SMartin Matuska 
1647*d9497217SMartin Matuska   for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {
1648*d9497217SMartin Matuska     if (p->name == pe->name)
1649*d9497217SMartin Matuska       break;
1650*d9497217SMartin Matuska   }
1651*d9497217SMartin Matuska 
1652*d9497217SMartin Matuska   if (pe == NULL)
1653*d9497217SMartin Matuska     return;
1654*d9497217SMartin Matuska 
1655*d9497217SMartin Matuska   for (values = pe->values; *values != NULL; values++) {
1656*d9497217SMartin Matuska     next = p + 1;
1657*d9497217SMartin Matuska     p->value = *values;
1658*d9497217SMartin Matuska     if (next->name == NULL) {
1659*d9497217SMartin Matuska       munit_test_runner_run_test_with_params(runner, test, params);
1660*d9497217SMartin Matuska     } else {
1661*d9497217SMartin Matuska       munit_test_runner_run_test_wild(runner, test, test_name, params, next);
1662*d9497217SMartin Matuska     }
1663*d9497217SMartin Matuska     if (runner->fatal_failures &&
1664*d9497217SMartin Matuska         (runner->report.failed != 0 || runner->report.errored != 0))
1665*d9497217SMartin Matuska       break;
1666*d9497217SMartin Matuska   }
1667*d9497217SMartin Matuska }
1668*d9497217SMartin Matuska 
1669*d9497217SMartin Matuska /* Run a single test, with every combination of parameters
1670*d9497217SMartin Matuska  * requested. */
munit_test_runner_run_test(MunitTestRunner * runner,const MunitTest * test,const char * prefix)1671*d9497217SMartin Matuska static void munit_test_runner_run_test(MunitTestRunner *runner,
1672*d9497217SMartin Matuska                                        const MunitTest *test,
1673*d9497217SMartin Matuska                                        const char *prefix) {
1674*d9497217SMartin Matuska   char *test_name =
1675*d9497217SMartin Matuska     munit_maybe_concat(NULL, (char *)prefix, (char *)test->name);
1676*d9497217SMartin Matuska   /* The array of parameters to pass to
1677*d9497217SMartin Matuska    * munit_test_runner_run_test_with_params */
1678*d9497217SMartin Matuska   MunitParameter *params = NULL;
1679*d9497217SMartin Matuska   size_t params_l = 0;
1680*d9497217SMartin Matuska   /* Wildcard parameters are parameters which have possible values
1681*d9497217SMartin Matuska    * specified in the test, but no specific value was passed to the
1682*d9497217SMartin Matuska    * CLI.  That means we want to run the test once for every
1683*d9497217SMartin Matuska    * possible combination of parameter values or, if --single was
1684*d9497217SMartin Matuska    * passed to the CLI, a single time with a random set of
1685*d9497217SMartin Matuska    * parameters. */
1686*d9497217SMartin Matuska   MunitParameter *wild_params = NULL;
1687*d9497217SMartin Matuska   size_t wild_params_l = 0;
1688*d9497217SMartin Matuska   const MunitParameterEnum *pe;
1689*d9497217SMartin Matuska   const MunitParameter *cli_p;
1690*d9497217SMartin Matuska   munit_bool filled;
1691*d9497217SMartin Matuska   unsigned int possible;
1692*d9497217SMartin Matuska   char **vals;
1693*d9497217SMartin Matuska   size_t first_wild;
1694*d9497217SMartin Matuska   const MunitParameter *wp;
1695*d9497217SMartin Matuska   int pidx;
1696*d9497217SMartin Matuska 
1697*d9497217SMartin Matuska   munit_rand_seed(runner->seed);
1698*d9497217SMartin Matuska 
1699*d9497217SMartin Matuska   fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s",
1700*d9497217SMartin Matuska           test_name);
1701*d9497217SMartin Matuska 
1702*d9497217SMartin Matuska   if (test->parameters == NULL) {
1703*d9497217SMartin Matuska     /* No parameters.  Simple, nice. */
1704*d9497217SMartin Matuska     munit_test_runner_run_test_with_params(runner, test, NULL);
1705*d9497217SMartin Matuska   } else {
1706*d9497217SMartin Matuska     fputc('\n', MUNIT_OUTPUT_FILE);
1707*d9497217SMartin Matuska 
1708*d9497217SMartin Matuska     for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {
1709*d9497217SMartin Matuska       /* Did we received a value for this parameter from the CLI? */
1710*d9497217SMartin Matuska       filled = 0;
1711*d9497217SMartin Matuska       for (cli_p = runner->parameters; cli_p != NULL && cli_p->name != NULL;
1712*d9497217SMartin Matuska            cli_p++) {
1713*d9497217SMartin Matuska         if (strcmp(cli_p->name, pe->name) == 0) {
1714*d9497217SMartin Matuska           if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params, pe->name,
1715*d9497217SMartin Matuska                                                   cli_p->value) != MUNIT_OK))
1716*d9497217SMartin Matuska             goto cleanup;
1717*d9497217SMartin Matuska           filled = 1;
1718*d9497217SMartin Matuska           break;
1719*d9497217SMartin Matuska         }
1720*d9497217SMartin Matuska       }
1721*d9497217SMartin Matuska       if (filled)
1722*d9497217SMartin Matuska         continue;
1723*d9497217SMartin Matuska 
1724*d9497217SMartin Matuska       /* Nothing from CLI, is the enum NULL/empty?  We're not a
1725*d9497217SMartin Matuska        * fuzzer… */
1726*d9497217SMartin Matuska       if (pe->values == NULL || pe->values[0] == NULL)
1727*d9497217SMartin Matuska         continue;
1728*d9497217SMartin Matuska 
1729*d9497217SMartin Matuska       /* If --single was passed to the CLI, choose a value from the
1730*d9497217SMartin Matuska        * list of possibilities randomly. */
1731*d9497217SMartin Matuska       if (runner->single_parameter_mode) {
1732*d9497217SMartin Matuska         possible = 0;
1733*d9497217SMartin Matuska         for (vals = pe->values; *vals != NULL; vals++)
1734*d9497217SMartin Matuska           possible++;
1735*d9497217SMartin Matuska         /* We want the tests to be reproducible, even if you're only
1736*d9497217SMartin Matuska          * running a single test, but we don't want every test with
1737*d9497217SMartin Matuska          * the same number of parameters to choose the same parameter
1738*d9497217SMartin Matuska          * number, so use the test name as a primitive salt. */
1739*d9497217SMartin Matuska         pidx = (int)munit_rand_at_most(munit_str_hash(test_name), possible - 1);
1740*d9497217SMartin Matuska         if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params, pe->name,
1741*d9497217SMartin Matuska                                                 pe->values[pidx]) != MUNIT_OK))
1742*d9497217SMartin Matuska           goto cleanup;
1743*d9497217SMartin Matuska       } else {
1744*d9497217SMartin Matuska         /* We want to try every permutation.  Put in a placeholder
1745*d9497217SMartin Matuska          * entry, we'll iterate through them later. */
1746*d9497217SMartin Matuska         if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params,
1747*d9497217SMartin Matuska                                                 pe->name, NULL) != MUNIT_OK))
1748*d9497217SMartin Matuska           goto cleanup;
1749*d9497217SMartin Matuska       }
1750*d9497217SMartin Matuska     }
1751*d9497217SMartin Matuska 
1752*d9497217SMartin Matuska     if (wild_params_l != 0) {
1753*d9497217SMartin Matuska       first_wild = params_l;
1754*d9497217SMartin Matuska       for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) {
1755*d9497217SMartin Matuska         for (pe = test->parameters;
1756*d9497217SMartin Matuska              pe != NULL && pe->name != NULL && pe->values != NULL; pe++) {
1757*d9497217SMartin Matuska           if (strcmp(wp->name, pe->name) == 0) {
1758*d9497217SMartin Matuska             if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params,
1759*d9497217SMartin Matuska                                                     pe->name,
1760*d9497217SMartin Matuska                                                     pe->values[0]) != MUNIT_OK))
1761*d9497217SMartin Matuska               goto cleanup;
1762*d9497217SMartin Matuska           }
1763*d9497217SMartin Matuska         }
1764*d9497217SMartin Matuska       }
1765*d9497217SMartin Matuska 
1766*d9497217SMartin Matuska       munit_test_runner_run_test_wild(runner, test, test_name, params,
1767*d9497217SMartin Matuska                                       params + first_wild);
1768*d9497217SMartin Matuska     } else {
1769*d9497217SMartin Matuska       munit_test_runner_run_test_with_params(runner, test, params);
1770*d9497217SMartin Matuska     }
1771*d9497217SMartin Matuska 
1772*d9497217SMartin Matuska   cleanup:
1773*d9497217SMartin Matuska     free(params);
1774*d9497217SMartin Matuska     free(wild_params);
1775*d9497217SMartin Matuska   }
1776*d9497217SMartin Matuska 
1777*d9497217SMartin Matuska   munit_maybe_free_concat(test_name, prefix, test->name);
1778*d9497217SMartin Matuska }
1779*d9497217SMartin Matuska 
1780*d9497217SMartin Matuska /* Recurse through the suite and run all the tests.  If a list of
1781*d9497217SMartin Matuska  * tests to run was provied on the command line, run only those
1782*d9497217SMartin Matuska  * tests.  */
munit_test_runner_run_suite(MunitTestRunner * runner,const MunitSuite * suite,const char * prefix)1783*d9497217SMartin Matuska static void munit_test_runner_run_suite(MunitTestRunner *runner,
1784*d9497217SMartin Matuska                                         const MunitSuite *suite,
1785*d9497217SMartin Matuska                                         const char *prefix) {
1786*d9497217SMartin Matuska   size_t pre_l;
1787*d9497217SMartin Matuska   char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);
1788*d9497217SMartin Matuska   const MunitTest *test;
1789*d9497217SMartin Matuska   const char **test_name;
1790*d9497217SMartin Matuska   const MunitSuite *child_suite;
1791*d9497217SMartin Matuska 
1792*d9497217SMartin Matuska   /* Run the tests. */
1793*d9497217SMartin Matuska   for (test = suite->tests; test != NULL && test->test != NULL; test++) {
1794*d9497217SMartin Matuska     if (runner->tests != NULL) { /* Specific tests were requested on the CLI */
1795*d9497217SMartin Matuska       for (test_name = runner->tests; test_name != NULL && *test_name != NULL;
1796*d9497217SMartin Matuska            test_name++) {
1797*d9497217SMartin Matuska         if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) &&
1798*d9497217SMartin Matuska             strncmp(test->name, *test_name + pre_l,
1799*d9497217SMartin Matuska                     strlen(*test_name + pre_l)) == 0) {
1800*d9497217SMartin Matuska           munit_test_runner_run_test(runner, test, pre);
1801*d9497217SMartin Matuska           if (runner->fatal_failures &&
1802*d9497217SMartin Matuska               (runner->report.failed != 0 || runner->report.errored != 0))
1803*d9497217SMartin Matuska             goto cleanup;
1804*d9497217SMartin Matuska         }
1805*d9497217SMartin Matuska       }
1806*d9497217SMartin Matuska     } else { /* Run all tests */
1807*d9497217SMartin Matuska       munit_test_runner_run_test(runner, test, pre);
1808*d9497217SMartin Matuska     }
1809*d9497217SMartin Matuska   }
1810*d9497217SMartin Matuska 
1811*d9497217SMartin Matuska   if (runner->fatal_failures &&
1812*d9497217SMartin Matuska       (runner->report.failed != 0 || runner->report.errored != 0))
1813*d9497217SMartin Matuska     goto cleanup;
1814*d9497217SMartin Matuska 
1815*d9497217SMartin Matuska   /* Run any child suites. */
1816*d9497217SMartin Matuska   for (child_suite = suite->suites;
1817*d9497217SMartin Matuska        child_suite != NULL && child_suite->prefix != NULL; child_suite++) {
1818*d9497217SMartin Matuska     munit_test_runner_run_suite(runner, child_suite, pre);
1819*d9497217SMartin Matuska   }
1820*d9497217SMartin Matuska 
1821*d9497217SMartin Matuska cleanup:
1822*d9497217SMartin Matuska 
1823*d9497217SMartin Matuska   munit_maybe_free_concat(pre, prefix, suite->prefix);
1824*d9497217SMartin Matuska }
1825*d9497217SMartin Matuska 
munit_test_runner_run(MunitTestRunner * runner)1826*d9497217SMartin Matuska static void munit_test_runner_run(MunitTestRunner *runner) {
1827*d9497217SMartin Matuska   munit_test_runner_run_suite(runner, runner->suite, NULL);
1828*d9497217SMartin Matuska }
1829*d9497217SMartin Matuska 
munit_print_help(int argc,char * const * argv,void * user_data,const MunitArgument arguments[])1830*d9497217SMartin Matuska static void munit_print_help(int argc, char *const *argv, void *user_data,
1831*d9497217SMartin Matuska                              const MunitArgument arguments[]) {
1832*d9497217SMartin Matuska   const MunitArgument *arg;
1833*d9497217SMartin Matuska   (void)argc;
1834*d9497217SMartin Matuska 
1835*d9497217SMartin Matuska   printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]);
1836*d9497217SMartin Matuska   puts(
1837*d9497217SMartin Matuska     " --seed SEED\n"
1838*d9497217SMartin Matuska     "           Value used to seed the PRNG.  Must be a 32-bit integer in "
1839*d9497217SMartin Matuska     "decimal\n"
1840*d9497217SMartin Matuska     "           notation with no separators (commas, decimals, spaces, "
1841*d9497217SMartin Matuska     "etc.), or\n"
1842*d9497217SMartin Matuska     "           hexidecimal prefixed by \"0x\".\n"
1843*d9497217SMartin Matuska     " --iterations N\n"
1844*d9497217SMartin Matuska     "           Run each test N times.  0 means the default number.\n"
1845*d9497217SMartin Matuska     " --param name value\n"
1846*d9497217SMartin Matuska     "           A parameter key/value pair which will be passed to any test "
1847*d9497217SMartin Matuska     "with\n"
1848*d9497217SMartin Matuska     "           takes a parameter of that name.  If not provided, the test "
1849*d9497217SMartin Matuska     "will be\n"
1850*d9497217SMartin Matuska     "           run once for each possible parameter value.\n"
1851*d9497217SMartin Matuska     " --list    Write a list of all available tests.\n"
1852*d9497217SMartin Matuska     " --list-params\n"
1853*d9497217SMartin Matuska     "           Write a list of all available tests and their possible "
1854*d9497217SMartin Matuska     "parameters.\n"
1855*d9497217SMartin Matuska     " --single  Run each parameterized test in a single configuration "
1856*d9497217SMartin Matuska     "instead of\n"
1857*d9497217SMartin Matuska     "           every possible combination\n"
1858*d9497217SMartin Matuska     " --log-visible debug|info|warning|error\n"
1859*d9497217SMartin Matuska     " --log-fatal debug|info|warning|error\n"
1860*d9497217SMartin Matuska     "           Set the level at which messages of different severities are "
1861*d9497217SMartin Matuska     "visible,\n"
1862*d9497217SMartin Matuska     "           or cause the test to terminate.\n"
1863*d9497217SMartin Matuska #if !defined(MUNIT_NO_FORK)
1864*d9497217SMartin Matuska     " --no-fork Do not execute tests in a child process.  If this option is "
1865*d9497217SMartin Matuska     "supplied\n"
1866*d9497217SMartin Matuska     "           and a test crashes (including by failing an assertion), no "
1867*d9497217SMartin Matuska     "further\n"
1868*d9497217SMartin Matuska     "           tests will be performed.\n"
1869*d9497217SMartin Matuska #endif
1870*d9497217SMartin Matuska     " --fatal-failures\n"
1871*d9497217SMartin Matuska     "           Stop executing tests as soon as a failure is found.\n"
1872*d9497217SMartin Matuska     " --show-stderr\n"
1873*d9497217SMartin Matuska     "           Show data written to stderr by the tests, even if the test "
1874*d9497217SMartin Matuska     "succeeds.\n"
1875*d9497217SMartin Matuska     " --color auto|always|never\n"
1876*d9497217SMartin Matuska     "           Colorize (or don't) the output.\n"
1877*d9497217SMartin Matuska     /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890
1878*d9497217SMartin Matuska      */
1879*d9497217SMartin Matuska     " --help    Print this help message and exit.\n");
1880*d9497217SMartin Matuska #if defined(MUNIT_NL_LANGINFO)
1881*d9497217SMartin Matuska   setlocale(LC_ALL, "");
1882*d9497217SMartin Matuska   fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit",
1883*d9497217SMartin Matuska         stdout);
1884*d9497217SMartin Matuska #else
1885*d9497217SMartin Matuska   puts("munit");
1886*d9497217SMartin Matuska #endif
1887*d9497217SMartin Matuska   printf(" %d.%d.%d\n"
1888*d9497217SMartin Matuska          "Full documentation at: https://nemequ.github.io/munit/\n",
1889*d9497217SMartin Matuska          (MUNIT_CURRENT_VERSION >> 16) & 0xff,
1890*d9497217SMartin Matuska          (MUNIT_CURRENT_VERSION >> 8) & 0xff,
1891*d9497217SMartin Matuska          (MUNIT_CURRENT_VERSION >> 0) & 0xff);
1892*d9497217SMartin Matuska   for (arg = arguments; arg != NULL && arg->name != NULL; arg++)
1893*d9497217SMartin Matuska     arg->write_help(arg, user_data);
1894*d9497217SMartin Matuska }
1895*d9497217SMartin Matuska 
1896*d9497217SMartin Matuska static const MunitArgument *
munit_arguments_find(const MunitArgument arguments[],const char * name)1897*d9497217SMartin Matuska munit_arguments_find(const MunitArgument arguments[], const char *name) {
1898*d9497217SMartin Matuska   const MunitArgument *arg;
1899*d9497217SMartin Matuska 
1900*d9497217SMartin Matuska   for (arg = arguments; arg != NULL && arg->name != NULL; arg++)
1901*d9497217SMartin Matuska     if (strcmp(arg->name, name) == 0)
1902*d9497217SMartin Matuska       return arg;
1903*d9497217SMartin Matuska 
1904*d9497217SMartin Matuska   return NULL;
1905*d9497217SMartin Matuska }
1906*d9497217SMartin Matuska 
munit_suite_list_tests(const MunitSuite * suite,munit_bool show_params,const char * prefix)1907*d9497217SMartin Matuska static void munit_suite_list_tests(const MunitSuite *suite,
1908*d9497217SMartin Matuska                                    munit_bool show_params, const char *prefix) {
1909*d9497217SMartin Matuska   size_t pre_l;
1910*d9497217SMartin Matuska   char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);
1911*d9497217SMartin Matuska   const MunitTest *test;
1912*d9497217SMartin Matuska   const MunitParameterEnum *params;
1913*d9497217SMartin Matuska   munit_bool first;
1914*d9497217SMartin Matuska   char **val;
1915*d9497217SMartin Matuska   const MunitSuite *child_suite;
1916*d9497217SMartin Matuska 
1917*d9497217SMartin Matuska   for (test = suite->tests; test != NULL && test->name != NULL; test++) {
1918*d9497217SMartin Matuska     if (pre != NULL)
1919*d9497217SMartin Matuska       fputs(pre, stdout);
1920*d9497217SMartin Matuska     puts(test->name);
1921*d9497217SMartin Matuska 
1922*d9497217SMartin Matuska     if (show_params) {
1923*d9497217SMartin Matuska       for (params = test->parameters; params != NULL && params->name != NULL;
1924*d9497217SMartin Matuska            params++) {
1925*d9497217SMartin Matuska         fprintf(stdout, " - %s: ", params->name);
1926*d9497217SMartin Matuska         if (params->values == NULL) {
1927*d9497217SMartin Matuska           puts("Any");
1928*d9497217SMartin Matuska         } else {
1929*d9497217SMartin Matuska           first = 1;
1930*d9497217SMartin Matuska           for (val = params->values; *val != NULL; val++) {
1931*d9497217SMartin Matuska             if (!first) {
1932*d9497217SMartin Matuska               fputs(", ", stdout);
1933*d9497217SMartin Matuska             } else {
1934*d9497217SMartin Matuska               first = 0;
1935*d9497217SMartin Matuska             }
1936*d9497217SMartin Matuska             fputs(*val, stdout);
1937*d9497217SMartin Matuska           }
1938*d9497217SMartin Matuska           putc('\n', stdout);
1939*d9497217SMartin Matuska         }
1940*d9497217SMartin Matuska       }
1941*d9497217SMartin Matuska     }
1942*d9497217SMartin Matuska   }
1943*d9497217SMartin Matuska 
1944*d9497217SMartin Matuska   for (child_suite = suite->suites;
1945*d9497217SMartin Matuska        child_suite != NULL && child_suite->prefix != NULL; child_suite++) {
1946*d9497217SMartin Matuska     munit_suite_list_tests(child_suite, show_params, pre);
1947*d9497217SMartin Matuska   }
1948*d9497217SMartin Matuska 
1949*d9497217SMartin Matuska   munit_maybe_free_concat(pre, prefix, suite->prefix);
1950*d9497217SMartin Matuska }
1951*d9497217SMartin Matuska 
munit_stream_supports_ansi(FILE * stream)1952*d9497217SMartin Matuska static munit_bool munit_stream_supports_ansi(FILE *stream) {
1953*d9497217SMartin Matuska #if !defined(_WIN32)
1954*d9497217SMartin Matuska   return isatty(fileno(stream));
1955*d9497217SMartin Matuska #else
1956*d9497217SMartin Matuska 
1957*d9497217SMartin Matuska #  if !defined(__MINGW32__)
1958*d9497217SMartin Matuska   size_t ansicon_size = 0;
1959*d9497217SMartin Matuska #  endif
1960*d9497217SMartin Matuska 
1961*d9497217SMartin Matuska   if (isatty(fileno(stream))) {
1962*d9497217SMartin Matuska #  if !defined(__MINGW32__)
1963*d9497217SMartin Matuska     getenv_s(&ansicon_size, NULL, 0, "ANSICON");
1964*d9497217SMartin Matuska     return ansicon_size != 0;
1965*d9497217SMartin Matuska #  else
1966*d9497217SMartin Matuska     return getenv("ANSICON") != NULL;
1967*d9497217SMartin Matuska #  endif
1968*d9497217SMartin Matuska   }
1969*d9497217SMartin Matuska   return 0;
1970*d9497217SMartin Matuska #endif
1971*d9497217SMartin Matuska }
1972*d9497217SMartin Matuska 
munit_suite_main_custom(const MunitSuite * suite,void * user_data,int argc,char * const * argv,const MunitArgument arguments[])1973*d9497217SMartin Matuska int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc,
1974*d9497217SMartin Matuska                             char *const *argv,
1975*d9497217SMartin Matuska                             const MunitArgument arguments[]) {
1976*d9497217SMartin Matuska   int result = EXIT_FAILURE;
1977*d9497217SMartin Matuska   MunitTestRunner runner;
1978*d9497217SMartin Matuska   size_t parameters_size = 0;
1979*d9497217SMartin Matuska   size_t tests_size = 0;
1980*d9497217SMartin Matuska   int arg;
1981*d9497217SMartin Matuska 
1982*d9497217SMartin Matuska   char *envptr;
1983*d9497217SMartin Matuska   unsigned long ts;
1984*d9497217SMartin Matuska   char *endptr;
1985*d9497217SMartin Matuska   unsigned long long iterations;
1986*d9497217SMartin Matuska   MunitLogLevel level;
1987*d9497217SMartin Matuska   const MunitArgument *argument;
1988*d9497217SMartin Matuska   const char **runner_tests;
1989*d9497217SMartin Matuska   unsigned int tests_run;
1990*d9497217SMartin Matuska   unsigned int tests_total;
1991*d9497217SMartin Matuska 
1992*d9497217SMartin Matuska   runner.prefix = NULL;
1993*d9497217SMartin Matuska   runner.suite = NULL;
1994*d9497217SMartin Matuska   runner.tests = NULL;
1995*d9497217SMartin Matuska   runner.seed = 0;
1996*d9497217SMartin Matuska   runner.iterations = 0;
1997*d9497217SMartin Matuska   runner.parameters = NULL;
1998*d9497217SMartin Matuska   runner.single_parameter_mode = 0;
1999*d9497217SMartin Matuska   runner.user_data = NULL;
2000*d9497217SMartin Matuska 
2001*d9497217SMartin Matuska   runner.report.successful = 0;
2002*d9497217SMartin Matuska   runner.report.skipped = 0;
2003*d9497217SMartin Matuska   runner.report.failed = 0;
2004*d9497217SMartin Matuska   runner.report.errored = 0;
2005*d9497217SMartin Matuska #if defined(MUNIT_ENABLE_TIMING)
2006*d9497217SMartin Matuska   runner.report.cpu_clock = 0;
2007*d9497217SMartin Matuska   runner.report.wall_clock = 0;
2008*d9497217SMartin Matuska #endif
2009*d9497217SMartin Matuska 
2010*d9497217SMartin Matuska   runner.colorize = 0;
2011*d9497217SMartin Matuska #if !defined(_WIN32)
2012*d9497217SMartin Matuska   runner.fork = 1;
2013*d9497217SMartin Matuska #else
2014*d9497217SMartin Matuska   runner.fork = 0;
2015*d9497217SMartin Matuska #endif
2016*d9497217SMartin Matuska   runner.show_stderr = 0;
2017*d9497217SMartin Matuska   runner.fatal_failures = 0;
2018*d9497217SMartin Matuska   runner.suite = suite;
2019*d9497217SMartin Matuska   runner.user_data = user_data;
2020*d9497217SMartin Matuska   runner.seed = munit_rand_generate_seed();
2021*d9497217SMartin Matuska   runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);
2022*d9497217SMartin Matuska 
2023*d9497217SMartin Matuska   for (arg = 1; arg < argc; arg++) {
2024*d9497217SMartin Matuska     if (strncmp("--", argv[arg], 2) == 0) {
2025*d9497217SMartin Matuska       if (strcmp("seed", argv[arg] + 2) == 0) {
2026*d9497217SMartin Matuska         if (arg + 1 >= argc) {
2027*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2028*d9497217SMartin Matuska                               "%s requires an argument", argv[arg]);
2029*d9497217SMartin Matuska           goto cleanup;
2030*d9497217SMartin Matuska         }
2031*d9497217SMartin Matuska 
2032*d9497217SMartin Matuska         envptr = argv[arg + 1];
2033*d9497217SMartin Matuska         ts = strtoul(argv[arg + 1], &envptr, 0);
2034*d9497217SMartin Matuska         if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) {
2035*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2036*d9497217SMartin Matuska                               "invalid value ('%s') passed to %s",
2037*d9497217SMartin Matuska                               argv[arg + 1], argv[arg]);
2038*d9497217SMartin Matuska           goto cleanup;
2039*d9497217SMartin Matuska         }
2040*d9497217SMartin Matuska         runner.seed = (munit_uint32_t)ts;
2041*d9497217SMartin Matuska 
2042*d9497217SMartin Matuska         arg++;
2043*d9497217SMartin Matuska       } else if (strcmp("iterations", argv[arg] + 2) == 0) {
2044*d9497217SMartin Matuska         if (arg + 1 >= argc) {
2045*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2046*d9497217SMartin Matuska                               "%s requires an argument", argv[arg]);
2047*d9497217SMartin Matuska           goto cleanup;
2048*d9497217SMartin Matuska         }
2049*d9497217SMartin Matuska 
2050*d9497217SMartin Matuska         endptr = argv[arg + 1];
2051*d9497217SMartin Matuska         iterations = strtoul(argv[arg + 1], &endptr, 0);
2052*d9497217SMartin Matuska         if (*endptr != '\0' || iterations > UINT_MAX) {
2053*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2054*d9497217SMartin Matuska                               "invalid value ('%s') passed to %s",
2055*d9497217SMartin Matuska                               argv[arg + 1], argv[arg]);
2056*d9497217SMartin Matuska           goto cleanup;
2057*d9497217SMartin Matuska         }
2058*d9497217SMartin Matuska 
2059*d9497217SMartin Matuska         runner.iterations = (unsigned int)iterations;
2060*d9497217SMartin Matuska 
2061*d9497217SMartin Matuska         arg++;
2062*d9497217SMartin Matuska       } else if (strcmp("param", argv[arg] + 2) == 0) {
2063*d9497217SMartin Matuska         if (arg + 2 >= argc) {
2064*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2065*d9497217SMartin Matuska                               "%s requires two arguments", argv[arg]);
2066*d9497217SMartin Matuska           goto cleanup;
2067*d9497217SMartin Matuska         }
2068*d9497217SMartin Matuska 
2069*d9497217SMartin Matuska         runner.parameters = realloc(runner.parameters, sizeof(MunitParameter) *
2070*d9497217SMartin Matuska                                                          (parameters_size + 2));
2071*d9497217SMartin Matuska         if (runner.parameters == NULL) {
2072*d9497217SMartin Matuska           munit_log_internal(MUNIT_LOG_ERROR, stderr,
2073*d9497217SMartin Matuska                              "failed to allocate memory");
2074*d9497217SMartin Matuska           goto cleanup;
2075*d9497217SMartin Matuska         }
2076*d9497217SMartin Matuska         runner.parameters[parameters_size].name = (char *)argv[arg + 1];
2077*d9497217SMartin Matuska         runner.parameters[parameters_size].value = (char *)argv[arg + 2];
2078*d9497217SMartin Matuska         parameters_size++;
2079*d9497217SMartin Matuska         runner.parameters[parameters_size].name = NULL;
2080*d9497217SMartin Matuska         runner.parameters[parameters_size].value = NULL;
2081*d9497217SMartin Matuska         arg += 2;
2082*d9497217SMartin Matuska       } else if (strcmp("color", argv[arg] + 2) == 0) {
2083*d9497217SMartin Matuska         if (arg + 1 >= argc) {
2084*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2085*d9497217SMartin Matuska                               "%s requires an argument", argv[arg]);
2086*d9497217SMartin Matuska           goto cleanup;
2087*d9497217SMartin Matuska         }
2088*d9497217SMartin Matuska 
2089*d9497217SMartin Matuska         if (strcmp(argv[arg + 1], "always") == 0)
2090*d9497217SMartin Matuska           runner.colorize = 1;
2091*d9497217SMartin Matuska         else if (strcmp(argv[arg + 1], "never") == 0)
2092*d9497217SMartin Matuska           runner.colorize = 0;
2093*d9497217SMartin Matuska         else if (strcmp(argv[arg + 1], "auto") == 0)
2094*d9497217SMartin Matuska           runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);
2095*d9497217SMartin Matuska         else {
2096*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2097*d9497217SMartin Matuska                               "invalid value ('%s') passed to %s",
2098*d9497217SMartin Matuska                               argv[arg + 1], argv[arg]);
2099*d9497217SMartin Matuska           goto cleanup;
2100*d9497217SMartin Matuska         }
2101*d9497217SMartin Matuska 
2102*d9497217SMartin Matuska         arg++;
2103*d9497217SMartin Matuska       } else if (strcmp("help", argv[arg] + 2) == 0) {
2104*d9497217SMartin Matuska         munit_print_help(argc, argv, user_data, arguments);
2105*d9497217SMartin Matuska         result = EXIT_SUCCESS;
2106*d9497217SMartin Matuska         goto cleanup;
2107*d9497217SMartin Matuska       } else if (strcmp("single", argv[arg] + 2) == 0) {
2108*d9497217SMartin Matuska         runner.single_parameter_mode = 1;
2109*d9497217SMartin Matuska       } else if (strcmp("show-stderr", argv[arg] + 2) == 0) {
2110*d9497217SMartin Matuska         runner.show_stderr = 1;
2111*d9497217SMartin Matuska #if !defined(_WIN32)
2112*d9497217SMartin Matuska       } else if (strcmp("no-fork", argv[arg] + 2) == 0) {
2113*d9497217SMartin Matuska         runner.fork = 0;
2114*d9497217SMartin Matuska #endif
2115*d9497217SMartin Matuska       } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) {
2116*d9497217SMartin Matuska         runner.fatal_failures = 1;
2117*d9497217SMartin Matuska       } else if (strcmp("log-visible", argv[arg] + 2) == 0 ||
2118*d9497217SMartin Matuska                  strcmp("log-fatal", argv[arg] + 2) == 0) {
2119*d9497217SMartin Matuska         if (arg + 1 >= argc) {
2120*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2121*d9497217SMartin Matuska                               "%s requires an argument", argv[arg]);
2122*d9497217SMartin Matuska           goto cleanup;
2123*d9497217SMartin Matuska         }
2124*d9497217SMartin Matuska 
2125*d9497217SMartin Matuska         if (strcmp(argv[arg + 1], "debug") == 0)
2126*d9497217SMartin Matuska           level = MUNIT_LOG_DEBUG;
2127*d9497217SMartin Matuska         else if (strcmp(argv[arg + 1], "info") == 0)
2128*d9497217SMartin Matuska           level = MUNIT_LOG_INFO;
2129*d9497217SMartin Matuska         else if (strcmp(argv[arg + 1], "warning") == 0)
2130*d9497217SMartin Matuska           level = MUNIT_LOG_WARNING;
2131*d9497217SMartin Matuska         else if (strcmp(argv[arg + 1], "error") == 0)
2132*d9497217SMartin Matuska           level = MUNIT_LOG_ERROR;
2133*d9497217SMartin Matuska         else {
2134*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2135*d9497217SMartin Matuska                               "invalid value ('%s') passed to %s",
2136*d9497217SMartin Matuska                               argv[arg + 1], argv[arg]);
2137*d9497217SMartin Matuska           goto cleanup;
2138*d9497217SMartin Matuska         }
2139*d9497217SMartin Matuska 
2140*d9497217SMartin Matuska         if (strcmp("log-visible", argv[arg] + 2) == 0)
2141*d9497217SMartin Matuska           munit_log_level_visible = level;
2142*d9497217SMartin Matuska         else
2143*d9497217SMartin Matuska           munit_log_level_fatal = level;
2144*d9497217SMartin Matuska 
2145*d9497217SMartin Matuska         arg++;
2146*d9497217SMartin Matuska       } else if (strcmp("list", argv[arg] + 2) == 0) {
2147*d9497217SMartin Matuska         munit_suite_list_tests(suite, 0, NULL);
2148*d9497217SMartin Matuska         result = EXIT_SUCCESS;
2149*d9497217SMartin Matuska         goto cleanup;
2150*d9497217SMartin Matuska       } else if (strcmp("list-params", argv[arg] + 2) == 0) {
2151*d9497217SMartin Matuska         munit_suite_list_tests(suite, 1, NULL);
2152*d9497217SMartin Matuska         result = EXIT_SUCCESS;
2153*d9497217SMartin Matuska         goto cleanup;
2154*d9497217SMartin Matuska       } else {
2155*d9497217SMartin Matuska         argument = munit_arguments_find(arguments, argv[arg] + 2);
2156*d9497217SMartin Matuska         if (argument == NULL) {
2157*d9497217SMartin Matuska           munit_logf_internal(MUNIT_LOG_ERROR, stderr,
2158*d9497217SMartin Matuska                               "unknown argument ('%s')", argv[arg]);
2159*d9497217SMartin Matuska           goto cleanup;
2160*d9497217SMartin Matuska         }
2161*d9497217SMartin Matuska 
2162*d9497217SMartin Matuska         if (!argument->parse_argument(suite, user_data, &arg, argc, argv))
2163*d9497217SMartin Matuska           goto cleanup;
2164*d9497217SMartin Matuska       }
2165*d9497217SMartin Matuska     } else {
2166*d9497217SMartin Matuska       runner_tests =
2167*d9497217SMartin Matuska         realloc((void *)runner.tests, sizeof(char *) * (tests_size + 2));
2168*d9497217SMartin Matuska       if (runner_tests == NULL) {
2169*d9497217SMartin Matuska         munit_log_internal(MUNIT_LOG_ERROR, stderr,
2170*d9497217SMartin Matuska                            "failed to allocate memory");
2171*d9497217SMartin Matuska         goto cleanup;
2172*d9497217SMartin Matuska       }
2173*d9497217SMartin Matuska       runner.tests = runner_tests;
2174*d9497217SMartin Matuska       runner.tests[tests_size++] = argv[arg];
2175*d9497217SMartin Matuska       runner.tests[tests_size] = NULL;
2176*d9497217SMartin Matuska     }
2177*d9497217SMartin Matuska   }
2178*d9497217SMartin Matuska 
2179*d9497217SMartin Matuska   fflush(stderr);
2180*d9497217SMartin Matuska   fprintf(MUNIT_OUTPUT_FILE,
2181*d9497217SMartin Matuska           "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed);
2182*d9497217SMartin Matuska 
2183*d9497217SMartin Matuska   munit_test_runner_run(&runner);
2184*d9497217SMartin Matuska 
2185*d9497217SMartin Matuska   tests_run =
2186*d9497217SMartin Matuska     runner.report.successful + runner.report.failed + runner.report.errored;
2187*d9497217SMartin Matuska   tests_total = tests_run + runner.report.skipped;
2188*d9497217SMartin Matuska   if (tests_run == 0) {
2189*d9497217SMartin Matuska     fprintf(stderr, "No tests run, %d (100%%) skipped.\n",
2190*d9497217SMartin Matuska             runner.report.skipped);
2191*d9497217SMartin Matuska   } else {
2192*d9497217SMartin Matuska     fprintf(MUNIT_OUTPUT_FILE,
2193*d9497217SMartin Matuska             "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n",
2194*d9497217SMartin Matuska             runner.report.successful, tests_run,
2195*d9497217SMartin Matuska             (((double)runner.report.successful) / ((double)tests_run)) * 100.0,
2196*d9497217SMartin Matuska             runner.report.skipped,
2197*d9497217SMartin Matuska             (((double)runner.report.skipped) / ((double)tests_total)) * 100.0);
2198*d9497217SMartin Matuska   }
2199*d9497217SMartin Matuska 
2200*d9497217SMartin Matuska   if (runner.report.failed == 0 && runner.report.errored == 0) {
2201*d9497217SMartin Matuska     result = EXIT_SUCCESS;
2202*d9497217SMartin Matuska   }
2203*d9497217SMartin Matuska 
2204*d9497217SMartin Matuska cleanup:
2205*d9497217SMartin Matuska   free(runner.parameters);
2206*d9497217SMartin Matuska   free((void *)runner.tests);
2207*d9497217SMartin Matuska 
2208*d9497217SMartin Matuska   return result;
2209*d9497217SMartin Matuska }
2210*d9497217SMartin Matuska 
munit_suite_main(const MunitSuite * suite,void * user_data,int argc,char * const * argv)2211*d9497217SMartin Matuska int munit_suite_main(const MunitSuite *suite, void *user_data, int argc,
2212*d9497217SMartin Matuska                      char *const *argv) {
2213*d9497217SMartin Matuska   return munit_suite_main_custom(suite, user_data, argc, argv, NULL);
2214*d9497217SMartin Matuska }
2215*d9497217SMartin Matuska 
2216*d9497217SMartin Matuska static uint8_t hexchars[] = "0123456789abcdef";
2217*d9497217SMartin Matuska 
hexdump_addr(uint8_t * dest,size_t addr)2218*d9497217SMartin Matuska static uint8_t *hexdump_addr(uint8_t *dest, size_t addr) {
2219*d9497217SMartin Matuska   size_t i;
2220*d9497217SMartin Matuska   uint8_t a;
2221*d9497217SMartin Matuska 
2222*d9497217SMartin Matuska   for (i = 0; i < 4; ++i) {
2223*d9497217SMartin Matuska     a = (addr >> (3 - i) * 8) & 0xff;
2224*d9497217SMartin Matuska 
2225*d9497217SMartin Matuska     *dest++ = hexchars[a >> 4];
2226*d9497217SMartin Matuska     *dest++ = hexchars[a & 0xf];
2227*d9497217SMartin Matuska   }
2228*d9497217SMartin Matuska 
2229*d9497217SMartin Matuska   return dest;
2230*d9497217SMartin Matuska }
2231*d9497217SMartin Matuska 
asciidump(uint8_t * dest,const uint8_t * data,size_t datalen)2232*d9497217SMartin Matuska static uint8_t *asciidump(uint8_t *dest, const uint8_t *data, size_t datalen) {
2233*d9497217SMartin Matuska   size_t i;
2234*d9497217SMartin Matuska 
2235*d9497217SMartin Matuska   *dest++ = '|';
2236*d9497217SMartin Matuska 
2237*d9497217SMartin Matuska   for (i = 0; i < datalen; ++i) {
2238*d9497217SMartin Matuska     if (0x20 <= data[i] && data[i] <= 0x7e) {
2239*d9497217SMartin Matuska       *dest++ = data[i];
2240*d9497217SMartin Matuska     } else {
2241*d9497217SMartin Matuska       *dest++ = '.';
2242*d9497217SMartin Matuska     }
2243*d9497217SMartin Matuska   }
2244*d9497217SMartin Matuska 
2245*d9497217SMartin Matuska   *dest++ = '|';
2246*d9497217SMartin Matuska 
2247*d9497217SMartin Matuska   return dest;
2248*d9497217SMartin Matuska }
2249*d9497217SMartin Matuska 
hexdump8(uint8_t * dest,const uint8_t * data,size_t datalen)2250*d9497217SMartin Matuska static uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) {
2251*d9497217SMartin Matuska   size_t i;
2252*d9497217SMartin Matuska 
2253*d9497217SMartin Matuska   for (i = 0; i < datalen; ++i) {
2254*d9497217SMartin Matuska     *dest++ = hexchars[data[i] >> 4];
2255*d9497217SMartin Matuska     *dest++ = hexchars[data[i] & 0xf];
2256*d9497217SMartin Matuska     *dest++ = ' ';
2257*d9497217SMartin Matuska   }
2258*d9497217SMartin Matuska 
2259*d9497217SMartin Matuska   for (; i < 8; ++i) {
2260*d9497217SMartin Matuska     *dest++ = ' ';
2261*d9497217SMartin Matuska     *dest++ = ' ';
2262*d9497217SMartin Matuska     *dest++ = ' ';
2263*d9497217SMartin Matuska   }
2264*d9497217SMartin Matuska 
2265*d9497217SMartin Matuska   return dest;
2266*d9497217SMartin Matuska }
2267*d9497217SMartin Matuska 
hexdump16(uint8_t * dest,const uint8_t * data,size_t datalen)2268*d9497217SMartin Matuska static uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) {
2269*d9497217SMartin Matuska   dest = hexdump8(dest, data, datalen < 8 ? datalen : 8);
2270*d9497217SMartin Matuska   *dest++ = ' ';
2271*d9497217SMartin Matuska 
2272*d9497217SMartin Matuska   if (datalen < 8) {
2273*d9497217SMartin Matuska     data = NULL;
2274*d9497217SMartin Matuska     datalen = 0;
2275*d9497217SMartin Matuska   } else {
2276*d9497217SMartin Matuska     data += 8;
2277*d9497217SMartin Matuska     datalen -= 8;
2278*d9497217SMartin Matuska   }
2279*d9497217SMartin Matuska 
2280*d9497217SMartin Matuska   dest = hexdump8(dest, data, datalen);
2281*d9497217SMartin Matuska   *dest++ = ' ';
2282*d9497217SMartin Matuska 
2283*d9497217SMartin Matuska   return dest;
2284*d9497217SMartin Matuska }
2285*d9497217SMartin Matuska 
hexdump_line(uint8_t * dest,const uint8_t * data,size_t datalen,size_t addr)2286*d9497217SMartin Matuska static uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen,
2287*d9497217SMartin Matuska                              size_t addr) {
2288*d9497217SMartin Matuska   dest = hexdump_addr(dest, addr);
2289*d9497217SMartin Matuska   *dest++ = ' ';
2290*d9497217SMartin Matuska   *dest++ = ' ';
2291*d9497217SMartin Matuska 
2292*d9497217SMartin Matuska   dest = hexdump16(dest, data, datalen);
2293*d9497217SMartin Matuska 
2294*d9497217SMartin Matuska   dest = asciidump(dest, data, datalen);
2295*d9497217SMartin Matuska 
2296*d9497217SMartin Matuska   return dest;
2297*d9497217SMartin Matuska }
2298*d9497217SMartin Matuska 
munit_hexdump(FILE * fp,const void * data,size_t datalen)2299*d9497217SMartin Matuska int munit_hexdump(FILE *fp, const void *data, size_t datalen) {
2300*d9497217SMartin Matuska   size_t offset = 0, n, len;
2301*d9497217SMartin Matuska   uint8_t buf[128], *p;
2302*d9497217SMartin Matuska   const uint8_t *s;
2303*d9497217SMartin Matuska   int repeated = 0;
2304*d9497217SMartin Matuska 
2305*d9497217SMartin Matuska   if (datalen == 0) {
2306*d9497217SMartin Matuska     return 0;
2307*d9497217SMartin Matuska   }
2308*d9497217SMartin Matuska 
2309*d9497217SMartin Matuska   for (; offset < datalen; offset += 16) {
2310*d9497217SMartin Matuska     n = datalen - offset;
2311*d9497217SMartin Matuska     s = (const uint8_t *)data + offset;
2312*d9497217SMartin Matuska 
2313*d9497217SMartin Matuska     if (n >= 16) {
2314*d9497217SMartin Matuska       n = 16;
2315*d9497217SMartin Matuska 
2316*d9497217SMartin Matuska       if (offset > 0) {
2317*d9497217SMartin Matuska         if (memcmp(s - 16, s, 16) == 0) {
2318*d9497217SMartin Matuska           if (repeated) {
2319*d9497217SMartin Matuska             continue;
2320*d9497217SMartin Matuska           }
2321*d9497217SMartin Matuska 
2322*d9497217SMartin Matuska           repeated = 1;
2323*d9497217SMartin Matuska 
2324*d9497217SMartin Matuska           if (fwrite("*\n", 1, 2, fp) < 2) {
2325*d9497217SMartin Matuska             return -1;
2326*d9497217SMartin Matuska           }
2327*d9497217SMartin Matuska 
2328*d9497217SMartin Matuska           continue;
2329*d9497217SMartin Matuska         }
2330*d9497217SMartin Matuska 
2331*d9497217SMartin Matuska         repeated = 0;
2332*d9497217SMartin Matuska       }
2333*d9497217SMartin Matuska     }
2334*d9497217SMartin Matuska 
2335*d9497217SMartin Matuska     p = hexdump_line(buf, s, n, offset);
2336*d9497217SMartin Matuska     *p++ = '\n';
2337*d9497217SMartin Matuska 
2338*d9497217SMartin Matuska     len = (size_t)(p - buf);
2339*d9497217SMartin Matuska 
2340*d9497217SMartin Matuska     if (fwrite(buf, 1, len, fp) < len) {
2341*d9497217SMartin Matuska       return -1;
2342*d9497217SMartin Matuska     }
2343*d9497217SMartin Matuska   }
2344*d9497217SMartin Matuska 
2345*d9497217SMartin Matuska   p = hexdump_addr(buf, datalen);
2346*d9497217SMartin Matuska   *p++ = '\n';
2347*d9497217SMartin Matuska 
2348*d9497217SMartin Matuska   len = (size_t)(p - buf);
2349*d9497217SMartin Matuska 
2350*d9497217SMartin Matuska   if (fwrite(buf, 1, len, fp) < len) {
2351*d9497217SMartin Matuska     return -1;
2352*d9497217SMartin Matuska   }
2353*d9497217SMartin Matuska 
2354*d9497217SMartin Matuska   return 0;
2355*d9497217SMartin Matuska }
2356*d9497217SMartin Matuska 
munit_hexdump_diff(FILE * fp,const void * a,size_t alen,const void * b,size_t blen)2357*d9497217SMartin Matuska int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b,
2358*d9497217SMartin Matuska                        size_t blen) {
2359*d9497217SMartin Matuska   size_t offset = 0, k, i, len, ncomp, maxlen, adoff = 0;
2360*d9497217SMartin Matuska   uint8_t buf[128], *p;
2361*d9497217SMartin Matuska   const uint8_t mk[2] = {'-', '+'};
2362*d9497217SMartin Matuska   struct datasource {
2363*d9497217SMartin Matuska     const uint8_t *data;
2364*d9497217SMartin Matuska     size_t datalen;
2365*d9497217SMartin Matuska     const uint8_t *s;
2366*d9497217SMartin Matuska     size_t n;
2367*d9497217SMartin Matuska   } ds[] = {{a, alen, NULL, 0}, {b, blen, NULL, 0}}, *dp;
2368*d9497217SMartin Matuska 
2369*d9497217SMartin Matuska   maxlen = alen < blen ? blen : alen;
2370*d9497217SMartin Matuska 
2371*d9497217SMartin Matuska   for (; offset < maxlen; offset += 16) {
2372*d9497217SMartin Matuska     for (k = 0; k < 2; ++k) {
2373*d9497217SMartin Matuska       dp = &ds[k];
2374*d9497217SMartin Matuska 
2375*d9497217SMartin Matuska       if (offset < dp->datalen) {
2376*d9497217SMartin Matuska         dp->s = (const uint8_t *)dp->data + offset;
2377*d9497217SMartin Matuska         dp->n = dp->datalen - offset;
2378*d9497217SMartin Matuska 
2379*d9497217SMartin Matuska         if (dp->n > 16) {
2380*d9497217SMartin Matuska           dp->n = 16;
2381*d9497217SMartin Matuska         }
2382*d9497217SMartin Matuska       } else {
2383*d9497217SMartin Matuska         dp->s = NULL;
2384*d9497217SMartin Matuska         dp->n = 0;
2385*d9497217SMartin Matuska       }
2386*d9497217SMartin Matuska     }
2387*d9497217SMartin Matuska 
2388*d9497217SMartin Matuska     if (ds[0].n == ds[1].n && memcmp(ds[0].s, ds[1].s, ds[0].n) == 0) {
2389*d9497217SMartin Matuska       continue;
2390*d9497217SMartin Matuska     }
2391*d9497217SMartin Matuska 
2392*d9497217SMartin Matuska     for (k = 0; k < 2; ++k) {
2393*d9497217SMartin Matuska       dp = &ds[k];
2394*d9497217SMartin Matuska 
2395*d9497217SMartin Matuska       if (!dp->n) {
2396*d9497217SMartin Matuska         continue;
2397*d9497217SMartin Matuska       }
2398*d9497217SMartin Matuska 
2399*d9497217SMartin Matuska       p = buf;
2400*d9497217SMartin Matuska       *p++ = mk[k];
2401*d9497217SMartin Matuska       *p++ = mk[k];
2402*d9497217SMartin Matuska       *p++ = mk[k];
2403*d9497217SMartin Matuska       *p++ = mk[k];
2404*d9497217SMartin Matuska 
2405*d9497217SMartin Matuska       p = hexdump_line(p, dp->s, dp->n, offset);
2406*d9497217SMartin Matuska       *p++ = '\n';
2407*d9497217SMartin Matuska 
2408*d9497217SMartin Matuska       len = (size_t)(p - buf);
2409*d9497217SMartin Matuska 
2410*d9497217SMartin Matuska       if (fwrite(buf, 1, len, fp) < len) {
2411*d9497217SMartin Matuska         return -1;
2412*d9497217SMartin Matuska       }
2413*d9497217SMartin Matuska     }
2414*d9497217SMartin Matuska 
2415*d9497217SMartin Matuska     if (!ds[0].n || !ds[1].n) {
2416*d9497217SMartin Matuska       continue;
2417*d9497217SMartin Matuska     }
2418*d9497217SMartin Matuska 
2419*d9497217SMartin Matuska     ncomp = ds[0].n < ds[1].n ? ds[0].n : ds[1].n;
2420*d9497217SMartin Matuska 
2421*d9497217SMartin Matuska     p = buf + 4 + 10;
2422*d9497217SMartin Matuska 
2423*d9497217SMartin Matuska     memset(buf, ' ', 4 + 78);
2424*d9497217SMartin Matuska 
2425*d9497217SMartin Matuska     for (i = 0; i < ncomp; ++i) {
2426*d9497217SMartin Matuska       if (ds[0].s[i] == ds[1].s[i]) {
2427*d9497217SMartin Matuska         *p++ = ' ';
2428*d9497217SMartin Matuska         *p++ = ' ';
2429*d9497217SMartin Matuska       } else {
2430*d9497217SMartin Matuska         adoff = 4 + 10 + 51 + i;
2431*d9497217SMartin Matuska         *(buf + adoff) = '^';
2432*d9497217SMartin Matuska 
2433*d9497217SMartin Matuska         *p++ = '^';
2434*d9497217SMartin Matuska         *p++ = '^';
2435*d9497217SMartin Matuska       }
2436*d9497217SMartin Matuska 
2437*d9497217SMartin Matuska       *p++ = ' ';
2438*d9497217SMartin Matuska 
2439*d9497217SMartin Matuska       if (i == 7) {
2440*d9497217SMartin Matuska         *p++ = ' ';
2441*d9497217SMartin Matuska       }
2442*d9497217SMartin Matuska     }
2443*d9497217SMartin Matuska 
2444*d9497217SMartin Matuska     if (adoff) {
2445*d9497217SMartin Matuska       len = adoff + 1;
2446*d9497217SMartin Matuska     } else {
2447*d9497217SMartin Matuska       len = (size_t)(p - buf);
2448*d9497217SMartin Matuska     }
2449*d9497217SMartin Matuska 
2450*d9497217SMartin Matuska     buf[len++] = '\n';
2451*d9497217SMartin Matuska 
2452*d9497217SMartin Matuska     if (fwrite(buf, 1, len, fp) < len) {
2453*d9497217SMartin Matuska       return -1;
2454*d9497217SMartin Matuska     }
2455*d9497217SMartin Matuska   }
2456*d9497217SMartin Matuska 
2457*d9497217SMartin Matuska   return 0;
2458*d9497217SMartin Matuska }
2459