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
12extern 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
27typedef 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
301typedef 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.
312extern const mos_test_func_t __MOS_TEST_CASES_START[];
313extern const mos_test_func_t __MOS_TEST_CASES_END[];
314