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