1 | // SPDX-License-Identifier: MIT |
2 | // Adapted from https://github.com/mateuszchudyk/tinytest |
3 | |
4 | #pragma once |
5 | |
6 | #include <mos/mos_global.h> |
7 | #include <mos/types.h> |
8 | #include <mos_stdio.h> |
9 | #include <mos_string.h> |
10 | #include <stdbool.h> |
11 | |
12 | extern s32 test_engine_n_warning_expected; |
13 | |
14 | #define mos_test_log(level, symbol, format, ...) \ |
15 | do \ |
16 | { \ |
17 | lprintk(MOS_LOG_UNSET, "\r\n"); \ |
18 | if (symbol) \ |
19 | lprintk(MOS_LOG_EMPH, "[%c] ", symbol); \ |
20 | else \ |
21 | lprintk(MOS_LOG_UNSET, " "); \ |
22 | lprintk(level, format, ##__VA_ARGS__); \ |
23 | } while (0) |
24 | |
25 | #define mos_test_log_cont(level, format, ...) lprintk(level, format, ##__VA_ARGS__) |
26 | |
27 | typedef struct |
28 | { |
29 | u32 n_total; |
30 | u32 n_failed; |
31 | u32 n_skipped; |
32 | } mos_test_result_t; |
33 | |
34 | #define MOS_TEST_DEFINE_CONDITION(condition, message) \ |
35 | const char *const _mt_test_cond_##condition##_message = message; \ |
36 | static bool condition |
37 | |
38 | #define MOS_TEST_CONDITIONAL(cond) \ |
39 | for (MOS_TEST_CURRENT_TEST_SKIPPED = !(cond), (*_mt_loop_leave) = false, __extension__({ \ |
40 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
41 | mos_test_log(MOS_LOG_WARN, '\0', "Skipped '%s': condition '%s' not met.", _mt_test_cond_##cond##_message, #cond); \ |
42 | }); \ |
43 | !(*_mt_loop_leave); (*_mt_loop_leave) = true, MOS_TEST_CURRENT_TEST_SKIPPED = false) |
44 | |
45 | #define MOS_TEST_SKIP() \ |
46 | do \ |
47 | { \ |
48 | ++_MT_result->n_total; \ |
49 | ++_MT_result->n_skipped; \ |
50 | } while (0) |
51 | |
52 | #define MOS_TEST_CASE(_TestName) \ |
53 | static void _TestName(mos_test_result_t *, bool *, bool *); \ |
54 | static void _MT_WRAP_TEST_NAME(_TestName)(mos_test_result_t * result) \ |
55 | { \ |
56 | mos_test_log(MOS_LOG_INFO, 'T', "Testing '" #_TestName "'... "); \ |
57 | _MT_RUN_TEST_AND_PRINT_RESULT(result, _TestName); \ |
58 | } \ |
59 | _MT_REGISTER_TEST_CASE(_TestName, _MT_WRAP_TEST_NAME(_TestName)); \ |
60 | static void _TestName(mos_test_result_t *_MT_result, __maybe_unused bool *_mt_test_skipped, __maybe_unused bool *_mt_loop_leave) |
61 | |
62 | #define MOS_TEST_DECL_PTEST(_PTestName, ptest_args_printf_format, ...) \ |
63 | static const char *_MT_PTEST_ARG_FORMAT(_PTestName) = "" ptest_args_printf_format ""; \ |
64 | static void _PTestName(mos_test_result_t *_MT_result, __maybe_unused bool *_mt_test_skipped, __maybe_unused bool *_mt_loop_leave, __VA_ARGS__) |
65 | |
66 | #define MOS_TEST_PTEST_INSTANCE(_PTestName, ...) \ |
67 | static void _MT_PTEST_CALLER(_PTestName)(mos_test_result_t * result, bool *_mt_test_skipped, bool *_mt_loop_leave) \ |
68 | { \ |
69 | _PTestName(result, _mt_test_skipped, _mt_loop_leave, __VA_ARGS__); \ |
70 | } \ |
71 | static void _MT_WRAP_PTEST_CALLER(_PTestName)(mos_test_result_t * result) \ |
72 | { \ |
73 | char __buf[MOS_PRINTK_BUFFER_SIZE] = { 0 }; \ |
74 | snprintf(__buf, MOS_PRINTK_BUFFER_SIZE, _MT_PTEST_ARG_FORMAT(_PTestName), __VA_ARGS__); \ |
75 | mos_test_log(MOS_LOG_INFO, 'P', "Test %s with parameters: ", #_PTestName); \ |
76 | mos_test_log_cont(MOS_LOG_UNSET, "(%s)... ", __buf); \ |
77 | _MT_RUN_TEST_AND_PRINT_RESULT(result, _MT_PTEST_CALLER(_PTestName)); \ |
78 | } \ |
79 | _MT_REGISTER_TEST_CASE(_TestName, _MT_WRAP_PTEST_CALLER(_PTestName)) |
80 | |
81 | #define MOS_TEST_EXPECT_WARNING_N(N, body, msg) \ |
82 | do \ |
83 | { \ |
84 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
85 | { \ |
86 | MOS_TEST_SKIP(); \ |
87 | break; \ |
88 | } \ |
89 | test_engine_n_warning_expected = N; \ |
90 | body; \ |
91 | if (test_engine_n_warning_expected != 0) \ |
92 | MOS_TEST_FAIL("%d more expected warning(s) not seen: %s, line %d", test_engine_n_warning_expected, msg, __LINE__); \ |
93 | } while (0) |
94 | |
95 | #define MOS_TEST_EXPECT_WARNING(body, msg) MOS_TEST_EXPECT_WARNING_N(1, body, msg) |
96 | /* |
97 | * Test force fail |
98 | * |
99 | * Arguments: |
100 | * - format - message printf format |
101 | * - args... [opt] - arguments |
102 | */ |
103 | #define MOS_TEST_FAIL(format, ...) \ |
104 | do \ |
105 | { \ |
106 | ++_MT_result->n_failed; \ |
107 | mos_test_log(MOS_LOG_EMERG, 'X', "line %d: " format, __LINE__, ##__VA_ARGS__); \ |
108 | } while (false) |
109 | |
110 | /* |
111 | * Assertion |
112 | * |
113 | * Arguments: |
114 | * - condition - condition to test |
115 | * - format - message printf format |
116 | * - args... [opt] - arguments |
117 | */ |
118 | #define MOS_TEST_ASSERT(condition, format, ...) \ |
119 | do \ |
120 | { \ |
121 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
122 | { \ |
123 | MOS_TEST_SKIP(); \ |
124 | break; \ |
125 | } \ |
126 | ++_MT_result->n_total; \ |
127 | if (!(condition)) \ |
128 | MOS_TEST_FAIL("ASSERTION FAILED: %s, " format, #condition, ##__VA_ARGS__); \ |
129 | } while (false) |
130 | |
131 | /* |
132 | * Check if actual value is equal expected value. |
133 | * |
134 | * Arguments: |
135 | * - expected - expected value |
136 | * - actual - actual value |
137 | */ |
138 | #define MOS_TEST_CHECK(actual, expected) \ |
139 | do \ |
140 | { \ |
141 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
142 | { \ |
143 | MOS_TEST_SKIP(); \ |
144 | break; \ |
145 | } \ |
146 | ++_MT_result->n_total; \ |
147 | if ((expected) != (actual)) \ |
148 | MOS_TEST_FAIL("'%s' is %lld, expected %lld", #actual, (s64) (actual), (s64) (expected)); \ |
149 | } while (false) |
150 | |
151 | /* |
152 | * Check if actual string is equal expected string. |
153 | * |
154 | * Arguments: |
155 | * - expected - expected string |
156 | * - actual - actual string |
157 | */ |
158 | #define MOS_TEST_CHECK_STRING(actual, expected) \ |
159 | do \ |
160 | { \ |
161 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
162 | { \ |
163 | MOS_TEST_SKIP(); \ |
164 | break; \ |
165 | } \ |
166 | ++_MT_result->n_total; \ |
167 | if (strcmp(expected, actual) != 0) \ |
168 | MOS_TEST_FAIL("values are different (expected = '%s', actual = '%s'), at line %u", (expected), (actual), (__LINE__)); \ |
169 | } while (false) |
170 | |
171 | /* |
172 | * Check if actual string is equal expected string, with n characters. |
173 | * |
174 | * Arguments: |
175 | * - expected - expected string |
176 | * - actual - actual string |
177 | * - n - number of characters to compare |
178 | */ |
179 | #define MOS_TEST_CHECK_STRING_N(actual, expected, n) \ |
180 | do \ |
181 | { \ |
182 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
183 | { \ |
184 | MOS_TEST_SKIP(); \ |
185 | break; \ |
186 | } \ |
187 | ++_MT_result->n_total; \ |
188 | if (strncmp(expected, actual, n) != 0) \ |
189 | MOS_TEST_FAIL("values are different (expected = '%.*s', actual = '%.*s'), at line %u", (n), (expected), (n), (actual), (__LINE__)); \ |
190 | } while (false); |
191 | |
192 | /* |
193 | * Check if actual value is not differ from expected value by more then epsilon. |
194 | * |
195 | * Arguments: |
196 | * - expected - expected value |
197 | * - actual - actual value |
198 | * - epsilon - maximum acceptable difference |
199 | */ |
200 | #define MOS_TEST_CHECK_EPS(actual, expected, epsilon) \ |
201 | do \ |
202 | { \ |
203 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
204 | { \ |
205 | MOS_TEST_SKIP(); \ |
206 | break; \ |
207 | } \ |
208 | ++_MT_result->n_total; \ |
209 | if (_MT_FLOATABS((expected) - (actual)) > (epsilon)) \ |
210 | MOS_TEST_FAIL("values differ by more then %f (expected = %f, actual = %f)", (epsilon), (expected), (actual)); \ |
211 | } while (false) |
212 | |
213 | /* |
214 | * Check if every value in the actual array is equal to corresponding value |
215 | * in the expected array. |
216 | * |
217 | * Arguments: |
218 | * - expected - expected array pointer |
219 | * - actual - actual array pointer |
220 | * - elements - number of array elements |
221 | */ |
222 | #define MOS_TEST_CHECK_ARRAY(actual, expected, elements) \ |
223 | do \ |
224 | { \ |
225 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
226 | { \ |
227 | MOS_TEST_SKIP(); \ |
228 | break; \ |
229 | } \ |
230 | ++_MT_result->n_total; \ |
231 | for (unsigned i = 0; i < (unsigned) (elements); ++i) \ |
232 | { \ |
233 | if ((expected)[i] != (actual)[i]) \ |
234 | { \ |
235 | MOS_TEST_FAIL("memories differ at %u-th position (expected = %d, actual = %d)", i, (expected)[i], (actual)[i]); \ |
236 | break; \ |
237 | } \ |
238 | } \ |
239 | } while (false) |
240 | |
241 | /* |
242 | * Check if every value in the actual array is not differ from corresponding value |
243 | * in the expected array by more then epsilon. |
244 | * |
245 | * Arguments: |
246 | * - expected - expected array pointer |
247 | * - actual - actual array pointer |
248 | * - epsilon - maximum acceptable difference |
249 | * - elements - number of array elements |
250 | */ |
251 | #define MOS_TEST_CHECK_ARRAY_EPS(actual, expected, elements, epsilon) \ |
252 | do \ |
253 | { \ |
254 | if (MOS_TEST_CURRENT_TEST_SKIPPED) \ |
255 | { \ |
256 | MOS_TEST_SKIP(); \ |
257 | break; \ |
258 | } \ |
259 | ++_MT_result->n_total; \ |
260 | for (unsigned i = 0; i < (unsigned) (elements); ++i) \ |
261 | { \ |
262 | if (_MT_FLOATABS((expected)[i] - (actual)[i]) > (epsilon)) \ |
263 | { \ |
264 | MOS_TEST_FAIL("memories differ at %u by more then %f (expected = %f, actual = %f)", i, (epsilon), (expected)[i], (actual)[i]); \ |
265 | break; \ |
266 | } \ |
267 | } \ |
268 | } while (false) |
269 | |
270 | #define _MT_RUN_TEST_AND_PRINT_RESULT(_ResultVar, _TestFunc) \ |
271 | do \ |
272 | { \ |
273 | bool _mt_test_skipped = false; \ |
274 | bool _mt_loop_leave = false; \ |
275 | _TestFunc(_ResultVar, &_mt_test_skipped, &_mt_loop_leave); \ |
276 | u32 total = _ResultVar->n_total; \ |
277 | u32 failed = _ResultVar->n_failed; \ |
278 | u32 skipped = _ResultVar->n_skipped; \ |
279 | u32 passed = total - failed - skipped; \ |
280 | if (failed == 0) \ |
281 | if (skipped == 0) \ |
282 | mos_test_log_cont(MOS_LOG_INFO2, "passed (%u tests)", total); \ |
283 | else \ |
284 | mos_test_log_cont(MOS_LOG_INFO2, "passed (%u tests, %u skipped)", total, skipped); \ |
285 | else \ |
286 | mos_test_log(MOS_LOG_EMERG, 'X', "%u failed, (%u tests, %u skipped, %u passed)", failed, total, skipped, passed); \ |
287 | } while (0) |
288 | |
289 | #define _MT_FLOATABS(a) ((a) < 0 ? -(a) : (a)) |
290 | |
291 | #define MOS_TEST_CURRENT_TEST_SKIPPED (*_mt_test_skipped) |
292 | |
293 | // Wrapper for the simple test |
294 | #define _MT_WRAP_TEST_NAME(test_name) __mos_test_wrapped_test_##test_name |
295 | |
296 | // Wrapper for the parameterized test |
297 | #define _MT_PTEST_ARG_FORMAT(ptest_name) __mos_test_ptest_args_format_##ptest_name |
298 | #define _MT_PTEST_CALLER(ptest_name) MOS_CONCAT(__mos_test_ptest_caller_##ptest_name, __LINE__) |
299 | #define _MT_WRAP_PTEST_CALLER(ptest_name) MOS_CONCAT(__mos_test_wrapped_ptest_caller_##ptest_name, __LINE__) |
300 | |
301 | typedef struct |
302 | { |
303 | void (*test_func)(mos_test_result_t *); |
304 | const char *test_name; |
305 | } mos_test_func_t; |
306 | |
307 | // ELF Section based test registration |
308 | #define _MT_REGISTER_TEST_CASE(_TName, _TFunc) MOS_PUT_IN_SECTION(".mos.test_cases", mos_test_func_t, MOS_CONCAT(test_cases_##_TName##_L, __LINE__), { _TFunc, #_TName }) |
309 | #define MOS_TEST_FOREACH_TEST_CASE(_FPtr) for (const mos_test_func_t *_FPtr = __MOS_TEST_CASES_START; _FPtr != __MOS_TEST_CASES_END; _FPtr++) |
310 | |
311 | // Defined by the linker, do not rename. |
312 | extern const mos_test_func_t __MOS_TEST_CASES_START[]; |
313 | extern const mos_test_func_t __MOS_TEST_CASES_END[]; |
314 | |