mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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'.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
43
tests/parallel/Makefile.am
Normal file
43
tests/parallel/Makefile.am
Normal file
@@ -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
|
||||
157
tests/parallel/test-parallel.c
Normal file
157
tests/parallel/test-parallel.c
Normal file
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user