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

OPTIONS

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

BASIC EXAMPLE

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

DIRECTIVE SUMMARY

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

TEST-DEFINING DIRECTIVES

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

USER CODE IN MAIN()

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

COMPREHENSIVE EXAMPLE

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

AUTHOR

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