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