xaizek / stic (License: MIT) (since 2018-12-07)
Simple Tests In C with optional automatic test registration in C.
Commit f94c8098eeed0d97218565fc83b72983cb69f0f7

Add simple parametrized tests to stic
Usage example:

TEST(my_test, REPEAT(2))
{
assert_true(STIC_TEST_PARAM >= 0);
assert_true(STIC_TEST_PARAM < 2);
}

When reporting tests, repeated tests count as one but reported with
"(iteration-number)" suffix.

Tests with repetition count less than 1 would never run and get counted
as skipped.

stic_test_data::C field is used to avoid initializing the same field
twice which can cause warnings.
Author: xaizek
Author date (UTC): 2024-08-04 09:14
Committer name: xaizek
Committer date (UTC): 2024-08-06 15:52
Parent(s): 316311703b55ba2df679a3747f3c41a15587c19e
Signing key: 99DC5E4DB05F6BE2
Tree: caac3d2217f136a667c5e40e5866273dde26cce2
File Lines added Lines deleted
src/stic.c 17 4
src/stic.h 43 23
File src/stic.c changed (mode: 100644) (index 9da27d3..0342ad9)
... ... static stic_void_void stic_fixture_teardown = 0;
91 91 const char *stic_current_test_name; const char *stic_current_test_name;
92 92 stic_test stic_current_test; stic_test stic_current_test;
93 93 const char *stic_suite_name; const char *stic_suite_name;
94 int stic_current_count;
95 int stic_max_count;
94 96
95 97 void (*stic_simple_test_result)(int passed, char* reason, const char* function, const char file[], unsigned int line) = stic_simple_test_result_log; void (*stic_simple_test_result)(int passed, char* reason, const char* function, const char file[], unsigned int line) = stic_simple_test_result_log;
96 98
 
... ... static int test_had_output(void)
192 194 void stic_simple_test_result_log(int passed, char* reason, const char* function, const char file[], unsigned int line) void stic_simple_test_result_log(int passed, char* reason, const char* function, const char file[], unsigned int line)
193 195 { {
194 196 static stic_test last_test; static stic_test last_test;
197 static int last_count;
195 198 static stic_test last_failed_test; static stic_test last_failed_test;
196 199
197 const char *test_name = (stic_current_test == last_test) ? "" : stic_current_test_name;
200 int same_test = (stic_current_test == last_test && stic_current_count == last_count);
201 const char *test_name = same_test ? "" : stic_current_test_name;
198 202
199 203 if (stic_current_test_name != NULL && strcmp(function, stic_current_test_name) == 0) if (stic_current_test_name != NULL && strcmp(function, stic_current_test_name) == 0)
200 204 { {
 
... ... void stic_simple_test_result_log(int passed, char* reason, const char* function,
219 223 } }
220 224 else else
221 225 { {
222 if(stic_current_test != last_test)
226 if(!same_test)
223 227 { {
224 printf("\n%s:\n", test_name);
228 if (stic_max_count > 1)
229 {
230 printf("\n%s(%d):\n", test_name, stic_current_count);
231 }
232 else
233 {
234 printf("\n%s:\n", test_name);
235 }
225 236 } }
226 237 printf(" (-) FAILED\n" printf(" (-) FAILED\n"
227 238 " %s:%u: error: check failed\n" " %s:%u: error: check failed\n"
 
... ... void stic_simple_test_result_log(int passed, char* reason, const char* function,
238 249 last_failed_test = stic_current_test; last_failed_test = stic_current_test;
239 250
240 251 last_test = stic_current_test; last_test = stic_current_test;
252 last_count = stic_current_count;
241 253 } }
242 254 else else
243 255 { {
 
... ... void stic_simple_test_result_log(int passed, char* reason, const char* function,
249 261 } }
250 262 else else
251 263 { {
252 if(stic_current_test != last_test)
264 if(!same_test)
253 265 { {
254 266 printf("\n%s\n", test_name); printf("\n%s\n", test_name);
255 267 } }
 
... ... void stic_simple_test_result_log(int passed, char* reason, const char* function,
258 270 " in %s\n", file, line, function); " in %s\n", file, line, function);
259 271 } }
260 272 last_test = stic_current_test; last_test = stic_current_test;
273 last_count = stic_current_count;
261 274 } }
262 275 stic_checks_passed++; stic_checks_passed++;
263 276 } }
File src/stic.h changed (mode: 100644) (index 7a81cd9..2baab06)
18 18
19 19 /* Widely used function types. */ /* Widely used function types. */
20 20
21 typedef void (*stic_test)(void);
21 typedef void (*stic_test)(int test_param);
22 22 typedef void (*stic_void_void)(void); typedef void (*stic_void_void)(void);
23 23 typedef void (*stic_void_string)(char[]); typedef void (*stic_void_string)(char[]);
24 24
 
... ... struct stic_test_data
150 150 const char *const f; const char *const f;
151 151 stic_test t; stic_test t;
152 152 int (*const p)(void); int (*const p)(void);
153 char C; /* Whether .c has been initialized. */
154 int c; /* Number of times to repeat the test (1 by default). */
153 155 }; };
154 156
155 157 /* Auxiliary macros for internal use. */ /* Auxiliary macros for internal use. */
 
