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