2 * Copyright (C) 2009-2010 Red Hat Inc.
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.
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.
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
19 (* Please read generator/README first. *)
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
31 (* Generate the tests. *)
32 let rec generate_tests () =
33 generate_header CStyle GPLv2plus;
40 #include <sys/types.h>
43 #include \"guestfs.h\"
44 #include \"guestfs-internal.h\"
47 static int suppress_error = 0;
49 static void print_error (guestfs_h *g, void *data, const char *msg)
52 fprintf (stderr, \"%%s\\n\", msg);
55 /* FIXME: nearly identical code appears in fish.c */
56 static void print_strings (char *const *argv)
60 for (argc = 0; argv[argc] != NULL; ++argc)
61 printf (\"\\t%%s\\n\", argv[argc]);
65 static void print_table (char const *const *argv)
69 for (i = 0; argv[i] != NULL; i += 2)
70 printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
75 is_available (const char *group)
77 const char *groups[] = { group, NULL };
81 r = guestfs_available (g, (char **) groups);
88 incr (guestfs_h *g, void *iv)
96 (* Generate a list of commands which are not tested anywhere. *)
97 pr "static void no_test_warnings (void)\n";
100 let hash : (string, bool) Hashtbl.t = Hashtbl.create 13 in
102 fun (_, _, _, _, tests, _, _) ->
103 let tests = filter_map (
105 | (_, (Always|If _|Unless _|IfAvailable _), test) -> Some test
106 | (_, Disabled, _) -> None
108 let seq = List.concat (List.map seq_of_test tests) in
109 let cmds_tested = List.map List.hd seq in
110 List.iter (fun cmd -> Hashtbl.replace hash cmd true) cmds_tested
114 fun (name, _, _, _, _, _, _) ->
115 if not (Hashtbl.mem hash name) then
116 pr " fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
122 (* Generate the actual tests. Note that we generate the tests
123 * in reverse order, deliberately, so that (in general) the
124 * newest tests run first. This makes it quicker and easier to
129 fun (name, _, _, flags, tests, _, _) ->
130 mapi (generate_one_test name flags) tests
131 ) (List.rev all_functions) in
132 let test_names = List.concat test_names in
133 let nr_tests = List.length test_names in
136 int main (int argc, char *argv[])
139 unsigned long int n_failed = 0;
140 const char *filename;
142 int nr_tests, test_num = 0;
144 setbuf (stdout, NULL);
148 g = guestfs_create ();
150 printf (\"guestfs_create FAILED\\n\");
154 guestfs_set_error_handler (g, print_error, NULL);
156 guestfs_set_path (g, \"../appliance\");
158 filename = \"test1.img\";
159 fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
164 if (lseek (fd, %d, SEEK_SET) == -1) {
170 if (write (fd, &c, 1) == -1) {
176 if (close (fd) == -1) {
181 if (guestfs_add_drive (g, filename) == -1) {
182 printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
186 filename = \"test2.img\";
187 fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
192 if (lseek (fd, %d, SEEK_SET) == -1) {
198 if (write (fd, &c, 1) == -1) {
204 if (close (fd) == -1) {
209 if (guestfs_add_drive (g, filename) == -1) {
210 printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
214 filename = \"test3.img\";
215 fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
220 if (lseek (fd, %d, SEEK_SET) == -1) {
226 if (write (fd, &c, 1) == -1) {
232 if (close (fd) == -1) {
237 if (guestfs_add_drive (g, filename) == -1) {
238 printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
242 if (guestfs_add_drive_ro (g, \"../images/test.iso\") == -1) {
243 printf (\"guestfs_add_drive_ro ../images/test.iso FAILED\\n\");
247 /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
250 if (guestfs_launch (g) == -1) {
251 printf (\"guestfs_launch FAILED\\n\");
255 /* Cancel previous alarm. */
260 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
265 pr " if (guestfs_get_verbose (g))\n";
266 pr " printf (\"-------------------------------------------------------------------------------\\n\");\n";
267 pr " printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
268 pr " if (%s () == -1) {\n" test_name;
269 pr " printf (\"%s FAILED\\n\");\n" test_name;
275 pr " /* Check close callback is called. */
276 int close_sentinel = 1;
277 guestfs_set_close_callback (g, incr, &close_sentinel);
281 if (close_sentinel != 2) {
282 fprintf (stderr, \"close callback was not called\\n\");
286 unlink (\"test1.img\");
287 unlink (\"test2.img\");
288 unlink (\"test3.img\");
292 pr " if (n_failed > 0) {\n";
293 pr " printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n";
294 pr " exit (EXIT_FAILURE);\n";
298 pr " exit (EXIT_SUCCESS);\n";
301 and generate_one_test name flags i (init, prereq, test) =
302 let test_name = sprintf "test_%s_%d" name i in
305 static int %s_skip (void)
309 str = getenv (\"TEST_ONLY\");
311 return strstr (str, \"%s\") == NULL;
312 str = getenv (\"SKIP_%s\");
313 if (str && STREQ (str, \"1\")) return 1;
314 str = getenv (\"SKIP_TEST_%s\");
315 if (str && STREQ (str, \"1\")) return 1;
319 " test_name name (String.uppercase test_name) (String.uppercase name);
322 | Disabled | Always | IfAvailable _ -> ()
323 | If code | Unless code ->
324 pr "static int %s_prereq (void)\n" test_name;
335 printf (\" %%s skipped (reason: environment variable set)\\n\", \"%s\");
339 " test_name test_name test_name;
341 (* Optional functions should only be tested if the relevant
342 * support is available in the daemon.
347 pr " if (!is_available (\"%s\")) {\n" group;
348 pr " printf (\" %%s skipped (reason: group %%s not available in daemon)\\n\", \"%s\", \"%s\");\n" test_name group;
356 pr " printf (\" %%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
358 pr " if (! %s_prereq ()) {\n" test_name;
359 pr " printf (\" %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
363 generate_one_test_body name i test_name init test;
365 pr " if (%s_prereq ()) {\n" test_name;
366 pr " printf (\" %%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
370 generate_one_test_body name i test_name init test;
371 | IfAvailable group ->
372 pr " if (!is_available (\"%s\")) {\n" group;
373 pr " printf (\" %%s skipped (reason: %%s not available)\\n\", \"%s\", \"%s\");\n" test_name group;
377 generate_one_test_body name i test_name init test;
379 generate_one_test_body name i test_name init test
387 and generate_one_test_body name i test_name init test =
389 | InitNone (* XXX at some point, InitNone and InitEmpty became
390 * folded together as the same thing. Really we should
391 * make InitNone do nothing at all, but the tests may
392 * need to be checked to make sure this is OK.
395 pr " /* InitNone|InitEmpty for %s */\n" test_name;
396 List.iter (generate_test_command_call test_name)
397 [["blockdev_setrw"; "/dev/sda"];
401 pr " /* InitPartition for %s: create /dev/sda1 */\n" test_name;
402 List.iter (generate_test_command_call test_name)
403 [["blockdev_setrw"; "/dev/sda"];
406 ["part_disk"; "/dev/sda"; "mbr"]]
408 pr " /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
409 List.iter (generate_test_command_call test_name)
410 [["blockdev_setrw"; "/dev/sda"];
413 ["part_disk"; "/dev/sda"; "mbr"];
414 ["mkfs"; "ext2"; "/dev/sda1"];
415 ["mount_options"; ""; "/dev/sda1"; "/"]]
416 | InitBasicFSonLVM ->
417 pr " /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
419 List.iter (generate_test_command_call test_name)
420 [["blockdev_setrw"; "/dev/sda"];
423 ["part_disk"; "/dev/sda"; "mbr"];
424 ["pvcreate"; "/dev/sda1"];
425 ["vgcreate"; "VG"; "/dev/sda1"];
426 ["lvcreate"; "LV"; "VG"; "8"];
427 ["mkfs"; "ext2"; "/dev/VG/LV"];
428 ["mount_options"; ""; "/dev/VG/LV"; "/"]]
430 pr " /* InitISOFS for %s */\n" test_name;
431 List.iter (generate_test_command_call test_name)
432 [["blockdev_setrw"; "/dev/sda"];
435 ["mount_ro"; "/dev/sdd"; "/"]]
438 let get_seq_last = function
440 failwithf "%s: you cannot use [] (empty list) when expecting a command"
443 let seq = List.rev seq in
444 List.rev (List.tl seq), List.hd seq
449 pr " /* TestRun for %s (%d) */\n" name i;
450 List.iter (generate_test_command_call test_name) seq
451 | TestOutput (seq, expected) ->
452 pr " /* TestOutput for %s (%d) */\n" name i;
453 pr " const char *expected = \"%s\";\n" (c_quote expected);
454 let seq, last = get_seq_last seq in
456 pr " if (STRNEQ (r, expected)) {\n";
457 pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
461 List.iter (generate_test_command_call test_name) seq;
462 generate_test_command_call ~test test_name last
463 | TestOutputList (seq, expected) ->
464 pr " /* TestOutputList for %s (%d) */\n" name i;
465 let seq, last = get_seq_last seq in
469 pr " if (!r[%d]) {\n" i;
470 pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
471 pr " print_strings (r);\n";
475 pr " const char *expected = \"%s\";\n" (c_quote str);
476 pr " if (STRNEQ (r[%d], expected)) {\n" i;
477 pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
482 pr " if (r[%d] != NULL) {\n" (List.length expected);
483 pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
485 pr " print_strings (r);\n";
489 List.iter (generate_test_command_call test_name) seq;
490 generate_test_command_call ~test test_name last
491 | TestOutputListOfDevices (seq, expected) ->
492 pr " /* TestOutputListOfDevices for %s (%d) */\n" name i;
493 let seq, last = get_seq_last seq in
497 pr " if (!r[%d]) {\n" i;
498 pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
499 pr " print_strings (r);\n";
503 pr " const char *expected = \"%s\";\n" (c_quote str);
504 pr " r[%d][5] = 's';\n" i;
505 pr " if (STRNEQ (r[%d], expected)) {\n" i;
506 pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
511 pr " if (r[%d] != NULL) {\n" (List.length expected);
512 pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
514 pr " print_strings (r);\n";
518 List.iter (generate_test_command_call test_name) seq;
519 generate_test_command_call ~test test_name last
520 | TestOutputInt (seq, expected) ->
521 pr " /* TestOutputInt for %s (%d) */\n" name i;
522 let seq, last = get_seq_last seq in
524 pr " if (r != %d) {\n" expected;
525 pr " fprintf (stderr, \"%s: expected %d but got %%d\\n\","
531 List.iter (generate_test_command_call test_name) seq;
532 generate_test_command_call ~test test_name last
533 | TestOutputIntOp (seq, op, expected) ->
534 pr " /* TestOutputIntOp for %s (%d) */\n" name i;
535 let seq, last = get_seq_last seq in
537 pr " if (! (r %s %d)) {\n" op expected;
538 pr " fprintf (stderr, \"%s: expected %s %d but got %%d\\n\","
539 test_name op expected;
544 List.iter (generate_test_command_call test_name) seq;
545 generate_test_command_call ~test test_name last
546 | TestOutputTrue seq ->
547 pr " /* TestOutputTrue for %s (%d) */\n" name i;
548 let seq, last = get_seq_last seq in
551 pr " fprintf (stderr, \"%s: expected true, got false\\n\");\n"
556 List.iter (generate_test_command_call test_name) seq;
557 generate_test_command_call ~test test_name last
558 | TestOutputFalse seq ->
559 pr " /* TestOutputFalse for %s (%d) */\n" name i;
560 let seq, last = get_seq_last seq in
563 pr " fprintf (stderr, \"%s: expected false, got true\\n\");\n"
568 List.iter (generate_test_command_call test_name) seq;
569 generate_test_command_call ~test test_name last
570 | TestOutputLength (seq, expected) ->
571 pr " /* TestOutputLength for %s (%d) */\n" name i;
572 let seq, last = get_seq_last seq in
575 pr " for (j = 0; j < %d; ++j)\n" expected;
576 pr " if (r[j] == NULL) {\n";
577 pr " fprintf (stderr, \"%s: short list returned\\n\");\n"
579 pr " print_strings (r);\n";
582 pr " if (r[j] != NULL) {\n";
583 pr " fprintf (stderr, \"%s: long list returned\\n\");\n"
585 pr " print_strings (r);\n";
589 List.iter (generate_test_command_call test_name) seq;
590 generate_test_command_call ~test test_name last
591 | TestOutputBuffer (seq, expected) ->
592 pr " /* TestOutputBuffer for %s (%d) */\n" name i;
593 pr " const char *expected = \"%s\";\n" (c_quote expected);
594 let seq, last = get_seq_last seq in
595 let len = String.length expected in
597 pr " if (size != %d) {\n" len;
598 pr " fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len;
601 pr " if (STRNEQLEN (r, expected, size)) {\n";
602 pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
606 List.iter (generate_test_command_call test_name) seq;
607 generate_test_command_call ~test test_name last
608 | TestOutputStruct (seq, checks) ->
609 pr " /* TestOutputStruct for %s (%d) */\n" name i;
610 let seq, last = get_seq_last seq in
614 | CompareWithInt (field, expected) ->
615 pr " if (r->%s != %d) {\n" field expected;
616 pr " fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
617 test_name field expected;
618 pr " (int) r->%s);\n" field;
621 | CompareWithIntOp (field, op, expected) ->
622 pr " if (!(r->%s %s %d)) {\n" field op expected;
623 pr " fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n"
624 test_name field op expected;
625 pr " (int) r->%s);\n" field;
628 | CompareWithString (field, expected) ->
629 pr " if (STRNEQ (r->%s, \"%s\")) {\n" field expected;
630 pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
631 test_name field expected;
632 pr " r->%s);\n" field;
635 | CompareFieldsIntEq (field1, field2) ->
636 pr " if (r->%s != r->%s) {\n" field1 field2;
637 pr " fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
638 test_name field1 field2;
639 pr " (int) r->%s, (int) r->%s);\n" field1 field2;
642 | CompareFieldsStrEq (field1, field2) ->
643 pr " if (STRNEQ (r->%s, r->%s)) {\n" field1 field2;
644 pr " fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
645 test_name field1 field2;
646 pr " r->%s, r->%s);\n" field1 field2;
651 List.iter (generate_test_command_call test_name) seq;
652 generate_test_command_call ~test test_name last
653 | TestLastFail seq ->
654 pr " /* TestLastFail for %s (%d) */\n" name i;
655 let seq, last = get_seq_last seq in
656 List.iter (generate_test_command_call test_name) seq;
657 generate_test_command_call test_name ~expect_error:true last
659 (* Generate the code to run a command, leaving the result in 'r'.
660 * If you expect to get an error then you should set expect_error:true.
662 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
666 (* Look up the command to find out what args/ret it has. *)
669 let _, style, _, _, _, _, _ =
670 List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
673 failwithf "%s: in test, command %s was not found" test_name name in
675 if List.length (snd style) <> List.length args then
676 failwithf "%s: in test, wrong number of args given to %s"
683 | OptString n, "NULL" -> ()
690 pr " const char *%s = \"%s\";\n" n (c_quote arg);
692 pr " const char *%s = \"%s\";\n" n (c_quote arg);
693 pr " size_t %s_size = %d;\n" n (String.length arg)
697 | FileIn _, _ | FileOut _, _ -> ()
698 | StringList n, "" | DeviceList n, "" ->
699 pr " const char *const %s[1] = { NULL };\n" n
700 | StringList n, arg | DeviceList n, arg ->
701 let strs = string_split " " arg in
704 pr " const char *%s_%d = \"%s\";\n" n i (c_quote str);
706 pr " const char *const %s[] = {\n" n;
708 fun i _ -> pr " %s_%d,\n" n i
712 ) (List.combine (snd style) args);
716 | RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1"
717 | RInt64 _ -> pr " int64_t r;\n"; "-1"
718 | RConstString _ | RConstOptString _ ->
719 pr " const char *r;\n"; "NULL"
720 | RString _ -> pr " char *r;\n"; "NULL"
721 | RStringList _ | RHashtable _ ->
725 | RStruct (_, typ) ->
726 pr " struct guestfs_%s *r;\n" typ; "NULL"
727 | RStructList (_, typ) ->
728 pr " struct guestfs_%s_list *r;\n" typ; "NULL"
731 pr " size_t size;\n";
734 pr " suppress_error = %d;\n" (if expect_error then 1 else 0);
735 pr " r = guestfs_%s (g" name;
737 (* Generate the parameters. *)
740 | OptString _, "NULL" -> pr ", NULL"
742 | Device n, _ | Dev_or_Path n, _
748 pr ", %s, %s_size" n n
749 | FileIn _, arg | FileOut _, arg ->
750 pr ", \"%s\"" (c_quote arg)
751 | StringList n, _ | DeviceList n, _ ->
752 pr ", (char **) %s" n
755 try int_of_string arg
756 with Failure "int_of_string" ->
757 failwithf "%s: expecting an int, but got '%s'" test_name arg in
761 try Int64.of_string arg
762 with Failure "int_of_string" ->
763 failwithf "%s: expecting an int64, but got '%s'" test_name arg in
766 let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
767 ) (List.combine (snd style) args);
769 (match fst style with
770 | RBufferOut _ -> pr ", &size"
776 if not expect_error then
777 pr " if (r == %s)\n" error_code
779 pr " if (r != %s)\n" error_code;
782 (* Insert the test code. *)
788 (match fst style with
789 | RErr | RInt _ | RInt64 _ | RBool _
790 | RConstString _ | RConstOptString _ -> ()
791 | RString _ | RBufferOut _ -> pr " free (r);\n"
792 | RStringList _ | RHashtable _ ->
793 pr " for (i = 0; r[i] != NULL; ++i)\n";
794 pr " free (r[i]);\n";
796 | RStruct (_, typ) ->
797 pr " guestfs_free_%s (r);\n" typ
798 | RStructList (_, typ) ->
799 pr " guestfs_free_%s_list (r);\n" typ