... ... struct stic_test_data
181 183
182 184 # define IF(...) .p = __VA_ARGS__, # define IF(...) .p = __VA_ARGS__,
183 185
186 /* Specifies number of times to repeat the test as an integer (1 by default). */
187 # define REPEAT(...) .C = 1, .c = (__VA_ARGS__),
188
189 /* Iteration number of the test (0..<repeat number - 1>) or 0. To be used only
190 * in test body. */
191 # define STIC_TEST_PARAM test_param
192
184 193 # define TEST(name, ...) \ # define TEST(name, ...) \
185 194 STIC_STATIC_ASSERT(STIC_CAT(too_many_lines_in_file_, __LINE__), \ STIC_STATIC_ASSERT(STIC_CAT(too_many_lines_in_file_, __LINE__), \
186 195 __LINE__ < STIC_MAX_LINES); \ __LINE__ < STIC_MAX_LINES); \
187 static void name(void); \
196 static void name(int test_param); \
188 197 static struct stic_test_data STIC_CAT(stic_test_data_, name) = { \ static struct stic_test_data STIC_CAT(stic_test_data_, name) = { \
189 198 .n = #name, \ .n = #name, \
190 199 .t = &name, \ .t = &name, \
 
... ... struct stic_test_data
192 201 __VA_ARGS__ \ __VA_ARGS__ \
193 202 }; \ }; \
194 203 static struct stic_test_data *STIC_CAT(l, __LINE__) = &STIC_CAT(stic_test_data_, name); \ static struct stic_test_data *STIC_CAT(l, __LINE__) = &STIC_CAT(stic_test_data_, name); \
195 static void name(void)
204 static void name(int test_param)
196 205
197 206 #else #else
198 207
 
... ... struct stic_test_data
203 212 static struct stic_test_data STIC_CAT(stic_test_data_, name) = { \ static struct stic_test_data STIC_CAT(stic_test_data_, name) = { \
204 213 /* .n = */ #name, \ /* .n = */ #name, \
205 214 /* .t = */ &name, \ /* .t = */ &name, \
206 /* .f = */ __FILE__ \
215 /* .f = */ __FILE__, \
207 216 }; \ }; \
208 217 static struct stic_test_data *STIC_CAT(l, __LINE__) = &STIC_CAT(stic_test_data_, name); \ static struct stic_test_data *STIC_CAT(l, __LINE__) = &STIC_CAT(stic_test_data_, name); \
209 static void name(void)
218 static void name(int test_param)
210 219
211 220 #endif #endif
212 221
213 222 /* Setup/teardown declaration macros. */ /* Setup/teardown declaration macros. */
214 223
215 224 #define SETUP_ONCE() \ #define SETUP_ONCE() \
216 static void stic_setup_once_func_impl(void); \
217 static void (*stic_setup_once_func)(void) = &stic_setup_once_func_impl; \
218 static void stic_setup_once_func_impl(void)
225 static void stic_setup_once_func_impl(int no_test_param); \
226 static void (*stic_setup_once_func)(int no_test_param) = &stic_setup_once_func_impl; \
227 static void stic_setup_once_func_impl(int no_test_param)
219 228
220 229 #define SETUP() \ #define SETUP() \
221 230 static void stic_setup_func_impl(void); \ static void stic_setup_func_impl(void); \
 
... ... struct stic_test_data
223 232 static void stic_setup_func_impl(void) static void stic_setup_func_impl(void)
224 233
225 234 #define TEARDOWN_ONCE() \ #define TEARDOWN_ONCE() \
226 static void stic_teardown_once_func_impl(void); \
227 static void (*stic_teardown_once_func)(void) = &stic_teardown_once_func_impl; \
228 static void stic_teardown_once_func_impl(void)
235 static void stic_teardown_once_func_impl(int no_test_param); \
236 static void (*stic_teardown_once_func)(int no_test_param) = &stic_teardown_once_func_impl; \
237 static void stic_teardown_once_func_impl(int no_test_param)
229 238
230 239 #define TEARDOWN() \ #define TEARDOWN() \
231 240 static void stic_teardown_func_impl(void); \ static void stic_teardown_func_impl(void); \
 
