configure: Fix info about virt-resize when OCaml bindings are disabled.
[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   /* Create ext2 filesystem on /dev/sdb1 partition. */
261   if (guestfs_part_disk (g, \"/dev/sdb\", \"mbr\") == -1) {
262     printf (\"guestfs_part_disk FAILED\\n\");
263     exit (EXIT_FAILURE);
264   }
265   if (guestfs_mkfs (g, \"ext2\", \"/dev/sdb1\") == -1) {
266     printf (\"guestfs_mkfs (/dev/sdb1) FAILED\\n\");
267     exit (EXIT_FAILURE);
268   }
269
270   nr_tests = %d;
271
272 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
273
274   iteri (
275     fun i test_name ->
276       pr "  test_num++;\n";
277       pr "  if (guestfs_get_verbose (g))\n";
278       pr "    printf (\"-------------------------------------------------------------------------------\\n\");\n";
279       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
280       pr "  if (%s () == -1) {\n" test_name;
281       pr "    printf (\"%s FAILED\\n\");\n" test_name;
282       pr "    n_failed++;\n";
283       pr "  }\n";
284   ) test_names;
285   pr "\n";
286
287   pr "  /* Check close callback is called. */
288   int close_sentinel = 1;
289   guestfs_set_close_callback (g, incr, &close_sentinel);
290
291   guestfs_close (g);
292
293   if (close_sentinel != 2) {
294     fprintf (stderr, \"close callback was not called\\n\");
295     exit (EXIT_FAILURE);
296   }
297
298   unlink (\"test1.img\");
299   unlink (\"test2.img\");
300   unlink (\"test3.img\");
301
302 ";
303
304   pr "  if (n_failed > 0) {\n";
305   pr "    printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n";
306   pr "    exit (EXIT_FAILURE);\n";
307   pr "  }\n";
308   pr "\n";
309
310   pr "  exit (EXIT_SUCCESS);\n";
311   pr "}\n"
312
313 and generate_one_test name flags i (init, prereq, test) =
314   let test_name = sprintf "test_%s_%d" name i in
315
316   pr "\
317 static int %s_skip (void)
318 {
319   const char *str;
320
321   str = getenv (\"TEST_ONLY\");
322   if (str)
323     return strstr (str, \"%s\") == NULL;
324   str = getenv (\"SKIP_%s\");
325   if (str && STREQ (str, \"1\")) return 1;
326   str = getenv (\"SKIP_TEST_%s\");
327   if (str && STREQ (str, \"1\")) return 1;
328   return 0;
329 }
330
331 " test_name name (String.uppercase test_name) (String.uppercase name);
332
333   (match prereq with
334    | Disabled | Always | IfAvailable _ -> ()
335    | If code | Unless code ->
336        pr "static int %s_prereq (void)\n" test_name;
337        pr "{\n";
338        pr "  %s\n" code;
339        pr "}\n";
340        pr "\n";
341   );
342
343   pr "\
344 static int %s (void)
345 {
346   if (%s_skip ()) {
347     printf (\"        %%s skipped (reason: environment variable set)\\n\", \"%s\");
348     return 0;
349   }
350
351 " test_name test_name test_name;
352
353   (* Optional functions should only be tested if the relevant
354    * support is available in the daemon.
355    *)
356   List.iter (
357     function
358     | Optional group ->
359         pr "  if (!is_available (\"%s\")) {\n" group;
360         pr "    printf (\"        %%s skipped (reason: group %%s not available in daemon)\\n\", \"%s\", \"%s\");\n" test_name group;
361         pr "    return 0;\n";
362         pr "  }\n";
363     | _ -> ()
364   ) flags;
365
366   (match prereq with
367    | Disabled ->
368        pr "  printf (\"        %%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
369    | If _ ->
370        pr "  if (! %s_prereq ()) {\n" test_name;
371        pr "    printf (\"        %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
372        pr "    return 0;\n";
373        pr "  }\n";
374        pr "\n";
375        generate_one_test_body name i test_name init test;
376    | Unless _ ->
377        pr "  if (%s_prereq ()) {\n" test_name;
378        pr "    printf (\"        %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
379        pr "    return 0;\n";
380        pr "  }\n";
381        pr "\n";
382        generate_one_test_body name i test_name init test;
383    | IfAvailable group ->
384        pr "  if (!is_available (\"%s\")) {\n" group;
385        pr "    printf (\"        %%s skipped (reason: %%s not available)\\n\", \"%s\", \"%s\");\n" test_name group;
386        pr "    return 0;\n";
387        pr "  }\n";
388        pr "\n";
389        generate_one_test_body name i test_name init test;
390    | Always ->
391        generate_one_test_body name i test_name init test
392   );
393
394   pr "  return 0;\n";
395   pr "}\n";
396   pr "\n";
397   test_name
398
399 and generate_one_test_body name i test_name init test =
400   (match init with
401    | InitNone (* XXX at some point, InitNone and InitEmpty became
402                * folded together as the same thing.  Really we should
403                * make InitNone do nothing at all, but the tests may
404                * need to be checked to make sure this is OK.
405                *)
406    | InitEmpty ->
407        pr "  /* InitNone|InitEmpty for %s */\n" test_name;
408        List.iter (generate_test_command_call test_name)
409          [["blockdev_setrw"; "/dev/sda"];
410           ["umount_all"];
411           ["lvm_remove_all"]]
412    | InitPartition ->
413        pr "  /* InitPartition for %s: create /dev/sda1 */\n" test_name;
414        List.iter (generate_test_command_call test_name)
415          [["blockdev_setrw"; "/dev/sda"];
416           ["umount_all"];
417           ["lvm_remove_all"];
418           ["part_disk"; "/dev/sda"; "mbr"]]
419    | InitBasicFS ->
420        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" 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           ["mkfs"; "ext2"; "/dev/sda1"];
427           ["mount_options"; ""; "/dev/sda1"; "/"]]
428    | InitBasicFSonLVM ->
429        pr "  /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
430          test_name;
431        List.iter (generate_test_command_call test_name)
432          [["blockdev_setrw"; "/dev/sda"];
433           ["umount_all"];
434           ["lvm_remove_all"];
435           ["part_disk"; "/dev/sda"; "mbr"];
436           ["pvcreate"; "/dev/sda1"];
437           ["vgcreate"; "VG"; "/dev/sda1"];
438           ["lvcreate"; "LV"; "VG"; "8"];
439           ["mkfs"; "ext2"; "/dev/VG/LV"];
440           ["mount_options"; ""; "/dev/VG/LV"; "/"]]
441    | InitISOFS ->
442        pr "  /* InitISOFS for %s */\n" test_name;
443        List.iter (generate_test_command_call test_name)
444          [["blockdev_setrw"; "/dev/sda"];
445           ["umount_all"];
446           ["lvm_remove_all"];
447           ["mount_ro"; "/dev/sdd"; "/"]]
448    | InitScratchFS ->
449        pr "  /* InitScratchFS for %s */\n" test_name;
450        List.iter (generate_test_command_call test_name)
451          [["blockdev_setrw"; "/dev/sda"];
452           ["umount_all"];
453           ["lvm_remove_all"];
454           ["mount_options"; ""; "/dev/sdb1"; "/"]]
455   );
456
457   let get_seq_last = function
458     | [] ->
459         failwithf "%s: you cannot use [] (empty list) when expecting a command"
460           test_name
461     | seq ->
462         let seq = List.rev seq in
463         List.rev (List.tl seq), List.hd seq
464   in
465
466   match test with
467   | TestRun seq ->
468       pr "  /* TestRun for %s (%d) */\n" name i;
469       List.iter (generate_test_command_call test_name) seq
470   | TestOutput (seq, expected) ->
471       pr "  /* TestOutput for %s (%d) */\n" name i;
472       pr "  const char *expected = \"%s\";\n" (c_quote expected);
473       let seq, last = get_seq_last seq in
474       let test () =
475         pr "    if (STRNEQ (r, expected)) {\n";
476         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
477         pr "      return -1;\n";
478         pr "    }\n"
479       in
480       List.iter (generate_test_command_call test_name) seq;
481       generate_test_command_call ~test test_name last
482   | TestOutputList (seq, expected) ->
483       pr "  /* TestOutputList for %s (%d) */\n" name i;
484       let seq, last = get_seq_last seq in
485       let test () =
486         iteri (
487           fun i str ->
488             pr "    if (!r[%d]) {\n" i;
489             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
490             pr "      print_strings (r);\n";
491             pr "      return -1;\n";
492             pr "    }\n";
493             pr "    {\n";
494             pr "      const char *expected = \"%s\";\n" (c_quote str);
495             pr "      if (STRNEQ (r[%d], expected)) {\n" i;
496             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
497             pr "        return -1;\n";
498             pr "      }\n";
499             pr "    }\n"
500         ) expected;
501         pr "    if (r[%d] != NULL) {\n" (List.length expected);
502         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
503           test_name;
504         pr "      print_strings (r);\n";
505         pr "      return -1;\n";
506         pr "    }\n"
507       in
508       List.iter (generate_test_command_call test_name) seq;
509       generate_test_command_call ~test test_name last
510   | TestOutputListOfDevices (seq, expected) ->
511       pr "  /* TestOutputListOfDevices for %s (%d) */\n" name i;
512       let seq, last = get_seq_last seq in
513       let test () =
514         iteri (
515           fun i str ->
516             pr "    if (!r[%d]) {\n" i;
517             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
518             pr "      print_strings (r);\n";
519             pr "      return -1;\n";
520             pr "    }\n";
521             pr "    {\n";
522             pr "      const char *expected = \"%s\";\n" (c_quote str);
523             pr "      r[%d][5] = 's';\n" i;
524             pr "      if (STRNEQ (r[%d], expected)) {\n" i;
525             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
526             pr "        return -1;\n";
527             pr "      }\n";
528             pr "    }\n"
529         ) expected;
530         pr "    if (r[%d] != NULL) {\n" (List.length expected);
531         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
532           test_name;
533         pr "      print_strings (r);\n";
534         pr "      return -1;\n";
535         pr "    }\n"
536       in
537       List.iter (generate_test_command_call test_name) seq;
538       generate_test_command_call ~test test_name last
539   | TestOutputInt (seq, expected) ->
540       pr "  /* TestOutputInt for %s (%d) */\n" name i;
541       let seq, last = get_seq_last seq in
542       let test () =
543         pr "    if (r != %d) {\n" expected;
544         pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
545           test_name expected;
546         pr "               (int) r);\n";
547         pr "      return -1;\n";
548         pr "    }\n"
549       in
550       List.iter (generate_test_command_call test_name) seq;
551       generate_test_command_call ~test test_name last
552   | TestOutputIntOp (seq, op, expected) ->
553       pr "  /* TestOutputIntOp for %s (%d) */\n" name i;
554       let seq, last = get_seq_last seq in
555       let test () =
556         pr "    if (! (r %s %d)) {\n" op expected;
557         pr "      fprintf (stderr, \"%s: expected %s %d but got %%d\\n\","
558           test_name op expected;
559         pr "               (int) r);\n";
560         pr "      return -1;\n";
561         pr "    }\n"
562       in
563       List.iter (generate_test_command_call test_name) seq;
564       generate_test_command_call ~test test_name last
565   | TestOutputTrue seq ->
566       pr "  /* TestOutputTrue for %s (%d) */\n" name i;
567       let seq, last = get_seq_last seq in
568       let test () =
569         pr "    if (!r) {\n";
570         pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
571           test_name;
572         pr "      return -1;\n";
573         pr "    }\n"
574       in
575       List.iter (generate_test_command_call test_name) seq;
576       generate_test_command_call ~test test_name last
577   | TestOutputFalse seq ->
578       pr "  /* TestOutputFalse for %s (%d) */\n" name i;
579       let seq, last = get_seq_last seq in
580       let test () =
581         pr "    if (r) {\n";
582         pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
583           test_name;
584         pr "      return -1;\n";
585         pr "    }\n"
586       in
587       List.iter (generate_test_command_call test_name) seq;
588       generate_test_command_call ~test test_name last
589   | TestOutputLength (seq, expected) ->
590       pr "  /* TestOutputLength for %s (%d) */\n" name i;
591       let seq, last = get_seq_last seq in
592       let test () =
593         pr "    int j;\n";
594         pr "    for (j = 0; j < %d; ++j)\n" expected;
595         pr "      if (r[j] == NULL) {\n";
596         pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
597           test_name;
598         pr "        print_strings (r);\n";
599         pr "        return -1;\n";
600         pr "      }\n";
601         pr "    if (r[j] != NULL) {\n";
602         pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
603           test_name;
604         pr "      print_strings (r);\n";
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   | TestOutputBuffer (seq, expected) ->
611       pr "  /* TestOutputBuffer for %s (%d) */\n" name i;
612       pr "  const char *expected = \"%s\";\n" (c_quote expected);
613       let seq, last = get_seq_last seq in
614       let len = String.length expected in
615       let test () =
616         pr "    if (size != %d) {\n" len;
617         pr "      fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len;
618         pr "      return -1;\n";
619         pr "    }\n";
620         pr "    if (STRNEQLEN (r, expected, size)) {\n";
621         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
622         pr "      return -1;\n";
623         pr "    }\n"
624       in
625       List.iter (generate_test_command_call test_name) seq;
626       generate_test_command_call ~test test_name last
627   | TestOutputStruct (seq, checks) ->
628       pr "  /* TestOutputStruct for %s (%d) */\n" name i;
629       let seq, last = get_seq_last seq in
630       let test () =
631         List.iter (
632           function
633           | CompareWithInt (field, expected) ->
634               pr "    if (r->%s != %d) {\n" field expected;
635               pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
636                 test_name field expected;
637               pr "               (int) r->%s);\n" field;
638               pr "      return -1;\n";
639               pr "    }\n"
640           | CompareWithIntOp (field, op, expected) ->
641               pr "    if (!(r->%s %s %d)) {\n" field op expected;
642               pr "      fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n"
643                 test_name field op expected;
644               pr "               (int) r->%s);\n" field;
645               pr "      return -1;\n";
646               pr "    }\n"
647           | CompareWithString (field, expected) ->
648               pr "    if (STRNEQ (r->%s, \"%s\")) {\n" field expected;
649               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
650                 test_name field expected;
651               pr "               r->%s);\n" field;
652               pr "      return -1;\n";
653               pr "    }\n"
654           | CompareFieldsIntEq (field1, field2) ->
655               pr "    if (r->%s != r->%s) {\n" field1 field2;
656               pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
657                 test_name field1 field2;
658               pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
659               pr "      return -1;\n";
660               pr "    }\n"
661           | CompareFieldsStrEq (field1, field2) ->
662               pr "    if (STRNEQ (r->%s, r->%s)) {\n" field1 field2;
663               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
664                 test_name field1 field2;
665               pr "               r->%s, r->%s);\n" field1 field2;
666               pr "      return -1;\n";
667               pr "    }\n"
668         ) checks
669       in
670       List.iter (generate_test_command_call test_name) seq;
671       generate_test_command_call ~test test_name last
672   | TestOutputFileMD5 (seq, filename) ->
673       pr "  /* TestOutputFileMD5 for %s (%d) */\n" name i;
674       pr "  char expected[33];\n";
675       pr "  md5sum (\"%s\", expected);\n" filename;
676       let seq, last = get_seq_last seq in
677       let test () =
678         pr "    if (STRNEQ (r, expected)) {\n";
679         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
680         pr "      return -1;\n";
681         pr "    }\n"
682       in
683       List.iter (generate_test_command_call test_name) seq;
684       generate_test_command_call ~test test_name last
685   | TestOutputDevice (seq, expected) ->
686       pr "  /* TestOutputDevice for %s (%d) */\n" name i;
687       pr "  const char *expected = \"%s\";\n" (c_quote expected);
688       let seq, last = get_seq_last seq in
689       let test () =
690         pr "    r[5] = 's';\n";
691         pr "    if (STRNEQ (r, expected)) {\n";
692         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
693         pr "      return -1;\n";
694         pr "    }\n"
695       in
696       List.iter (generate_test_command_call test_name) seq;
697       generate_test_command_call ~test test_name last
698   | TestLastFail seq ->
699       pr "  /* TestLastFail for %s (%d) */\n" name i;
700       let seq, last = get_seq_last seq in
701       List.iter (generate_test_command_call test_name) seq;
702       generate_test_command_call test_name ~expect_error:true last
703
704 (* Generate the code to run a command, leaving the result in 'r'.
705  * If you expect to get an error then you should set expect_error:true.
706  *)
707 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
708   match cmd with
709   | [] -> assert false
710   | name :: args ->
711       (* Look up the command to find out what args/ret it has. *)
712       let style_ret, style_args, style_optargs =
713         try
714           let _, style, _, _, _, _, _ =
715             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
716           style
717         with Not_found ->
718           failwithf "%s: in test, command %s was not found" test_name name in
719
720       (* Match up the arguments strings and argument types. *)
721       let args, optargs =
722         let rec loop argts args =
723           match argts, args with
724           | (t::ts), (s::ss) ->
725               let args, rest = loop ts ss in
726               ((t, s) :: args), rest
727           | [], ss -> [], ss
728           | ts, [] ->
729               failwithf "%s: in test, too few args given to function %s"
730                 test_name name
731         in
732         let args, optargs = loop style_args args in
733         let optargs, rest = loop style_optargs optargs in
734         if rest <> [] then
735           failwithf "%s: in test, too many args given to function %s"
736             test_name name;
737         args, optargs in
738
739       pr "  {\n";
740
741       List.iter (
742         function
743         | OptString n, "NULL" -> ()
744         | Pathname n, arg
745         | Device n, arg
746         | Dev_or_Path n, arg
747         | String n, arg
748         | OptString n, arg
749         | Key n, arg ->
750             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
751         | BufferIn n, arg ->
752             pr "    const char *%s = \"%s\";\n" n (c_quote arg);
753             pr "    size_t %s_size = %d;\n" n (String.length arg)
754         | Int _, _
755         | Int64 _, _
756         | Bool _, _
757         | FileIn _, _ | FileOut _, _ -> ()
758         | StringList n, "" | DeviceList n, "" ->
759             pr "    const char *const %s[1] = { NULL };\n" n
760         | StringList n, arg | DeviceList n, arg ->
761             let strs = string_split " " arg in
762             iteri (
763               fun i str ->
764                 pr "    const char *%s_%d = \"%s\";\n" n i (c_quote str);
765             ) strs;
766             pr "    const char *const %s[] = {\n" n;
767             iteri (
768               fun i _ -> pr "      %s_%d,\n" n i
769             ) strs;
770             pr "      NULL\n";
771             pr "    };\n";
772         | Pointer _, _ ->
773             (* Difficult to make these pointers in order to run a test. *)
774             assert false
775       ) args;
776
777       if optargs <> [] then (
778         pr "    struct guestfs_%s_argv optargs;\n" name;
779         let bitmask = List.fold_left (
780           fun bitmask optarg ->
781             let is_set =
782               match optarg with
783               | Bool n, "" -> false
784               | Bool n, "true" ->
785                   pr "    optargs.%s = 1;\n" n; true
786               | Bool n, "false" ->
787                   pr "    optargs.%s = 0;\n" n; true
788               | Bool n, arg ->
789                   failwithf "boolean optional arg '%s' should be empty string or \"true\" or \"false\"" n
790               | Int n, "" -> false
791               | Int n, i ->
792                   let i =
793                     try int_of_string i
794                     with Failure _ -> failwithf "integer optional arg '%s' should be empty string or number" n in
795                   pr "    optargs.%s = %d;\n" n i; true
796               | Int64 n, "" -> false
797               | Int64 n, i ->
798                   let i =
799                     try Int64.of_string i
800                     with Failure _ -> failwithf "int64 optional arg '%s' should be empty string or number" n in
801                   pr "    optargs.%s = %Ld;\n" n i; true
802               | String n, "NOARG" -> false
803               | String n, arg ->
804                   pr "    optargs.%s = \"%s\";\n" n (c_quote arg); true
805               | _ -> assert false in
806             let bitmask = Int64.shift_left bitmask 1 in
807             let bitmask = if is_set then Int64.succ bitmask else bitmask in
808             bitmask
809         ) 0L optargs in
810         pr "    optargs.bitmask = UINT64_C(0x%Lx);\n" bitmask;
811       );
812
813       (match style_ret with
814        | RErr | RInt _ | RBool _ -> pr "    int r;\n"
815        | RInt64 _ -> pr "    int64_t r;\n"
816        | RConstString _ | RConstOptString _ ->
817            pr "    const char *r;\n"
818        | RString _ -> pr "    char *r;\n"
819        | RStringList _ | RHashtable _ ->
820            pr "    char **r;\n";
821            pr "    size_t i;\n"
822        | RStruct (_, typ) ->
823            pr "    struct guestfs_%s *r;\n" typ
824        | RStructList (_, typ) ->
825            pr "    struct guestfs_%s_list *r;\n" typ
826        | RBufferOut _ ->
827            pr "    char *r;\n";
828            pr "    size_t size;\n"
829       );
830
831       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
832       if optargs = [] then
833         pr "    r = guestfs_%s (g" name
834       else
835         pr "    r = guestfs_%s_argv (g" name;
836
837       (* Generate the parameters. *)
838       List.iter (
839         function
840         | OptString _, "NULL" -> pr ", NULL"
841         | Pathname n, _
842         | Device n, _ | Dev_or_Path n, _
843         | String n, _
844         | OptString n, _
845         | Key n, _ ->
846             pr ", %s" n
847         | BufferIn n, _ ->
848             pr ", %s, %s_size" n n
849         | FileIn _, arg | FileOut _, arg ->
850             pr ", \"%s\"" (c_quote arg)
851         | StringList n, _ | DeviceList n, _ ->
852             pr ", (char **) %s" n
853         | Int _, arg ->
854             let i =
855               try int_of_string arg
856               with Failure "int_of_string" ->
857                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
858             pr ", %d" i
859         | Int64 _, arg ->
860             let i =
861               try Int64.of_string arg
862               with Failure "int_of_string" ->
863                 failwithf "%s: expecting an int64, but got '%s'" test_name arg in
864             pr ", %Ld" i
865         | Bool _, arg ->
866             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
867         | Pointer _, _ -> assert false
868       ) args;
869
870       (match style_ret with
871        | RBufferOut _ -> pr ", &size"
872        | _ -> ()
873       );
874
875       if optargs <> [] then
876         pr ", &optargs";
877
878       pr ");\n";
879
880       (match errcode_of_ret style_ret, expect_error with
881        | `CannotReturnError, _ -> ()
882        | `ErrorIsMinusOne, false ->
883            pr "    if (r == -1)\n";
884            pr "      return -1;\n";
885        | `ErrorIsMinusOne, true ->
886            pr "    if (r != -1)\n";
887            pr "      return -1;\n";
888        | `ErrorIsNULL, false ->
889            pr "    if (r == NULL)\n";
890            pr "      return -1;\n";
891        | `ErrorIsNULL, true ->
892            pr "    if (r != NULL)\n";
893            pr "      return -1;\n";
894       );
895
896       (* Insert the test code. *)
897       (match test with
898        | None -> ()
899        | Some f -> f ()
900       );
901
902       (match style_ret with
903        | RErr | RInt _ | RInt64 _ | RBool _
904        | RConstString _ | RConstOptString _ -> ()
905        | RString _ | RBufferOut _ -> pr "    free (r);\n"
906        | RStringList _ | RHashtable _ ->
907            pr "    for (i = 0; r[i] != NULL; ++i)\n";
908            pr "      free (r[i]);\n";
909            pr "    free (r);\n"
910        | RStruct (_, typ) ->
911            pr "    guestfs_free_%s (r);\n" typ
912        | RStructList (_, typ) ->
913            pr "    guestfs_free_%s_list (r);\n" typ
914       );
915
916       pr "  }\n"