2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2024 Alessio Chiapperini
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #define US_PER_SECOND 1000000
39 #define SEED_CONSTANT 0xA3
40 #define UNI_32BIT_INV 2.3283064365386962890625e-10
41 #define VNI_32BIT_INV 4.6566128730773925781250e-10 /* UNI_32BIT_INV * 2 */
43 struct harness_report
{
44 struct timeval total_time
;
45 struct timeval suite_time
;
53 timeval_sub(struct timeval t1
, struct timeval t2
, struct timeval
*res
)
55 res
->tv_usec
= t2
.tv_usec
- t1
.tv_usec
;
56 res
->tv_sec
= t2
.tv_sec
- t1
.tv_sec
;
57 if (res
->tv_sec
> 0 && res
->tv_usec
< 0) {
58 res
->tv_usec
+= US_PER_SECOND
;
60 } else if (res
->tv_sec
< 0 && res
->tv_usec
> 0) {
61 res
->tv_usec
-= US_PER_SECOND
;
67 timeval_add(struct timeval t1
, struct timeval t2
, struct timeval
*res
)
69 res
->tv_usec
= t2
.tv_usec
+ t1
.tv_usec
;
70 res
->tv_sec
= t2
.tv_sec
+ t1
.tv_sec
;
71 if (res
->tv_usec
>= US_PER_SECOND
) {
72 res
->tv_usec
-= US_PER_SECOND
;
74 } else if (res
->tv_usec
<= -US_PER_SECOND
) {
75 res
->tv_usec
+= US_PER_SECOND
;
80 static uint32_t state
[2] = {0, SEED_CONSTANT
};
85 state
[0] = (uint32_t)time(0);
86 state
[1] = SEED_CONSTANT
;
89 static inline uint32_t
90 rotl(const uint32_t x
, int k
)
92 return ((x
<< k
) | (x
>> (32 - k
)));
96 int_to_float(uint32_t rand
)
98 union { uint32_t u32
; float f
; } u
= { .u32
= rand
>> 9 | 0x3f800000 };
105 const uint32_t s0
= state
[0];
106 uint32_t s1
= state
[1];
107 const uint32_t result
= rotl(s0
* 0x9E3779BB, 5) * 5;
110 state
[0] = rotl(s0
, 26) ^ s1
^ (s1
<< 9); /* a, b */
111 state
[1] = rotl(s1
, 13); /* c */
117 random_bounded(uint32_t range
)
119 uint32_t x
= random_next();
120 uint64_t m
= (uint64_t)x
* (uint64_t)range
;
121 uint32_t l
= (uint32_t)m
;
132 m
= (uint64_t)x
* (uint64_t)range
;
142 return (int_to_float(random_next()));
145 static struct harness_cfg
*cfg
= NULL
;
146 static struct harness_suite test_suites
[MAX_SUITES
] = {0};
149 harness_init(struct harness_cfg
*harness_cfg
)
151 if (harness_cfg
== NULL
) {
152 (void)fprintf(stderr
, "harness: harness_cfg cannot be NULL\n");
153 return (HARNESS_FAIL
);
161 if (cfg
->seed
== 0) {
162 cfg
->seed
= random_next();
165 return (HARNESS_PASS
);
169 harness_add_test(char *test_suite
, char *test_name
, test_function
*test
,
170 setup_function
*setup
, teardown_function
*teardown
, void *user_data
)
174 int nsuites
= cfg
->nsuites
;
177 if (nsuites
>= MAX_SUITES
) {
178 (void)fprintf(stderr
, "harness: Unable to add more suites\n");
179 return (HARNESS_FAIL
);
182 for (suite_idx
= 0; suite_idx
< nsuites
; suite_idx
++) {
183 if (test_suites
[suite_idx
].suite_name
== NULL
) {
184 (void)fprintf(stderr
, "harness: suite name is NULL\n");
185 return (HARNESS_FAIL
);
188 if (strcmp(test_suite
,
189 test_suites
[suite_idx
].suite_name
) == 0) {
197 test_suites
[nsuites
].suite_name
= test_suite
;
200 ntests
= test_suites
[suite_idx
].ntests
;
201 if (ntests
>= MAX_TESTS
) {
202 (void)fprintf(stderr
, "harness: Unable to add more tests "
203 "to suite %s\n", test_suites
[nsuites
].suite_name
);
204 return (HARNESS_FAIL
);
206 test_suites
[suite_idx
].tests
[ntests
].test_function
= test
;
207 test_suites
[suite_idx
].tests
[ntests
].test_name
= test_name
;
208 test_suites
[suite_idx
].tests
[ntests
].user_data
= user_data
;
209 test_suites
[suite_idx
].tests
[ntests
].setup
= setup
;
210 test_suites
[suite_idx
].tests
[ntests
].teardown
= teardown
;
211 test_suites
[suite_idx
].ntests
++;
214 /* Increment the number of suites only if the suite is new */
219 return (HARNESS_PASS
);
223 harness_add_suite(char *test_suite
, struct harness_test
*tests
) {
224 int res
= HARNESS_PASS
;
225 for (struct harness_test
*t
= tests
; t
!= NULL
&&
226 t
->test_function
!= NULL
; t
++) {
227 res
= harness_add_test(test_suite
, t
->test_name
,
228 t
->test_function
, t
->setup
, t
->teardown
, t
->user_data
);
231 return (HARNESS_FAIL
);
239 harness_list_tests(char *test_suite
)
243 if (test_suite
!= NULL
) {
244 for (s
= 0; s
< cfg
->nsuites
; s
++) {
245 if (!strcmp(test_suites
[s
].suite_name
, test_suite
)) {
249 (void)fprintf(stdout
, "%s\n", test_suite
);
251 for (int t
= 0; t
< test_suites
[s
].ntests
; t
++) {
252 (void)fprintf(stdout
, " %s\n",
253 test_suites
[s
].tests
[t
].test_name
);
256 for (s
= 0; s
< cfg
->nsuites
; s
++) {
257 (void)fprintf(stdout
, "%s\n",
258 test_suites
[s
].suite_name
);
259 for (int t
= 0; t
< test_suites
[s
].ntests
; t
++) {
260 (void)fprintf(stdout
, " %s\n",
261 test_suites
[s
].tests
[t
].test_name
);
268 harness_run_test(int s
, int t
, struct harness_report
*report
)
270 struct timeval test_time
= {0};
271 struct timeval start_time
= {0};
272 struct timeval end_time
= {0};
277 (void)fprintf(stdout
, "[ %-8s ] %s.%s\n", "RUN",
278 test_suites
[s
].suite_name
,
279 test_suites
[s
].tests
[t
].test_name
);
281 (void)memset(&test_time
, 0, sizeof(struct timeval
));
283 if (test_suites
[s
].tests
[t
].setup
!= NULL
) {
284 res
= test_suites
[s
].tests
[t
].setup(
285 test_suites
[s
].tests
[t
].user_data
);
286 if (res
!= HARNESS_PASS
) {
291 gettimeofday(&start_time
, NULL
);
292 res
= test_suites
[s
].tests
[t
].test_function(
293 test_suites
[s
].tests
[t
].user_data
);
294 gettimeofday(&end_time
, NULL
);
295 timeval_sub(start_time
, end_time
, &test_time
);
317 (void)fprintf(stdout
, "[ %8s ] %s.%s "
320 test_suites
[s
].suite_name
,
321 test_suites
[s
].tests
[t
].test_name
,
322 (test_time
.tv_usec
/ 1000) +
323 test_time
.tv_sec
* 1000);
324 timeval_add(report
->suite_time
, test_time
, &report
->suite_time
);
326 if (test_suites
[s
].tests
[t
].teardown
!= NULL
) {
327 test_suites
[s
].tests
[t
].teardown(
328 test_suites
[s
].tests
[t
].user_data
);
335 harness_run(void *user_data
)
337 struct harness_report report
= {0};
339 if (cfg
->setup
!= NULL
) {
340 cfg
->setup(user_data
);
341 (void)fprintf(stdout
, "[----------] Test environment "
345 (void)fprintf(stdout
, "[==========] Running %d test cases from %d "
346 "test suites with seed 0x%08" PRIx32
".\n", cfg
->ntests
,
347 cfg
->nsuites
, cfg
->seed
);
349 for (int s
= 0; s
< cfg
->nsuites
; s
++) {
350 (void)fprintf(stdout
, "[----------] %d tests from %s.\n",
351 test_suites
[s
].ntests
, test_suites
[s
].suite_name
);
353 (void)memset(&report
.suite_time
, 0, sizeof(struct timeval
));
354 for (int t
= 0; t
< test_suites
[s
].ntests
; t
++) {
355 (void)harness_run_test(s
, t
, &report
);
357 (void)fprintf(stdout
, "[----------] %d tests from %s (%ld ms "
359 test_suites
[s
].ntests
, test_suites
[s
].suite_name
,
360 (report
.suite_time
.tv_usec
/ 1000) +
361 report
.suite_time
.tv_sec
* 1000);
362 timeval_add(report
.total_time
, report
.suite_time
,
366 (void)fprintf(stdout
, "[==========] %d test cases from %d test suites "
367 "ran (%ld ms total).\n", cfg
->ntests
, cfg
->nsuites
,
368 (report
.total_time
.tv_usec
/ 1000) +
369 report
.total_time
.tv_sec
* 1000);
370 (void)fprintf(stdout
, "[ PASSED ] %d tests.\n", report
.npass
);
371 (void)fprintf(stdout
, "[ FAILED ] %d tests.\n", report
.nfail
);
372 (void)fprintf(stdout
, "[ SKIPPED ] %d tests.\n", report
.nskip
);
373 (void)fprintf(stdout
, "[ ERRORS ] %d tests.\n", report
.nerror
);
375 if (cfg
->teardown
!= NULL
) {
376 cfg
->teardown(user_data
);
377 (void)fprintf(stdout
, "[----------] Test environment "
381 if (report
.nfail
!= 0) {
382 return (EXIT_FAILURE
);
385 return (EXIT_SUCCESS
);
389 harness_destroy(struct harness_cfg
*harness_cfg
)
391 for (int i
= 0; i
< cfg
->nsuites
; i
++) {
392 (void)memset(test_suites
[i
].tests
, 0,
393 sizeof(struct harness_test
));
395 (void)memset(test_suites
, 0, sizeof(test_suites
));
398 *harness_cfg
= (struct harness_cfg
) {0};