... ... struct stic_test_data
262 271 const int file_has_tests = (stic_get_fixture_name() != NULL); \ const int file_has_tests = (stic_get_fixture_name() != NULL); \
263 272 if(!file_has_tests && stic_setup_once_func != NULL) \ if(!file_has_tests && stic_setup_once_func != NULL) \
264 273 { \ { \
265 stic_setup_once_func(); \
274 stic_setup_once_func(/*no_test_param=*/0); \
266 275 } \ } \
267 276 r = stic_testrunner(argc, argv, stic_suite, stic_setup_func, stic_teardown_func) == 0; \ r = stic_testrunner(argc, argv, stic_suite, stic_setup_func, stic_teardown_func) == 0; \
268 277 if(!file_has_tests && stic_teardown_once_func != NULL) \ if(!file_has_tests && stic_teardown_once_func != NULL) \
269 278 { \ { \
270 stic_teardown_once_func(); \
279 stic_teardown_once_func(/*no_test_param=*/0); \
271 280 } \ } \
272 281 return r; \ return r; \
273 282 } }
 
... ... typedef struct stic_test_data *stic_test_data_p;
280 289 static stic_test_data_p TEST_DATA_STRUCTS; static stic_test_data_p TEST_DATA_STRUCTS;
281 290
282 291 static void (*stic_setup_func)(void); static void (*stic_setup_func)(void);
283 static void (*stic_setup_once_func)(void);
292 static void (*stic_setup_once_func)(int no_test_param);
284 293 static void (*stic_teardown_func)(void); static void (*stic_teardown_func)(void);
285 static void (*stic_teardown_once_func)(void);
294 static void (*stic_teardown_once_func)(int no_test_param);
286 295
287 296 static struct stic_test_data *const *const stic_test_data[] = { static struct stic_test_data *const *const stic_test_data[] = {
288 297 TEST_DATA_STRUCTS_REF TEST_DATA_STRUCTS_REF
 
... ... static void stic_fixture(void)
305 314 { {
306 315 extern const char *stic_current_test_name; extern const char *stic_current_test_name;
307 316 extern stic_test stic_current_test; extern stic_test stic_current_test;
317 extern int stic_current_count;
318 extern int stic_max_count;
308 319
309 320 size_t i; size_t i;
321 int c;
310 322 int has_any_tests = 0; int has_any_tests = 0;
311 323
312 324 const char *fixture_name = stic_get_fixture_name(); const char *fixture_name = stic_get_fixture_name();
 
... ... static void stic_fixture(void)
335 347 { {
336 348 stic_current_test_name = "<setup once>"; stic_current_test_name = "<setup once>";
337 349 stic_current_test = stic_setup_once_func; stic_current_test = stic_setup_once_func;
338 stic_setup_once_func();
350 stic_setup_once_func(/*no_test_param=*/0);
339 351 } }
340 352 } }
341 353
342 if(td->p != NULL && !td->p())
354 if((td->p != NULL && !td->p()) || (td->C && td->c < 1))
343 355 { {
344 356 stic_skip_test(fixture_name, td->n); stic_skip_test(fixture_name, td->n);
345 357 continue; continue;
 
... ... static void stic_fixture(void)
347 359
348 360 stic_current_test_name = td->n; stic_current_test_name = td->n;
349 361 stic_current_test = td->t; stic_current_test = td->t;
350 stic_suite_setup();
351 stic_setup();
352 td->t();
353 stic_teardown();
354 stic_suite_teardown();
362 stic_max_count = (td->C ? td->c : 1);
363
364 for(c = 0; c < stic_max_count; ++c)
365 {
366 stic_current_count = c;
367
368 stic_suite_setup();
369 stic_setup();
370 td->t(c);
371 stic_teardown();
372 stic_suite_teardown();
373 }
374
355 375 stic_run_test(fixture_name, td->n); stic_run_test(fixture_name, td->n);
356 376 } }
357 377
 
... ... static void stic_fixture(void)
359 379 { {
360 380 stic_current_test_name = "<teardown once>"; stic_current_test_name = "<teardown once>";
361 381 stic_current_test = stic_teardown_once_func; stic_current_test = stic_teardown_once_func;
362 stic_teardown_once_func();
382 stic_teardown_once_func(/*no_test_param=*/0);
363 383 } }
364 384 test_fixture_end(); test_fixture_end();
365 385 } }
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/stic

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/stic

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master