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