diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2012-11-13 14:31:01 +0000 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2012-11-13 15:26:29 +0000 |
commit | 5c44c691f2c288e04571b54c1b6844402f9865e4 (patch) | |
tree | 7af80066286772060f9fca2ac9cb8e574dffc11e | |
parent | ee5f18293b72779ea7387414cca5cd8c35230fc8 (diff) | |
download | libguestfs-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-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | tests/parallel/Makefile.am | 43 | ||||
-rw-r--r-- | tests/parallel/test-parallel.c | 157 |
5 files changed, 203 insertions, 0 deletions
@@ -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); +} |