| FishAlias of string (* provide an alias for this cmd in guestfish *)
| FishAction of string (* call this function in guestfish *)
| NotInFish (* do not export via guestfish *)
+ | NotInDocs (* do not add this function to documentation *)
let protocol_limit_warning =
"Because of the message protocol, there is a transfer limit
* the virtual machine and block devices are reused between tests.
* So don't try testing kill_subprocess :-x
*
- * Between each test we blockdev-setrw, umount-all, lvm-remove-all
- * (except InitNone).
+ * Between each test we blockdev-setrw, umount-all, lvm-remove-all.
*
* If the appliance is running an older Linux kernel (eg. RHEL 5) then
* devices are named /dev/hda etc. To cope with this, the test suite
* skipped. Useful if testing a command which might not work on
* all variations of libguestfs builds. A test that has prerequisite
* of 'Always' is run unconditionally.
+ *
+ * In addition, packagers can skip individual tests by setting the
+ * environment variables: eg:
+ * SKIP_TEST_<CMD>_<NUM>=1 SKIP_TEST_COMMAND_3=1 (skips test #3 of command)
+ * SKIP_TEST_<CMD>=1 SKIP_TEST_ZEROFREE=1 (skips all zerofree tests)
*)
type tests = (test_init * test_prereq * test) list
and test =
* unimplemented feature.
*)
| Disabled
+ (* 'string' is some C code (a function body) that should return
+ * true or false. The test will run if the code returns true.
+ *)
+ | If of string
+ (* As for 'If' but the test runs _unless_ the code returns true. *)
+ | Unless of string
(* Some initial scenarios for testing. *)
and test_init =
* Apart from that, long descriptions are just perldoc paragraphs.
*)
-let non_daemon_functions = [
+(* These test functions are used in the language binding tests. *)
+
+let test_all_args = [
+ String "str";
+ OptString "optstr";
+ StringList "strlist";
+ Bool "b";
+ Int "integer";
+ FileIn "filein";
+ FileOut "fileout";
+]
+
+let test_all_rets = [
+ (* except for RErr, which is tested thoroughly elsewhere *)
+ "test0rint", RInt "valout";
+ "test0rint64", RInt64 "valout";
+ "test0rbool", RBool "valout";
+ "test0rconststring", RConstString "valout";
+ "test0rstring", RString "valout";
+ "test0rstringlist", RStringList "valout";
+ "test0rintbool", RIntBool ("valout", "valout");
+ "test0rpvlist", RPVList "valout";
+ "test0rvglist", RVGList "valout";
+ "test0rlvlist", RLVList "valout";
+ "test0rstat", RStat "valout";
+ "test0rstatvfs", RStatVFS "valout";
+ "test0rhashtable", RHashtable "valout";
+]
+
+let test_functions = [
+ ("test0", (RErr, test_all_args), -1, [NotInFish; NotInDocs],
+ [],
+ "internal test function - do not use",
+ "\
+This is an internal test function which is used to test whether
+the automatically generated bindings can handle every possible
+parameter type correctly.
+
+It echos the contents of each parameter to stdout.
+
+You probably don't want to call this function.");
+] @ List.flatten (
+ List.map (
+ fun (name, ret) ->
+ [(name, (ret, [String "val"]), -1, [NotInFish; NotInDocs],
+ [],
+ "internal test function - do not use",
+ "\
+This is an internal test function which is used to test whether
+the automatically generated bindings can handle every possible
+return type correctly.
+
+It converts string C<val> to the return type.
+
+You probably don't want to call this function.");
+ (name ^ "err", (ret, []), -1, [NotInFish; NotInDocs],
+ [],
+ "internal test function - do not use",
+ "\
+This is an internal test function which is used to test whether
+the automatically generated bindings can handle every possible
+return type correctly.
+
+This function always returns an error.
+
+You probably don't want to call this function.")]
+ ) test_all_rets
+)
+
+(* non_daemon_functions are any functions which don't get processed
+ * in the daemon, eg. functions for setting and getting local
+ * configuration values.
+ *)
+
+let non_daemon_functions = test_functions @ [
("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"],
[],
"launch the qemu subprocess",
]
+(* daemon_functions are any functions which cause some action
+ * to take place in the daemon.
+ *)
+
let daemon_functions = [
("mount", (RErr, [String "device"; String "mountpoint"]), 1, [],
[InitEmpty, Always, TestOutput (
To create a single partition occupying the whole disk, you would
pass C<lines> as a single element list, when the single element being
-the string C<,> (comma).");
+the string C<,> (comma).
+
+See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>");
("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
[InitBasicFS, Always, TestOutput (
("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [],
[InitBasicFS, Always, TestOutput (
- [["tar_in"; "images/helloworld.tar"; "/"];
+ [["tar_in"; "../images/helloworld.tar"; "/"];
["cat"; "/hello"]], "hello\n")],
"unpack tarfile to directory",
"\
("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [],
[InitBasicFS, Always, TestOutput (
- [["tgz_in"; "images/helloworld.tar.gz"; "/"];
+ [["tgz_in"; "../images/helloworld.tar.gz"; "/"];
["cat"; "/hello"]], "hello\n")],
"unpack compressed tarball to directory",
"\
("lvremove", (RErr, [String "device"]), 77, [],
[InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["lvremove"; "/dev/VG/LV1"];
["lvs"]], ["/dev/VG/LV2"]);
InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["lvremove"; "/dev/VG"];
["lvs"]], []);
InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["lvremove"; "/dev/VG"];
("vgremove", (RErr, [String "vgname"]), 78, [],
[InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["vgremove"; "VG"];
["lvs"]], []);
InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["vgremove"; "VG"];
("pvremove", (RErr, [String "device"]), 79, [],
[InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["vgremove"; "VG"];
- ["pvremove"; "/dev/sda"];
+ ["pvremove"; "/dev/sda1"];
["lvs"]], []);
InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["vgremove"; "VG"];
- ["pvremove"; "/dev/sda"];
+ ["pvremove"; "/dev/sda1"];
["vgs"]], []);
InitEmpty, Always, TestOutputList (
- [["pvcreate"; "/dev/sda"];
- ["vgcreate"; "VG"; "/dev/sda"];
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
["lvcreate"; "LV1"; "VG"; "50"];
["lvcreate"; "LV2"; "VG"; "50"];
["vgremove"; "VG"];
- ["pvremove"; "/dev/sda"];
+ ["pvremove"; "/dev/sda1"];
["pvs"]], [])],
"remove an LVM physical volume",
"\
This runs C<hexdump -C> on the given C<path>. The result is
the human-readable, canonical hex dump of the file.");
+ ("zerofree", (RErr, [String "device"]), 97, [],
+ [InitNone, Always, TestOutput (
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["mkfs"; "ext3"; "/dev/sda1"];
+ ["mount"; "/dev/sda1"; "/"];
+ ["write_file"; "/new"; "test file"; "0"];
+ ["umount"; "/dev/sda1"];
+ ["zerofree"; "/dev/sda1"];
+ ["mount"; "/dev/sda1"; "/"];
+ ["cat"; "/new"]], "test file")],
+ "zero unused inodes and disk blocks on ext2/3 filesystem",
+ "\
+This runs the I<zerofree> program on C<device>. This program
+claims to zero unused inodes and disk blocks on an ext2/3
+filesystem, thus making it possible to compress the filesystem
+more effectively.
+
+You should B<not> run this program if the filesystem is
+mounted.
+
+It is possible that using this program can damage the filesystem
+or data on the filesystem.");
+
+ ("pvresize", (RErr, [String "device"]), 98, [],
+ [],
+ "resize an LVM physical volume",
+ "\
+This resizes (expands or shrinks) an existing LVM physical
+volume to match the new size of the underlying device.");
+
+ ("sfdisk_N", (RErr, [String "device"; Int "n";
+ Int "cyls"; Int "heads"; Int "sectors";
+ String "line"]), 99, [DangerWillRobinson],
+ [],
+ "modify a single partition on a block device",
+ "\
+This runs L<sfdisk(8)> option to modify just the single
+partition C<n> (note: C<n> counts from 1).
+
+For other parameters, see C<guestfs_sfdisk>. You should usually
+pass C<0> for the cyls/heads/sectors parameters.");
+
+ ("sfdisk_l", (RString "partitions", [String "device"]), 100, [],
+ [],
+ "display the partition table",
+ "\
+This displays the partition table on C<device>, in the
+human-readable output of the L<sfdisk(8)> command. It is
+not intended to be parsed.");
+
+ ("sfdisk_kernel_geometry", (RString "partitions", [String "device"]), 101, [],
+ [],
+ "display the kernel geometry",
+ "\
+This displays the kernel's idea of the geometry of C<device>.
+
+The result is in human-readable format, and not designed to
+be parsed.");
+
+ ("sfdisk_disk_geometry", (RString "partitions", [String "device"]), 102, [],
+ [],
+ "display the disk geometry from the partition table",
+ "\
+This displays the disk geometry of C<device> read from the
+partition table. Especially in the case where the underlying
+block device has been resized, this can be different from the
+kernel's idea of the geometry (see C<guestfs_sfdisk_kernel_geometry>).
+
+The result is in human-readable format, and not designed to
+be parsed.");
+
+ ("vg_activate_all", (RErr, [Bool "activate"]), 103, [],
+ [],
+ "activate or deactivate all volume groups",
+ "\
+This command activates or (if C<activate> is false) deactivates
+all logical volumes in all volume groups.
+If activated, then they are made known to the
+kernel, ie. they appear as C</dev/mapper> devices. If deactivated,
+then those devices disappear.
+
+This command is the same as running C<vgchange -a y|n>");
+
+ ("vg_activate", (RErr, [Bool "activate"; StringList "volgroups"]), 104, [],
+ [],
+ "activate or deactivate some volume groups",
+ "\
+This command activates or (if C<activate> is false) deactivates
+all logical volumes in the listed volume groups C<volgroups>.
+If activated, then they are made known to the
+kernel, ie. they appear as C</dev/mapper> devices. If deactivated,
+then those devices disappear.
+
+This command is the same as running C<vgchange -a y|n volgroups...>
+
+Note that if C<volgroups> is an empty list then B<all> volume groups
+are activated or deactivated.");
+
+ ("lvresize", (RErr, [String "device"; Int "mbytes"]), 105, [],
+ [InitNone, Always, TestOutput (
+ [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
+ ["pvcreate"; "/dev/sda1"];
+ ["vgcreate"; "VG"; "/dev/sda1"];
+ ["lvcreate"; "LV"; "VG"; "10"];
+ ["mkfs"; "ext2"; "/dev/VG/LV"];
+ ["mount"; "/dev/VG/LV"; "/"];
+ ["write_file"; "/new"; "test content"; "0"];
+ ["umount"; "/"];
+ ["lvresize"; "/dev/VG/LV"; "20"];
+ ["e2fsck_f"; "/dev/VG/LV"];
+ ["resize2fs"; "/dev/VG/LV"];
+ ["mount"; "/dev/VG/LV"; "/"];
+ ["cat"; "/new"]], "test content")],
+ "resize an LVM logical volume",
+ "\
+This resizes (expands or shrinks) an existing LVM logical
+volume to C<mbytes>. When reducing, data in the reduced part
+is lost.");
+
+ ("resize2fs", (RErr, [String "device"]), 106, [],
+ [], (* lvresize tests this *)
+ "resize an ext2/ext3 filesystem",
+ "\
+This resizes an ext2 or ext3 filesystem to match the size of
+the underlying device.
+
+I<Note:> It is sometimes required that you run C<guestfs_e2fsck_f>
+on the C<device> before calling this command. For unknown reasons
+C<resize2fs> sometimes gives an error about this and sometimes not.
+In any case, it is always safe to call C<guestfs_e2fsck_f> before
+calling this function.");
+
+ ("find", (RStringList "names", [String "directory"]), 107, [],
+ [InitBasicFS, Always, TestOutputList (
+ [["find"; "/"]], ["lost+found"]);
+ InitBasicFS, Always, TestOutputList (
+ [["touch"; "/a"];
+ ["mkdir"; "/b"];
+ ["touch"; "/b/c"];
+ ["find"; "/"]], ["a"; "b"; "b/c"; "lost+found"]);
+ InitBasicFS, Always, TestOutputList (
+ [["mkdir_p"; "/a/b/c"];
+ ["touch"; "/a/b/c/d"];
+ ["find"; "/a/b/"]], ["c"; "c/d"])],
+ "find all files and directories",
+ "\
+This command lists out all files and directories, recursively,
+starting at C<directory>. It is essentially equivalent to
+running the shell command C<find directory -print> but some
+post-processing happens on the output, described below.
+
+This returns a list of strings I<without any prefix>. Thus
+if the directory structure was:
+
+ /tmp/a
+ /tmp/b
+ /tmp/c/d
+
+then the returned list from C<guestfs_find> C</tmp> would be
+4 elements:
+
+ a
+ b
+ c
+ c/d
+
+If C<directory> is not a directory, then this command returns
+an error.
+
+The returned list is sorted.");
+
+ ("e2fsck_f", (RErr, [String "device"]), 108, [],
+ [], (* lvresize tests this *)
+ "check an ext2/ext3 filesystem",
+ "\
+This runs C<e2fsck -p -f device>, ie. runs the ext2/ext3
+filesystem checker on C<device>, noninteractively (C<-p>),
+even if the filesystem appears to be clean (C<-f>).
+
+This command is only needed because of C<guestfs_resize2fs>
+(q.v.). Normally you should use C<guestfs_fsck>.");
+
]
let all_functions = non_daemon_functions @ daemon_functions
fun (name, _, _, _, _, _, _) ->
if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
failwithf "function name %s does not need 'guestfs' prefix" name;
- if contains_uppercase name then
- failwithf "function name %s should not contain uppercase chars" name;
+ if name = "" then
+ failwithf "function name is empty";
+ if name.[0] < 'a' || name.[0] > 'z' then
+ failwithf "function name %s must start with lowercase a-z" name;
if String.contains name '-' then
failwithf "function name %s should not contain '-', use '_' instead."
name
failwithf "%s param/ret %s should not contain '-' or '_'"
name n;
if n = "value" then
- failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n;
+ failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" name;
+ if n = "int" || n = "char" || n = "short" || n = "long" then
+ failwithf "%s has a param/ret which conflicts with a C type (eg. 'int', 'char' etc.)" name;
+ if n = "i" then
+ failwithf "%s has a param/ret called 'i', which will cause some conflicts in the generated code" name;
if n = "argv" || n = "args" then
- failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
+ failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" name
in
(match fst style with
let rec generate_actions_pod () =
List.iter (
fun (shortname, style, _, flags, _, _, longdesc) ->
- let name = "guestfs_" ^ shortname in
- pr "=head2 %s\n\n" name;
- pr " ";
- generate_prototype ~extern:false ~handle:"handle" name style;
- pr "\n\n";
- pr "%s\n\n" longdesc;
- (match fst style with
- | RErr ->
- pr "This function returns 0 on success or -1 on error.\n\n"
- | RInt _ ->
- pr "On error this function returns -1.\n\n"
- | RInt64 _ ->
- pr "On error this function returns -1.\n\n"
- | RBool _ ->
- pr "This function returns a C truth value on success or -1 on error.\n\n"
- | RConstString _ ->
- pr "This function returns a string, or NULL on error.
+ if not (List.mem NotInDocs flags) then (
+ let name = "guestfs_" ^ shortname in
+ pr "=head2 %s\n\n" name;
+ pr " ";
+ generate_prototype ~extern:false ~handle:"handle" name style;
+ pr "\n\n";
+ pr "%s\n\n" longdesc;
+ (match fst style with
+ | RErr ->
+ pr "This function returns 0 on success or -1 on error.\n\n"
+ | RInt _ ->
+ pr "On error this function returns -1.\n\n"
+ | RInt64 _ ->
+ pr "On error this function returns -1.\n\n"
+ | RBool _ ->
+ pr "This function returns a C truth value on success or -1 on error.\n\n"
+ | RConstString _ ->
+ pr "This function returns a string, or NULL on error.
The string is owned by the guest handle and must I<not> be freed.\n\n"
- | RString _ ->
- pr "This function returns a string, or NULL on error.
+ | RString _ ->
+ pr "This function returns a string, or NULL on error.
I<The caller must free the returned string after use>.\n\n"
- | RStringList _ ->
- pr "This function returns a NULL-terminated array of strings
+ | RStringList _ ->
+ pr "This function returns a NULL-terminated array of strings
(like L<environ(3)>), or NULL if there was an error.
I<The caller must free the strings and the array after use>.\n\n"
- | RIntBool _ ->
- pr "This function returns a C<struct guestfs_int_bool *>,
+ | RIntBool _ ->
+ pr "This function returns a C<struct guestfs_int_bool *>,
or NULL if there was an error.
I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
- | RPVList _ ->
- pr "This function returns a C<struct guestfs_lvm_pv_list *>
+ | RPVList _ ->
+ pr "This function returns a C<struct guestfs_lvm_pv_list *>
(see E<lt>guestfs-structs.hE<gt>),
or NULL if there was an error.
I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
- | RVGList _ ->
- pr "This function returns a C<struct guestfs_lvm_vg_list *>
+ | RVGList _ ->
+ pr "This function returns a C<struct guestfs_lvm_vg_list *>
(see E<lt>guestfs-structs.hE<gt>),
or NULL if there was an error.
I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
- | RLVList _ ->
- pr "This function returns a C<struct guestfs_lvm_lv_list *>
+ | RLVList _ ->
+ pr "This function returns a C<struct guestfs_lvm_lv_list *>
(see E<lt>guestfs-structs.hE<gt>),
or NULL if there was an error.
I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
- | RStat _ ->
- pr "This function returns a C<struct guestfs_stat *>
+ | RStat _ ->
+ pr "This function returns a C<struct guestfs_stat *>
(see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
or NULL if there was an error.
I<The caller must call C<free> after use>.\n\n"
- | RStatVFS _ ->
- pr "This function returns a C<struct guestfs_statvfs *>
+ | RStatVFS _ ->
+ pr "This function returns a C<struct guestfs_statvfs *>
(see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
or NULL if there was an error.
I<The caller must call C<free> after use>.\n\n"
- | RHashtable _ ->
- pr "This function returns a NULL-terminated array of
+ | RHashtable _ ->
+ pr "This function returns a NULL-terminated array of
strings, or NULL if there was an error.
The array of strings will always have length C<2n+1>, where
C<n> keys and values alternate, followed by the trailing NULL entry.
I<The caller must free the strings and the array after use>.\n\n"
- );
- if List.mem ProtocolLimitWarning flags then
- pr "%s\n\n" protocol_limit_warning;
- if List.mem DangerWillRobinson flags then
- pr "%s\n\n" danger_will_robinson;
+ );
+ if List.mem ProtocolLimitWarning flags then
+ pr "%s\n\n" protocol_limit_warning;
+ if List.mem DangerWillRobinson flags then
+ pr "%s\n\n" danger_will_robinson
+ )
) all_functions_sorted
and generate_structs_pod () =
guestfs_set_error_handler (g, print_error, NULL);
- srcdir = getenv (\"srcdir\");
- if (!srcdir) srcdir = \".\";
- chdir (srcdir);
- guestfs_set_path (g, \".\");
+ guestfs_set_path (g, \"../appliance\");
filename = \"test1.img\";
fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
and generate_one_test name i (init, prereq, test) =
let test_name = sprintf "test_%s_%d" name i in
- pr "static int %s (void)\n" test_name;
- pr "{\n";
+ pr "\
+static int %s_skip (void)
+{
+ const char *str;
+
+ str = getenv (\"SKIP_%s\");
+ if (str && strcmp (str, \"1\") == 0) return 1;
+ str = getenv (\"SKIP_TEST_%s\");
+ if (str && strcmp (str, \"1\") == 0) return 1;
+ return 0;
+}
+
+" test_name (String.uppercase test_name) (String.uppercase name);
+
+ (match prereq with
+ | Disabled | Always -> ()
+ | If code | Unless code ->
+ pr "static int %s_prereq (void)\n" test_name;
+ pr "{\n";
+ pr " %s\n" code;
+ pr "}\n";
+ pr "\n";
+ );
+
+ pr "\
+static int %s (void)
+{
+ if (%s_skip ()) {
+ printf (\"%%s skipped (reason: SKIP_TEST_* variable set)\\n\", \"%s\");
+ return 0;
+ }
+
+" test_name test_name test_name;
(match prereq with
| Disabled ->
- pr " printf (\"%%s skipped (test disabled in generator)\\n\", \"%s\");\n" test_name
+ pr " printf (\"%%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
+ | If _ ->
+ pr " if (! %s_prereq ()) {\n" test_name;
+ pr " printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
+ pr " return 0;\n";
+ pr " }\n";
+ pr "\n";
+ generate_one_test_body name i test_name init test;
+ | Unless _ ->
+ pr " if (%s_prereq ()) {\n" test_name;
+ pr " printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
+ pr " return 0;\n";
+ pr " }\n";
+ pr "\n";
+ generate_one_test_body name i test_name init test;
| Always ->
generate_one_test_body name i test_name init test
);
and generate_one_test_body name i test_name init test =
(match init with
- | InitNone -> ()
+ | InitNone
| InitEmpty ->
- pr " /* InitEmpty for %s (%d) */\n" name i;
+ pr " /* InitNone|InitEmpty for %s */\n" test_name;
List.iter (generate_test_command_call test_name)
[["blockdev_setrw"; "/dev/sda"];
["umount_all"];
["lvm_remove_all"]]
| InitBasicFS ->
- pr " /* InitBasicFS for %s (%d): create ext2 on /dev/sda1 */\n" name i;
+ pr " /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
List.iter (generate_test_command_call test_name)
[["blockdev_setrw"; "/dev/sda"];
["umount_all"];
["mkfs"; "ext2"; "/dev/sda1"];
["mount"; "/dev/sda1"; "/"]]
| InitBasicFSonLVM ->
- pr " /* InitBasicFSonLVM for %s (%d): create ext2 on /dev/VG/LV */\n"
- name i;
+ pr " /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
+ test_name;
List.iter (generate_test_command_call test_name)
[["blockdev_setrw"; "/dev/sda"];
["umount_all"];
#ifdef HAVE_LIBREADLINE
static const char *const commands[] = {
+ BUILTIN_COMMANDS_FOR_COMPLETION,
";
- (* Get the commands and sort them, including the aliases. *)
+ (* Get the commands, including the aliases. They don't need to be
+ * sorted - the generator() function just does a dumb linear search.
+ *)
let commands =
List.map (
fun (name, _, _, flags, _, _, _) ->
if name <> alias then [name2; alias] else [name2]
) all_functions in
let commands = List.flatten commands in
- let commands = List.sort compare commands in
List.iter (pr " \"%s\",\n") commands;
and generate_fish_actions_pod () =
let all_functions_sorted =
List.filter (
- fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
+ fun (_, _, _, flags, _, _, _) ->
+ not (List.mem NotInFish flags || List.mem NotInDocs flags)
) all_functions_sorted in
let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
*)
List.iter (
fun (name, style, _, flags, _, _, longdesc) ->
- let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
- pr "=item ";
- generate_perl_prototype name style;
- pr "\n\n";
- pr "%s\n\n" longdesc;
- if List.mem ProtocolLimitWarning flags then
- pr "%s\n\n" protocol_limit_warning;
- if List.mem DangerWillRobinson flags then
- pr "%s\n\n" danger_will_robinson
+ if not (List.mem NotInDocs flags) then (
+ let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
+ pr "=item ";
+ generate_perl_prototype name style;
+ pr "\n\n";
+ pr "%s\n\n" longdesc;
+ if List.mem ProtocolLimitWarning flags then
+ pr "%s\n\n" protocol_limit_warning;
+ if List.mem DangerWillRobinson flags then
+ pr "%s\n\n" danger_will_robinson
+ )
) all_functions_sorted;
(* End of file. *)
List.iter (
fun (name, style, _, flags, _, _, longdesc) ->
- let doc = replace_str longdesc "C<guestfs_" "C<g." in
- let doc =
- match fst style with
- | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
- | RString _ -> doc
- | RStringList _ ->
- doc ^ "\n\nThis function returns a list of strings."
- | RIntBool _ ->
- doc ^ "\n\nThis function returns a tuple (int, bool).\n"
- | RPVList _ ->
- doc ^ "\n\nThis function returns a list of PVs. Each PV is represented as a dictionary."
- | RVGList _ ->
- doc ^ "\n\nThis function returns a list of VGs. Each VG is represented as a dictionary."
- | RLVList _ ->
- doc ^ "\n\nThis function returns a list of LVs. Each LV is represented as a dictionary."
- | RStat _ ->
- doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
- | RStatVFS _ ->
- doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
- | RHashtable _ ->
- doc ^ "\n\nThis function returns a dictionary." in
- let doc =
- if List.mem ProtocolLimitWarning flags then
- doc ^ "\n\n" ^ protocol_limit_warning
- else doc in
- let doc =
- if List.mem DangerWillRobinson flags then
- doc ^ "\n\n" ^ danger_will_robinson
- else doc in
- let doc = pod2text ~width:60 name doc in
- let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
- let doc = String.concat "\n " doc in
-
pr " def %s " name;
generate_call_args ~handle:"self" (snd style);
pr ":\n";
- pr " u\"\"\"%s\"\"\"\n" doc;
+
+ if not (List.mem NotInDocs flags) then (
+ let doc = replace_str longdesc "C<guestfs_" "C<g." in
+ let doc =
+ match fst style with
+ | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
+ | RString _ -> doc
+ | RStringList _ ->
+ doc ^ "\n\nThis function returns a list of strings."
+ | RIntBool _ ->
+ doc ^ "\n\nThis function returns a tuple (int, bool).\n"
+ | RPVList _ ->
+ doc ^ "\n\nThis function returns a list of PVs. Each PV is represented as a dictionary."
+ | RVGList _ ->
+ doc ^ "\n\nThis function returns a list of VGs. Each VG is represented as a dictionary."
+ | RLVList _ ->
+ doc ^ "\n\nThis function returns a list of LVs. Each LV is represented as a dictionary."
+ | RStat _ ->
+ doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
+ | RStatVFS _ ->
+ doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
+ | RHashtable _ ->
+ doc ^ "\n\nThis function returns a dictionary." in
+ let doc =
+ if List.mem ProtocolLimitWarning flags then
+ doc ^ "\n\n" ^ protocol_limit_warning
+ else doc in
+ let doc =
+ if List.mem DangerWillRobinson flags then
+ doc ^ "\n\n" ^ danger_will_robinson
+ else doc in
+ let doc = pod2text ~width:60 name doc in
+ let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
+ let doc = String.concat "\n " doc in
+ pr " u\"\"\"%s\"\"\"\n" doc;
+ );
pr " return libguestfsmod.%s " name;
generate_call_args ~handle:"self._o" (snd style);
pr "\n";
List.iter (
fun (name, style, _, flags, _, shortdesc, longdesc) ->
- let doc = replace_str longdesc "C<guestfs_" "C<g." in
- let doc =
- if List.mem ProtocolLimitWarning flags then
- doc ^ "\n\n" ^ protocol_limit_warning
- else doc in
- let doc =
- if List.mem DangerWillRobinson flags then
- doc ^ "\n\n" ^ danger_will_robinson
- else doc in
- let doc = pod2text ~width:60 name doc in
- let doc = String.concat "\n * " doc in
-
- pr " /**\n";
- pr " * %s\n" shortdesc;
- pr " *\n";
- pr " * %s\n" doc;
- pr " * @throws LibGuestFSException\n";
- pr " */\n";
- pr " ";
+ if not (List.mem NotInDocs flags); then (
+ let doc = replace_str longdesc "C<guestfs_" "C<g." in
+ let doc =
+ if List.mem ProtocolLimitWarning flags then
+ doc ^ "\n\n" ^ protocol_limit_warning
+ else doc in
+ let doc =
+ if List.mem DangerWillRobinson flags then
+ doc ^ "\n\n" ^ danger_will_robinson
+ else doc in
+ let doc = pod2text ~width:60 name doc in
+ let doc = List.map ( (* RHBZ#501883 *)
+ function
+ | "" -> "<p>"
+ | nonempty -> nonempty
+ ) doc in
+ let doc = String.concat "\n * " doc in
+
+ pr " /**\n";
+ pr " * %s\n" shortdesc;
+ pr " * <p>\n";
+ pr " * %s\n" doc;
+ pr " * @throws LibGuestFSException\n";
+ pr " */\n";
+ pr " ";
+ );
generate_java_prototype ~public:true ~semicolon:false name style;
pr "\n";
pr " {\n";
);
pr ")"
+and generate_bindtests () =
+ generate_header CStyle LGPLv2;
+
+ pr "\
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include \"guestfs.h\"
+#include \"guestfs_protocol.h\"
+
+#define error guestfs_error
+
+static void
+print_strings (char * const* const argv)
+{
+ int argc;
+
+ printf (\"[\");
+ for (argc = 0; argv[argc] != NULL; ++argc) {
+ if (argc > 0) printf (\", \");
+ printf (\"\\\"%%s\\\"\", argv[argc]);
+ }
+ printf (\"]\\n\");
+}
+
+/* The test0 function prints its parameters to stdout. */
+";
+
+ let test0, tests =
+ match test_functions with
+ | [] -> assert false
+ | test0 :: tests -> test0, tests in
+
+ let () =
+ let (name, style, _, _, _, _, _) = test0 in
+ generate_prototype ~extern:false ~semicolon:false ~newline:true
+ ~handle:"g" ~prefix:"guestfs_" name style;
+ pr "{\n";
+ List.iter (
+ function
+ | String n
+ | FileIn n
+ | FileOut n -> pr " printf (\"%%s\\n\", %s);\n" n
+ | OptString n -> pr " printf (\"%%s\\n\", %s ? %s : \"null\");\n" n n
+ | StringList n -> pr " print_strings (%s);\n" n
+ | Bool n -> pr " printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n
+ | Int n -> pr " printf (\"%%d\\n\", %s);\n" n
+ ) (snd style);
+ pr " return 0;\n";
+ pr "}\n";
+ pr "\n" in
+
+ List.iter (
+ fun (name, style, _, _, _, _, _) ->
+ if String.sub name (String.length name - 3) 3 <> "err" then (
+ pr "/* Test normal return. */\n";
+ generate_prototype ~extern:false ~semicolon:false ~newline:true
+ ~handle:"g" ~prefix:"guestfs_" name style;
+ pr "{\n";
+ (match fst style with
+ | RErr ->
+ pr " return 0;\n"
+ | RInt _ ->
+ pr " int r;\n";
+ pr " sscanf (val, \"%%d\", &r);\n";
+ pr " return r;\n"
+ | RInt64 _ ->
+ pr " int64_t r;\n";
+ pr " sscanf (val, \"%%\" SCNi64, &r);\n";
+ pr " return r;\n"
+ | RBool _ ->
+ pr " return strcmp (val, \"true\") == 0;\n"
+ | RConstString _ ->
+ (* Can't return the input string here. Return a static
+ * string so we ensure we get a segfault if the caller
+ * tries to free it.
+ *)
+ pr " return \"static string\";\n"
+ | RString _ ->
+ pr " return strdup (val);\n"
+ | RStringList _ ->
+ pr " char **strs;\n";
+ pr " int n, i;\n";
+ pr " sscanf (val, \"%%d\", &n);\n";
+ pr " strs = malloc ((n+1) * sizeof (char *));\n";
+ pr " for (i = 0; i < n; ++i) {\n";
+ pr " strs[i] = malloc (16);\n";
+ pr " snprintf (strs[i], 16, \"%%d\", i);\n";
+ pr " }\n";
+ pr " strs[n] = NULL;\n";
+ pr " return strs;\n"
+ | RIntBool _ ->
+ pr " struct guestfs_int_bool *r;\n";
+ pr " r = malloc (sizeof (struct guestfs_int_bool));\n";
+ pr " sscanf (val, \"%%\" SCNi32, &r->i);\n";
+ pr " r->b = 0;\n";
+ pr " return r;\n"
+ | RPVList _ ->
+ pr " struct guestfs_lvm_pv_list *r;\n";
+ pr " int i;\n";
+ pr " r = malloc (sizeof (struct guestfs_lvm_pv_list));\n";
+ pr " sscanf (val, \"%%d\", &r->len);\n";
+ pr " r->val = calloc (r->len, sizeof (struct guestfs_lvm_pv));\n";
+ pr " for (i = 0; i < r->len; ++i) {\n";
+ pr " r->val[i].pv_name = malloc (16);\n";
+ pr " snprintf (r->val[i].pv_name, 16, \"%%d\", i);\n";
+ pr " }\n";
+ pr " return r;\n"
+ | RVGList _ ->
+ pr " struct guestfs_lvm_vg_list *r;\n";
+ pr " int i;\n";
+ pr " r = malloc (sizeof (struct guestfs_lvm_vg_list));\n";
+ pr " sscanf (val, \"%%d\", &r->len);\n";
+ pr " r->val = calloc (r->len, sizeof (struct guestfs_lvm_vg));\n";
+ pr " for (i = 0; i < r->len; ++i) {\n";
+ pr " r->val[i].vg_name = malloc (16);\n";
+ pr " snprintf (r->val[i].vg_name, 16, \"%%d\", i);\n";
+ pr " }\n";
+ pr " return r;\n"
+ | RLVList _ ->
+ pr " struct guestfs_lvm_lv_list *r;\n";
+ pr " int i;\n";
+ pr " r = malloc (sizeof (struct guestfs_lvm_lv_list));\n";
+ pr " sscanf (val, \"%%d\", &r->len);\n";
+ pr " r->val = calloc (r->len, sizeof (struct guestfs_lvm_lv));\n";
+ pr " for (i = 0; i < r->len; ++i) {\n";
+ pr " r->val[i].lv_name = malloc (16);\n";
+ pr " snprintf (r->val[i].lv_name, 16, \"%%d\", i);\n";
+ pr " }\n";
+ pr " return r;\n"
+ | RStat _ ->
+ pr " struct guestfs_stat *r;\n";
+ pr " r = calloc (1, sizeof (*r));\n";
+ pr " sscanf (val, \"%%\" SCNi64, &r->dev);\n";
+ pr " return r;\n"
+ | RStatVFS _ ->
+ pr " struct guestfs_statvfs *r;\n";
+ pr " r = calloc (1, sizeof (*r));\n";
+ pr " sscanf (val, \"%%\" SCNi64, &r->bsize);\n";
+ pr " return r;\n"
+ | RHashtable _ ->
+ pr " char **strs;\n";
+ pr " int n, i;\n";
+ pr " sscanf (val, \"%%d\", &n);\n";
+ pr " strs = malloc ((n*2+1) * sizeof (char *));\n";
+ pr " for (i = 0; i < n; ++i) {\n";
+ pr " strs[i*2] = malloc (16);\n";
+ pr " strs[i*2+1] = malloc (16);\n";
+ pr " snprintf (strs[i*2], 16, \"%%d\", i);\n";
+ pr " snprintf (strs[i*2+1], 16, \"%%d\", i);\n";
+ pr " }\n";
+ pr " strs[n*2] = NULL;\n";
+ pr " return strs;\n"
+ );
+ pr "}\n";
+ pr "\n"
+ ) else (
+ pr "/* Test error return. */\n";
+ generate_prototype ~extern:false ~semicolon:false ~newline:true
+ ~handle:"g" ~prefix:"guestfs_" name style;
+ pr "{\n";
+ pr " error (g, \"error\");\n";
+ (match fst style with
+ | RErr | RInt _ | RInt64 _ | RBool _ ->
+ pr " return -1;\n"
+ | RConstString _
+ | RString _ | RStringList _ | RIntBool _
+ | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _
+ | RHashtable _ ->
+ pr " return NULL;\n"
+ );
+ pr "}\n";
+ pr "\n"
+ )
+ ) tests
+
let output_to filename =
let filename_new = filename ^ ".new" in
chan := open_out filename_new;
generate_daemon_actions ();
close ();
- let close = output_to "tests.c" in
+ let close = output_to "capitests/tests.c" in
generate_tests ();
close ();
+ let close = output_to "src/guestfs-bindtests.c" in
+ generate_bindtests ();
+ close ();
+
let close = output_to "fish/cmds.c" in
generate_fish_cmds ();
close ();