diff options
author | joshua <> | 2025-05-21 08:57:13 +0000 |
---|---|---|
committer | joshua <> | 2025-05-21 08:57:13 +0000 |
commit | c149f5ceaf380116e82c4ff7b1292c52e6bf5b07 (patch) | |
tree | 1fd293c8abe2b36faafb9680953c04e06ca524c7 /src/regress/lib/libcrypto/test/test.c | |
parent | 90e88c12b98a159dbce4ca3352ec5a9d8ddabed9 (diff) | |
download | openbsd-c149f5ceaf380116e82c4ff7b1292c52e6bf5b07.tar.gz openbsd-c149f5ceaf380116e82c4ff7b1292c52e6bf5b07.tar.bz2 openbsd-c149f5ceaf380116e82c4ff7b1292c52e6bf5b07.zip |
Add initial regress test framework
Add a test framework for use in LibreSSL regression tests. This test
framework aims to be as lightweight and as simple to use as possible.
The design is mostly inspired by Go's test system, and aims to be a
drop-in utility in most existing regress tests.
ok jsing tb beck
Diffstat (limited to 'src/regress/lib/libcrypto/test/test.c')
-rw-r--r-- | src/regress/lib/libcrypto/test/test.c | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/test/test.c b/src/regress/lib/libcrypto/test/test.c new file mode 100644 index 0000000000..b48711919d --- /dev/null +++ b/src/regress/lib/libcrypto/test/test.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* $OpenBSD: test.c,v 1.1 2025/05/21 08:57:13 joshua Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2025 Joshua Sing <joshua@joshuasing.dev> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #include <err.h> | ||
19 | #include <libgen.h> | ||
20 | #include <stdarg.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <unistd.h> | ||
25 | |||
26 | #include "test.h" | ||
27 | |||
28 | struct test { | ||
29 | struct test *parent; | ||
30 | char *name; | ||
31 | FILE *out; | ||
32 | int skipped; | ||
33 | int failed; | ||
34 | }; | ||
35 | |||
36 | static struct test * | ||
37 | test_new(struct test *pt, const char *name) | ||
38 | { | ||
39 | struct test *t; | ||
40 | |||
41 | if ((t = calloc(1, sizeof(*t))) == NULL) | ||
42 | err(1, "calloc"); | ||
43 | |||
44 | if (name != NULL) { | ||
45 | if ((t->name = strdup(name)) == NULL) | ||
46 | err(1, "strdup"); | ||
47 | } | ||
48 | |||
49 | t->out = pt->out; | ||
50 | t->parent = pt; | ||
51 | |||
52 | return t; | ||
53 | } | ||
54 | |||
55 | struct test * | ||
56 | test_init(void) | ||
57 | { | ||
58 | struct test *t; | ||
59 | char *tmp_file; | ||
60 | int out_fd; | ||
61 | char *v; | ||
62 | |||
63 | t = test_new(NULL, NULL); | ||
64 | t->out = stderr; | ||
65 | |||
66 | if (((v = getenv("TEST_VERBOSE")) != NULL) && strcmp(v, "0") != 0) | ||
67 | return t; | ||
68 | |||
69 | /* Create a temporary file for logging in non-verbose mode */ | ||
70 | if ((tmp_file = strdup("/tmp/libressl-test.XXXXXXXX")) == NULL) | ||
71 | err(1, "strdup"); | ||
72 | if ((out_fd = mkstemp(tmp_file)) == -1) | ||
73 | err(1, "mkstemp"); | ||
74 | |||
75 | unlink(tmp_file); | ||
76 | if ((t->out = fdopen(out_fd, "w+")) == NULL) | ||
77 | err(1, "fdopen"); | ||
78 | |||
79 | return t; | ||
80 | } | ||
81 | |||
82 | static void | ||
83 | test_cleanup(struct test *t) | ||
84 | { | ||
85 | free(t->name); | ||
86 | free(t); | ||
87 | } | ||
88 | |||
89 | int | ||
90 | test_result(struct test *t) | ||
91 | { | ||
92 | int failed = t->failed; | ||
93 | |||
94 | if (t->parent == NULL && t->out != stderr) | ||
95 | fclose(t->out); | ||
96 | |||
97 | test_cleanup(t); | ||
98 | |||
99 | return failed; | ||
100 | } | ||
101 | |||
102 | void | ||
103 | test_fail(struct test *t) | ||
104 | { | ||
105 | t->failed = 1; | ||
106 | |||
107 | /* Also fail parent. */ | ||
108 | if (t->parent != NULL) | ||
109 | test_fail(t->parent); | ||
110 | } | ||
111 | |||
112 | static void | ||
113 | test_vprintf(struct test *t, const char *fmt, va_list ap) | ||
114 | { | ||
115 | if (vfprintf(t->out, fmt, ap) == -1) | ||
116 | err(1, "vfprintf"); | ||
117 | } | ||
118 | |||
119 | void | ||
120 | test_printf(struct test *t, const char *fmt, ...) | ||
121 | { | ||
122 | va_list ap; | ||
123 | |||
124 | va_start(ap, fmt); | ||
125 | test_vprintf(t, fmt, ap); | ||
126 | va_end(ap); | ||
127 | } | ||
128 | |||
129 | static void | ||
130 | test_vlogf_internal(struct test *t, const char *label, const char *func, | ||
131 | const char *file, int line, const char *fmt, va_list ap) | ||
132 | { | ||
133 | char *msg = NULL; | ||
134 | char *l = ": "; | ||
135 | const char *filename; | ||
136 | |||
137 | if (label == NULL) { | ||
138 | label = ""; | ||
139 | l = ""; | ||
140 | } | ||
141 | |||
142 | if (vasprintf(&msg, fmt, ap) == -1) | ||
143 | err(1, "vasprintf"); | ||
144 | |||
145 | if ((filename = strrchr(file, '/')) != NULL) | ||
146 | filename++; | ||
147 | else | ||
148 | filename = file; | ||
149 | |||
150 | test_printf(t, "%s [%s:%d]%s%s: %s\n", | ||
151 | func, filename, line, l, label, msg); | ||
152 | |||
153 | free(msg); | ||
154 | } | ||
155 | |||
156 | void | ||
157 | test_logf_internal(struct test *t, const char *label, const char *func, | ||
158 | const char *file, int line, const char *fmt, ...) | ||
159 | { | ||
160 | va_list ap; | ||
161 | |||
162 | va_start(ap, fmt); | ||
163 | test_vlogf_internal(t, label, func, file, line, fmt, ap); | ||
164 | va_end(ap); | ||
165 | } | ||
166 | |||
167 | void | ||
168 | test_skip(struct test *t, const char *reason) | ||
169 | { | ||
170 | t->skipped = 1; | ||
171 | test_printf(t, "%s\n", reason); | ||
172 | } | ||
173 | |||
174 | void | ||
175 | test_skipf(struct test *t, const char *fmt, ...) | ||
176 | { | ||
177 | va_list ap; | ||
178 | |||
179 | t->skipped = 1; | ||
180 | |||
181 | va_start(ap, fmt); | ||
182 | test_vprintf(t, fmt, ap); | ||
183 | if (fputc('\n', t->out) == EOF) | ||
184 | err(1, "fputc"); | ||
185 | va_end(ap); | ||
186 | } | ||
187 | |||
188 | void | ||
189 | test_run(struct test *pt, const char *name, test_run_func *fn, const void *arg) | ||
190 | { | ||
191 | struct test *t = test_new(pt, name); | ||
192 | char *status = "PASS"; | ||
193 | char buf[1024]; | ||
194 | size_t buflen; | ||
195 | int ferr; | ||
196 | |||
197 | /* Run test */ | ||
198 | test_printf(t, "=== RUN %s\n", t->name); | ||
199 | fn(t, arg); | ||
200 | |||
201 | if (t->skipped) | ||
202 | status = "SKIP"; | ||
203 | if (t->failed) | ||
204 | status = "FAIL"; | ||
205 | |||
206 | test_printf(t, "--- %s: %s\n\n", status, t->name); | ||
207 | |||
208 | /* Print result of test */ | ||
209 | if (t->failed && t->out != stderr) { | ||
210 | /* Copy logs to stderr */ | ||
211 | rewind(t->out); | ||
212 | while ((buflen = fread(buf, 1, sizeof(buf), t->out)) > 0) | ||
213 | fwrite(buf, 1, buflen, stderr); | ||
214 | if ((ferr = ferror(t->out)) != 0) | ||
215 | errx(1, "ferror: %d", ferr); | ||
216 | } | ||
217 | |||
218 | if (t->out != NULL && t->out != stderr) { | ||
219 | /* Reset output file */ | ||
220 | rewind(t->out); | ||
221 | ftruncate(fileno(t->out), 0); | ||
222 | } | ||
223 | |||
224 | test_cleanup(t); | ||
225 | } | ||