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

DIRECTIVE SUMMARY

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

TEST-DEFINING DIRECTIVES

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

USER CODE IN MAIN()

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

COMPREHENSIVE EXAMPLE

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

AUTHOR

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