generator: Properly lay out and indent multi-line C function decls.
[libguestfs.git] / generator / generator_capitests.ml
1 (* libguestfs
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Printf
22
23 open Generator_types
24 open Generator_utils
25 open Generator_pr
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30
31 (* Generate the tests. *)
32 let rec generate_tests () =
33   generate_header CStyle GPLv2plus;
34
35   pr "\
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <fcntl.h>
42
43 #include \"guestfs.h\"
44 #include \"guestfs-internal.h\"
45
46 static guestfs_h *g;
47 static int suppress_error = 0;
48
49 static void print_error (guestfs_h *g, void *data, const char *msg)
50 {
51   if (!suppress_error)
52     fprintf (stderr, \"%%s\\n\", msg);
53 }
54
55 /* FIXME: nearly identical code appears in fish.c */
56 static void print_strings (char *const *argv)
57 {
58   size_t argc;
59
60   for (argc = 0; argv[argc] != NULL; ++argc)
61     printf (\"\\t%%s\\n\", argv[argc]);
62 }
63
64 /*
65 static void print_table (char const *const *argv)
66 {
67   size_t i;
68
69   for (i = 0; argv[i] != NULL; i += 2)
70     printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
71 }
72 */
73
74 static int
75 is_available (const char *group)
76 {
77   const char *groups[] = { group, NULL };
78   int r;
79
80   suppress_error = 1;
81   r = guestfs_available (g, (char **) groups);
82   suppress_error = 0;
83
84   return r == 0;
85 }
86
87 static void
88 incr (guestfs_h *g, void *iv)
89 {
90   int *i = (int *) iv;
91   (*i)++;
92 }
93
94 /* Get md5sum of the named file. */
95 static void
96 md5sum (const char *filename, char *result)
97 {
98   char cmd[256];
99   snprintf (cmd, sizeof cmd, \"md5sum %%s\", filename);
100   FILE *pp = popen (cmd, \"r\");
101   if (pp == NULL) {
102     perror (cmd);
103     exit (EXIT_FAILURE);
104   }
105   if (fread (result, 1, 32, pp) != 32) {
106     perror (\"md5sum: fread\");
107     exit (EXIT_FAILURE);
108   }
109   if (pclose (pp) == -1) {
110     perror (\"pclose\");
111     exit (EXIT_FAILURE);
112   }
113   result[32] = '\\0';
114 }
115
116 ";
117
118   (* Generate a list of commands which are not tested anywhere. *)
119   pr "static void no_test_warnings (void)\n";
120   pr "{\n";
121
122   let hash : (string, bool) Hashtbl.t = Hashtbl.create 13 in
123   List.iter (
124     fun (_, _, _, _, tests, _, _) ->
125       let tests = filter_map (
126         function
127         | (_, (Always|If _|Unless _|IfAvailable _), test) -> Some test
128         | (_, Disabled, _) -> None
129       ) tests in
130       let seq = List.concat (List.map seq_of_test tests) in
131       let cmds_tested = List.map List.hd seq in
132       List.iter (fun cmd -> Hashtbl.replace hash cmd true) cmds_tested
133   ) all_functions;
134
135   List.iter (
136     fun (name, _, _, _, _, _, _) ->
137       if not (Hashtbl.mem hash name) then
138         pr "  fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
139   ) all_functions;
140
141   pr "}\n";
142   pr "\n";
143
144   (* Generate the actual tests.  Note that we generate the tests
145    * in reverse order, deliberately, so that (in general) the
146    * newest tests run first.  This makes it quicker and easier to
147    * debug them.
148    *)
149   let test_names =
150     List.map (
151       fun (name, _, _, flags, tests, _, _) ->
152         mapi (generate_one_test name flags) tests
153     ) (List.rev all_functions) in
154   let test_names = List.concat test_names in
155   let nr_tests = List.length test_names in
156
157   pr "\
158 int main (int argc, char *argv[])
159 {
160   char c = 0;
161   unsigned long int n_failed = 0;
162   const char *filename;
163   int fd;
164   int nr_tests, test_num = 0;
165
166   setbuf (stdout, NULL);
167
168   no_test_warnings ();
169
170   g = guestfs_create ();
171   if (g == NULL) {
172     printf (\"guestfs_create FAILED\\n\");
173     exit (EXIT_FAILURE);
174   }
175
176   guestfs_set_error_handler (g, print_error, NULL);
177
178   filename = \"test1.img\";
179   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
180   if (fd == -1) {
181     perror (filename);
182     exit (EXIT_FAILURE);
183   }
184   if (ftruncate (fd, %d) == -1) {
185     perror (\"ftruncate\");
186     close (fd);
187     unlink (filename);
188     exit (EXIT_FAILURE);
189   }
190   if (close (fd) == -1) {
191     perror (filename);
192     unlink (filename);
193     exit (EXIT_FAILURE);
194   }
195   if (guestfs_add_drive (g, filename) == -1) {
196     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
197     exit (EXIT_FAILURE);
198   }
199
200   filename = \"test2.img\";
201   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
202   if (fd == -1) {
203     perror (filename);
204     exit (EXIT_FAILURE);
205   }
206   if (ftruncate (fd, %d) == -1) {
207     perror (\"ftruncate\");
208     close (fd);
209     unlink (filename);
210     exit (EXIT_FAILURE);
211   }
212   if (close (fd) == -1) {
213     perror (filename);
214     unlink (filename);
215     exit (EXIT_FAILURE);
216   }
217   if (guestfs_add_drive (g, filename) == -1) {
218     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
219     exit (EXIT_FAILURE);
220   }
221
222   filename = \"test3.img\";
223   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
224   if (fd == -1) {
225     perror (filename);
226     exit (EXIT_FAILURE);
227   }
228   if (ftruncate (fd, %d) == -1) {
229     perror (\"ftruncate\");
230     close (fd);
231     unlink (filename);
232     exit (EXIT_FAILURE);
233   }
234   if (close (fd) == -1) {
235     perror (filename);
236     unlink (filename);
237     exit (EXIT_FAILURE);
238   }
239   if (guestfs_add_drive (g, filename) == -1) {
240     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
241     exit (EXIT_FAILURE);
242   }
243
244   if (guestfs_add_drive_ro (g, \"../images/test.iso\") == -1) {
245     printf (\"guestfs_add_drive_ro ../images/test.iso FAILED\\n\");
246     exit (EXIT_FAILURE);
247   }
248
249   /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
250   alarm (600);
251
252   if (guestfs_launch (g) == -1) {
253     printf (\"guestfs_launch FAILED\\n\");
254     exit (EXIT_FAILURE);
255   }
256
257   /* Cancel previous alarm. */
258   alarm (0);
259
260   nr_tests = %d;
261
262 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
263
264   iteri (
265     fun i test_name ->
266       pr "  test_num++;\n";
267       pr "  if (guestfs_get_verbose (g))\n";
268       pr "    printf (\"-------------------------------------------------------------------------------\\n\");\n";
269       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
270       pr "  if (%s () == -1) {\n" test_name;
271       pr "    printf (\"%s FAILED\\n\");\n" test_name;
272       pr "    n_failed++;\n";
273       pr "  }\n";
274   ) test_names;
275   pr "\n";
276
277   pr "  /* Check close callback is called. */
278   int close_sentinel = 1;
279   guestfs_set_close_callback (g, incr, &close_sentinel);
280
281   guestfs_close (g);
282
283   if (close_sentinel != 2) {
284     fprintf (stderr, \"close callback was not called\\n\");
285     exit (EXIT_FAILURE);
286   }
287
288   unlink (\"test1.img\");
289   unlink (\"test2.img\");
290   unlink (\"test3.img\");
291
292 ";
293
294   pr "  if (n_failed > 0) {\n";
295   pr "    printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n";
296   pr "    exit (EXIT_FAILURE);\n";
297   pr "  }\n";
298   pr "\n";
299
300   pr "  exit (EXIT_SUCCESS);\n";
301   pr "}\n"
302
303 and generate_one_test name flags i (init, prereq, test) =
304   let test_name = sprintf "test_%s_%d" name i in
305
306   pr "\
307 static int %s_skip (void)
308 {
309   const char *str;
310
311   str = getenv (\"TEST_ONLY\");
312   if (str)
313     return strstr (str, \"%s\") == NULL;
314   str = getenv (\"SKIP_%s\");
315   if (str && STREQ (str, \"1\")) return 1;
316   str = getenv (\"SKIP_TEST_%s\");
317   if (str && STREQ (str, \"1\")) return 1;
318   return 0;
319 }
320
321 " test_name name (String.uppercase test_name) (String.uppercase name);
322
323   (match prereq with
324    | Disabled | Always | IfAvailable _ -> ()
325    | If code | Unless code ->
326        pr "static int %s_prereq (void)\n" test_name;
327        pr "{\n";
328        pr "  %s\n" code;
329        pr "}\n";
330        pr "\n";
331   );
332
333   pr "\
334 static int %s (void)
335 {
336   if (%s_skip ()) {
337     printf (\"        %%s skipped (reason: environment variable set)\\n\", \"%s\");
338     return 0;
339   }
340
341 " test_name test_name test_name;
342
343   (* Optional functions should only be tested if the relevant
344    * support is available in the daemon.
345    *)
346   List.iter (
347     function
348     | Optional group ->
349         pr "  if (!is_available (\"%s\")) {\n" group;
350         pr "    printf (\"        %%s skipped (reason: group %%s not available in daemon)\\n\", \"%s\", \"%s\");\n" test_name group;
351         pr "    return 0;\n";
352         pr "  }\n";
353     | _ -> ()
354   ) flags;
355
356   (match prereq with
357    | Disabled ->
358        pr "  printf (\"        %%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
359    | If _ ->
360        pr "  if (! %s_prereq ()) {\n" test_name;
361        pr "    printf (\"        %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
362        pr "    return 0;\n";
363        pr "  }\n";
364        pr "\n";
365        generate_one_test_body name i test_name init test;
366    | Unless _ ->
367        pr "  if (%s_prereq ()) {\n" test_name;
368        pr "    printf (\"        %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
369        pr "    return 0;\n";
370        pr "  }\n";
371        pr "\n";
372        generate_one_test_body name i test_name init test;
373    | IfAvailable group ->
374        pr "  if (!is_available (\"%s\")) {\n" group;
375        pr "    printf (\"        %%s skipped (reason: %%s not available)\\n\", \"%s\", \"%s\");\n" test_name group;
376        pr "    return 0;\n";
377        pr "  }\n";
378        pr "\n";
379        generate_one_test_body name i test_name init test;
380    | Always ->
381        generate_one_test_body name i test_name init test
382   );
383
384   pr "  return 0;\n";
385   pr "}\n";
386   pr "\n";
387   test_name
388
389 and generate_one_test_body name i test_name init test =
390   (match init with
391    | InitNone (* XXX at some point, InitNone and InitEmpty became
392                * folded together as the same thing.  Really we should
393                * make InitNone do nothing at all, but the tests may
394                * need to be checked to make sure this is OK.
395                *)
396    | InitEmpty ->
397        pr "  /* InitNone|InitEmpty for %s */\n" test_name;
398        List.iter (generate_test_command_call test_name)
399          [["blockdev_setrw"; "/dev/sda"];
400           ["umount_all"];
401           ["lvm_remove_all"]]
402    | InitPartition ->
403        pr "  /* InitPartition for %s: create /dev/sda1 */\n" test_name;
404        List.iter (generate_test_command_call test_name)
405          [["blockdev_setrw"; "/dev/sda"];
406           ["umount_all"];
407           ["lvm_remove_all"];
408           ["part_disk"; "/dev/sda"; "mbr"]]
409    | InitBasicFS ->
410        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
411        List.iter (generate_test_command_call test_name)
412          [["blockdev_setrw"; "/dev/sda"];
413           ["umount_all"];
414           ["lvm_remove_all"];
415           ["part_disk"; "/dev/sda"; "mbr"];
416           ["mkfs"; "ext2"; "/dev/sda1"];
417           ["mount_options"; ""; "/dev/sda1"; "/"]]
418    | InitBasicFSonLVM ->
419        pr "  /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
420          test_name;
421        List.iter (generate_test_command_call test_name)
422          [["blockdev_setrw"; "/dev/sda"];
423           ["umount_all"];
424           ["lvm_remove_all"];
425           ["part_disk"; "/dev/sda"; "mbr"];
426           ["pvcreate"; "/dev/sda1"];
427           ["vgcreate"; "VG"; "/dev/sda1"];
428           ["lvcreate"; "LV"; "VG"; "8"];
429           ["mkfs"; "ext2"; "/dev/VG/LV"];
430           ["mount_options"; ""; "/dev/VG/LV"; "/"]]
431    | InitISOFS ->
432        pr "  /* InitISOFS for %s */\n" test_name;
433        List.iter (generate_test_command_call test_name)
434          [["blockdev_setrw"; "/dev/sda"];
435           ["umount_all"];
436           ["lvm_remove_all"];
437           ["mount_ro"; "/dev/sdd"; "/"]]
438   );
439
440   let get_seq_last = function
441     | [] ->
442         failwithf "%s: you cannot use [] (empty list) when expecting a command"
443           test_name
444     | seq ->
445         let seq = List.rev seq in
446         List.rev (List.tl seq), List.hd seq
447   in
448
449   match test with
450   | TestRun seq ->
451       pr "  /* TestRun for %s (%d) */\n" name i;
452       List.iter (generate_test_command_call test_name) seq
453   | TestOutput (seq, expected) ->
454       pr "  /* TestOutput for %s (%d) */\n" name i;
455       pr "  const char *expected = \"%s\";\n" (c_quote expected);
456       let seq, last = get_seq_last seq in
457       let test () =
458         pr "    if (STRNEQ (r, expected)) {\n";
459         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
460         pr "      return -1;\n";
461         pr "    }\n"
462       in
463       List.iter (generate_test_command_call test_name) seq;
464       generate_test_command_call ~test test_name last
465   | TestOutputList (seq, expected) ->
466       pr "  /* TestOutputList for %s (%d) */\n" name i;
467       let seq, last = get_seq_last seq in
468       let test () =
469         iteri (
470           fun i str ->
471             pr "    if (!r[%d]) {\n" i;
472             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
473             pr "      print_strings (r);\n";
474             pr "      return -1;\n";
475             pr "    }\n";
476             pr "    {\n";
477             pr "      const char *expected = \"%s\";\n" (c_quote str);
478             pr "      if (STRNEQ (r[%d], expected)) {\n" i;
479             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
480             pr "        return -1;\n";
481             pr "      }\n";
482             pr "    }\n"
483         ) expected;
484         pr "    if (r[%d] != NULL) {\n" (List.length expected);
485         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
486           test_name;
487         pr "      print_strings (r);\n";
488         pr "      return -1;\n";
489         pr "    }\n"
490       in
491       List.iter (generate_test_command_call test_name) seq;
492       generate_test_command_call ~test test_name last
493   | TestOutputListOfDevices (seq, expected) ->
494       pr "  /* TestOutputListOfDevices for %s (%d) */\n" name i;
495       let seq, last = get_seq_last seq in
496       let test () =
497         iteri (
498           fun i str ->
499             pr "    if (!r[%d]) {\n" i;
500             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
501             pr "      print_strings (r);\n";
502             pr "      return -1;\n";
503             pr "    }\n";
504             pr "    {\n";
505             pr "      const char *expected = \"%s\";\n" (c_quote str);
506             pr "      r[%d][5] = 's';\n" i;
507             pr "      if (STRNEQ (r[%d], expected)) {\n" i;
508             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
509             pr "        return -1;\n";
510             pr "      }\n";
511             pr "    }\n"
512         ) expected;
513         pr "    if (r[%d] != NULL) {\n" (List.length expected);
514         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
515           test_name;
516         pr "      print_strings (r);\n";
517         pr "      return -1;\n";
518         pr "    }\n"
519       in
520       List.iter (generate_test_command_call test_name) seq;
521       generate_test_command_call ~test test_name last
522   | TestOutputInt (seq, expected) ->
523       pr "  /* TestOutputInt for %s (%d) */\n" name i;
524       let seq, last = get_seq_last seq in
525       let test () =
526         pr "    if (r != %d) {\n" expected;
527         pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
528           test_name expected;
529         pr "               (int) r);\n";
530         pr "      return -1;\n";
531         pr "    }\n"
532       in
533       List.iter (generate_test_command_call test_name) seq;
534       generate_test_command_call ~test test_name last
535   | TestOutputIntOp (seq, op, expected) ->
536       pr "  /* TestOutputIntOp for %s (%d) */\n" name i;
537       let seq, last = get_seq_last seq in
538       let test () =
539         pr "    if (! (r %s %d)) {\n" op expected;
540         pr "      fprintf (stderr, \"%s: expected %s %d but got %%d\\n\","
541           test_name op expected;
542         pr "               (int) r);\n";
543         pr "      return -1;\n";
544         pr "    }\n"
545       in
546       List.iter (generate_test_command_call test_name) seq;
547       generate_test_command_call ~test test_name last
548   | TestOutputTrue seq ->
549       pr "  /* TestOutputTrue for %s (%d) */\n" name i;
550       let seq, last = get_seq_last seq in
551       let test () =
552         pr "    if (!r) {\n";
553         pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
554           test_name;
555         pr "      return -1;\n";
556         pr "    }\n"
557       in
558       List.iter (generate_test_command_call test_name) seq;
559       generate_test_command_call ~test test_name last
560   | TestOutputFalse seq ->
561       pr "  /* TestOutputFalse for %s (%d) */\n" name i;
562       let seq, last = get_seq_last seq in
563       let test () =
564         pr "    if (r) {\n";
565         pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
566           test_name;
567         pr "      return -1;\n";
568         pr "    }\n"
569       in
570       List.iter (generate_test_command_call test_name) seq;
571       generate_test_command_call ~test test_name last
572   | TestOutputLength (seq, expected) ->
573       pr "  /* TestOutputLength for %s (%d) */\n" name i;
574       let seq, last = get_seq_last seq in
575       let test () =
576         pr "    int j;\n";
577         pr "    for (j = 0; j < %d; ++j)\n" expected;
578         pr "      if (r[j] == NULL) {\n";
579         pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
580           test_name;
581         pr "        print_strings (r);\n";
582         pr "        return -1;\n";
583         pr "      }\n";
584         pr "    if (r[j] != NULL) {\n";
585         pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
586           test_name;
587         pr "      print_strings (r);\n";
588         pr "      return -1;\n";
589         pr "    }\n"
590       in
591       List.iter (generate_test_command_call test_name) seq;
592       generate_test_command_call ~test test_name last
593   | TestOutputBuffer (seq, expected) ->
594       pr "  /* TestOutputBuffer for %s (%d) */\n" name i;
595       pr "  const char *expected = \"%s\";\n" (c_quote expected);
596       let seq, last = get_seq_last seq in
597       let len = String.length expected in
598       let test () =
599         pr "    if (size != %d) {\n" len;
600         pr "      fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len;
601         pr "      return -1;\n";
602         pr "    }\n";
603         pr "    if (STRNEQLEN (r, expected, size)) {\n";
604         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
605         pr "      return -1;\n";
606         pr "    }\n"
607       in
608       List.iter (generate_test_command_call test_name) seq;
609       generate_test_command_call ~test test_name last
610   | TestOutputStruct (seq, checks) ->
611       pr "  /* TestOutputStruct for %s (%d) */\n" name i;
612       let seq, last = get_seq_last seq in
613       let test () =
614         List.iter (
615           function
616           | CompareWithInt (field, expected) ->
617               pr "    if (r->%s != %d) {\n" field expected;
618               pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
619                 test_name field expected;
620               pr "               (int) r->%s);\n" field;
621               pr "      return -1;\n";
622               pr "    }\n"
623           | CompareWithIntOp (field, op, expected) ->
624               pr "    if (!(r->%s %s %d)) {\n" field op expected;
625               pr "      fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n"
626                 test_name field op expected;
627               pr "               (int) r->%s);\n" field;
628               pr "      return -1;\n";
629               pr "    }\n"
630           | CompareWithString (field, expected) ->
631               pr "    if (STRNEQ (r->%s, \"%s\")) {\n" field expected;
632               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
633                 test_name field expected;
634               pr "               r->%s);\n" field;
635               pr "      return -1;\n";
636               pr "    }\n"
637           | CompareFieldsIntEq (field1, field2) ->
638               pr "    if (r->%s != r->%s) {\n" field1 field2;
639               pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
640                 test_name field1 field2;
641               pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
642               pr "      return -1;\n";
643               pr "    }\n"
644           | CompareFieldsStrEq (field1, field2) ->
645               pr "    if (STRNEQ (r->%s, r->%s)) {\n" field1 field2;
646               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
647                 test_name field1 field2;
648               pr "               r->%s, r->%s);\n" field1 field2;
649               pr "      return -1;\n";
650               pr "    }\n"
651         ) checks
652       in
653       List.iter (generate_test_command_call test_name) seq;
654       generate_test_command_call ~test test_name last
655   | TestOutputFileMD5 (seq, filename) ->
656       pr "  /* TestOutputFileMD5 for %s (%d) */\n" name i;
657       pr "  char expected[33];\n";
658       pr "  md5sum (\"%s\", expected);\n" filename;
659       let seq, last = get_seq_last seq in
660       let test () =
661         pr "    if (STRNEQ (r, expected)) {\n";
662         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
663         pr "      return -1;\n";
664         pr "    }\n"
665       in
666       List.iter (generate_test_command_call test_name) seq;
667       generate_test_command_call ~test test_name last
668   | TestOutputDevice (seq, expected) ->
669       pr "  /* TestOutputDevice for %s (%d) */\n" name i;
670       pr "  const char *expected = \"%s\";\n" (c_quote expected);
671       let seq, last = get_seq_last seq in
672       let test () =
673         pr "    r[5] = 's';\n";
674         pr "    if (STRNEQ (r, expected)) {\n";
675         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
676         pr "      return -1;\n";
677         pr "    }\n"
678       in
679       List.iter (generate_test_command_call test_name) seq;
680       generate_test_command_call ~test test_name last
681   | TestLastFail seq ->
682       pr "  /* TestLastFail for %s (%d) */\n" name i;
683       let seq, last = get_seq_last seq in
684       List.iter (generate_test_command_call test_name) seq;
685       generate_test_command_call test_name ~expect_error:true last
686
687 (* Generate the code to run a command, leaving the result in 'r'.
688  * If you expect to get an error then you should set expect_error:true.
689  *)
690 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
691   match cmd with
692   | [] -> assert false
693   | name :: args ->
694       (* Look up the command to find out what args/ret it has. *)
695       let style =
696         try
697           let _, style, _, _, _, _, _ =
698             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
699           style
700         with Not_found ->
701           failwithf "%s: in test, command %s was not found" test_name name in
702
703       (* If the call has optional args, fold them all together.  We cannot
704        * test partial optional args yet.
705        *)
706       let style =
707         let ret, args, optargs = style in
708         ret, args@optargs in
709
710       if List.length (snd style) <> List.length args then
711         failwithf "%s: in test, wrong number of args given to %s"
712           test_name name;
713
714       pr "  {\n";
715
716       List.iter (
717         function
718         | OptString n, "NULL" -> ()
719         | Pathname n, arg
720         | Device n, arg
721         | Dev_or_Path n, arg
722         | String n, arg
723         | OptString n, arg
724         | Key n, arg ->
725             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
726         | BufferIn n, arg ->
727             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
728             pr "    size_t %s_size = %d;\n" n (String.length arg)
729         | Int _, _
730         | Int64 _, _
731         | Bool _, _
732         | FileIn _, _ | FileOut _, _ -> ()
733         | StringList n, "" | DeviceList n, "" ->
734             pr "    const char *const %s[1] = { NULL };\n" n
735         | StringList n, arg | DeviceList n, arg ->
736             let strs = string_split " " arg in
737             iteri (
738               fun i str ->
739                 pr "    const char *%s_%d = \"%s\";\n" n i (c_quote str);
740             ) strs;
741             pr "    const char *const %s[] = {\n" n;
742             iteri (
743               fun i _ -> pr "      %s_%d,\n" n i
744             ) strs;
745             pr "      NULL\n";
746             pr "    };\n";
747       ) (List.combine (snd style) args);
748
749       let error_code =
750         match fst style with
751         | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
752         | RInt64 _ -> pr "    int64_t r;\n"; "-1"
753         | RConstString _ | RConstOptString _ ->
754             pr "    const char *r;\n"; "NULL"
755         | RString _ -> pr "    char *r;\n"; "NULL"
756         | RStringList _ | RHashtable _ ->
757             pr "    char **r;\n";
758             pr "    size_t i;\n";
759             "NULL"
760         | RStruct (_, typ) ->
761             pr "    struct guestfs_%s *r;\n" typ; "NULL"
762         | RStructList (_, typ) ->
763             pr "    struct guestfs_%s_list *r;\n" typ; "NULL"
764         | RBufferOut _ ->
765             pr "    char *r;\n";
766             pr "    size_t size;\n";
767             "NULL" in
768
769       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
770       pr "    r = guestfs_%s (g" name;
771
772       (* Generate the parameters. *)
773       List.iter (
774         function
775         | OptString _, "NULL" -> pr ", NULL"
776         | Pathname n, _
777         | Device n, _ | Dev_or_Path n, _
778         | String n, _
779         | OptString n, _
780         | Key n, _ ->
781             pr ", %s" n
782         | BufferIn n, _ ->
783             pr ", %s, %s_size" n n
784         | FileIn _, arg | FileOut _, arg ->
785             pr ", \"%s\"" (c_quote arg)
786         | StringList n, _ | DeviceList n, _ ->
787             pr ", (char **) %s" n
788         | Int _, arg ->
789             let i =
790               try int_of_string arg
791               with Failure "int_of_string" ->
792                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
793             pr ", %d" i
794         | Int64 _, arg ->
795             let i =
796               try Int64.of_string arg
797               with Failure "int_of_string" ->
798                 failwithf "%s: expecting an int64, but got '%s'" test_name arg in
799             pr ", %Ld" i
800         | Bool _, arg ->
801             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
802       ) (List.combine (snd style) args);
803
804       (match fst style with
805        | RBufferOut _ -> pr ", &size"
806        | _ -> ()
807       );
808
809       pr ");\n";
810
811       if not expect_error then
812         pr "    if (r == %s)\n" error_code
813       else
814         pr "    if (r != %s)\n" error_code;
815       pr "      return -1;\n";
816
817       (* Insert the test code. *)
818       (match test with
819        | None -> ()
820        | Some f -> f ()
821       );
822
823       (match fst style with
824        | RErr | RInt _ | RInt64 _ | RBool _
825        | RConstString _ | RConstOptString _ -> ()
826        | RString _ | RBufferOut _ -> pr "    free (r);\n"
827        | RStringList _ | RHashtable _ ->
828            pr "    for (i = 0; r[i] != NULL; ++i)\n";
829            pr "      free (r[i]);\n";
830            pr "    free (r);\n"
831        | RStruct (_, typ) ->
832            pr "    guestfs_free_%s (r);\n" typ
833        | RStructList (_, typ) ->
834            pr "    guestfs_free_%s_list (r);\n" typ
835       );
836
837       pr "  }\n"