summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjoshua <>2025-05-21 08:57:13 +0000
committerjoshua <>2025-05-21 08:57:13 +0000
commit18475d634569c6d51839b297c41b2a4fa487df13 (patch)
tree1fd293c8abe2b36faafb9680953c04e06ca524c7 /src
parentcef53fabc81a75137b27740f687ac1d22bfdfdf2 (diff)
downloadopenbsd-18475d634569c6d51839b297c41b2a4fa487df13.tar.gz
openbsd-18475d634569c6d51839b297c41b2a4fa487df13.tar.bz2
openbsd-18475d634569c6d51839b297c41b2a4fa487df13.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')
-rw-r--r--src/regress/lib/libcrypto/test/test.c225
-rw-r--r--src/regress/lib/libcrypto/test/test.h132
-rw-r--r--src/regress/lib/libcrypto/test/test_util.c51
3 files changed, 408 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
28struct test {
29 struct test *parent;
30 char *name;
31 FILE *out;
32 int skipped;
33 int failed;
34};
35
36static struct test *
37test_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
55struct test *
56test_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
82static void
83test_cleanup(struct test *t)
84{
85 free(t->name);
86 free(t);
87}
88
89int
90test_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
102void
103test_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
112static void
113test_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
119void
120test_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
129static void
130test_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
156void
157test_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
167void
168test_skip(struct test *t, const char *reason)
169{
170 t->skipped = 1;
171 test_printf(t, "%s\n", reason);
172}
173
174void
175test_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
188void
189test_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}
diff --git a/src/regress/lib/libcrypto/test/test.h b/src/regress/lib/libcrypto/test/test.h
new file mode 100644
index 0000000000..e6cfec80ea
--- /dev/null
+++ b/src/regress/lib/libcrypto/test/test.h
@@ -0,0 +1,132 @@
1/* $OpenBSD: test.h,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#ifndef HEADER_TEST_H
19#define HEADER_TEST_H
20
21#include <stdint.h>
22
23struct test;
24
25/*
26 * test_init creates a new root test struct.
27 *
28 * Additional tests may be run under the root test struct by calling test_run.
29 *
30 * If the TEST_VERBOSE environment variable is set and not equal to "0", then
31 * verbose mode will be enabled and all test logs will be written to stderr.
32 */
33struct test *test_init(void);
34
35/*
36 * test_result cleans up after all tests have completed and returns an
37 * appropriate exit code indicating the result of the tests.
38 */
39int test_result(struct test *_t);
40
41/*
42 * test_run_func is an individual test function. It is passed the test struct
43 * and an arbitrary argument which may be passed when test_run is called.
44 */
45typedef void (test_run_func)(struct test *_t, const void *_arg);
46
47/*
48 * test_fail marks the test and its parents as failed.
49 */
50void test_fail(struct test *_t);
51
52/*
53 * test_printf prints a test log message. When in verbose mode, the log message
54 * will be written to stderr, otherwise it will be buffered and only written to
55 * stderr if the test fails.
56 *
57 * This printf will write directly, without any additional formatting.
58 */
59void test_printf(struct test *_t, const char *_fmt, ...);
60 __attribute__((__format__ (printf, 2, 3)))
61 __attribute__((__nonnull__ (2)));
62
63/*
64 * test_logf_internal prints a test log message. When in verbose mode, the
65 * log message will be written to stderr, otherwise it will be buffered and
66 * only written to stderr if the test fails.
67 *
68 * label is an optional label indicating the severity of the log.
69 * func, file and line are used to show where the log comes from and are
70 * automatically set in the test log macros.
71 *
72 * This function should never be called directly.
73 */
74void test_logf_internal(struct test *_t, const char *_label, const char *_func,
75 const char *_file, int _line, const char *_fmt, ...);
76 __attribute__((__format__ (printf, 6, 7)))
77 __attribute__((__nonnull__ (6)));
78
79/*
80 * test_logf prints an informational log message. When in verbose mode, the log
81 * will be written to stderr, otherwise it will be buffered and only written to
82 * stderr if the test fails.
83 */
84#define test_logf(t, fmt, ...) \
85 test_logf_internal(t, NULL, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
86
87/*
88 * test_errorf prints an error message. It will also cause the test to fail.
89 * If the test cannot proceed, it is recommended to return or goto a cleanup
90 * label.
91 *
92 * Tests should not fail-fast if continuing will provide more detailed
93 * information about what is broken.
94 */
95#define test_errorf(t, fmt, ...) \
96 test_logf_internal(t, "ERROR", __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \
97 test_fail(t)
98
99/*
100 * test_skip marks the test as skipped. Once called, the test should return.
101 */
102void test_skip(struct test *_t, const char *_reason);
103
104/*
105 * test_skipf marks the test as skipped with a formatted reason. Once called,
106 * the test should return.
107 */
108void test_skipf(struct test *_t, const char *_fmt, ...);
109 __attribute__((__format__ (printf, 2, 3)))
110 __attribute__((__nonnull__ (2)));
111
112/*
113 * test_run runs a test function. It will create a new test struct with the
114 * given test as the parent. An argument may be provided to pass data to the
115 * test function, otherwise NULL should be passed.
116 *
117 * Each test should have a unique and informational name.
118 */
119void test_run(struct test *_t, const char *_name, test_run_func *_fn, const void *_arg);
120
121/*
122 * test_hexdump prints the given data as hexadecimal.
123 */
124void test_hexdump(struct test *_t, const unsigned char *_buf, size_t _len);
125
126/*
127 * test_hexdiff prints the given data as hexadecimal. If a second comparison
128 * buffer is not NULL, any differing bytes will be marked with an astrix.
129 */
130void test_hexdiff(struct test *_t, const uint8_t *_buf, size_t _len, const uint8_t *_compare);
131
132#endif /* HEADER_TEST_H */
diff --git a/src/regress/lib/libcrypto/test/test_util.c b/src/regress/lib/libcrypto/test/test_util.c
new file mode 100644
index 0000000000..6ecb574788
--- /dev/null
+++ b/src/regress/lib/libcrypto/test/test_util.c
@@ -0,0 +1,51 @@
1/* $OpenBSD: test_util.c,v 1.1 2025/05/21 08:57:13 joshua Exp $ */
2/*
3 * Copyright (c) 2017 Joel Sing <jsing@openbsd.org>
4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <stdio.h>
20#include <stdint.h>
21
22#include "test.h"
23
24void
25test_hexdump(struct test *t, const unsigned char *buf, size_t len)
26{
27 size_t i;
28
29 for (i = 1; i <= len; i++)
30 test_printf(t, " 0x%02x,%s", buf[i - 1], i % 8 ? "" : "\n");
31
32 if ((len % 8) != 0)
33 test_printf(t, "\n");
34}
35
36void
37test_hexdiff(struct test *t, const uint8_t *buf, size_t len, const uint8_t *compare)
38{
39 const char *mark = "", *newline;
40 size_t i;
41
42 for (i = 1; i <= len; i++) {
43 if (compare != NULL)
44 mark = (buf[i - 1] != compare[i - 1]) ? "*" : " ";
45 newline = i % 8 ? "" : "\n";
46 test_printf(t, " %s0x%02x,%s", mark, buf[i - 1], newline);
47 }
48
49 if ((len % 8) != 0)
50 test_printf(t, "\n");
51}