summaryrefslogtreecommitdiffstats
path: root/tests/test.h
blob: ca349b05c62e457079bfad5e4dd239bd04e95795 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* PipeWire
 *
 * Copyright © 2021 Red Hat, Inc.
 * Copyright © 2021 Collabora Ltd.
 *
 * SPDX-License-Identifier: MIT
 */

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>

#ifdef __GNUC__
#define TEST_NORETURN __attribute__ ((noreturn))
#define TEST_LIKELY(x) (__builtin_expect(!!(x),1))
#else
#define TEST_NORETURN
#define TEST_LIKELY(x) (x)
#endif

enum {
        TEST_PASS = EXIT_SUCCESS,
        TEST_FAIL = EXIT_FAILURE,
        TEST_SKIP = 77,
};

#define test_log(...) fprintf(stderr, __VA_ARGS__)
#define test_vlog(format_, args_) vfprintf(stderr, format_, args_)

static inline bool _test_streq(const char *s1, const char *s2) {
        return TEST_LIKELY(s1 && s2) ? strcmp(s1, s2) == 0 : s1 == s2;
}

TEST_NORETURN
static inline void _test_fail(
                const char *file, int line, const char *func,
                const char *message) {
        test_log("FAILED: %s\n", message);
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

TEST_NORETURN
static inline void _test_fail_comparison_bool(
                const char *file, int line, const char *func,
                const char *operator, bool a, bool b,
                const char *astr, const char *bstr) {
        test_log("FAILED COMPARISON: %s %s %s\n", astr, operator, bstr);
        test_log("Resolved to: %s %s %s\n", a ? "true" : "false", operator,
                        b ? "true" : "false");
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

TEST_NORETURN
static inline void _test_fail_comparison_int(
                const char *file, int line, const char *func,
                const char *operator, int a, int b,
                const char *astr, const char *bstr) {
        test_log("FAILED COMPARISON: %s %s %s\n", astr, operator, bstr);
        test_log("Resolved to: %d %s %d\n", a, operator, b);
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

TEST_NORETURN
static inline void _test_fail_comparison_double(
                const char *file, int line, const char *func,
                const char *operator, double a, double b,
                const char *astr, const char *bstr) {
        test_log("FAILED COMPARISON: %s %s %s\n", astr, operator, bstr);
        test_log("Resolved to: %.3f %s %.3f\n", a, operator, b);
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

TEST_NORETURN
static inline void _test_fail_comparison_ptr(
                const char *file, int line, const char *func,
                const char *comparison) {
        test_log("FAILED COMPARISON: %s\n", comparison);
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

TEST_NORETURN
static inline void _test_fail_comparison_str(
                const char *file, int line, const char *func,
                const char *comparison, const char *a, const char *b) {
        test_log("FAILED COMPARISON: %s, expanded (\"%s\" vs \"%s\")\n",
                        comparison, a, b);
        test_log("in %s() (%s:%d)\n", func, file, line);
        exit(TEST_FAIL);
}

#define test_fail_if_reached() \
        _test_fail(__FILE__, __LINE__, __func__, \
                        "This line is supposed to be unreachable")

#define test_cmpbool(a_, op_, b_) \
        do { \
                bool _a = !!(a_); \
                bool _b = !!(b_); \
                if (!(_a op_ _b)) \
                        _test_fail_comparison_bool(__FILE__, __LINE__, __func__,\
                                        #op_, _a, _b, #a_, #b_); \
        } while(0)

#define test_bool_true(cond_) \
        test_cmpbool(cond_, ==, true)

#define test_bool_false(cond_) \
        test_cmpbool(cond_, ==, false)

#define test_cmpint(a_, op_, b_) \
        do { \
                __typeof__(a_) _a = a_; \
                __typeof__(b_) _b = b_; \
                if (trunc(_a) != _a || trunc(_b) != _b) \
                        _test_fail(__FILE__, __LINE__, __func__, \
                                "test_int_* used for non-integer value"); \
                if (!((_a) op_ (_b))) \
                        _test_fail_comparison_int(__FILE__, __LINE__, __func__,\
                                        #op_, _a, _b, #a_, #b_); \
        } while(0)

#define test_cmpptr(a_, op_, b_) \
        do { \
                __typeof__(a_) _a = a_; \
                __typeof__(b_) _b = b_; \
                if (!((_a) op_ (_b))) \
                        _test_fail_comparison_ptr(__FILE__, __LINE__, __func__,\
                                        #a_ " " #op_ " " #b_); \
        } while(0)

#define test_ptr_null(a_) \
        test_cmpptr(a_, ==, NULL)

#define test_ptr_notnull(a_) \
        test_cmpptr(a_, !=, NULL)

#define test_cmpdouble(a_, op_, b_) \
        do { \
                const double EPSILON = 1.0/256; \
                __typeof__(a_) _a = a_; \
                __typeof__(b_) _b = b_; \
                if (!((_a) op_ (_b)) && fabs((_a) - (_b)) > EPSILON)  \
                        _test_fail_comparison_double(__FILE__, __LINE__, __func__,\
                                        #op_, _a, _b, #a_, #b_); \
        } while(0)

#define test_str_eq(a_, b_) \
        do { \
                const char *_a = a_; \
                const char *_b = b_; \
                if (!_test_streq(_a, _b)) \
                        _test_fail_comparison_str(__FILE__, __LINE__, __func__, \
                                        #a_ " equals " #b_, _a, _b); \
        } while(0)

#define test_str_ne(a_, b_) \
        do { \
                const char *_a = a_; \
                const char *_b = b_; \
                if (_test_streq(_a, _b)) \
                        _test_fail_comparison_str(__FILE__, __LINE__, __func__, \
                                        #a_ " not equal to " #b_, _a, _b); \
        } while(0)