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