From 5d93d70b4d36b2337104b3dbca07722fa4d47ff5 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 22 Dec 2011 08:40:30 +0000 Subject: tests: Rename capitests -> tests/c-api. --- .gitignore | 30 +- Makefile.am | 4 +- capitests/Makefile.am | 152 ------ capitests/test-add-drive-opts.c | 58 --- capitests/test-add-libvirt-dom.c | 151 ------ capitests/test-command.c | 67 --- capitests/test-config.c | 68 --- capitests/test-create-handle.c | 42 -- capitests/test-debug-to-file.c | 91 ---- capitests/test-just-header.c | 30 -- capitests/test-last-errno.c | 142 ------ capitests/test-private-data.c | 120 ----- capitests/test-user-cancel.c | 371 -------------- configure.ac | 2 +- extratests/Makefile.am | 8 +- generator/.depend | 24 +- generator/Makefile.am | 2 +- generator/generator_actions.ml | 8 +- generator/generator_capitests.ml | 955 ------------------------------------- generator/generator_main.ml | 4 +- generator/generator_tests_c_api.ml | 955 +++++++++++++++++++++++++++++++++++++ regressions/Makefile.am | 2 - src/guestfs.pod | 6 +- tests/c-api/Makefile.am | 152 ++++++ tests/c-api/test-add-drive-opts.c | 58 +++ tests/c-api/test-add-libvirt-dom.c | 151 ++++++ tests/c-api/test-command.c | 67 +++ tests/c-api/test-config.c | 68 +++ tests/c-api/test-create-handle.c | 42 ++ tests/c-api/test-debug-to-file.c | 91 ++++ tests/c-api/test-just-header.c | 30 ++ tests/c-api/test-last-errno.c | 142 ++++++ tests/c-api/test-private-data.c | 120 +++++ tests/c-api/test-user-cancel.c | 371 ++++++++++++++ tests/qemu/Makefile.am | 2 - 35 files changed, 2289 insertions(+), 2297 deletions(-) delete mode 100644 capitests/Makefile.am delete mode 100644 capitests/test-add-drive-opts.c delete mode 100644 capitests/test-add-libvirt-dom.c delete mode 100644 capitests/test-command.c delete mode 100644 capitests/test-config.c delete mode 100644 capitests/test-create-handle.c delete mode 100644 capitests/test-debug-to-file.c delete mode 100644 capitests/test-just-header.c delete mode 100644 capitests/test-last-errno.c delete mode 100644 capitests/test-private-data.c delete mode 100644 capitests/test-user-cancel.c delete mode 100644 generator/generator_capitests.ml create mode 100644 generator/generator_tests_c_api.ml create mode 100644 tests/c-api/Makefile.am create mode 100644 tests/c-api/test-add-drive-opts.c create mode 100644 tests/c-api/test-add-libvirt-dom.c create mode 100644 tests/c-api/test-command.c create mode 100644 tests/c-api/test-config.c create mode 100644 tests/c-api/test-create-handle.c create mode 100644 tests/c-api/test-debug-to-file.c create mode 100644 tests/c-api/test-just-header.c create mode 100644 tests/c-api/test-last-errno.c create mode 100644 tests/c-api/test-private-data.c create mode 100644 tests/c-api/test-user-cancel.c diff --git a/.gitignore b/.gitignore index aab6cd0a..c322a279 100644 --- a/.gitignore +++ b/.gitignore @@ -13,21 +13,6 @@ appliance/supermin.d autom4te.cache *.bak bindtests.tmp -capitests/test.log -capitests/test-add-drive-opts -capitests/test-add-libvirt-dom -capitests/test-command -capitests/test-config -capitests/test-create-handle -capitests/test-debug-to-file -capitests/test-just-header -capitests/test-last-errno -capitests/test-private-data -capitests/test-user-cancel -capitests/test*.img -capitests/tests -capitests/tests.c -capitests/test*.tmp cat/stamp-virt-*.pod cat/virt-cat cat/virt-cat.1 @@ -359,6 +344,21 @@ test1.img test-tool/libguestfs-test-tool test-tool/libguestfs-test-tool.1 test-tool/libguestfs-test-tool-helper +tests/c-api/test.log +tests/c-api/test-add-drive-opts +tests/c-api/test-add-libvirt-dom +tests/c-api/test-command +tests/c-api/test-config +tests/c-api/test-create-handle +tests/c-api/test-debug-to-file +tests/c-api/test-just-header +tests/c-api/test-last-errno +tests/c-api/test-private-data +tests/c-api/test-user-cancel +tests/c-api/test*.img +tests/c-api/tests +tests/c-api/tests.c +tests/c-api/test*.tmp tools/test.img tools/virt-*.1 tools/virt-*.pl diff --git a/Makefile.am b/Makefile.am index eddf4f9d..5c8ff677 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,7 +30,7 @@ SUBDIRS += appliance endif # Tests and the test-tool. -SUBDIRS += gnulib/tests capitests tests/qemu regressions extratests test-tool +SUBDIRS += gnulib/tests tests/c-api tests/qemu regressions extratests test-tool # Guestfish. SUBDIRS += fish @@ -218,7 +218,7 @@ all-local: find $(DIST_SUBDIRS) \ -name '*.c' -o -name '*.pl' -o -name '*.pm' | \ grep -v '^perl/blib/' | \ - grep -v '^capitests/' | \ + grep -v '^tests/' | \ grep -v '^daemon/lib/' | \ grep -v '^daemon/tests/' | \ grep -v '^examples/' | \ diff --git a/capitests/Makefile.am b/capitests/Makefile.am deleted file mode 100644 index d32b7fc3..00000000 --- a/capitests/Makefile.am +++ /dev/null @@ -1,152 +0,0 @@ -# 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 \ - ../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) ../gnulib/lib/libgnu.la -#endif - -# Run the tests under valgrind. - -valgrind: - $(MAKE) check VG="valgrind --quiet --leak-check=full" diff --git a/capitests/test-add-drive-opts.c b/capitests/test-add-drive-opts.c deleted file mode 100644 index 897c6fac..00000000 --- a/capitests/test-add-drive-opts.c +++ /dev/null @@ -1,58 +0,0 @@ -/* 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 - -#include -#include -#include -#include - -#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/capitests/test-add-libvirt-dom.c b/capitests/test-add-libvirt-dom.c deleted file mode 100644 index 6f994056..00000000 --- a/capitests/test-add-libvirt-dom.c +++ /dev/null @@ -1,151 +0,0 @@ -/* 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 - -#include -#include -#include -#include - -#include "xgetcwd.h" - -#include -#include - -#include "guestfs.h" - -static void -make_test_xml (FILE *fp, const char *cwd) -{ - fprintf (fp, - "\n" - "\n" - " \n" - " guest\n" - " \n" - " hvm\n" - " \n" - " \n" - " 524288\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "", - 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/capitests/test-command.c b/capitests/test-command.c deleted file mode 100644 index c23b7d17..00000000 --- a/capitests/test-command.c +++ /dev/null @@ -1,67 +0,0 @@ -/* 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 -#include -#include -#include -#include - -#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/capitests/test-config.c b/capitests/test-config.c deleted file mode 100644 index c5411232..00000000 --- a/capitests/test-config.c +++ /dev/null @@ -1,68 +0,0 @@ -/* 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 - -#include -#include -#include -#include - -#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/capitests/test-create-handle.c b/capitests/test-create-handle.c deleted file mode 100644 index edea19f6..00000000 --- a/capitests/test-create-handle.c +++ /dev/null @@ -1,42 +0,0 @@ -/* 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 - -#include -#include -#include -#include - -#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/capitests/test-debug-to-file.c b/capitests/test-debug-to-file.c deleted file mode 100644 index 5eb56104..00000000 --- a/capitests/test-debug-to-file.c +++ /dev/null @@ -1,91 +0,0 @@ -/* 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 - -#include -#include -#include -#include - -#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/capitests/test-just-header.c b/capitests/test-just-header.c deleted file mode 100644 index 57551f53..00000000 --- a/capitests/test-just-header.c +++ /dev/null @@ -1,30 +0,0 @@ -/* 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/capitests/test-last-errno.c b/capitests/test-last-errno.c deleted file mode 100644 index 31a5c4de..00000000 --- a/capitests/test-last-errno.c +++ /dev/null @@ -1,142 +0,0 @@ -/* 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 - -#include -#include -#include -#include -#include -#include - -#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/capitests/test-private-data.c b/capitests/test-private-data.c deleted file mode 100644 index f2ff647b..00000000 --- a/capitests/test-private-data.c +++ /dev/null @@ -1,120 +0,0 @@ -/* 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 - -#include -#include -#include -#include -#include - -#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/capitests/test-user-cancel.c b/capitests/test-user-cancel.c deleted file mode 100644 index 0cc049a0..00000000 --- a/capitests/test-user-cancel.c +++ /dev/null @@ -1,371 +0,0 @@ -/* 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 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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/configure.ac b/configure.ac index ce38b41e..ac09844e 100644 --- a/configure.ac +++ b/configure.ac @@ -1006,7 +1006,6 @@ AC_CONFIG_FILES([run], AC_CONFIG_FILES([Makefile align/Makefile appliance/Makefile - capitests/Makefile cat/Makefile clone/Makefile csharp/Makefile @@ -1051,6 +1050,7 @@ AC_CONFIG_FILES([Makefile sparsify/Makefile src/Makefile test-tool/Makefile + tests/c-api/Makefile tests/qemu/Makefile tools/Makefile]) AC_OUTPUT diff --git a/extratests/Makefile.am b/extratests/Makefile.am index 32dcbf2b..0b4e20ea 100644 --- a/extratests/Makefile.am +++ b/extratests/Makefile.am @@ -70,7 +70,7 @@ GUESTS = $(shell virsh -c $(LIBVIRT_DEFAULT_URI) list --all | \ extra-tests: \ test-prerequisites \ - test-capitests \ + test-c-api \ test-tools-null \ test-tools-internal \ test-df-real \ @@ -150,9 +150,9 @@ test-resize: test-sparsify: $(MAKE) -C ../sparsify VG="$(VG)" check -# Run capitests under valgrind. -test-capitests: - $(MAKE) -C ../capitests VG="$(RUN_VG)" check +# Run c-api tests under valgrind. +test-c-api: + $(MAKE) -C ../tests/c-api VG="$(RUN_VG)" check # Test OCaml bindings under valgrind. test-ocaml: diff --git a/generator/.depend b/generator/.depend index ca7eb6a6..acbce64a 100644 --- a/generator/.depend +++ b/generator/.depend @@ -54,10 +54,10 @@ generator_daemon.cmo: generator_utils.cmi generator_types.cmo \ generator_daemon.cmx: generator_utils.cmx generator_types.cmx \ generator_structs.cmx generator_pr.cmx generator_optgroups.cmx \ generator_docstrings.cmx generator_c.cmx generator_actions.cmx -generator_capitests.cmo: generator_utils.cmi generator_types.cmo \ +generator_tests_c_api.cmo: generator_utils.cmi generator_types.cmo \ generator_structs.cmi generator_pr.cmi generator_optgroups.cmo \ generator_docstrings.cmo generator_actions.cmi -generator_capitests.cmx: generator_utils.cmx generator_types.cmx \ +generator_tests_c_api.cmx: generator_utils.cmx generator_types.cmx \ generator_structs.cmx generator_pr.cmx generator_optgroups.cmx \ generator_docstrings.cmx generator_actions.cmx generator_fish.cmo: generator_utils.cmi generator_types.cmo \ @@ -142,17 +142,17 @@ generator_errnostring.cmo: generator_utils.cmi generator_types.cmo \ generator_pr.cmi generator_docstrings.cmo generator_errnostring.cmx: generator_utils.cmx generator_types.cmx \ generator_pr.cmx generator_docstrings.cmx -generator_main.cmo: generator_xdr.cmo generator_structs.cmi \ - generator_ruby.cmo generator_python.cmo generator_pr.cmi \ - generator_php.cmo generator_perl.cmo generator_ocaml.cmo \ +generator_main.cmo: generator_xdr.cmo generator_tests_c_api.cmo \ + generator_structs.cmi generator_ruby.cmo generator_python.cmo \ + generator_pr.cmi generator_php.cmo generator_perl.cmo generator_ocaml.cmo \ generator_java.cmo generator_haskell.cmo generator_fish.cmo \ generator_errnostring.cmo generator_erlang.cmo generator_daemon.cmo \ - generator_csharp.cmo generator_capitests.cmo generator_c.cmo \ - generator_bindtests.cmo generator_api_versions.cmi -generator_main.cmx: generator_xdr.cmx generator_structs.cmx \ - generator_ruby.cmx generator_python.cmx generator_pr.cmx \ - generator_php.cmx generator_perl.cmx generator_ocaml.cmx \ + generator_csharp.cmo generator_c.cmo generator_bindtests.cmo \ + generator_api_versions.cmi +generator_main.cmx: generator_xdr.cmx generator_tests_c_api.cmx \ + generator_structs.cmx generator_ruby.cmx generator_python.cmx \ + generator_pr.cmx generator_php.cmx generator_perl.cmx generator_ocaml.cmx \ generator_java.cmx generator_haskell.cmx generator_fish.cmx \ generator_errnostring.cmx generator_erlang.cmx generator_daemon.cmx \ - generator_csharp.cmx generator_capitests.cmx generator_c.cmx \ - generator_bindtests.cmx generator_api_versions.cmx + generator_csharp.cmx generator_c.cmx generator_bindtests.cmx \ + generator_api_versions.cmx diff --git a/generator/Makefile.am b/generator/Makefile.am index d6120e35..dfa58934 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -36,7 +36,7 @@ SOURCES = \ generator_c.ml \ generator_xdr.ml \ generator_daemon.ml \ - generator_capitests.ml \ + generator_tests_c_api.ml \ generator_fish.ml \ generator_ocaml.ml \ generator_perl.ml \ diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index d506d2fb..f7a6a6cf 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -2718,7 +2718,7 @@ This uses the L command."); [InitScratchFS, Always, TestOutput ( (* Pick a file from cwd which isn't likely to change. *) [["mkdir"; "/upload"]; - ["upload"; "../COPYING.LIB"; "/upload/COPYING.LIB"]; + ["upload"; "../../COPYING.LIB"; "/upload/COPYING.LIB"]; ["checksum"; "md5"; "/upload/COPYING.LIB"]], Digest.to_hex (Digest.file "COPYING.LIB"))], "upload a file from the local machine", @@ -2734,7 +2734,7 @@ See also C."); [InitScratchFS, Always, TestOutput ( (* Pick a file from cwd which isn't likely to change. *) [["mkdir"; "/download"]; - ["upload"; "../COPYING.LIB"; "/download/COPYING.LIB"]; + ["upload"; "../../COPYING.LIB"; "/download/COPYING.LIB"]; ["download"; "/download/COPYING.LIB"; "testdownload.tmp"]; ["upload"; "testdownload.tmp"; "/download/upload"]; ["checksum"; "md5"; "/download/upload"]], @@ -5936,7 +5936,7 @@ See also C."); ("upload_offset", (RErr, [FileIn "filename"; Dev_or_Path "remotefilename"; Int64 "offset"], []), 273, [Progress], (let md5 = Digest.to_hex (Digest.file "COPYING.LIB") in [InitScratchFS, Always, TestOutput ( - [["upload_offset"; "../COPYING.LIB"; "/upload_offset"; "0"]; + [["upload_offset"; "../../COPYING.LIB"; "/upload_offset"; "0"]; ["checksum"; "md5"; "/upload_offset"]], md5)]), "upload a file from the local machine with offset", "\ @@ -5964,7 +5964,7 @@ See also C, C."); [InitScratchFS, Always, TestOutput ( (* Pick a file from cwd which isn't likely to change. *) [["mkdir"; "/download_offset"]; - ["upload"; "../COPYING.LIB"; "/download_offset/COPYING.LIB"]; + ["upload"; "../../COPYING.LIB"; "/download_offset/COPYING.LIB"]; ["download_offset"; "/download_offset/COPYING.LIB"; "testdownload.tmp"; offset; size]; ["upload_offset"; "testdownload.tmp"; "/download_offset/COPYING.LIB"; offset]; ["checksum"; "md5"; "/download_offset/COPYING.LIB"]], md5)]), diff --git a/generator/generator_capitests.ml b/generator/generator_capitests.ml deleted file mode 100644 index f9cacf24..00000000 --- a/generator/generator_capitests.ml +++ /dev/null @@ -1,955 +0,0 @@ -(* 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 - *) - -(* Please read generator/README first. *) - -open Printf - -open Generator_types -open Generator_utils -open Generator_pr -open Generator_docstrings -open Generator_optgroups -open Generator_actions -open Generator_structs - -(* Generate the tests. *) -let rec generate_tests () = - generate_header CStyle GPLv2plus; - - pr "\ -#include -#include -#include -#include -#include -#include - -#include \"guestfs.h\" -#include \"guestfs-internal.h\" - -static guestfs_h *g; -static int suppress_error = 0; - -static void print_error (guestfs_h *g, void *data, const char *msg) -{ - if (!suppress_error) - fprintf (stderr, \"%%s\\n\", msg); -} - -/* FIXME: nearly identical code appears in fish.c */ -static void print_strings (char *const *argv) -{ - size_t argc; - - for (argc = 0; argv[argc] != NULL; ++argc) - printf (\"\\t%%s\\n\", argv[argc]); -} - -/* -static void print_table (char const *const *argv) -{ - size_t i; - - for (i = 0; argv[i] != NULL; i += 2) - printf (\"%%s: %%s\\n\", argv[i], argv[i+1]); -} -*/ - -static int -is_available (const char *group) -{ - const char *groups[] = { group, NULL }; - int r; - - suppress_error = 1; - r = guestfs_available (g, (char **) groups); - suppress_error = 0; - - return r == 0; -} - -static void -incr (guestfs_h *g, void *iv) -{ - int *i = (int *) iv; - (*i)++; -} - -/* Get md5sum of the named file. */ -static void -md5sum (const char *filename, char *result) -{ - char cmd[256]; - snprintf (cmd, sizeof cmd, \"md5sum %%s\", filename); - FILE *pp = popen (cmd, \"r\"); - if (pp == NULL) { - perror (cmd); - exit (EXIT_FAILURE); - } - if (fread (result, 1, 32, pp) != 32) { - perror (\"md5sum: fread\"); - exit (EXIT_FAILURE); - } - if (pclose (pp) != 0) { - perror (\"pclose\"); - exit (EXIT_FAILURE); - } - result[32] = '\\0'; -} - -/* Return the value for a key in a hashtable. - * Note: the return value is part of the hash and should not be freed. - */ -static const char * -get_key (char **hash, const char *key) -{ - size_t i; - - for (i = 0; hash[i] != NULL; i += 2) { - if (STREQ (hash[i], key)) - return hash[i+1]; - } - - return NULL; /* key not found */ -} - -"; - - (* Generate a list of commands which are not tested anywhere. *) - pr "static void no_test_warnings (void)\n"; - pr "{\n"; - - let hash : (string, bool) Hashtbl.t = Hashtbl.create 13 in - List.iter ( - fun (_, _, _, _, tests, _, _) -> - let tests = filter_map ( - function - | (_, (Always|If _|Unless _|IfAvailable _), test) -> Some test - | (_, Disabled, _) -> None - ) tests in - let seq = List.concat (List.map seq_of_test tests) in - let cmds_tested = List.map List.hd seq in - List.iter (fun cmd -> Hashtbl.replace hash cmd true) cmds_tested - ) all_functions; - - List.iter ( - fun (name, _, _, _, _, _, _) -> - if not (Hashtbl.mem hash name) then - pr " fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name - ) all_functions; - - pr "}\n"; - pr "\n"; - - (* Generate the actual tests. Note that we generate the tests - * in reverse order, deliberately, so that (in general) the - * newest tests run first. This makes it quicker and easier to - * debug them. - *) - let test_names = - List.map ( - fun (name, _, _, flags, tests, _, _) -> - mapi (generate_one_test name flags) tests - ) (List.rev all_functions) in - let test_names = List.concat test_names in - let nr_tests = List.length test_names in - - pr "\ -int main (int argc, char *argv[]) -{ - char c = 0; - unsigned long int n_failed = 0; - const char *filename; - int fd; - int nr_tests, test_num = 0; - - setbuf (stdout, NULL); - - no_test_warnings (); - - g = guestfs_create (); - if (g == NULL) { - printf (\"guestfs_create FAILED\\n\"); - exit (EXIT_FAILURE); - } - - guestfs_set_error_handler (g, print_error, NULL); - - filename = \"test1.img\"; - fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); - if (fd == -1) { - perror (filename); - exit (EXIT_FAILURE); - } - if (ftruncate (fd, %d) == -1) { - perror (\"ftruncate\"); - close (fd); - unlink (filename); - exit (EXIT_FAILURE); - } - if (close (fd) == -1) { - perror (filename); - unlink (filename); - exit (EXIT_FAILURE); - } - if (guestfs_add_drive (g, filename) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", filename); - exit (EXIT_FAILURE); - } - - filename = \"test2.img\"; - fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); - if (fd == -1) { - perror (filename); - exit (EXIT_FAILURE); - } - if (ftruncate (fd, %d) == -1) { - perror (\"ftruncate\"); - close (fd); - unlink (filename); - exit (EXIT_FAILURE); - } - if (close (fd) == -1) { - perror (filename); - unlink (filename); - exit (EXIT_FAILURE); - } - if (guestfs_add_drive (g, filename) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", filename); - exit (EXIT_FAILURE); - } - - filename = \"test3.img\"; - fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); - if (fd == -1) { - perror (filename); - exit (EXIT_FAILURE); - } - if (ftruncate (fd, %d) == -1) { - perror (\"ftruncate\"); - close (fd); - unlink (filename); - exit (EXIT_FAILURE); - } - if (close (fd) == -1) { - perror (filename); - unlink (filename); - exit (EXIT_FAILURE); - } - if (guestfs_add_drive (g, filename) == -1) { - printf (\"guestfs_add_drive %%s FAILED\\n\", filename); - exit (EXIT_FAILURE); - } - - if (guestfs_add_drive_ro (g, \"../images/test.iso\") == -1) { - printf (\"guestfs_add_drive_ro ../images/test.iso FAILED\\n\"); - exit (EXIT_FAILURE); - } - - /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */ - alarm (600); - - if (guestfs_launch (g) == -1) { - printf (\"guestfs_launch FAILED\\n\"); - exit (EXIT_FAILURE); - } - - /* Cancel previous alarm. */ - alarm (0); - - /* Create ext2 filesystem on /dev/sdb1 partition. */ - if (guestfs_part_disk (g, \"/dev/sdb\", \"mbr\") == -1) { - printf (\"guestfs_part_disk FAILED\\n\"); - exit (EXIT_FAILURE); - } - if (guestfs_mkfs (g, \"ext2\", \"/dev/sdb1\") == -1) { - printf (\"guestfs_mkfs (/dev/sdb1) FAILED\\n\"); - exit (EXIT_FAILURE); - } - - nr_tests = %d; - -" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests; - - iteri ( - fun i test_name -> - pr " test_num++;\n"; - pr " if (guestfs_get_verbose (g))\n"; - pr " printf (\"-------------------------------------------------------------------------------\\n\");\n"; - pr " printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name; - pr " if (%s () == -1) {\n" test_name; - pr " printf (\"%s FAILED\\n\");\n" test_name; - pr " n_failed++;\n"; - pr " }\n"; - ) test_names; - pr "\n"; - - pr " /* Check close callback is called. */ - int close_sentinel = 1; - guestfs_set_close_callback (g, incr, &close_sentinel); - - guestfs_close (g); - - if (close_sentinel != 2) { - fprintf (stderr, \"close callback was not called\\n\"); - exit (EXIT_FAILURE); - } - - unlink (\"test1.img\"); - unlink (\"test2.img\"); - unlink (\"test3.img\"); - -"; - - pr " if (n_failed > 0) {\n"; - pr " printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n"; - pr " exit (EXIT_FAILURE);\n"; - pr " }\n"; - pr "\n"; - - pr " exit (EXIT_SUCCESS);\n"; - pr "}\n" - -and generate_one_test name flags i (init, prereq, test) = - let test_name = sprintf "test_%s_%d" name i in - - pr "\ -static int %s_skip (void) -{ - const char *str; - - str = getenv (\"TEST_ONLY\"); - if (str) - return strstr (str, \"%s\") == NULL; - str = getenv (\"SKIP_%s\"); - if (str && STREQ (str, \"1\")) return 1; - str = getenv (\"SKIP_TEST_%s\"); - if (str && STREQ (str, \"1\")) return 1; - return 0; -} - -" test_name name (String.uppercase test_name) (String.uppercase name); - - (match prereq with - | Disabled | Always | IfAvailable _ -> () - | 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: environment variable set)\\n\", \"%s\"); - return 0; - } - -" test_name test_name test_name; - - (* Optional functions should only be tested if the relevant - * support is available in the daemon. - *) - List.iter ( - function - | Optional group -> - pr " if (!is_available (\"%s\")) {\n" group; - pr " printf (\" %%s skipped (reason: group %%s not available in daemon)\\n\", \"%s\", \"%s\");\n" test_name group; - pr " return 0;\n"; - pr " }\n"; - | _ -> () - ) flags; - - (match prereq with - | Disabled -> - 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; - | IfAvailable group -> - pr " if (!is_available (\"%s\")) {\n" group; - pr " printf (\" %%s skipped (reason: %%s not available)\\n\", \"%s\", \"%s\");\n" test_name group; - 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 - ); - - pr " return 0;\n"; - pr "}\n"; - pr "\n"; - test_name - -and generate_one_test_body name i test_name init test = - (match init with - | InitNone (* XXX at some point, InitNone and InitEmpty became - * folded together as the same thing. Really we should - * make InitNone do nothing at all, but the tests may - * need to be checked to make sure this is OK. - *) - | InitEmpty -> - 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"]] - | InitPartition -> - pr " /* InitPartition for %s: create /dev/sda1 */\n" test_name; - List.iter (generate_test_command_call test_name) - [["blockdev_setrw"; "/dev/sda"]; - ["umount_all"]; - ["lvm_remove_all"]; - ["part_disk"; "/dev/sda"; "mbr"]] - | InitBasicFS -> - 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"]; - ["lvm_remove_all"]; - ["part_disk"; "/dev/sda"; "mbr"]; - ["mkfs"; "ext2"; "/dev/sda1"]; - ["mount_options"; ""; "/dev/sda1"; "/"]] - | InitBasicFSonLVM -> - 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"]; - ["lvm_remove_all"]; - ["part_disk"; "/dev/sda"; "mbr"]; - ["pvcreate"; "/dev/sda1"]; - ["vgcreate"; "VG"; "/dev/sda1"]; - ["lvcreate"; "LV"; "VG"; "8"]; - ["mkfs"; "ext2"; "/dev/VG/LV"]; - ["mount_options"; ""; "/dev/VG/LV"; "/"]] - | InitISOFS -> - pr " /* InitISOFS for %s */\n" test_name; - List.iter (generate_test_command_call test_name) - [["blockdev_setrw"; "/dev/sda"]; - ["umount_all"]; - ["lvm_remove_all"]; - ["mount_ro"; "/dev/sdd"; "/"]] - | InitScratchFS -> - pr " /* InitScratchFS for %s */\n" test_name; - List.iter (generate_test_command_call test_name) - [["blockdev_setrw"; "/dev/sda"]; - ["umount_all"]; - ["lvm_remove_all"]; - ["mount_options"; ""; "/dev/sdb1"; "/"]] - ); - - let get_seq_last = function - | [] -> - failwithf "%s: you cannot use [] (empty list) when expecting a command" - test_name - | seq -> - let seq = List.rev seq in - List.rev (List.tl seq), List.hd seq - in - - match test with - | TestRun seq -> - pr " /* TestRun for %s (%d) */\n" name i; - List.iter (generate_test_command_call test_name) seq - | TestOutput (seq, expected) -> - pr " /* TestOutput for %s (%d) */\n" name i; - pr " const char *expected = \"%s\";\n" (c_quote expected); - let seq, last = get_seq_last seq in - let test () = - pr " if (STRNEQ (r, expected)) {\n"; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputList (seq, expected) -> - pr " /* TestOutputList for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - iteri ( - fun i str -> - pr " if (!r[%d]) {\n" i; - pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n"; - pr " {\n"; - pr " const char *expected = \"%s\";\n" (c_quote str); - pr " if (STRNEQ (r[%d], expected)) {\n" i; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i; - pr " return -1;\n"; - pr " }\n"; - pr " }\n" - ) expected; - pr " if (r[%d] != NULL) {\n" (List.length expected); - pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n" - test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputListOfDevices (seq, expected) -> - pr " /* TestOutputListOfDevices for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - iteri ( - fun i str -> - pr " if (!r[%d]) {\n" i; - pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n"; - pr " {\n"; - pr " const char *expected = \"%s\";\n" (c_quote str); - pr " r[%d][5] = 's';\n" i; - pr " if (STRNEQ (r[%d], expected)) {\n" i; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i; - pr " return -1;\n"; - pr " }\n"; - pr " }\n" - ) expected; - pr " if (r[%d] != NULL) {\n" (List.length expected); - pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n" - test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputInt (seq, expected) -> - pr " /* TestOutputInt for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - pr " if (r != %d) {\n" expected; - pr " fprintf (stderr, \"%s: expected %d but got %%d\\n\"," - test_name expected; - pr " (int) r);\n"; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputIntOp (seq, op, expected) -> - pr " /* TestOutputIntOp for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - pr " if (! (r %s %d)) {\n" op expected; - pr " fprintf (stderr, \"%s: expected %s %d but got %%d\\n\"," - test_name op expected; - pr " (int) r);\n"; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputTrue seq -> - pr " /* TestOutputTrue for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - pr " if (!r) {\n"; - pr " fprintf (stderr, \"%s: expected true, got false\\n\");\n" - test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputFalse seq -> - pr " /* TestOutputFalse for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - pr " if (r) {\n"; - pr " fprintf (stderr, \"%s: expected false, got true\\n\");\n" - test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputLength (seq, expected) -> - pr " /* TestOutputLength for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - pr " int j;\n"; - pr " for (j = 0; j < %d; ++j)\n" expected; - pr " if (r[j] == NULL) {\n"; - pr " fprintf (stderr, \"%s: short list returned\\n\");\n" - test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n"; - pr " if (r[j] != NULL) {\n"; - pr " fprintf (stderr, \"%s: long list returned\\n\");\n" - test_name; - pr " print_strings (r);\n"; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputBuffer (seq, expected) -> - pr " /* TestOutputBuffer for %s (%d) */\n" name i; - pr " const char *expected = \"%s\";\n" (c_quote expected); - let seq, last = get_seq_last seq in - let len = String.length expected in - let test () = - pr " if (size != %d) {\n" len; - pr " fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len; - pr " return -1;\n"; - pr " }\n"; - pr " if (STRNEQLEN (r, expected, size)) {\n"; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputStruct (seq, checks) -> - pr " /* TestOutputStruct for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - let test () = - List.iter ( - function - | CompareWithInt (field, expected) -> - pr " if (r->%s != %d) {\n" field expected; - pr " fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n" - test_name field expected; - pr " (int) r->%s);\n" field; - pr " return -1;\n"; - pr " }\n" - | CompareWithIntOp (field, op, expected) -> - pr " if (!(r->%s %s %d)) {\n" field op expected; - pr " fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n" - test_name field op expected; - pr " (int) r->%s);\n" field; - pr " return -1;\n"; - pr " }\n" - | CompareWithString (field, expected) -> - pr " if (STRNEQ (r->%s, \"%s\")) {\n" field expected; - pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n" - test_name field expected; - pr " r->%s);\n" field; - pr " return -1;\n"; - pr " }\n" - | CompareFieldsIntEq (field1, field2) -> - pr " if (r->%s != r->%s) {\n" field1 field2; - pr " fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n" - test_name field1 field2; - pr " (int) r->%s, (int) r->%s);\n" field1 field2; - pr " return -1;\n"; - pr " }\n" - | CompareFieldsStrEq (field1, field2) -> - pr " if (STRNEQ (r->%s, r->%s)) {\n" field1 field2; - pr " fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n" - test_name field1 field2; - pr " r->%s, r->%s);\n" field1 field2; - pr " return -1;\n"; - pr " }\n" - ) checks - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputFileMD5 (seq, filename) -> - pr " /* TestOutputFileMD5 for %s (%d) */\n" name i; - pr " char expected[33];\n"; - pr " md5sum (\"%s\", expected);\n" filename; - let seq, last = get_seq_last seq in - let test () = - pr " if (STRNEQ (r, expected)) {\n"; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputDevice (seq, expected) -> - pr " /* TestOutputDevice for %s (%d) */\n" name i; - pr " const char *expected = \"%s\";\n" (c_quote expected); - let seq, last = get_seq_last seq in - let test () = - pr " r[5] = 's';\n"; - pr " if (STRNEQ (r, expected)) {\n"; - pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; - pr " return -1;\n"; - pr " }\n" - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestOutputHashtable (seq, fields) -> - pr " /* TestOutputHashtable for %s (%d) */\n" name i; - pr " const char *key, *expected, *value;\n"; - let seq, last = get_seq_last seq in - let test () = - List.iter ( - fun (key, value) -> - pr " key = \"%s\";\n" (c_quote key); - pr " expected = \"%s\";\n" (c_quote value); - pr " value = get_key (r, key);\n"; - pr " if (value == NULL) {\n"; - pr " fprintf (stderr, \"%s: key \\\"%%s\\\" not found in hash: expecting \\\"%%s\\\"\\n\", key, expected);\n" test_name; - pr " return -1;\n"; - pr " }\n"; - pr " if (STRNEQ (value, expected)) {\n"; - pr " fprintf (stderr, \"%s: key \\\"%%s\\\": expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", key, expected, value);\n" test_name; - pr " return -1;\n"; - pr " }\n"; - ) fields - in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call ~test test_name last - | TestLastFail seq -> - pr " /* TestLastFail for %s (%d) */\n" name i; - let seq, last = get_seq_last seq in - List.iter (generate_test_command_call test_name) seq; - generate_test_command_call test_name ~expect_error:true last - -(* Generate the code to run a command, leaving the result in 'r'. - * If you expect to get an error then you should set expect_error:true. - *) -and generate_test_command_call ?(expect_error = false) ?test test_name cmd = - match cmd with - | [] -> assert false - | name :: args -> - (* Look up the command to find out what args/ret it has. *) - let style_ret, style_args, style_optargs = - try - let _, style, _, _, _, _, _ = - List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in - style - with Not_found -> - failwithf "%s: in test, command %s was not found" test_name name in - - (* Match up the arguments strings and argument types. *) - let args, optargs = - let rec loop argts args = - match argts, args with - | (t::ts), (s::ss) -> - let args, rest = loop ts ss in - ((t, s) :: args), rest - | [], ss -> [], ss - | ts, [] -> - failwithf "%s: in test, too few args given to function %s" - test_name name - in - let args, optargs = loop style_args args in - let optargs, rest = loop style_optargs optargs in - if rest <> [] then - failwithf "%s: in test, too many args given to function %s" - test_name name; - args, optargs in - - pr " {\n"; - - List.iter ( - function - | OptString n, "NULL" -> () - | Pathname n, arg - | Device n, arg - | Dev_or_Path n, arg - | String n, arg - | OptString n, arg - | Key n, arg -> - pr " const char *%s = \"%s\";\n" n (c_quote arg); - | BufferIn n, arg -> - pr " const char *%s = \"%s\";\n" n (c_quote arg); - pr " size_t %s_size = %d;\n" n (String.length arg) - | Int _, _ - | Int64 _, _ - | Bool _, _ - | FileIn _, _ | FileOut _, _ -> () - | StringList n, "" | DeviceList n, "" -> - pr " const char *const %s[1] = { NULL };\n" n - | StringList n, arg | DeviceList n, arg -> - let strs = string_split " " arg in - iteri ( - fun i str -> - pr " const char *%s_%d = \"%s\";\n" n i (c_quote str); - ) strs; - pr " const char *const %s[] = {\n" n; - iteri ( - fun i _ -> pr " %s_%d,\n" n i - ) strs; - pr " NULL\n"; - pr " };\n"; - | Pointer _, _ -> - (* Difficult to make these pointers in order to run a test. *) - assert false - ) args; - - if optargs <> [] then ( - pr " struct guestfs_%s_argv optargs;\n" name; - let _, bitmask = List.fold_left ( - fun (shift, bitmask) optarg -> - let is_set = - match optarg with - | Bool n, "" -> false - | Bool n, "true" -> - pr " optargs.%s = 1;\n" n; true - | Bool n, "false" -> - pr " optargs.%s = 0;\n" n; true - | Bool n, arg -> - failwithf "boolean optional arg '%s' should be empty string or \"true\" or \"false\"" n - | Int n, "" -> false - | Int n, i -> - let i = - try int_of_string i - with Failure _ -> failwithf "integer optional arg '%s' should be empty string or number" n in - pr " optargs.%s = %d;\n" n i; true - | Int64 n, "" -> false - | Int64 n, i -> - let i = - try Int64.of_string i - with Failure _ -> failwithf "int64 optional arg '%s' should be empty string or number" n in - pr " optargs.%s = %Ld;\n" n i; true - | String n, "NOARG" -> false - | String n, arg -> - pr " optargs.%s = \"%s\";\n" n (c_quote arg); true - | _ -> assert false in - let bit = if is_set then Int64.shift_left 1L shift else 0L in - let bitmask = Int64.logor bitmask bit in - let shift = shift + 1 in - (shift, bitmask) - ) (0, 0L) optargs in - pr " optargs.bitmask = UINT64_C(0x%Lx);\n" bitmask; - ); - - (match style_ret with - | RErr | RInt _ | RBool _ -> pr " int r;\n" - | RInt64 _ -> pr " int64_t r;\n" - | RConstString _ | RConstOptString _ -> - pr " const char *r;\n" - | RString _ -> pr " char *r;\n" - | RStringList _ | RHashtable _ -> - pr " char **r;\n"; - pr " size_t i;\n" - | RStruct (_, typ) -> - pr " struct guestfs_%s *r;\n" typ - | RStructList (_, typ) -> - pr " struct guestfs_%s_list *r;\n" typ - | RBufferOut _ -> - pr " char *r;\n"; - pr " size_t size;\n" - ); - - pr " suppress_error = %d;\n" (if expect_error then 1 else 0); - if optargs = [] then - pr " r = guestfs_%s (g" name - else - pr " r = guestfs_%s_argv (g" name; - - (* Generate the parameters. *) - List.iter ( - function - | OptString _, "NULL" -> pr ", NULL" - | Pathname n, _ - | Device n, _ | Dev_or_Path n, _ - | String n, _ - | OptString n, _ - | Key n, _ -> - pr ", %s" n - | BufferIn n, _ -> - pr ", %s, %s_size" n n - | FileIn _, arg | FileOut _, arg -> - pr ", \"%s\"" (c_quote arg) - | StringList n, _ | DeviceList n, _ -> - pr ", (char **) %s" n - | Int _, arg -> - let i = - try int_of_string arg - with Failure "int_of_string" -> - failwithf "%s: expecting an int, but got '%s'" test_name arg in - pr ", %d" i - | Int64 _, arg -> - let i = - try Int64.of_string arg - with Failure "int_of_string" -> - failwithf "%s: expecting an int64, but got '%s'" test_name arg in - pr ", %Ld" i - | Bool _, arg -> - let b = bool_of_string arg in pr ", %d" (if b then 1 else 0) - | Pointer _, _ -> assert false - ) args; - - (match style_ret with - | RBufferOut _ -> pr ", &size" - | _ -> () - ); - - if optargs <> [] then - pr ", &optargs"; - - pr ");\n"; - - (match errcode_of_ret style_ret, expect_error with - | `CannotReturnError, _ -> () - | `ErrorIsMinusOne, false -> - pr " if (r == -1)\n"; - pr " return -1;\n"; - | `ErrorIsMinusOne, true -> - pr " if (r != -1)\n"; - pr " return -1;\n"; - | `ErrorIsNULL, false -> - pr " if (r == NULL)\n"; - pr " return -1;\n"; - | `ErrorIsNULL, true -> - pr " if (r != NULL)\n"; - pr " return -1;\n"; - ); - - (* Insert the test code. *) - (match test with - | None -> () - | Some f -> f () - ); - - (match style_ret with - | RErr | RInt _ | RInt64 _ | RBool _ - | RConstString _ | RConstOptString _ -> () - | RString _ | RBufferOut _ -> pr " free (r);\n" - | RStringList _ | RHashtable _ -> - pr " for (i = 0; r[i] != NULL; ++i)\n"; - pr " free (r[i]);\n"; - pr " free (r);\n" - | RStruct (_, typ) -> - pr " guestfs_free_%s (r);\n" typ - | RStructList (_, typ) -> - pr " guestfs_free_%s_list (r);\n" typ - ); - - pr " }\n" diff --git a/generator/generator_main.ml b/generator/generator_main.ml index 951474ac..7e4e4ed4 100644 --- a/generator/generator_main.ml +++ b/generator/generator_main.ml @@ -28,7 +28,7 @@ open Generator_api_versions open Generator_c open Generator_xdr open Generator_daemon -open Generator_capitests +open Generator_tests_c_api open Generator_fish open Generator_ocaml open Generator_perl @@ -95,7 +95,7 @@ Run it from the top source directory using the command output_to "daemon/names.c" generate_daemon_names; output_to "daemon/optgroups.c" generate_daemon_optgroups_c; output_to "daemon/optgroups.h" generate_daemon_optgroups_h; - output_to "capitests/tests.c" generate_tests; + output_to "tests/c-api/tests.c" generate_tests; output_to "fish/cmds_gperf.gperf" generate_fish_cmds_gperf; output_to "fish/cmds.c" generate_fish_cmds; output_to "fish/completion.c" generate_fish_completion; diff --git a/generator/generator_tests_c_api.ml b/generator/generator_tests_c_api.ml new file mode 100644 index 00000000..5b4f7a80 --- /dev/null +++ b/generator/generator_tests_c_api.ml @@ -0,0 +1,955 @@ +(* 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 + *) + +(* Please read generator/README first. *) + +open Printf + +open Generator_types +open Generator_utils +open Generator_pr +open Generator_docstrings +open Generator_optgroups +open Generator_actions +open Generator_structs + +(* Generate the tests. *) +let rec generate_tests () = + generate_header CStyle GPLv2plus; + + pr "\ +#include +#include +#include +#include +#include +#include + +#include \"guestfs.h\" +#include \"guestfs-internal.h\" + +static guestfs_h *g; +static int suppress_error = 0; + +static void print_error (guestfs_h *g, void *data, const char *msg) +{ + if (!suppress_error) + fprintf (stderr, \"%%s\\n\", msg); +} + +/* FIXME: nearly identical code appears in fish.c */ +static void print_strings (char *const *argv) +{ + size_t argc; + + for (argc = 0; argv[argc] != NULL; ++argc) + printf (\"\\t%%s\\n\", argv[argc]); +} + +/* +static void print_table (char const *const *argv) +{ + size_t i; + + for (i = 0; argv[i] != NULL; i += 2) + printf (\"%%s: %%s\\n\", argv[i], argv[i+1]); +} +*/ + +static int +is_available (const char *group) +{ + const char *groups[] = { group, NULL }; + int r; + + suppress_error = 1; + r = guestfs_available (g, (char **) groups); + suppress_error = 0; + + return r == 0; +} + +static void +incr (guestfs_h *g, void *iv) +{ + int *i = (int *) iv; + (*i)++; +} + +/* Get md5sum of the named file. */ +static void +md5sum (const char *filename, char *result) +{ + char cmd[256]; + snprintf (cmd, sizeof cmd, \"md5sum %%s\", filename); + FILE *pp = popen (cmd, \"r\"); + if (pp == NULL) { + perror (cmd); + exit (EXIT_FAILURE); + } + if (fread (result, 1, 32, pp) != 32) { + perror (\"md5sum: fread\"); + exit (EXIT_FAILURE); + } + if (pclose (pp) != 0) { + perror (\"pclose\"); + exit (EXIT_FAILURE); + } + result[32] = '\\0'; +} + +/* Return the value for a key in a hashtable. + * Note: the return value is part of the hash and should not be freed. + */ +static const char * +get_key (char **hash, const char *key) +{ + size_t i; + + for (i = 0; hash[i] != NULL; i += 2) { + if (STREQ (hash[i], key)) + return hash[i+1]; + } + + return NULL; /* key not found */ +} + +"; + + (* Generate a list of commands which are not tested anywhere. *) + pr "static void no_test_warnings (void)\n"; + pr "{\n"; + + let hash : (string, bool) Hashtbl.t = Hashtbl.create 13 in + List.iter ( + fun (_, _, _, _, tests, _, _) -> + let tests = filter_map ( + function + | (_, (Always|If _|Unless _|IfAvailable _), test) -> Some test + | (_, Disabled, _) -> None + ) tests in + let seq = List.concat (List.map seq_of_test tests) in + let cmds_tested = List.map List.hd seq in + List.iter (fun cmd -> Hashtbl.replace hash cmd true) cmds_tested + ) all_functions; + + List.iter ( + fun (name, _, _, _, _, _, _) -> + if not (Hashtbl.mem hash name) then + pr " fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name + ) all_functions; + + pr "}\n"; + pr "\n"; + + (* Generate the actual tests. Note that we generate the tests + * in reverse order, deliberately, so that (in general) the + * newest tests run first. This makes it quicker and easier to + * debug them. + *) + let test_names = + List.map ( + fun (name, _, _, flags, tests, _, _) -> + mapi (generate_one_test name flags) tests + ) (List.rev all_functions) in + let test_names = List.concat test_names in + let nr_tests = List.length test_names in + + pr "\ +int main (int argc, char *argv[]) +{ + char c = 0; + unsigned long int n_failed = 0; + const char *filename; + int fd; + int nr_tests, test_num = 0; + + setbuf (stdout, NULL); + + no_test_warnings (); + + g = guestfs_create (); + if (g == NULL) { + printf (\"guestfs_create FAILED\\n\"); + exit (EXIT_FAILURE); + } + + guestfs_set_error_handler (g, print_error, NULL); + + filename = \"test1.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); + if (fd == -1) { + perror (filename); + exit (EXIT_FAILURE); + } + if (ftruncate (fd, %d) == -1) { + perror (\"ftruncate\"); + close (fd); + unlink (filename); + exit (EXIT_FAILURE); + } + if (close (fd) == -1) { + perror (filename); + unlink (filename); + exit (EXIT_FAILURE); + } + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); + exit (EXIT_FAILURE); + } + + filename = \"test2.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); + if (fd == -1) { + perror (filename); + exit (EXIT_FAILURE); + } + if (ftruncate (fd, %d) == -1) { + perror (\"ftruncate\"); + close (fd); + unlink (filename); + exit (EXIT_FAILURE); + } + if (close (fd) == -1) { + perror (filename); + unlink (filename); + exit (EXIT_FAILURE); + } + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); + exit (EXIT_FAILURE); + } + + filename = \"test3.img\"; + fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666); + if (fd == -1) { + perror (filename); + exit (EXIT_FAILURE); + } + if (ftruncate (fd, %d) == -1) { + perror (\"ftruncate\"); + close (fd); + unlink (filename); + exit (EXIT_FAILURE); + } + if (close (fd) == -1) { + perror (filename); + unlink (filename); + exit (EXIT_FAILURE); + } + if (guestfs_add_drive (g, filename) == -1) { + printf (\"guestfs_add_drive %%s FAILED\\n\", filename); + exit (EXIT_FAILURE); + } + + if (guestfs_add_drive_ro (g, \"../../images/test.iso\") == -1) { + printf (\"guestfs_add_drive_ro ../../images/test.iso FAILED\\n\"); + exit (EXIT_FAILURE); + } + + /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */ + alarm (600); + + if (guestfs_launch (g) == -1) { + printf (\"guestfs_launch FAILED\\n\"); + exit (EXIT_FAILURE); + } + + /* Cancel previous alarm. */ + alarm (0); + + /* Create ext2 filesystem on /dev/sdb1 partition. */ + if (guestfs_part_disk (g, \"/dev/sdb\", \"mbr\") == -1) { + printf (\"guestfs_part_disk FAILED\\n\"); + exit (EXIT_FAILURE); + } + if (guestfs_mkfs (g, \"ext2\", \"/dev/sdb1\") == -1) { + printf (\"guestfs_mkfs (/dev/sdb1) FAILED\\n\"); + exit (EXIT_FAILURE); + } + + nr_tests = %d; + +" (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests; + + iteri ( + fun i test_name -> + pr " test_num++;\n"; + pr " if (guestfs_get_verbose (g))\n"; + pr " printf (\"-------------------------------------------------------------------------------\\n\");\n"; + pr " printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name; + pr " if (%s () == -1) {\n" test_name; + pr " printf (\"%s FAILED\\n\");\n" test_name; + pr " n_failed++;\n"; + pr " }\n"; + ) test_names; + pr "\n"; + + pr " /* Check close callback is called. */ + int close_sentinel = 1; + guestfs_set_close_callback (g, incr, &close_sentinel); + + guestfs_close (g); + + if (close_sentinel != 2) { + fprintf (stderr, \"close callback was not called\\n\"); + exit (EXIT_FAILURE); + } + + unlink (\"test1.img\"); + unlink (\"test2.img\"); + unlink (\"test3.img\"); + +"; + + pr " if (n_failed > 0) {\n"; + pr " printf (\"***** %%lu / %%d tests FAILED *****\\n\", n_failed, nr_tests);\n"; + pr " exit (EXIT_FAILURE);\n"; + pr " }\n"; + pr "\n"; + + pr " exit (EXIT_SUCCESS);\n"; + pr "}\n" + +and generate_one_test name flags i (init, prereq, test) = + let test_name = sprintf "test_%s_%d" name i in + + pr "\ +static int %s_skip (void) +{ + const char *str; + + str = getenv (\"TEST_ONLY\"); + if (str) + return strstr (str, \"%s\") == NULL; + str = getenv (\"SKIP_%s\"); + if (str && STREQ (str, \"1\")) return 1; + str = getenv (\"SKIP_TEST_%s\"); + if (str && STREQ (str, \"1\")) return 1; + return 0; +} + +" test_name name (String.uppercase test_name) (String.uppercase name); + + (match prereq with + | Disabled | Always | IfAvailable _ -> () + | 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: environment variable set)\\n\", \"%s\"); + return 0; + } + +" test_name test_name test_name; + + (* Optional functions should only be tested if the relevant + * support is available in the daemon. + *) + List.iter ( + function + | Optional group -> + pr " if (!is_available (\"%s\")) {\n" group; + pr " printf (\" %%s skipped (reason: group %%s not available in daemon)\\n\", \"%s\", \"%s\");\n" test_name group; + pr " return 0;\n"; + pr " }\n"; + | _ -> () + ) flags; + + (match prereq with + | Disabled -> + 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; + | IfAvailable group -> + pr " if (!is_available (\"%s\")) {\n" group; + pr " printf (\" %%s skipped (reason: %%s not available)\\n\", \"%s\", \"%s\");\n" test_name group; + 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 + ); + + pr " return 0;\n"; + pr "}\n"; + pr "\n"; + test_name + +and generate_one_test_body name i test_name init test = + (match init with + | InitNone (* XXX at some point, InitNone and InitEmpty became + * folded together as the same thing. Really we should + * make InitNone do nothing at all, but the tests may + * need to be checked to make sure this is OK. + *) + | InitEmpty -> + 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"]] + | InitPartition -> + pr " /* InitPartition for %s: create /dev/sda1 */\n" test_name; + List.iter (generate_test_command_call test_name) + [["blockdev_setrw"; "/dev/sda"]; + ["umount_all"]; + ["lvm_remove_all"]; + ["part_disk"; "/dev/sda"; "mbr"]] + | InitBasicFS -> + 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"]; + ["lvm_remove_all"]; + ["part_disk"; "/dev/sda"; "mbr"]; + ["mkfs"; "ext2"; "/dev/sda1"]; + ["mount_options"; ""; "/dev/sda1"; "/"]] + | InitBasicFSonLVM -> + 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"]; + ["lvm_remove_all"]; + ["part_disk"; "/dev/sda"; "mbr"]; + ["pvcreate"; "/dev/sda1"]; + ["vgcreate"; "VG"; "/dev/sda1"]; + ["lvcreate"; "LV"; "VG"; "8"]; + ["mkfs"; "ext2"; "/dev/VG/LV"]; + ["mount_options"; ""; "/dev/VG/LV"; "/"]] + | InitISOFS -> + pr " /* InitISOFS for %s */\n" test_name; + List.iter (generate_test_command_call test_name) + [["blockdev_setrw"; "/dev/sda"]; + ["umount_all"]; + ["lvm_remove_all"]; + ["mount_ro"; "/dev/sdd"; "/"]] + | InitScratchFS -> + pr " /* InitScratchFS for %s */\n" test_name; + List.iter (generate_test_command_call test_name) + [["blockdev_setrw"; "/dev/sda"]; + ["umount_all"]; + ["lvm_remove_all"]; + ["mount_options"; ""; "/dev/sdb1"; "/"]] + ); + + let get_seq_last = function + | [] -> + failwithf "%s: you cannot use [] (empty list) when expecting a command" + test_name + | seq -> + let seq = List.rev seq in + List.rev (List.tl seq), List.hd seq + in + + match test with + | TestRun seq -> + pr " /* TestRun for %s (%d) */\n" name i; + List.iter (generate_test_command_call test_name) seq + | TestOutput (seq, expected) -> + pr " /* TestOutput for %s (%d) */\n" name i; + pr " const char *expected = \"%s\";\n" (c_quote expected); + let seq, last = get_seq_last seq in + let test () = + pr " if (STRNEQ (r, expected)) {\n"; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputList (seq, expected) -> + pr " /* TestOutputList for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + iteri ( + fun i str -> + pr " if (!r[%d]) {\n" i; + pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n"; + pr " {\n"; + pr " const char *expected = \"%s\";\n" (c_quote str); + pr " if (STRNEQ (r[%d], expected)) {\n" i; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i; + pr " return -1;\n"; + pr " }\n"; + pr " }\n" + ) expected; + pr " if (r[%d] != NULL) {\n" (List.length expected); + pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputListOfDevices (seq, expected) -> + pr " /* TestOutputListOfDevices for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + iteri ( + fun i str -> + pr " if (!r[%d]) {\n" i; + pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n"; + pr " {\n"; + pr " const char *expected = \"%s\";\n" (c_quote str); + pr " r[%d][5] = 's';\n" i; + pr " if (STRNEQ (r[%d], expected)) {\n" i; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i; + pr " return -1;\n"; + pr " }\n"; + pr " }\n" + ) expected; + pr " if (r[%d] != NULL) {\n" (List.length expected); + pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputInt (seq, expected) -> + pr " /* TestOutputInt for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (r != %d) {\n" expected; + pr " fprintf (stderr, \"%s: expected %d but got %%d\\n\"," + test_name expected; + pr " (int) r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputIntOp (seq, op, expected) -> + pr " /* TestOutputIntOp for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (! (r %s %d)) {\n" op expected; + pr " fprintf (stderr, \"%s: expected %s %d but got %%d\\n\"," + test_name op expected; + pr " (int) r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputTrue seq -> + pr " /* TestOutputTrue for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (!r) {\n"; + pr " fprintf (stderr, \"%s: expected true, got false\\n\");\n" + test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputFalse seq -> + pr " /* TestOutputFalse for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " if (r) {\n"; + pr " fprintf (stderr, \"%s: expected false, got true\\n\");\n" + test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputLength (seq, expected) -> + pr " /* TestOutputLength for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + pr " int j;\n"; + pr " for (j = 0; j < %d; ++j)\n" expected; + pr " if (r[j] == NULL) {\n"; + pr " fprintf (stderr, \"%s: short list returned\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n"; + pr " if (r[j] != NULL) {\n"; + pr " fprintf (stderr, \"%s: long list returned\\n\");\n" + test_name; + pr " print_strings (r);\n"; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputBuffer (seq, expected) -> + pr " /* TestOutputBuffer for %s (%d) */\n" name i; + pr " const char *expected = \"%s\";\n" (c_quote expected); + let seq, last = get_seq_last seq in + let len = String.length expected in + let test () = + pr " if (size != %d) {\n" len; + pr " fprintf (stderr, \"%s: returned size of buffer wrong, expected %d but got %%zu\\n\", size);\n" test_name len; + pr " return -1;\n"; + pr " }\n"; + pr " if (STRNEQLEN (r, expected, size)) {\n"; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputStruct (seq, checks) -> + pr " /* TestOutputStruct for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + let test () = + List.iter ( + function + | CompareWithInt (field, expected) -> + pr " if (r->%s != %d) {\n" field expected; + pr " fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n" + test_name field expected; + pr " (int) r->%s);\n" field; + pr " return -1;\n"; + pr " }\n" + | CompareWithIntOp (field, op, expected) -> + pr " if (!(r->%s %s %d)) {\n" field op expected; + pr " fprintf (stderr, \"%s: %s was %%d, expected %s %d\\n\",\n" + test_name field op expected; + pr " (int) r->%s);\n" field; + pr " return -1;\n"; + pr " }\n" + | CompareWithString (field, expected) -> + pr " if (STRNEQ (r->%s, \"%s\")) {\n" field expected; + pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n" + test_name field expected; + pr " r->%s);\n" field; + pr " return -1;\n"; + pr " }\n" + | CompareFieldsIntEq (field1, field2) -> + pr " if (r->%s != r->%s) {\n" field1 field2; + pr " fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n" + test_name field1 field2; + pr " (int) r->%s, (int) r->%s);\n" field1 field2; + pr " return -1;\n"; + pr " }\n" + | CompareFieldsStrEq (field1, field2) -> + pr " if (STRNEQ (r->%s, r->%s)) {\n" field1 field2; + pr " fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n" + test_name field1 field2; + pr " r->%s, r->%s);\n" field1 field2; + pr " return -1;\n"; + pr " }\n" + ) checks + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputFileMD5 (seq, filename) -> + pr " /* TestOutputFileMD5 for %s (%d) */\n" name i; + pr " char expected[33];\n"; + pr " md5sum (\"%s\", expected);\n" filename; + let seq, last = get_seq_last seq in + let test () = + pr " if (STRNEQ (r, expected)) {\n"; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputDevice (seq, expected) -> + pr " /* TestOutputDevice for %s (%d) */\n" name i; + pr " const char *expected = \"%s\";\n" (c_quote expected); + let seq, last = get_seq_last seq in + let test () = + pr " r[5] = 's';\n"; + pr " if (STRNEQ (r, expected)) {\n"; + pr " fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name; + pr " return -1;\n"; + pr " }\n" + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestOutputHashtable (seq, fields) -> + pr " /* TestOutputHashtable for %s (%d) */\n" name i; + pr " const char *key, *expected, *value;\n"; + let seq, last = get_seq_last seq in + let test () = + List.iter ( + fun (key, value) -> + pr " key = \"%s\";\n" (c_quote key); + pr " expected = \"%s\";\n" (c_quote value); + pr " value = get_key (r, key);\n"; + pr " if (value == NULL) {\n"; + pr " fprintf (stderr, \"%s: key \\\"%%s\\\" not found in hash: expecting \\\"%%s\\\"\\n\", key, expected);\n" test_name; + pr " return -1;\n"; + pr " }\n"; + pr " if (STRNEQ (value, expected)) {\n"; + pr " fprintf (stderr, \"%s: key \\\"%%s\\\": expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", key, expected, value);\n" test_name; + pr " return -1;\n"; + pr " }\n"; + ) fields + in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call ~test test_name last + | TestLastFail seq -> + pr " /* TestLastFail for %s (%d) */\n" name i; + let seq, last = get_seq_last seq in + List.iter (generate_test_command_call test_name) seq; + generate_test_command_call test_name ~expect_error:true last + +(* Generate the code to run a command, leaving the result in 'r'. + * If you expect to get an error then you should set expect_error:true. + *) +and generate_test_command_call ?(expect_error = false) ?test test_name cmd = + match cmd with + | [] -> assert false + | name :: args -> + (* Look up the command to find out what args/ret it has. *) + let style_ret, style_args, style_optargs = + try + let _, style, _, _, _, _, _ = + List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in + style + with Not_found -> + failwithf "%s: in test, command %s was not found" test_name name in + + (* Match up the arguments strings and argument types. *) + let args, optargs = + let rec loop argts args = + match argts, args with + | (t::ts), (s::ss) -> + let args, rest = loop ts ss in + ((t, s) :: args), rest + | [], ss -> [], ss + | ts, [] -> + failwithf "%s: in test, too few args given to function %s" + test_name name + in + let args, optargs = loop style_args args in + let optargs, rest = loop style_optargs optargs in + if rest <> [] then + failwithf "%s: in test, too many args given to function %s" + test_name name; + args, optargs in + + pr " {\n"; + + List.iter ( + function + | OptString n, "NULL" -> () + | Pathname n, arg + | Device n, arg + | Dev_or_Path n, arg + | String n, arg + | OptString n, arg + | Key n, arg -> + pr " const char *%s = \"%s\";\n" n (c_quote arg); + | BufferIn n, arg -> + pr " const char *%s = \"%s\";\n" n (c_quote arg); + pr " size_t %s_size = %d;\n" n (String.length arg) + | Int _, _ + | Int64 _, _ + | Bool _, _ + | FileIn _, _ | FileOut _, _ -> () + | StringList n, "" | DeviceList n, "" -> + pr " const char *const %s[1] = { NULL };\n" n + | StringList n, arg | DeviceList n, arg -> + let strs = string_split " " arg in + iteri ( + fun i str -> + pr " const char *%s_%d = \"%s\";\n" n i (c_quote str); + ) strs; + pr " const char *const %s[] = {\n" n; + iteri ( + fun i _ -> pr " %s_%d,\n" n i + ) strs; + pr " NULL\n"; + pr " };\n"; + | Pointer _, _ -> + (* Difficult to make these pointers in order to run a test. *) + assert false + ) args; + + if optargs <> [] then ( + pr " struct guestfs_%s_argv optargs;\n" name; + let _, bitmask = List.fold_left ( + fun (shift, bitmask) optarg -> + let is_set = + match optarg with + | Bool n, "" -> false + | Bool n, "true" -> + pr " optargs.%s = 1;\n" n; true + | Bool n, "false" -> + pr " optargs.%s = 0;\n" n; true + | Bool n, arg -> + failwithf "boolean optional arg '%s' should be empty string or \"true\" or \"false\"" n + | Int n, "" -> false + | Int n, i -> + let i = + try int_of_string i + with Failure _ -> failwithf "integer optional arg '%s' should be empty string or number" n in + pr " optargs.%s = %d;\n" n i; true + | Int64 n, "" -> false + | Int64 n, i -> + let i = + try Int64.of_string i + with Failure _ -> failwithf "int64 optional arg '%s' should be empty string or number" n in + pr " optargs.%s = %Ld;\n" n i; true + | String n, "NOARG" -> false + | String n, arg -> + pr " optargs.%s = \"%s\";\n" n (c_quote arg); true + | _ -> assert false in + let bit = if is_set then Int64.shift_left 1L shift else 0L in + let bitmask = Int64.logor bitmask bit in + let shift = shift + 1 in + (shift, bitmask) + ) (0, 0L) optargs in + pr " optargs.bitmask = UINT64_C(0x%Lx);\n" bitmask; + ); + + (match style_ret with + | RErr | RInt _ | RBool _ -> pr " int r;\n" + | RInt64 _ -> pr " int64_t r;\n" + | RConstString _ | RConstOptString _ -> + pr " const char *r;\n" + | RString _ -> pr " char *r;\n" + | RStringList _ | RHashtable _ -> + pr " char **r;\n"; + pr " size_t i;\n" + | RStruct (_, typ) -> + pr " struct guestfs_%s *r;\n" typ + | RStructList (_, typ) -> + pr " struct guestfs_%s_list *r;\n" typ + | RBufferOut _ -> + pr " char *r;\n"; + pr " size_t size;\n" + ); + + pr " suppress_error = %d;\n" (if expect_error then 1 else 0); + if optargs = [] then + pr " r = guestfs_%s (g" name + else + pr " r = guestfs_%s_argv (g" name; + + (* Generate the parameters. *) + List.iter ( + function + | OptString _, "NULL" -> pr ", NULL" + | Pathname n, _ + | Device n, _ | Dev_or_Path n, _ + | String n, _ + | OptString n, _ + | Key n, _ -> + pr ", %s" n + | BufferIn n, _ -> + pr ", %s, %s_size" n n + | FileIn _, arg | FileOut _, arg -> + pr ", \"%s\"" (c_quote arg) + | StringList n, _ | DeviceList n, _ -> + pr ", (char **) %s" n + | Int _, arg -> + let i = + try int_of_string arg + with Failure "int_of_string" -> + failwithf "%s: expecting an int, but got '%s'" test_name arg in + pr ", %d" i + | Int64 _, arg -> + let i = + try Int64.of_string arg + with Failure "int_of_string" -> + failwithf "%s: expecting an int64, but got '%s'" test_name arg in + pr ", %Ld" i + | Bool _, arg -> + let b = bool_of_string arg in pr ", %d" (if b then 1 else 0) + | Pointer _, _ -> assert false + ) args; + + (match style_ret with + | RBufferOut _ -> pr ", &size" + | _ -> () + ); + + if optargs <> [] then + pr ", &optargs"; + + pr ");\n"; + + (match errcode_of_ret style_ret, expect_error with + | `CannotReturnError, _ -> () + | `ErrorIsMinusOne, false -> + pr " if (r == -1)\n"; + pr " return -1;\n"; + | `ErrorIsMinusOne, true -> + pr " if (r != -1)\n"; + pr " return -1;\n"; + | `ErrorIsNULL, false -> + pr " if (r == NULL)\n"; + pr " return -1;\n"; + | `ErrorIsNULL, true -> + pr " if (r != NULL)\n"; + pr " return -1;\n"; + ); + + (* Insert the test code. *) + (match test with + | None -> () + | Some f -> f () + ); + + (match style_ret with + | RErr | RInt _ | RInt64 _ | RBool _ + | RConstString _ | RConstOptString _ -> () + | RString _ | RBufferOut _ -> pr " free (r);\n" + | RStringList _ | RHashtable _ -> + pr " for (i = 0; r[i] != NULL; ++i)\n"; + pr " free (r[i]);\n"; + pr " free (r);\n" + | RStruct (_, typ) -> + pr " guestfs_free_%s (r);\n" typ + | RStructList (_, typ) -> + pr " guestfs_free_%s_list (r);\n" typ + ); + + pr " }\n" diff --git a/regressions/Makefile.am b/regressions/Makefile.am index 928905cc..00c25f1e 100644 --- a/regressions/Makefile.am +++ b/regressions/Makefile.am @@ -18,8 +18,6 @@ # Regression tests and other important tests which are not # specific to the C API. We can write these more easily in # higher level languages than C. -# -# See also capitests/ include $(top_srcdir)/subdir-rules.mk diff --git a/src/guestfs.pod b/src/guestfs.pod index 2e18fd6d..45f63297 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -2799,7 +2799,7 @@ Packagers can run only certain tests by setting for example: TEST_ONLY="vfs_type zerofree" -See C for more details of how these environment +See C for more details of how these environment variables work. =head2 DEBUGGING NEW API ACTIONS @@ -2917,10 +2917,6 @@ L command and documentation. The libguestfs appliance, build scripts and so on. -=item C - -Automated tests of the C API. - =item C The L, L and L commands 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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#include "xgetcwd.h" + +#include +#include + +#include "guestfs.h" + +static void +make_test_xml (FILE *fp, const char *cwd) +{ + fprintf (fp, + "\n" + "\n" + " \n" + " guest\n" + " \n" + " hvm\n" + " \n" + " \n" + " 524288\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", + 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 +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 -- cgit