1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "test_util.h"
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <io.h>
#include <setjmpex.h>
#include <signal.h>
#include <windows.h>
bool SIGABRT_RAISED = false;
jmp_buf env;
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
bool isclose(double d1, double d2) {
return fabs(d1 - d2) < 1e-7;
}
bool streq(const char *s1, const char *s2) {
return strcmp(s1, s2) == 0;
}
bool strarray_eq(strarray_t *arr1, strarray_t *arr2) {
if (arr1->length != arr2->length) {
return false;
}
for (size_t i = 0; i < arr1->length; i++) {
if (!streq(arr1->data[i], arr2->data[i])) {
return false;
}
}
return true;
}
bool strarray_matches(strarray_t *arr, const char *expected[], size_t length) {
if (arr->length != length) {
return false;
}
for (size_t i = 0; i < length; i++) {
if (!streq(arr->data[i], expected[i])) {
return false;
}
}
return true;
}
int wrap_strcmp(const void *s1, const void *s2) {
return strcmp(* (char **) s1, * (char **) s2);
}
bool strarray_set_matches(strarray_t *arr, const char *expected[], size_t length) {
if (arr->length != length) {
return false;
}
char **exp_sort = malloc(sizeof(char *) * length);
memcpy(exp_sort, expected, sizeof(char *) * length);
qsort(exp_sort, length, sizeof(char *), wrap_strcmp);
char **act_sort = malloc(sizeof(char *) * length);
memcpy(act_sort, arr->data, sizeof(char *) * length);
qsort(act_sort, length, sizeof(char *), wrap_strcmp);
bool ret = true;
for (size_t i = 0; i < length; i++) {
if (!streq(exp_sort[i], act_sort[i])) {
ret = false;
break;
}
}
free(exp_sort);
free(act_sort);
return ret;
}
strarray_t *strarray_from_array(const char *arr[], size_t length) {
strarray_t *strarr = strarray_init(length);
for (size_t i = 0; i < length; i++) {
strarr->data[i] = strdup(arr[i]);
}
return strarr;
}
void read_testname(char *filename, char *testname, size_t testname_size) {
FILE *f = fopen(filename, "r");
if (f == NULL) {
printf("Couldn't open file %s\n", filename);
exit(1);
}
// Generate format string
char fmt[12];
snprintf(fmt, sizeof(fmt), "%%%lus", (unsigned long) testname_size - 1);
fscanf(f, fmt, testname);
fclose(f);
}
bool contains_any(char **testnames, char *test) {
for (size_t i = 0; testnames[i] != NULL; i++) {
if (strstr(test, testnames[i]) != NULL) {
return true;
}
}
return false;
}
#ifdef _WIN32
void signal_handler(int signum) {
if (signum == SIGABRT) {
SIGABRT_RAISED = true;
// Unregister self, and tell kernel to ignore
signal(signum, SIG_DFL);
// Jump out to avoid abort call after signal_handler returns
// Safe-ish, since our code is (probably) dead simple?
// Some comments
// https://gist.github.com/kekyo/cc9bace942b8c2aa2484431e047d267d
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=vs-2019#remarks
longjmp(env, 1);
}
}
#endif
bool test_assert_fail(void (*run)(void *aux), void *aux) {
// Windows can't use POSIX apis
#ifndef _WIN32
if (fork()) { // parent process
int *status = malloc(sizeof(*status));
assert(status != NULL);
wait(status);
// Check whether child process aborted
bool aborted = WIFSIGNALED(*status) && WTERMSIG(*status) == SIGABRT;
free(status);
return aborted;
} else { // child process
freopen("/dev/null", "w", stderr); // suppress assertion message
run(aux);
exit(0); // should not be reached
}
#else
// NOTE: this is super sketch. A "better" alternative may be to just not run
// at all.
// Register signal handler to trap SIGABRT from assert
signal(SIGABRT, signal_handler);
SIGABRT_RAISED = false;
// Store another file descriptor for stderr so that we can get it back later
int cstderr = _dup(_fileno(stderr));
// Run expected failure, jumping back when SIGABRT is raised
if (setjmp(env) == 0) {
// Suppress assertion message by piping to NUL
freopen("NUL", "w", stderr);
run(aux);
}
// Since we are in the same process, undo the suppression by reassigning
// stderr to the saved descriptor
_dup2(cstderr, _fileno(stderr));
// Return whether we failed or not.
return SIGABRT_RAISED;
#endif
}