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