Coverage Report

Created: 2023-12-01 06:05

/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
    size_t enabled_count = 0;
129
138
    for(size_t i = 0; i < tests_len; 
i++137
) {
130
137
        if(enabled_tests[i]) enabled_count++;
131
137
    }
132
1
133
1
    size_t failures = 0;
134
1
    size_t success = 0;
135
138
    for(size_t i = 0; tests[i].name; 
++i137
) {
136
137
        if(enabled_tests[i]) {
137
137
            test_run r = {
138
137
                .entry = tests[i],
139
137
                .verbosity = verbosity,
140
137
                .number = success + failures,
141
137
                .amount = enabled_count};
142
137
143
137
            if(run(r)) {
144
137
                success += 1;
145
137
            } else {
146
0
                failures += 1;
147
0
            }
148
137
        }
149
137
    }
150
1
151
1
    if(success == enabled_count) {
152
1
        switch(verbosity) {
153
1
            case 0:
154
1
                printf("=> %zi/%zi tests passed.\n", success, enabled_count);
155
1
                break;
156
0
            case 1:
157
0
            case 2:
158
0
                printf(
159
0
                    "\033[1;34m=>\033[0m %zi/%zi tests "
160
0
                    "\033[32mpassed\033[0m.\n",
161
0
                    success,
162
0
                    enabled_count);
163
0
                break;
164
0
        }
165
0
    } else {
166
0
        switch(verbosity) {
167
0
            case 0:
168
0
                printf(
169
0
                    "=> %zi/%zi tests passed, %zi failures.\n",
170
0
                    success,
171
0
                    enabled_count,
172
0
                    failures);
173
0
                break;
174
0
            case 1:
175
0
            case 2:
176
0
                printf(
177
0
                    "\033[1;34m=>\033[0m %zi/%zi tests "
178
0
                    "\033[32mpassed\033[0m, "
179
0
                    "%zi \033[31mfailure\033[0m.\n",
180
0
                    success,
181
0
                    enabled_count,
182
0
                    failures);
183
0
                break;
184
1
        }
185
1
    }
186
1
187
1
    if(failures) {
188
0
        return EXIT_FAILURE;
189
1
    } else {
190
1
        return EXIT_SUCCESS;
191
1
    }
192
1
}
193
194
137
bool run(test_run test) {
195
137
    switch(test.verbosity) {
196
137
        case 0:
197
137
            // not a tty, regular
198
137
            printf(
199
137
                "[%i/%i] running %s\n",
200
137
                test.number + 1,
201
137
                test.amount,
202
137
                test.entry.name);
203
137
            fflush(stdout);
204
137
            break;
205
0
        case 1:
206
0
            // tty, regular
207
0
            printf(
208
0
                "\033[1K\r[%i/%i] running %s",
209
0
                test.number + 1,
210
0
                test.amount,
211
0
                test.entry.name);
212
0
            fflush(stdout);
213
0
            break;
214
0
        default:
215
0
            break;
216
137
    }
217
137
218
137
    clock_t before = clock();
219
137
    test_result ret = test.entry.func();
220
137
    clock_t total = clock() - before;
221
137
222
137
    double time = total / (CLOCKS_PER_SEC * 1.0);
223
137
224
137
    switch(test.verbosity) {
225
137
        case 0:
226
137
            if(!ret.ok) {
227
0
                printf(
228
0
                    "  %s failed at %s:%zi\n",
229
0
                    ret.assertion,
230
0
                    ret.func,
231
0
                    ret.line);
232
137
            } else if(time > 0.1) {
233
3
                printf("=> test run took too long (%4.3lfs).\n", time);
234
3
            }
235
137
            break;
236
0
        case 1:
237
0
            if(!ret.ok) {
238
0
                printf(
239
0
                    "\n  \033[0;33m%s\033[0m \033[0;31mfailed\033[0m at "
240
0
                    "%s:%zi\n",
241
0
                    ret.assertion,
242
0
                    ret.func,
243
0
                    ret.line);
244
0
            } else if(time > 0.1) {
245
0
                printf("\n  \033[0;33mtest run took %4.3lfs.\033[0m\n", time);
246
0
            } else if((test.number + 1) == test.amount) {
247
0
                printf("\n");
248
0
            }
249
0
            break;
250
0
        case 2:
251
0
            if(ret.ok) {
252
0
                printf(
253
0
                    "%-30s \033[0;32mpassed\033[0m in %s%4.3lfs\033[0m.\n",
254
0
                    test.entry.name,
255
0
                    (time > 0.1) ? "\033[0;33m" : "",
256
0
                    time);
257
0
            } else {
258
0
                printf(
259
0
                    "%-30s \033[0;31mfailed\033[0m in %4.3lfs.\n",
260
0
                    test.entry.name,
261
0
                    time);
262
0
                printf(
263
0
                    "    \033[0;31m%s\033[0m failed at %s:%zi\n",
264
0
                    ret.assertion,
265
0
                    ret.func,
266
0
                    ret.line);
267
0
            }
268
0
            break;
269
0
        default:
270
0
            break;
271
137
    }
272
137
273
137
    return ret.ok;
274
137
}