1CHECKMK(1) CHECKMK(1)
2
3
4
6 checkmk - Awk script for generating C unit tests for use with the
7 Check unit testing framework.
8
10 checkmk [ clean_mode=1 ] [ input-file ]
11
12
14 Generate C-language source files containing unit tests for use with the
15 Check unit testing framework. The aim of this script is to automate
16 away some of the typical boilerplate one must write when writing a test
17 suite using Check: specifically, the instantiation of an SRunner,
18 Suite(s), and TCase(s), and the building of relationships between these
19 objects and the test functions.
20
21 This tool is intended to be used by those who are familiar with the
22 Check unit testing framework. Familiarity with the framework will be
23 assumed throughout this manual.
24
25 The Check framework, along with information regarding it, is available
26 at https://libcheck.github.io/check/
27 <URL:https://libcheck.github.io/check/>.
28
29 The input-file argument to checkmk uses a simple, C-preprocessor-like
30 syntax to declare test functions, and to describe their relationships
31 to Suites and TCases in Check. checkmk then uses this information to
32 automatically write a main() function containing all of the necessary
33 declarations, and whatever code is needed to run the test suites. The
34 final C-language output is printed to checkmk's standard output.
35
36 Facilities are provided for the insertion of user code into the gener‐
37 ated main() function, to provide for the use of logging, test fixtures
38 or specialized exit values.
39
40 While it is possible to omit the input-file argument to checkmk and
41 provide the input file on checkmk's standard input instead, it is gen‐
42 erally recommended to provide it as an argument. Doing this allows
43 checkmk to be aware of the file's name, to place references to it in
44 the initial comments of the C-language output, and to intersperse C
45 #line directives throughout, to facilitate in debugging problems by
46 directing the user to the original input file.
47
49 The only officially supported option is specifying a true value (using
50 Awk's definition for "true") for the variable clean_mode. This causes
51 checkmk not to place appropriate #line directives in the source code,
52 which some might find to be unnecessary clutter.
53
54 The author recommends against the use of this option, as it will cause
55 C compilers and debugging tools to refer to lines in the automatically
56 generated output, rather than the original input files to checkmk. This
57 would encourage users to edit the output files instead of the original
58 input files, would make it difficult for intelligent editors or IDEs to
59 pull up the right file to edit, and could result in the fixes being
60 overwritten when the output files are regenerated.
61
62 #line directives are automatically supressed when the input file is
63 provided on standard input instead of as a command-line argument.
64
66 In its most basic form, an input file can be simply a prologue and a
67 test function. Anything that appears before the first test function is
68 in the prologue, and will be copied into the output verbatim. The test
69 function is begun by a line in the form:
70
71 #test test_name
72
73 Where test_name is the name of your test function. This will be used to
74 name a C function, so it must be a valid C identifier.
75
76 Here is a small, complete example:
77
78 --------------------------------------------------
79 /* A complete test example */
80
81 #include <stdio.h>
82
83 #test the_test
84 int nc;
85 const char msg[] = "\n\n Hello, world!\n";
86
87 nc = printf("%s", msg);
88 ck_assert(nc == (sizeof(msg) - 1)); /* for terminating NUL. */
89 --------------------------------------------------
90
91 If you place the above into a file named basic_complete.ts and process
92 it using the following command:
93
94 $ checkmk basic_complete.ts > basic_complete.c
95
96 basic_complete.c will contain output similar to:
97
98 --------------------------------------------------
99 /*
100 * DO NOT EDIT THIS FILE. Generated by checkmk.
101 * Edit the original source file "in" instead.
102 */
103
104 #include <check.h>
105
106 /* A complete test example */
107
108 #include <stdio.h>
109
110 START_TEST(the_test)
111 {
112 int nc;
113 const char msg[] = "\n\n Hello, world!\n";
114
115 nc = printf("%s", msg);
116 ck_assert(nc == (sizeof(msg) - 1)); /* for terminating NUL. */
117 }
118 END_TEST
119
120 int main(void)
121 {
122 Suite *s1 = suite_create("Core");
123 TCase *tc1_1 = tcase_create("Core");
124 SRunner *sr = srunner_create(s1);
125 int nf;
126
127 suite_add_tcase(s1, tc1_1);
128 tcase_add_test(tc1_1, the_test);
129
130 srunner_run_all(sr, CK_ENV);
131 nf = srunner_ntests_failed(sr);
132 srunner_free(sr);
133
134 return nf == 0 ? 0 : 1;
135 }
136 --------------------------------------------------
137
138 In real usage, basic_complete.c would also contain #line directives.
139
141 Here is a complete summary of all the C-preprocessor-style directives
142 that are understood by checkmk. See below for more details.
143
144 # test test_name
145 # test-signal(signal) test_name
146 # test-exit(exit_code) test_name
147 # test-loop(start, end) test_name
148 # test-loop-signal(signal, start, end) test_name
149 # test-loop-exit(exit_code, start, end) test_name
150 # suite TestSuiteName
151 # tcase TestCaseName
152 # main-pre
153 # main-post
154
155 All directives are case-insensitive. Whitespace may appear at the
156 beginning of the line before the #, between the # and the directive,
157 between the directive and any argument, and at the end of the line.
158
160 Here is a more detailed explanation of the directives that may be used
161 to define test functions and their containers.
162
163 TEST FUNCTIONS
164 # test test_name
165 # test-signal(signal) test_name
166 # test-exit(exit_code) test_name
167 # test-loop(start, end) test_name
168 # test-loop-signal(signal, start, end) test_name
169 # test-loop-exit(exit_code, start, end) test_name
170
171 These are the most basic directives for creating a template for input
172 to checkmk. They are the only directives that are required: there must
173 be at least one #test* directive appearing in the template, or checkmk
174 will fail with an error message. The #test* directives may be specified
175 several times, each one beginning the definition of a new test func‐
176 tion.
177
178 The test_name argument will be used as the name of a test function in
179 the C-language output, so it must be a valid C identifier. That is, it
180 must begin with an alphabetic character or the underscore (_), followed
181 by optional alpha-numeric characters and/or underscores.
182
183 Universal Character Names (introduced in C99) are also allowed, of the
184 form \uXXXX or \UXXXXXXXX, where the X's represent hexadecimal digits.
185
186 It is an error to specify the same test_name in more than one #test*
187 directive, regardless of whether they are associated with different
188 test cases or suites.
189
190 See CHECKMK IDENTIFIERS for the list of identifiers which should be
191 avoided for use as test function names.
192
193 TEST SUITES
194 # suite TestSuiteName
195
196 This directive specifies the name of the test suite (Suite object in
197 the Check test framework) to which all future test cases (and their
198 test functions) will be added.
199
200 The TestSuiteName is a text string, and may contain any sort of charac‐
201 ters at all (other than ASCII NUL character, and the newline, which
202 would terminate the directive). Any leading or trailing whitespace will
203 be omitted from the test suite name.
204
205 Starting a new test suite also begins a new test case, whose name is
206 identical to the new test suite. This test case name may be overridden
207 by a subsequent #tcase directive.
208
209 Note that a Suite object won't actually be defined by checkmk in the C
210 output, unless it is followed at some point by a #test directive (with‐
211 out an intervening #suite). It is not an error for a #suite to have no
212 associated #test's; the #suite (and any associated #tcase's) simply
213 won't result in any action on the part of checkmk (and would therefore
214 be useless).
215
216 It is an error for a #suite directive to specify the same (case sensi‐
217 tive) suite multiple times, unless the previous uses were not instanti‐
218 ated by the presence of at least one associated #test directive.
219
220 If you do not specify a #suite directive before the first #test direc‐
221 tive, checkmk performs the equivalent of an implicit #suite directive,
222 with the string "Core" as the value for TestSuiteName (this also
223 implies a "Core" test case object). This is demonstrated above in BASIC
224 EXAMPLE.
225
226 TEST CASES
227 # tcase TestCaseName
228
229 This directive specifies the name of the test case (TCase object in the
230 Check test framework) to which all future test functions will be added.
231
232 The #tcase works very in a way very similar to #suite. The TestCaseName
233 is a text string, and may contain arbitrary characters; and a TCase
234 object won't actually be defined unless it is followed by an associated
235 #test directive.
236
237 It is an error for a #tcase directive to specify the same (case sensi‐
238 tive) test case multiple times, unless the previous uses were not
239 instantiated by the presence of at least one associated #test direc‐
240 tive.
241
242 See also the #suite directive, described above.
243
245 The C main() is automatically generated by checkmk, defining the neces‐
246 sary SRunner's, Suite's, and TCase's required by the test-defining
247 directives specified by the user.
248
249 For most situations, this completely automated main() is quite suitable
250 as-is. However, there are situations where one might wish to add custom
251 code to the main(). For instance, if the user wishes to:
252
253 · change the test timeout value via tcase_set_timeout(),
254
255 · specify Check's "no-fork-mode" via srunner_set_fork_status(),
256
257 · set up test fixtures for some test cases, via tcase_add_checked_fix‐
258 ture() or tcase_add_unchecked_fixture(),
259
260 · set up test logging for the suite runner, via srunner_set_log()
261 or srunner_set_xml(), or
262
263 · perform custom wrap-up after the test suites have been run.
264
265 For these purposes, the #main-pre and #main-post directives have been
266 provided.
267
268 MAIN() PROLOGUE
269 # main-pre
270
271 The text following this directive will be placed verbatim into the body
272 of the generated main() function, just after checkmk's own local vari‐
273 able declarations, and before any test running has taken place (indeed,
274 before even the relationships between the tests, test cases, and test
275 suites have been set up, though that fact shouldn't make much differ‐
276 ence). Since checkmk has only just finished making its declarations, it
277 is permissible, even under strict 1990 ISO C guidelines, to make custom
278 variable declarations here.
279
280 Unlike the previously-described directives, #main-pre may be specified
281 at most once. It may not be preceded by the #main-post directive, and
282 no #suite, #tcase, or #test directive may appear after it.
283
284 #main-pre is a good place to tweak settings or set up test fixtures. Of
285 course, in order to do so, you need to know what names checkmk has used
286 to instantiate the SRunner's, Suite's, and TCase's.
287
288 CHECKMK IDENTIFIERS
289 Pointers to Suite's are declared using the pattern sX, where X is a
290 number that starts at 1, and is incremented for each subsequent #suite
291 directive. s1 always exists, and contains the test function declared
292 by the first #test directive. If that directive was not preceded by a
293 #suite, it will be given the name "Core".
294
295 Pointers to TCase's are declared using the pattern tcX_Y, where X cor‐
296 responds to the number used for the name of the Suite that will contain
297 this TCase; and Y is a number that starts at 1 for each new Suite, and
298 is incremented for each TCase in that Suite.
299
300 A pointer to SRunner is declared using the identifier sr; there is also
301 an integer named nf which holds the number of test failures (after the
302 tests have run).
303
304 For obvious reasons, the user should not attempt to declare local iden‐
305 tifiers in main(), or define any macros or test functions, whose names
306 might conflict with the local variable names used by checkmk. To summa‐
307 rize, these names are:
308
309 sX
310
311 tcX_Y
312
313 sr
314
315 nf.
316
317 MAIN() EPILOGUE
318 # main-post
319
320 Though it is not as useful, checkmk also provides a #main-post direc‐
321 tive to insert custom code at the end of main(), after the tests have
322 run. This could be used to clean up resources that were allocated in
323 the prologue, or to print information about the failed tests, or to
324 provide a custom exit status code.
325
326 Note that, if you make use of this directive, checkmk will not provide
327 a return statement: you will need to provide one yourself.
328
329 The #main-post directive may not be followed by any other directives
330 recognized by checkmk.
331
333 Now that you've gotten the detailed descriptions of the various direc‐
334 tives, let's see it all put to action with this fairly comprehensive
335 template.
336
337 --------------------------------------------------
338 #include "mempool.h" /* defines MEMPOOLSZ, prototypes for
339 mempool_init() and mempool_free() */
340
341 void *mempool;
342
343 void mp_setup(void)
344 {
345 mempool = mempool_init(MEMPOOLSZ);
346 ck_assert_msg(mempool != NULL, "Couldn't allocate mempool.");
347 }
348
349 void mp_teardown(void)
350 {
351 mempool_free(mempool);
352 }
353
354 /* end of prologue */
355
356 #suite Mempool
357
358 #tcase MP Init
359
360 #test mempool_init_zero_test
361 mempool = mempool_init(0);
362 ck_assert_msg(mempool == NULL, "Allocated a zero-sized mempool!");
363 ck_assert_msg(mempool_error(), "Didn't get an error for zero alloc.");
364
365 /* "MP Util" TCase uses checked fixture. */
366 #tcase MP Util
367
368 #test mempool_copy_test
369 void *cp = mempool_copy(mempool);
370 ck_assert_msg(cp != NULL, "Couldn't perform mempool copy.");
371 ck_assert_msg(cp != mempool, "Copy returned original pointer!");
372
373 #test mempool_size_test
374 ck_assert(mempool_getsize(mempool) == MEMPOOLSZ);
375
376 #main-pre
377 tcase_add_checked_fixture(tc1_2, mp_setup, mp_teardown);
378 srunner_set_log(sr, "mplog.txt");
379
380 #main-post
381 if (nf != 0) {
382 printf("Hey, something's wrong! %d whole tests failed!\n", nf);
383 }
384 return 0; /* Harness checks for output, always return success
385 regardless. */
386 --------------------------------------------------
387
388 Plugging this into checkmk, we'll get output roughly like the follow‐
389 ing:
390
391 --------------------------------------------------
392 /*
393 * DO NOT EDIT THIS FILE. Generated by checkmk.
394 * Edit the original source file "comprehensive.ts" instead.
395 */
396
397 #include <check.h>
398
399 #include "mempool.h"
400
401 void *mempool;
402
403 void mp_setup(void)
404 {
405 ...
406 }
407
408 void mp_teardown(void)
409 {
410 ...
411 }
412
413 /* end of prologue */
414
415 START_TEST(mempool_init_zero_test)
416 {
417 ...
418 }
419 END_TEST
420
421 START_TEST(mempool_copy_test)
422 {
423 ...
424 }
425 END_TEST
426
427 START_TEST(mempool_size_test)
428 {
429 ...
430 }
431 END_TEST
432
433 int main(void)
434 {
435 Suite *s1 = suite_create("Mempool");
436 TCase *tc1_1 = tcase_create("MP Init");
437 TCase *tc1_2 = tcase_create("MP Util");
438 SRunner *sr = srunner_create(s1);
439 int nf;
440
441 /* User-specified pre-run code */
442 tcase_add_checked_fixture(tc1_2, mp_setup, mp_teardown);
443 srunner_set_log(sr, "mplog.txt");
444
445 suite_add_tcase(s1, tc1_1);
446 tcase_add_test(tc1_1, mempool_init_zero_test);
447 suite_add_tcase(s1, tc1_2);
448 tcase_add_test(tc1_2, mempool_copy_test);
449 tcase_add_test(tc1_2, mempool_size_test);
450
451 srunner_run_all(sr, CK_ENV);
452 nf = srunner_ntests_failed(sr);
453 srunner_free(sr);
454
455 /* User-specified post-run code */
456 if (nf != 0) {
457 printf("Hey, something's wrong! %d whole tests failed!\n", nf);
458 }
459 return 0; /* Harness checks for output, always return success
460 regardless. */
461 }
462 --------------------------------------------------
463
465 checkmk and this manual were written by Micah J Cowan.
466
467 Copyright (C) 2006, 2010 Micah J Cowan.
468
469
470
471 09 February 2010 CHECKMK(1)