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(¶ms_l, ¶ms, 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(¶ms_l, ¶ms, 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(¶ms_l, ¶ms,
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