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