diff --git a/.gitignore b/.gitignore index fc21badce23e8cfb42543e880e22f47e40a3d7b2..ee8978782dd52e4c027ce269ca5a937e30277709 100644 --- a/.gitignore +++ b/.gitignore @@ -169,6 +169,7 @@ /tests/objectlocking.cm[ix] /tests/reconnect /tests/ssh +/tests/test_file_access.txt /tests/test_conf /tools/libvirt-guests.sh /tools/virt-login-shell diff --git a/HACKING b/HACKING index e308568a186a31ea09e8541fc45413f783f64daf..0aba279bf73e89fcb6125161129a63292565d891 100644 --- a/HACKING +++ b/HACKING @@ -152,6 +152,17 @@ There is also a "./run" script at the top level, to make it easier to run programs that have not yet been installed, as well as to wrap invocations of various tests under gdb or Valgrind. +When running our test suite it may happen that the test result is +nondeterministic because of the test suite relying on a particular file in the +system being accessible or having some specific value. To catch this kind of +errors, the test suite has a module for that prints any path touched that +fulfils constraints described above into a file. To enable it just set +"VIR_TEST_FILE_ACCESS" environment variable. Then +"VIR_TEST_FILE_ACCESS_OUTPUT" environment variable can alter location where +the file is stored. + + VIR_TEST_FILE_ACCESS=1 VIR_TEST_FILE_ACCESS_OUTPUT="/tmp/file_access.txt" ./qemuxml2argvtest + (9) The Valgrind test should produce similar output to "make check". If the output diff --git a/docs/hacking.html.in b/docs/hacking.html.in index 5cd23a25a5a9fb779eb4262b1942f5402a08ee54..a471d88552f91dd20afdfc911ebdef9a3035503f 100644 --- a/docs/hacking.html.in +++ b/docs/hacking.html.in @@ -189,6 +189,19 @@ under gdb or Valgrind.

+

When running our test suite it may happen that the test result is + nondeterministic because of the test suite relying on a particular file + in the system being accessible or having some specific value. To catch + this kind of errors, the test suite has a module for that prints any + path touched that fulfils constraints described above + into a file. To enable it just set + VIR_TEST_FILE_ACCESS environment variable. + Then VIR_TEST_FILE_ACCESS_OUTPUT environment + variable can alter location where the file is stored.

