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