1CHECKMK(1)                                                          CHECKMK(1)
2
3
4

NAME

6       checkmk  -  Awk  script  for  generating  C unit tests for use with the
7       Check unit testing framework.
8

SYNOPSIS

10       checkmk [ clean_mode=1 ] [ input-file ]
11
12

DESCRIPTION

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

OPTIONS

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

BASIC EXAMPLE

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

DIRECTIVE SUMMARY

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

TEST-DEFINING DIRECTIVES

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

USER CODE IN MAIN()

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

COMPREHENSIVE EXAMPLE

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

AUTHOR

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)
Impressum