+
+  VIR_TEST_FILE_ACCESS=1 VIR_TEST_FILE_ACCESS_OUTPUT="/tmp/file_access.txt" ./qemuxml2argvtest
+
+
  • The Valgrind test should produce similar output to make check. If the output has traces within libvirt diff --git a/tests/Makefile.am b/tests/Makefile.am index 0d722458947affe2a432a8a527b68586b23189a4..ca3c8c34a18c5f8b20a3f689735ad1134548e51c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,9 @@ # old automake does not provide abs_{src,build}dir variables abs_builddir = $(shell pwd) +abs_topbuilddir = $(shell cd .. && pwd) abs_srcdir = $(shell cd $(srcdir) && pwd) +abs_topsrcdir = $(shell cd $(top_srcdir) && pwd) SHELL = $(PREFERABLY_POSIX_SHELL) @@ -33,7 +35,9 @@ INCLUDES = \ AM_CFLAGS = \ -Dabs_builddir="\"$(abs_builddir)\"" \ + -Dabs_topbuilddir="\"$(abs_topbuilddir)\"" \ -Dabs_srcdir="\"$(abs_srcdir)\"" \ + -Dabs_topsrcdir="\"$(abs_topsrcdir)\"" \ $(LIBXML_CFLAGS) \ $(LIBNL_CFLAGS) \ $(GNUTLS_CFLAGS) \ @@ -1147,7 +1151,9 @@ virtestmock_la_SOURCES = \ virtestmock.c virtestmock_la_CFLAGS = $(AM_CFLAGS) virtestmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) -virtestmock_la_LIBADD = $(MOCKLIBS_LIBS) +virtestmock_la_LIBADD = \ + $(MOCKLIBS_LIBS) \ + ../src/libvirt_util.la else ! WITH_LINUX EXTRA_DIST += virusbtest.c virusbmock.c \ virnetdevbandwidthtest.c virnetdevbandwidthmock.c \ diff --git a/tests/testutils.c b/tests/testutils.c index 79d076378a169bad9d7a9623356f0aaab3b3fa82..9180e86ed2d22f537252d48c8e2e7fa312f62418 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -156,6 +156,11 @@ virtTestRun(const char *title, { int ret = 0; + /* Some test are fragile about environ settings. If that's + * the case, don't poison it. */ + if (getenv("VIR_TEST_MOCK_PROGNAME")) + setenv("VIR_TEST_MOCK_TESTNAME", title, 1); + if (testCounter == 0 && !virTestGetVerbose()) fprintf(stderr, " "); @@ -280,6 +285,7 @@ virtTestRun(const char *title, } #endif /* TEST_OOM */ + unsetenv("VIR_TEST_MOCK_TESTNAME"); return ret; } @@ -832,8 +838,11 @@ virTestSetEnvPath(void) return ret; } +#define TEST_MOCK (abs_builddir "/.libs/virtestmock.so") + int virtTestMain(int argc, char **argv, + const char *lib, int (*func)(void)) { int ret; @@ -842,6 +851,18 @@ int virtTestMain(int argc, char *oomstr; #endif + if (getenv("VIR_TEST_FILE_ACCESS")) + VIRT_TEST_PRELOAD(TEST_MOCK); + + if (lib) + VIRT_TEST_PRELOAD(lib); + + progname = last_component(argv[0]); + if (STRPREFIX(progname, "lt-")) + progname += 3; + + setenv("VIR_TEST_MOCK_PROGNAME", progname, 1); + virFileActivateDirOverride(argv[0]); if (virTestSetEnvPath() < 0) @@ -850,9 +871,6 @@ int virtTestMain(int argc, if (!virFileExists(abs_srcdir)) return EXIT_AM_HARDFAIL; - progname = last_component(argv[0]); - if (STRPREFIX(progname, "lt-")) - progname += 3; if (argc > 1) { fprintf(stderr, "Usage: %s\n", argv[0]); fputs("effective environment variables:\n" diff --git a/tests/testutils.h b/tests/testutils.h index 123a4fb17bb58665aa362bc9cd09d0b294938c31..d1caf20e70a178daf1bb80e1002bcab195ba20ed 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -102,12 +102,13 @@ const char *virtTestCounterNext(void); int virtTestMain(int argc, char **argv, + const char *lib, int (*func)(void)); /* Setup, then call func() */ -# define VIRT_TEST_MAIN(func) \ - int main(int argc, char **argv) { \ - return virtTestMain(argc, argv, func); \ +# define VIRT_TEST_MAIN(func) \ + int main(int argc, char **argv) { \ + return virtTestMain(argc, argv, NULL, func); \ } # define VIRT_TEST_PRELOAD(lib) \ @@ -132,8 +133,7 @@ int virtTestMain(int argc, # define VIRT_TEST_MAIN_PRELOAD(func, lib) \ int main(int argc, char **argv) { \ - VIRT_TEST_PRELOAD(lib); \ - return virtTestMain(argc, argv, func); \ + return virtTestMain(argc, argv, lib, func); \ } virCapsPtr virTestGenericCapsInit(void); diff --git a/tests/virtestmock.c b/tests/virtestmock.c index 0877956b027f44791e3deb95ad103b1433a188d3..59ca5e5b078f4b90dcee22b862fef0f3cd162a97 100644 --- a/tests/virtestmock.c +++ b/tests/virtestmock.c @@ -24,9 +24,14 @@ #include #include #include +#include +#include #include "internal.h" #include "configmake.h" +#include "virstring.h" +#include "viralloc.h" +#include "virfile.h" static int (*real_open)(const char *path, int flags, ...); static FILE *(*real_fopen)(const char *path, const char *mode); @@ -36,6 +41,11 @@ static int (*real___xstat)(int ver, const char *path, struct stat *sb); static int (*real_lstat)(const char *path, struct stat *sb); static int (*real___lxstat)(int ver, const char *path, struct stat *sb); +static const char *progname; +const char *output; + +#define VIR_FILE_ACCESS_DEFAULT abs_builddir "/test_file_access.txt" + static void init_syms(void) { if (real_open) @@ -49,9 +59,90 @@ static void init_syms(void) } static void -checkPath(const char *path ATTRIBUTE_UNUSED) +printFile(const char *file) { - /* Nada */ + FILE *fp; + const char *testname = getenv("VIR_TEST_MOCK_TESTNAME"); + + if (!progname) { + progname = getenv("VIR_TEST_MOCK_PROGNAME"); + + if (!progname) + return; + + output = getenv("VIR_TEST_FILE_ACCESS_OUTPUT"); + if (!output) + output = VIR_FILE_ACCESS_DEFAULT; + } + + if (!(fp = real_fopen(output, "a"))) { + fprintf(stderr, "Unable to open %s: %s\n", output, strerror(errno)); + abort(); + } + + if (flock(fileno(fp), LOCK_EX) < 0) { + fprintf(stderr, "Unable to lock %s: %s\n", output, strerror(errno)); + fclose(fp); + abort(); + } + + /* Now append the following line into the output file: + * $file: $progname $testname */ + + fprintf(fp, "%s: %s", file, progname); + if (testname) + fprintf(fp, ": %s", testname); + + fputc('\n', fp); + + flock(fileno(fp), LOCK_UN); + fclose(fp); +} + +static void +checkPath(const char *path) +{ + char *fullPath = NULL; + char *relPath = NULL; + char *crippledPath = NULL; + + if (path[0] != '/' && + virAsprintfQuiet(&relPath, "./%s", path) < 0) + goto error; + + /* Le sigh. Both canonicalize_file_name() and realpath() + * expect @path to exist otherwise they return an error. So + * if we are called over an non-existent file, this could + * return an error. In that case do our best and hope we will + * catch possible error. */ + if ((fullPath = canonicalize_file_name(relPath ? relPath : path))) { + path = fullPath; + } else { + /* Yeah, our worst nightmares just became true. Path does + * not exist. Cut off the last component and retry. */ + if (VIR_STRDUP_QUIET(crippledPath, relPath ? relPath : path) < 0) + goto error; + + virFileRemoveLastComponent(crippledPath); + + if ((fullPath = canonicalize_file_name(crippledPath))) + path = fullPath; + } + + + if (!STRPREFIX(path, abs_topsrcdir) && + !STRPREFIX(path, abs_topbuilddir)) { + printFile(path); + } + + VIR_FREE(crippledPath); + VIR_FREE(relPath); + VIR_FREE(fullPath); + + return; + error: + fprintf(stderr, "Out of memory\n"); + abort(); }