Coverage Report

Created: 2024-04-19 06:04

/builds/xfbs/passgen/src/tests/tests.c
Line
Count
Source (jump to first uncovered line)
1
#include "tests.h"
2
#include <getopt.h>
3
#include <stdbool.h>
4
#include <stdlib.h>
5
#include <string.h>
6
#include <time.h>
7
#include <unistd.h>
8
9
test_result test_ok = {.ok = true};
10
11
// check if a string has the given prefix
12
0
static inline bool strprefix(const char *prefix, const char *string) {
13
0
    for(size_t offset = 0; prefix[offset]; offset++) {
14
0
        if(prefix[offset] != string[offset]) {
15
0
            return false;
16
0
        }
17
0
    }
18
0
19
0
    return true;
20
0
}
21
22
// swap memory of a given size
23
0
static inline void memswap(void *a, void *b, size_t size) {
24
0
    char data[size];
25
0
26
0
    memcpy(data, a, size);
27
0
    memcpy(a, b, size);
28
0
    memcpy(b, data, size);
29
0
}
30
31
// shuffle the list of tests
32
0
static inline void shuffle(test_entry *tests) {
33
0
    for(size_t i = 0; i < tests_len - 1; i++) {
34
0
        size_t j = i + rand() / (RAND_MAX / (tests_len - i) + 1);
35
0
        memswap(&tests[i], &tests[j], sizeof(test_entry));
36
0
    }
37
0
}
38
39
0
static inline void filter(bool *enabled, const char *name) {
40
0
    for(size_t i = 0; i < tests_len; i++) {
41
0
        if(strprefix(name, tests[i].name)) {
42
0
            enabled[i] = true;
43
0
        }
44
0
    }
45
0
}
46
47
0
static void show_help(const char *name) {
48
0
    fprintf(
49
0
        stderr,
50
0
        "%s%s",
51
0
        name,
52
0
        " [OPTIONS] [FILTERS...]\n"
53
0
        "Runs unit tests for passgen and reports results.\n\n"
54
0
        "Options\n"
55
0
        "  -h, --help\n"
56
0
        "    Shows this help text\n"
57
0
        "  -v, --verbose\n"
58
0
        "    Shows more verbose information\n"
59
0
        "  -s, --shuffle\n"
60
0
        "    Randomises the order in which tests are run\n\n"
61
0
        "Filters\n"
62
0
        "  Match based on prefix. A filter such as 'random' would match any "
63
0
        "test "
64
0
        "case\n"
65
0
        "  beginning with random, such as random_init, random_u8, etc.\n");
66
0
}
67
68
1
int main(int argc, char *argv[]) {
69
1
    // initialise shitty pseudorandom number generator
70
1
    srand(time(NULL));
71
1
    int verbosity = isatty(fileno(stdout));
72
1
73
1
    static struct option long_options[] = {
74
1
        {"verbose", no_argument, 0, 'v'},
75
1
        {"shuffle", no_argument, 0, 's'},
76
1
        {"help", no_argument, 0, 'h'},
77
1
        {0, 0, 0, 0}};
78
1
79
1
    while(1) {
80
1
        int option_index = 0;
81
1
82
1
        int c = getopt_long(argc, argv, "vsh", long_options, &option_index);
83
1
84
1
        /* Detect the end of the options. */
85
1
        if(c == -1) break;
86
0
87
0
        switch(c) {
88
0
            case 0:
89
0
                /* If this option set a flag, do nothing else now. */
90
0
                if(long_options[option_index].flag != 0) break;
91
0
                printf("option %s", long_options[option_index].name);
92
0
                if(optarg) printf(" with arg %s", optarg);
93
0
                printf("\n");
94
0
                break;
95
0
96
0
            case 's':
97
0
                shuffle(tests);
98
0
                break;
99
0
100
0
            case 'v':
101
0
                verbosity = 2;
102
0
                break;
103
0
104
0
            case 'h':
105
0
                show_help(argv[0]);
106
0
                return EXIT_SUCCESS;
107
0
108
0
            case '?':
109
0
                show_help(argv[0]);
110
0
                return EXIT_FAILURE;
111
0
112
0
            default:
113
0
                abort();
114
0
        }
115
0
    }
116
1
117
1
    bool enabled_tests[tests_len];
118
1
    if(optind < argc) {
119
0
        memset(enabled_tests, 0, sizeof(enabled_tests));
120
0
121
0
        for(int i = optind; i < argc; i++) {
122
0
            filter(enabled_tests, argv[i]);
123
0
        }
124
1
    } else {
125
1
        memset(enabled_tests, 1, sizeof(enabled_tests));
126
1
    }
127
1
128
1
    // determine max test name width and count of enabled tests.
129
1
    int width = 20;
130
1
    size_t enabled_count = 0;
131
139
    for(size_t i = 0; i < tests_len; 
i++138
) {
132
138
        if(enabled_tests[i]) {
133
138
            enabled_count++;
134
138
            int len = strlen(tests[i].name);
135
138
            if(len > width) 
width = len3
;
136
138
        }
137
138
    }
138
1
139
1
    size_t failures = 0;
140
1
    size_t success = 0;
141
139
    for(size_t i = 0; tests[i].name; 
++i138
) {
142
138
        if(enabled_tests[i]) {
143
138
            test_run r = {
144
138
                .entry = tests[i],
145
138
                .verbosity = verbosity,
146
138
                .number = success + failures,
147
138
                .amount = enabled_count};
148
138
149
138
            if(run(r, width)) {
150
138
                success += 1;
151
138
            } else {
152
0
                failures += 1;
153
0
            }
154
138
        }
155
138
    }
156
1
157
1
    if(success == enabled_count) {
158
1
        switch(verbosity) {
159
1
            case 0:
160
1
                printf("=> %zi/%zi tests passed.\n", success, enabled_count);
161
1
                break;
162
0
            case 1:
163
0
            case 2:
164
0
                printf(
165
0
                    "\033[1;34m=>\033[0m %zi/%zi tests "
166
0
                    "\033[32mpassed\033[0m.\n",
167
0
                    success,
168
0
                    enabled_count);
169
0
                break;
170
0
        }
171
0
    } else {
172
0
        switch(verbosity) {
173
0
            case 0:
174
0
                printf(
175
0
                    "=> %zi/%zi tests passed, %zi failures.\n",
176
0
                    success,
177
0
                    enabled_count,
178
0
                    failures);
179
0
                break;
180
0
            case 1:
181
0
            case 2:
182
0
                printf(
183
0
                    "\033[1;34m=>\033[0m %zi/%zi tests "
184
0
                    "\033[32mpassed\033[0m, "
185
0
                    "%zi \033[31mfailure\033[0m.\n",
186
0
                    success,
187
0
                    enabled_count,
188
0
                    failures);
189
0
                break;
190
1
        }
191
1
    }
192
1
193
1
    if(failures) {
194
0
        return EXIT_FAILURE;
195
1
    } else {
196
1
        return EXIT_SUCCESS;
197
1
    }
198
1
}
199
200
138
bool run(test_run test, int padding) {
201
138
    switch(test.verbosity) {
202
138
        case 0:
203
138
            // not a tty, regular
204
138
            printf(
205
138
                "[%i/%i] running %s\n",
206
138
                test.number + 1,
207
138
                test.amount,
208
138
                test.entry.name);
209
138
            fflush(stdout);
210
138
            break;
211
0
        case 1:
212
0
            // tty, regular
213
0
            printf(
214
0
                "\033[1K\r[%i/%i] running %s",
215
0
                test.number + 1,
216
0
                test.amount,
217
0
                test.entry.name);
218
0
            fflush(stdout);
219
0
            break;
220
0
        default:
221
0
            break;
222
138
    }
223
138
224
138
    clock_t before = clock();
225
138
    test_result ret = test.entry.func();
226
138
    clock_t total = clock() - before;
227
138
228
138
    double time = total / (CLOCKS_PER_SEC * 1.0);
229
138
230
138
    switch(test.verbosity) {
231
138
        case 0:
232
138
            if(!ret.ok) {
233
0
                printf(
234
0
                    "  %s failed at %s:%zi\n",
235
0
                    ret.assertion,
236
0
                    ret.func,
237
0
                    ret.line);
238
138
            } else if(time > 0.1) {
239
3
                printf("=> test run took too long (%4.3lfs).\n", time);
240
3
            }
241
138
            break;
242
0
        case 1:
243
0
            if(!ret.ok) {
244
0
                printf(
245
0
                    "\n  \033[0;33m%s\033[0m \033[0;31mfailed\033[0m at "
246
0
                    "%s:%zi\n",
247
0
                    ret.assertion,
248
0
                    ret.func,
249
0
                    ret.line);
250
0
            } else if(time > 0.1) {
251
0
                printf("\n  \033[0;33mtest run took %4.3lfs.\033[0m\n", time);
252
0
            } else if((test.number + 1) == test.amount) {
253
0
                printf("\n");
254
0
            }
255
0
            break;
256
0
        case 2:
257
0
            if(ret.ok) {
258
0
                printf(
259
0
                    "%-*s \033[0;32mpassed\033[0m in %s%4.3lfs\033[0m.\n",
260
0
                    padding,
261
0
                    test.entry.name,
262
0
                    (time > 0.1) ? "\033[0;33m" : "",
263
0
                    time);
264
0
            } else {
265
0
                printf(
266
0
                    "%-*s \033[0;31mfailed\033[0m in %4.3lfs.\n",
267
0
                    padding,
268
0
                    test.entry.name,
269
0
                    time);
270
0
                printf(
271
0
                    "    \033[0;31m%s\033[0m failed at %s:%zi\n",
272
0
                    ret.assertion,
273
0
                    ret.func,
274
0
                    ret.line);
275
0
            }
276
0
            break;
277
0
        default:
278
0
            break;
279
138
    }
280
138
281
138
    return ret.ok;
282
138
}