summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/c-api/Makefile.am152
-rw-r--r--tests/c-api/test-add-drive-opts.c58
-rw-r--r--tests/c-api/test-add-libvirt-dom.c151
-rw-r--r--tests/c-api/test-command.c67
-rw-r--r--tests/c-api/test-config.c68
-rw-r--r--tests/c-api/test-create-handle.c42
-rw-r--r--tests/c-api/test-debug-to-file.c91
-rw-r--r--tests/c-api/test-just-header.c30
-rw-r--r--tests/c-api/test-last-errno.c142
-rw-r--r--tests/c-api/test-private-data.c120
-rw-r--r--tests/c-api/test-user-cancel.c371
-rw-r--r--tests/qemu/Makefile.am2
12 files changed, 1292 insertions, 2 deletions
diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am
new file mode 100644
index 00000000..0b6c6838
--- /dev/null
+++ b/tests/c-api/Makefile.am
@@ -0,0 +1,152 @@
+# libguestfs
+# Copyright (C) 2009-2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+generator_built = tests.c
+
+BUILT_SOURCES = $(generator_built)
+
+EXTRA_DIST = $(BUILT_SOURCES)
+
+check_PROGRAMS = \
+ tests \
+ test-command \
+ test-just-header \
+ test-create-handle \
+ test-config \
+ test-add-drive-opts \
+ test-last-errno \
+ test-private-data \
+ test-user-cancel \
+ test-debug-to-file
+
+TESTS = \
+ tests \
+ test-just-header \
+ test-create-handle \
+ test-config \
+ test-add-drive-opts \
+ test-last-errno \
+ test-private-data \
+ test-user-cancel \
+ test-debug-to-file
+
+# The API behind this test is not baked yet.
+#if HAVE_LIBVIRT
+#check_PROGRAMS += test-add-libvirt-dom
+#TESTS += test-add-libvirt-dom
+#endif
+EXTRA_DIST += test-add-libvirt-dom.c
+
+TESTS_ENVIRONMENT = \
+ SKIP_TEST_COMMAND=$(shell ldd test-command | grep -sq 'not a dynamic executable' || echo 1) \
+ SKIP_TEST_COMMAND_LINES=$(shell ldd test-command | grep -sq 'not a dynamic executable' || echo 1) \
+ LIBGUESTFS_PATH=$(top_builddir)/appliance \
+ TMPDIR=$(top_builddir) \
+ $(VG)
+
+#SKIP_TEST_CHECKSUM_8=$(shell if test `find ../initramfs -name squashfs.ko | wc -l` -eq 0; then echo 1; fi)
+
+tests_SOURCES = tests.c
+tests_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+tests_LDADD = $(top_builddir)/src/libguestfs.la
+
+# This binary must be statically linked. It is used for testing
+# the "guestfs_command" and "guestfs_command_lines" functions.
+
+test_command_SOURCES = test-command.c
+test_command_LDFLAGS = -all-static
+
+# Hand-written C API tests.
+
+test_just_header_SOURCES = test-just-header.c
+test_just_header_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_just_header_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_create_handle_SOURCES = test-create-handle.c
+test_create_handle_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_create_handle_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_config_SOURCES = test-config.c
+test_config_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_config_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_add_drive_opts_SOURCES = test-add-drive-opts.c
+test_add_drive_opts_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_add_drive_opts_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_last_errno_SOURCES = test-last-errno.c
+test_last_errno_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_last_errno_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_private_data_SOURCES = test-private-data.c
+test_private_data_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_private_data_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
+test_user_cancel_SOURCES = test-user-cancel.c
+test_user_cancel_CFLAGS = \
+ -pthread \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_user_cancel_LDADD = \
+ $(top_builddir)/src/libguestfs.la -lm
+
+test_debug_to_file_SOURCES = test-debug-to-file.c
+test_debug_to_file_CFLAGS = \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ -I$(top_srcdir)/gnulib/lib \
+ -I$(top_builddir)/gnulib/lib \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_debug_to_file_LDADD = \
+ $(top_builddir)/src/libguestfs.la \
+ $(top_builddir)/gnulib/lib/libgnu.la
+
+#if HAVE_LIBVIRT
+#test_add_libvirt_dom_SOURCES = test-add-libvirt-dom.c
+#test_add_libvirt_dom_CFLAGS = \
+# -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/gnulib/lib \
+# $(LIBVIRT_CFLAGS) \
+# $(WARN_CFLAGS) $(WERROR_CFLAGS)
+#test_add_libvirt_dom_LDADD = \
+# $(top_builddir)/src/libguestfs.la $(LIBVIRT_LIBS) \
+# $(LTLIBTHREAD) $(top_builddir)/gnulib/lib/libgnu.la
+#endif
+
+# Run the tests under valgrind.
+
+valgrind:
+ $(MAKE) check VG="valgrind --quiet --leak-check=full"
diff --git a/tests/c-api/test-add-drive-opts.c b/tests/c-api/test-add-drive-opts.c
new file mode 100644
index 00000000..897c6fac
--- /dev/null
+++ b/tests/c-api/test-add-drive-opts.c
@@ -0,0 +1,58 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "guestfs.h"
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ int r;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ r = guestfs_add_drive_opts (g, "/dev/null", -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ r = guestfs_add_drive_opts (g, "/dev/null",
+ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
+ -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ r = guestfs_add_drive_opts (g, "/dev/null",
+ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ guestfs_close (g);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-add-libvirt-dom.c b/tests/c-api/test-add-libvirt-dom.c
new file mode 100644
index 00000000..6f994056
--- /dev/null
+++ b/tests/c-api/test-add-libvirt-dom.c
@@ -0,0 +1,151 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xgetcwd.h"
+
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+
+#include "guestfs.h"
+
+static void
+make_test_xml (FILE *fp, const char *cwd)
+{
+ fprintf (fp,
+ "<?xml version=\"1.0\"?>\n"
+ "<node>\n"
+ " <domain type='test'>\n"
+ " <name>guest</name>\n"
+ " <os>\n"
+ " <type>hvm</type>\n"
+ " <boot dev='hd'/>\n"
+ " </os>\n"
+ " <memory>524288</memory>\n"
+ " <devices>\n"
+ " <disk type='file'>\n"
+ " <source file='%s/test1.img'/>\n"
+ " <target dev='hda'/>\n"
+ " </disk>\n"
+ " <disk type='file'>\n"
+ " <driver name='qemu' type='raw'/>\n"
+ " <source file='%s/test2.img'/>\n"
+ " <target dev='hdb'/>\n"
+ " </disk>\n"
+ " <disk type='file'>\n"
+ " <driver name='qemu' type='qcow2'/>\n"
+ " <source file='%s/test3.img'/>\n"
+ " <target dev='hdc'/>\n"
+ " </disk>\n"
+ " </devices>\n"
+ " </domain>\n"
+ "</node>",
+ cwd, cwd, cwd);
+}
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ virConnectPtr conn;
+ virDomainPtr dom;
+ virErrorPtr err;
+ int r;
+ const char *test_xml;
+ char *cwd;
+ FILE *fp;
+ char libvirt_uri[1024];
+
+ cwd = xgetcwd ();
+
+ /* Create the libvirt XML and test images in the current directory. */
+ fp = fopen ("test.xml", "w");
+ if (fp == NULL) {
+ perror ("test.xml");
+ exit (EXIT_FAILURE);
+ }
+ make_test_xml (fp, cwd);
+ fclose (fp);
+
+ fp = fopen ("test1.img", "w");
+ if (fp == NULL) {
+ perror ("test1.img");
+ exit (EXIT_FAILURE);
+ }
+ fclose (fp);
+
+ fp = fopen ("test2.img", "w");
+ if (fp == NULL) {
+ perror ("test2.img");
+ exit (EXIT_FAILURE);
+ }
+ fclose (fp);
+
+ fp = fopen ("test3.img", "w");
+ if (fp == NULL) {
+ perror ("test3.img");
+ exit (EXIT_FAILURE);
+ }
+ fclose (fp);
+
+ /* Create the guestfs handle. */
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Create the libvirt connection. */
+ snprintf (libvirt_uri, sizeof libvirt_uri, "test://%s/test.xml", cwd);
+ conn = virConnectOpenReadOnly (libvirt_uri);
+ if (!conn) {
+ err = virGetLastError ();
+ fprintf (stderr, "could not connect to libvirt (code %d, domain %d): %s\n",
+ err->code, err->domain, err->message);
+ exit (EXIT_FAILURE);
+ }
+
+ dom = virDomainLookupByName (conn, "guest");
+ if (!dom) {
+ err = virGetLastError ();
+ fprintf (stderr,
+ "no libvirt domain called '%s': %s\n", "guest", err->message);
+ exit (EXIT_FAILURE);
+ }
+
+ r = guestfs_add_libvirt_dom (g, dom,
+ GUESTFS_ADD_LIBVIRT_DOM_READONLY, 1,
+ -1);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+
+ guestfs_close (g);
+
+ unlink ("test.xml");
+ unlink ("test1.img");
+ unlink ("test2.img");
+ unlink ("test3.img");
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-command.c b/tests/c-api/test-command.c
new file mode 100644
index 00000000..c23b7d17
--- /dev/null
+++ b/tests/c-api/test-command.c
@@ -0,0 +1,67 @@
+/* libguestfs
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* This program, which must be statically linked, is used to test the
+ * guestfs_command and guestfs_command_lines functions.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+
+int
+main (int argc, char *argv[])
+{
+ if (argc > 1) {
+ if (STREQ (argv[1], "1")) {
+ printf ("Result1");
+ } else if (STREQ (argv[1], "2")) {
+ printf ("Result2\n");
+ } else if (STREQ (argv[1], "3")) {
+ printf ("\nResult3");
+ } else if (STREQ (argv[1], "4")) {
+ printf ("\nResult4\n");
+ } else if (STREQ (argv[1], "5")) {
+ printf ("\nResult5\n\n");
+ } else if (STREQ (argv[1], "6")) {
+ printf ("\n\nResult6\n\n");
+ } else if (STREQ (argv[1], "7")) {
+ /* nothing */
+ } else if (STREQ (argv[1], "8")) {
+ printf ("\n");
+ } else if (STREQ (argv[1], "9")) {
+ printf ("\n\n");
+ } else if (STREQ (argv[1], "10")) {
+ printf ("Result10-1\nResult10-2\n");
+ } else if (STREQ (argv[1], "11")) {
+ printf ("Result11-1\nResult11-2");
+ } else {
+ fprintf (stderr, "unknown parameter: %s\n", argv[1]);
+ exit (EXIT_FAILURE);
+ }
+ } else {
+ fprintf (stderr, "missing parameter\n");
+ exit (EXIT_FAILURE);
+ }
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-config.c b/tests/c-api/test-config.c
new file mode 100644
index 00000000..c5411232
--- /dev/null
+++ b/tests/c-api/test-config.c
@@ -0,0 +1,68 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "guestfs.h"
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ int r;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* If these fail, the default error handler will print an error
+ * message to stderr, so we don't need to print anything. This code
+ * is very pedantic, but after all we are testing the details of the
+ * C API.
+ */
+
+ if (guestfs_set_verbose (g, 1) == -1)
+ exit (EXIT_FAILURE);
+ r = guestfs_get_verbose (g);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ if (!r) {
+ fprintf (stderr, "set_verbose not true\n");
+ exit (EXIT_FAILURE);
+ }
+ if (guestfs_set_verbose (g, 0) == -1)
+ exit (EXIT_FAILURE);
+ r = guestfs_get_verbose (g);
+ if (r == -1)
+ exit (EXIT_FAILURE);
+ if (r) {
+ fprintf (stderr, "set_verbose not false\n");
+ exit (EXIT_FAILURE);
+ }
+
+ guestfs_close (g);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-create-handle.c b/tests/c-api/test-create-handle.c
new file mode 100644
index 00000000..edea19f6
--- /dev/null
+++ b/tests/c-api/test-create-handle.c
@@ -0,0 +1,42 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "guestfs.h"
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ guestfs_close (g);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-debug-to-file.c b/tests/c-api/test-debug-to-file.c
new file mode 100644
index 00000000..5eb56104
--- /dev/null
+++ b/tests/c-api/test-debug-to-file.c
@@ -0,0 +1,91 @@
+/* libguestfs
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test that we can use the new event API to capture all debugging
+ * messages to a file.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ignore-value.h"
+
+#include "guestfs.h"
+
+static void
+debug_to_file (guestfs_h *g,
+ void *opaque,
+ uint64_t event,
+ int event_handle,
+ int flags,
+ const char *buf, size_t buf_len,
+ const uint64_t *array, size_t array_len)
+{
+ FILE *fp = opaque;
+
+ ignore_value (fwrite (buf, 1, buf_len, fp));
+}
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ const char *filename = "test.log";
+ FILE *debugfp;
+
+ debugfp = fopen (filename, "w");
+ if (debugfp == NULL) {
+ perror (filename);
+ exit (EXIT_FAILURE);
+ }
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_set_event_callback
+ (g, debug_to_file,
+ GUESTFS_EVENT_LIBRARY|GUESTFS_EVENT_APPLIANCE|GUESTFS_EVENT_TRACE,
+ 0, debugfp) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_set_verbose (g, 1) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_set_trace (g, 1) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_add_drive_opts (g, "/dev/null",
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
+ -1) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_launch (g) == -1)
+ exit (EXIT_FAILURE);
+
+ guestfs_close (g);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-just-header.c b/tests/c-api/test-just-header.c
new file mode 100644
index 00000000..57551f53
--- /dev/null
+++ b/tests/c-api/test-just-header.c
@@ -0,0 +1,30 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Check that just including the header and nothing else works, ie.
+ * that there are no implicit dependencies in the header file.
+ */
+
+#include "guestfs.h"
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g = guestfs_create ();
+ return 0;
+}
diff --git a/tests/c-api/test-last-errno.c b/tests/c-api/test-last-errno.c
new file mode 100644
index 00000000..31a5c4de
--- /dev/null
+++ b/tests/c-api/test-last-errno.c
@@ -0,0 +1,142 @@
+/* libguestfs
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test that we can get correct errnos all the way back from the
+ * appliance, translated to the local operating system.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "guestfs.h"
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ int fd, r, err;
+ struct guestfs_stat *stat;
+ const char *filename = "test1.img";
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
+ if (fd == -1) {
+ perror (filename);
+ exit (EXIT_FAILURE);
+ }
+ if (ftruncate (fd, 524288000) == -1) {
+ perror (filename);
+ close (fd);
+ unlink (filename);
+ exit (EXIT_FAILURE);
+ }
+ if (close (fd) == -1) {
+ perror (filename);
+ unlink (filename);
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_add_drive_opts (g, filename,
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ -1) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_launch (g) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1)
+ exit (EXIT_FAILURE);
+
+ /* Mount read-only, and check that errno == EROFS is passed back when
+ * we create a file.
+ */
+ if (guestfs_mount_ro (g, "/dev/sda1", "/") == -1)
+ exit (EXIT_FAILURE);
+
+ r = guestfs_touch (g, "/test");
+ if (r != -1) {
+ fprintf (stderr,
+ "guestfs_touch: expected error for read-only filesystem\n");
+ exit (EXIT_FAILURE);
+ }
+
+ err = guestfs_last_errno (g);
+ if (err != EROFS) {
+ fprintf (stderr,
+ "guestfs_touch: expected errno == EROFS, but got %d\n", err);
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_umount (g, "/") == -1)
+ exit (EXIT_FAILURE);
+
+ /* Mount it writable and test some other errors. */
+ if (guestfs_mount_options (g, "", "/dev/sda1", "/") == -1)
+ exit (EXIT_FAILURE);
+
+ stat = guestfs_lstat (g, "/nosuchfile");
+ if (stat != NULL) {
+ fprintf (stderr,
+ "guestfs_lstat: expected error for missing file\n");
+ exit (EXIT_FAILURE);
+ }
+
+ err = guestfs_last_errno (g);
+ if (err != ENOENT) {
+ fprintf (stderr,
+ "guestfs_lstat: expected errno == ENOENT, but got %d\n", err);
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_touch (g, "/test") == -1)
+ exit (EXIT_FAILURE);
+
+ r = guestfs_mkdir (g, "/test");
+ if (r != -1) {
+ fprintf (stderr,
+ "guestfs_mkdir: expected error for file which exists\n");
+ exit (EXIT_FAILURE);
+ }
+
+ err = guestfs_last_errno (g);
+ if (err != EEXIST) {
+ fprintf (stderr,
+ "guestfs_mkdir: expected errno == EEXIST, but got %d\n", err);
+ exit (EXIT_FAILURE);
+ }
+
+ guestfs_close (g);
+
+ unlink (filename);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-private-data.c b/tests/c-api/test-private-data.c
new file mode 100644
index 00000000..f2ff647b
--- /dev/null
+++ b/tests/c-api/test-private-data.c
@@ -0,0 +1,120 @@
+/* libguestfs
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test aspects of the private data area API. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "guestfs.h"
+
+#define PREFIX "test_"
+
+static size_t close_callback_called = 0;
+
+/* This callback deletes all test keys in the handle. */
+static void
+close_callback (guestfs_h *g,
+ void *opaque,
+ uint64_t event,
+ int event_handle,
+ int flags,
+ const char *buf, size_t buf_len,
+ const uint64_t *array, size_t array_len)
+{
+ const char *key;
+ void *data;
+
+ close_callback_called++;
+
+ again:
+ data = guestfs_first_private (g, &key);
+ while (data != NULL) {
+ if (strncmp (key, PREFIX, strlen (PREFIX)) == 0) {
+ guestfs_set_private (g, key, NULL);
+ goto again;
+ }
+ data = guestfs_next_private (g, &key);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ const char *key;
+ void *data;
+ size_t count;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_set_event_callback (g, close_callback, GUESTFS_EVENT_CLOSE,
+ 0, NULL) == -1)
+ exit (EXIT_FAILURE);
+
+ guestfs_set_private (g, PREFIX "a", (void *) 1);
+ guestfs_set_private (g, PREFIX "b", (void *) 2);
+ guestfs_set_private (g, PREFIX "c", (void *) 3);
+ guestfs_set_private (g, PREFIX "a", (void *) 4); /* overwrites previous */
+
+ /* Check we can fetch keys. */
+ assert (guestfs_get_private (g, PREFIX "a") == (void *) 4);
+ assert (guestfs_get_private (g, PREFIX "b") == (void *) 2);
+ assert (guestfs_get_private (g, PREFIX "c") == (void *) 3);
+ assert (guestfs_get_private (g, PREFIX "d") == NULL);
+
+ /* Check we can count keys by iterating. */
+ count = 0;
+ data = guestfs_first_private (g, &key);
+ while (data != NULL) {
+ if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
+ count++;
+ data = guestfs_next_private (g, &key);
+ }
+ assert (count == 3);
+
+ /* Delete some keys. */
+ guestfs_set_private (g, PREFIX "a", NULL);
+ guestfs_set_private (g, PREFIX "b", NULL);
+
+ /* Count them again. */
+ count = 0;
+ data = guestfs_first_private (g, &key);
+ while (data != NULL) {
+ if (strncmp (key, PREFIX, strlen (PREFIX)) == 0)
+ count++;
+ data = guestfs_next_private (g, &key);
+ }
+ assert (count == 1);
+
+ /* Closing should implicitly call the close_callback function. */
+ guestfs_close (g);
+
+ assert (close_callback_called == 1);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/tests/c-api/test-user-cancel.c b/tests/c-api/test-user-cancel.c
new file mode 100644
index 00000000..0cc049a0
--- /dev/null
+++ b/tests/c-api/test-user-cancel.c
@@ -0,0 +1,371 @@
+/* libguestfs
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test user cancellation.
+ *
+ * We perform the test using two threads. The main thread issues
+ * guestfs commands to download and upload large files. Uploads and
+ * downloads are done to/from a pipe which is connected back to the
+ * current process. The second test thread sits on the other end of
+ * the pipe, feeding or consuming data slowly, and injecting the user
+ * cancel events at a particular place in the transfer.
+ *
+ * It is important to test both download and upload separately, since
+ * these exercise different code paths in the library. However this
+ * adds complexity here because these tests are symmetric-but-opposite
+ * cases.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <math.h>
+
+#include <pthread.h>
+
+#include "guestfs.h"
+
+static const char *filename = "test.img";
+static const off_t filesize = 1024*1024*1024;
+
+static void remove_test_img (void);
+static void *start_test_thread (void *);
+static off_t random_cancel_posn (void);
+
+struct test_thread_data {
+ guestfs_h *g; /* handle */
+ int direction; /* direction of transfer */
+#define DIRECTION_UP 1 /* upload (test thread is writing) */
+#define DIRECTION_DOWN 2 /* download (test thread is reading) */
+ int fd; /* pipe to read/write */
+ off_t cancel_posn; /* position at which to cancel */
+ off_t transfer_size; /* how much data thread wrote/read */
+};
+
+int
+main (int argc, char *argv[])
+{
+ guestfs_h *g;
+ int fd;
+ char c = 0;
+ pthread_t test_thread;
+ struct test_thread_data data;
+ int fds[2], r, op_error, op_errno, errors = 0;
+ char dev_fd[64];
+
+ srand48 (time (NULL));
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ fprintf (stderr, "failed to create handle\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Create a test image and test data. */
+ fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666);
+ if (fd == -1) {
+ perror (filename);
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (remove_test_img);
+
+ if (lseek (fd, filesize - 1, SEEK_SET) == (off_t) -1) {
+ perror ("lseek");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+
+ if (write (fd, &c, 1) != 1) {
+ perror ("write");
+ close (fd);
+ exit (EXIT_FAILURE);
+ }
+
+ if (close (fd) == -1) {
+ perror ("test.img");
+ exit (EXIT_FAILURE);
+ }
+
+ if (guestfs_add_drive_opts (g, filename,
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ -1) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_launch (g) == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_mount_options (g, "", "/dev/sda1", "/") == -1)
+ exit (EXIT_FAILURE);
+
+ /*----- Upload cancellation test -----*/
+
+ data.g = g;
+ data.direction = DIRECTION_UP;
+
+ if (pipe (fds) == -1) {
+ perror ("pipe");
+ exit (EXIT_FAILURE);
+ }
+
+ data.fd = fds[1];
+ snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[0]);
+
+ data.cancel_posn = random_cancel_posn ();
+
+ /* Create the test thread. */
+ r = pthread_create (&test_thread, NULL, start_test_thread, &data);
+ if (r != 0) {
+ fprintf (stderr, "pthread_create: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+
+ /* Do the upload. */
+ op_error = guestfs_upload (g, dev_fd, "/upload");
+ op_errno = guestfs_last_errno (g);
+
+ /* Kill the test thread and clean up. */
+ r = pthread_cancel (test_thread);
+ if (r != 0) {
+ fprintf (stderr, "pthread_cancel: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ r = pthread_join (test_thread, NULL);
+ if (r != 0) {
+ fprintf (stderr, "pthread_join: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+
+ close (fds[0]);
+ close (fds[1]);
+
+ /* We expect to get an error, with errno == EINTR. */
+ if (op_error == -1 && op_errno == EINTR)
+ printf ("test-user-cancel: upload cancellation test passed (%ld/%ld)\n",
+ (long) data.cancel_posn, (long) data.transfer_size);
+ else {
+ fprintf (stderr, "test-user-cancel: upload cancellation test FAILED\n");
+ fprintf (stderr, "cancel_posn %ld, upload returned %d, errno = %d (%s)\n",
+ (long) data.cancel_posn, op_error, op_errno, strerror (op_errno));
+ errors++;
+ }
+
+ if (guestfs_rm (g, "/upload") == -1)
+ exit (EXIT_FAILURE);
+
+ /*----- Download cancellation test -----*/
+
+ if (guestfs_touch (g, "/download") == -1)
+ exit (EXIT_FAILURE);
+
+ if (guestfs_truncate_size (g, "/download", filesize/4) == -1)
+ exit (EXIT_FAILURE);
+
+ data.g = g;
+ data.direction = DIRECTION_DOWN;
+
+ if (pipe (fds) == -1) {
+ perror ("pipe");
+ exit (EXIT_FAILURE);
+ }
+
+ data.fd = fds[0];
+ snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[1]);
+
+ data.cancel_posn = random_cancel_posn ();
+
+ /* Create the test thread. */
+ r = pthread_create (&test_thread, NULL, start_test_thread, &data);
+ if (r != 0) {
+ fprintf (stderr, "pthread_create: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+
+ /* Do the download. */
+ op_error = guestfs_download (g, "/download", dev_fd);
+ op_errno = guestfs_last_errno (g);
+
+ /* Kill the test thread and clean up. */
+ r = pthread_cancel (test_thread);
+ if (r != 0) {
+ fprintf (stderr, "pthread_cancel: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ r = pthread_join (test_thread, NULL);
+ if (r != 0) {
+ fprintf (stderr, "pthread_join: %s", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+
+ close (fds[0]);
+ close (fds[1]);
+
+ /* We expect to get an error, with errno == EINTR. */
+ if (op_error == -1 && op_errno == EINTR)
+ printf ("test-user-cancel: download cancellation test passed (%ld/%ld)\n",
+ (long) data.cancel_posn, (long) data.transfer_size);
+ else {
+ fprintf (stderr, "test-user-cancel: download cancellation test FAILED\n");
+ fprintf (stderr, "cancel_posn %ld, upload returned %d, errno = %d (%s)\n",
+ (long) data.cancel_posn, op_error, op_errno, strerror (op_errno));
+ errors++;
+ }
+
+ exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void
+remove_test_img (void)
+{
+ unlink (filename);
+}
+
+static char buffer[BUFSIZ];
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+static void *
+start_test_thread (void *datav)
+{
+ struct test_thread_data *data = datav;
+ ssize_t r;
+ size_t n;
+
+ data->transfer_size = 0;
+
+ if (data->direction == DIRECTION_UP) { /* thread is writing */
+ /* Feed data in, up to the cancellation point. */
+ while (data->transfer_size < data->cancel_posn) {
+ n = MIN (sizeof buffer,
+ (size_t) (data->cancel_posn - data->transfer_size));
+ r = write (data->fd, buffer, n);
+ if (r == -1) {
+ perror ("test thread: write to pipe before user cancel");
+ exit (EXIT_FAILURE);
+ }
+ data->transfer_size += r;
+ }
+
+ /* Keep feeding data after the cancellation point for as long as
+ * the main thread wants it.
+ */
+ while (1) {
+ /* Repeatedly assert the cancel flag. We have to do this because
+ * the guestfs_upload command in the main thread may not have
+ * started yet.
+ */
+ guestfs_user_cancel (data->g);
+
+ r = write (data->fd, buffer, sizeof buffer);
+ if (r == -1) {
+ perror ("test thread: write to pipe after user cancel");
+ exit (EXIT_FAILURE);
+ }
+ data->transfer_size += r;
+ }
+ } else { /* thread is reading */
+ /* Sink data, up to the cancellation point. */
+ while (data->transfer_size < data->cancel_posn) {
+ n = MIN (sizeof buffer,
+ (size_t) (data->cancel_posn - data->transfer_size));
+ r = read (data->fd, buffer, n);
+ if (r == -1) {
+ perror ("test thread: read from pipe before user cancel");
+ exit (EXIT_FAILURE);
+ }
+ if (r == 0) {
+ perror ("test thread: unexpected end of file before user cancel");
+ exit (EXIT_FAILURE);
+ }
+ data->transfer_size += r;
+ }
+
+ /* Do user cancellation. */
+ guestfs_user_cancel (data->g);
+
+ /* Keep sinking data as long as the main thread is writing. */
+ while (1) {
+ r = read (data->fd, buffer, sizeof buffer);
+ if (r == -1) {
+ perror ("test thread: read from pipe after user cancel");
+ exit (EXIT_FAILURE);
+ }
+ if (r == 0)
+ break;
+ data->transfer_size += r;
+ }
+
+ while (1)
+ pause ();
+ }
+
+ return NULL;
+}
+
+static double random_gauss (double mu, double sd);
+
+/* Generate a random cancellation position, but skew it towards
+ * smaller numbers.
+ */
+static off_t
+random_cancel_posn (void)
+{
+ const double mu = 65536;
+ const double sd = 65536 * 4;
+ double r;
+
+ do {
+ r = random_gauss (mu, sd);
+ } while (r <= 0);
+
+ return (off_t) r;
+}
+
+/* Generate a random Gaussian distributed number using the Box-Muller
+ * transformation. (http://www.taygeta.com/random/gaussian.html)
+ */
+static double
+random_gauss (double mu, double sd)
+{
+ double x1, x2, w, y1;
+
+ do {
+ x1 = 2. * drand48 () - 1.;
+ x2 = 2. * drand48 () - 1.;
+ w = x1 * x1 + x2 * x2;
+ } while (w >= 1.);
+
+ w = sqrt ((-2. * log (w)) / w);
+ y1 = x1 * w;
+ //y2 = x2 * w;
+ return mu + y1 * sd;
+}
diff --git a/tests/qemu/Makefile.am b/tests/qemu/Makefile.am
index fb95a747..ad735101 100644
--- a/tests/qemu/Makefile.am
+++ b/tests/qemu/Makefile.am
@@ -17,8 +17,6 @@
# Safety and liveness tests of components that libguestfs depends upon
# (not of libguestfs itself). Mainly this is for qemu and the kernel.
-#
-# See also capitests and regressions directories.
include $(top_srcdir)/subdir-rules.mk