summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-11-13 14:31:01 +0000
committerRichard W.M. Jones <rjones@redhat.com>2012-11-13 15:26:29 +0000
commit5c44c691f2c288e04571b54c1b6844402f9865e4 (patch)
tree7af80066286772060f9fca2ac9cb8e574dffc11e
parentee5f18293b72779ea7387414cca5cd8c35230fc8 (diff)
downloadlibguestfs-5c44c691f2c288e04571b54c1b6844402f9865e4.tar.gz
libguestfs-5c44c691f2c288e04571b54c1b6844402f9865e4.tar.xz
libguestfs-5c44c691f2c288e04571b54c1b6844402f9865e4.zip
tests: Add a parallel launch test.
This is designed to reveal libvirt race conditions such as bug 875741. This is a "slow test" so it only runs if you do 'make check-slow'.
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac1
-rw-r--r--tests/parallel/Makefile.am43
-rw-r--r--tests/parallel/test-parallel.c157
5 files changed, 203 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index b61e44f6..a8d9155f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -419,6 +419,7 @@ Makefile.in
/tests/guests/ubuntu.img
/tests/guests/windows.img
/tests/mount-local/test-parallel-mount-local
+/tests/parallel/test-parallel
/tests/regressions/rhbz501893
/tests/regressions/rhbz790721
/tests/rsync/rsyncd.pid
diff --git a/Makefile.am b/Makefile.am
index cdf4dd5c..92187204 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -41,6 +41,7 @@ SUBDIRS += tests/guests
SUBDIRS += tests/c-api
SUBDIRS += tests/tmpdirs
SUBDIRS += tests/protocol
+SUBDIRS += tests/parallel
SUBDIRS += tests/disks
SUBDIRS += tests/lvm
SUBDIRS += tests/luks
diff --git a/configure.ac b/configure.ac
index b66c9e6c..40a793b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1406,6 +1406,7 @@ AC_CONFIG_FILES([Makefile
tests/md/Makefile
tests/mount-local/Makefile
tests/ntfsclone/Makefile
+ tests/parallel/Makefile
tests/protocol/Makefile
tests/qemu/Makefile
tests/regressions/Makefile
diff --git a/tests/parallel/Makefile.am b/tests/parallel/Makefile.am
new file mode 100644
index 00000000..0e59268c
--- /dev/null
+++ b/tests/parallel/Makefile.am
@@ -0,0 +1,43 @@
+# libguestfs
+# Copyright (C) 2012 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
+
+# Don't run this test by default. However we have to have an empty
+# TESTS rule otherwise you can't run the test from the command line
+# using 'make TESTS=test-parallel check'
+TESTS =
+TESTS_ENVIRONMENT = $(top_builddir)/run --test
+
+check_PROGRAMS = test-parallel
+
+test_parallel_SOURCES = test-parallel.c
+test_parallel_CFLAGS = \
+ -DGUESTFS_WARN_DEPRECATED=1 \
+ -pthread \
+ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_parallel_LDADD = \
+ $(top_builddir)/src/libguestfs.la \
+ $(top_builddir)/gnulib/lib/libgnu.la
+
+#check-valgrind:
+# $(MAKE) VG="$(top_builddir)/run @VG@" TESTS="test-parallel" check
+
+check-slow:
+ $(MAKE) TESTS="test-parallel" check
diff --git a/tests/parallel/test-parallel.c b/tests/parallel/test-parallel.c
new file mode 100644
index 00000000..03da540e
--- /dev/null
+++ b/tests/parallel/test-parallel.c
@@ -0,0 +1,157 @@
+/* libguestfs
+ * Copyright (C) 2012 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 test is mainly aimed at libvirt: There appear to be a lot of
+ * cases where libvirt is racy when creating transient guests.
+ * Therefore this test simply launches lots of handles in parallel for
+ * many minutes, hoping to reveal problems in libvirt this way.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <error.h>
+
+#include <pthread.h>
+
+#include "guestfs.h"
+
+#include "ignore-value.h"
+
+#define TOTAL_TIME 600 /* Seconds, excluding launch. */
+#define NR_THREADS 5
+
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+
+struct thread_state {
+ pthread_t thread; /* Thread handle. */
+ int exit_status; /* Thread exit status. */
+};
+static struct thread_state threads[NR_THREADS];
+
+static void *start_thread (void *) __attribute__((noreturn));
+
+static volatile sig_atomic_t quit = 0;
+
+static void
+catch_sigint (int signal)
+{
+ static char cleaning_up[] = "\ngot signal, cleaning up ...\n";
+
+ if (quit == 0) {
+ quit = 1;
+ ignore_value (write (2, cleaning_up, sizeof cleaning_up));
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *skip;
+ struct sigaction sa;
+ int r;
+ size_t i, errors = 0;
+ void *status;
+
+ srandom (time (NULL));
+
+ /* Allow the test to be skipped by setting an environment variable. */
+ skip = getenv ("SKIP_TEST_PARALLEL");
+ if (skip && STREQ (skip, "1")) {
+ fprintf (stderr, "%s: test skipped because environment variable set.\n",
+ argv[0]);
+ exit (77);
+ }
+
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = catch_sigint;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGINT, &sa, NULL);
+ sigaction (SIGQUIT, &sa, NULL);
+
+ for (i = 0; i < NR_THREADS; ++i) {
+ /* Start the thread. */
+ r = pthread_create (&threads[i].thread, NULL, start_thread,
+ &threads[i]);
+ if (r != 0)
+ error (EXIT_FAILURE, r, "pthread_create");
+ }
+
+ /* Wait for the threads to exit. */
+ for (i = 0; i < NR_THREADS; ++i) {
+ r = pthread_join (threads[i].thread, &status);
+ if (r != 0)
+ error (EXIT_FAILURE, r, "pthread_join");
+ if (*(int *)status != 0) {
+ fprintf (stderr, "%zu: thread returned an error\n", i);
+ errors++;
+ }
+ }
+
+ exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/* Run the test in a single thread. */
+static void *
+start_thread (void *statevp)
+{
+ struct thread_state *state = statevp;
+ guestfs_h *g;
+ time_t start_t, t;
+
+ time (&start_t);
+
+ for (;;) {
+ /* Keep testing until we run out of time. */
+ time (&t);
+ if (quit || t - start_t >= TOTAL_TIME)
+ break;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ perror ("guestfs_create");
+ state->exit_status = 1;
+ pthread_exit (&state->exit_status);
+ }
+
+ if (guestfs_add_drive_opts (g, "/dev/null",
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
+ -1) == -1) {
+ error:
+ guestfs_close (g);
+ state->exit_status = 1;
+ pthread_exit (&state->exit_status);
+ }
+ if (guestfs_launch (g) == -1)
+ goto error;
+
+ if (guestfs_shutdown (g) == -1)
+ goto error;
+
+ guestfs_close (g);
+ }
+
+ /* Test finished successfully. */
+ state->exit_status = 0;
+ pthread_exit (&state->exit_status);
+}