tests: Introduce virpcitest

Among with this test introduce virpcimock as we need to mock some
syscalls, e.g. redirect open() of a file under /sys/bus/pci to a
stub sysfs tree.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Michal Privoznik 2013-10-23 14:44:40 +01:00
parent 629aff378f
commit d770618842
5 changed files with 430 additions and 1 deletions

1
.gitignore vendored
View File

@ -212,6 +212,7 @@
/tests/virlockspacetest
/tests/virlogtest
/tests/virnet*test
/tests/virpcitest
/tests/virportallocatortest
/tests/virshtest
/tests/virstoragetest

2
cfg.mk
View File

@ -946,7 +946,7 @@ exclude_file_name_regexp--sc_bindtextdomain = ^(tests|examples)/
exclude_file_name_regexp--sc_copyright_usage = \
^COPYING(|\.LESSER)$$
exclude_file_name_regexp--sc_flags_usage = ^(docs/|src/util/virnetdevtap\.c$$|tests/vircgroupmock\.c$$)
exclude_file_name_regexp--sc_flags_usage = ^(docs/|src/util/virnetdevtap\.c$$|tests/vir(cgroup|pci)mock\.c$$)
exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \
^(src/rpc/gendispatch\.pl$$|tests/)

View File

@ -123,6 +123,7 @@ test_programs = virshtest sockettest \
virauthconfigtest \
virbitmaptest \
vircgrouptest \
virpcitest \
virendiantest \
viridentitytest \
virkeycodetest \
@ -306,6 +307,7 @@ test_libraries = libshunload.la \
libvirportallocatormock.la \
virnetserverclientmock.la \
vircgroupmock.la \
virpcimock.la \
$(NULL)
if WITH_QEMU
test_libraries += libqemumonitortestutils.la
@ -742,6 +744,17 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS)
vircgroupmock_la_LDFLAGS = -module -avoid-version \
-rpath /evil/libtool/hack/to/force/shared/lib/creation
virpcitest_SOURCES = \
virpcitest.c testutils.h testutils.c
virpcitest_LDADD = $(LDADDS)
virpcimock_la_SOURCES = \
virpcimock.c
virpcimock_la_CFLAGS = $(AM_CFLAGS)
virpcimock_la_LIBADD = ../src/libvirt.la
virpcimock_la_LDFLAGS = -module -avoid-version \
-rpath /evil/libtool/hack/to/force/shared/lib/creation
if WITH_DBUS
virdbustest_SOURCES = \
virdbustest.c testutils.h testutils.c

312
tests/virpcimock.c Normal file
View File

@ -0,0 +1,312 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Michal Privoznik <mprivozn@redhat.com>
*/
#include <config.h>
#ifdef __linux__
# include "internal.h"
# include <stdio.h>
# include <dlfcn.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdarg.h>
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"
static int (*realaccess)(const char *path, int mode);
static int (*reallstat)(const char *path, struct stat *sb);
static int (*real__lxstat)(int ver, const char *path, struct stat *sb);
static int (*realopen)(const char *path, int flags, ...);
/* Don't make static, since it causes problems with clang
* when passed as an arg to virAsprintf()
* vircgroupmock.c:462:22: error: static variable 'fakesysfsdir' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
*/
char *fakesysfsdir;
# define PCI_SYSFS_PREFIX "/sys/bus/pci/"
# define STDERR(...) \
fprintf(stderr, "%s %zu: ", __FUNCTION__, (size_t) __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
# define ABORT(...) \
do { \
STDERR(__VA_ARGS__); \
abort(); \
} while (0)
# define ABORT_OOM() \
ABORT("Out of memory")
/*
* The plan:
*
* Mock some file handling functions. Redirect them into a stub tree passed via
* LIBVIRT_FAKE_SYSFS_DIR env variable. All files and links within stub tree is
* created by us.
*/
/*
*
* Functions to model kernel behavior
*
*/
struct pciDevice {
char *id;
int vendor;
int device;
};
struct pciDevice **pciDevices = NULL;
size_t nPciDevices = 0;
static void init_env(void);
static void pci_device_new_from_stub(const struct pciDevice *data);
/*
* Helper functions
*/
static void
make_file(const char *path,
const char *name,
const char *value)
{
int fd = -1;
char *filepath = NULL;
if (virAsprintfQuiet(&filepath, "%s/%s", path, name) < 0)
ABORT_OOM();
if ((fd = realopen(filepath, O_CREAT|O_WRONLY, 0666)) < 0)
ABORT("Unable to open: %s", filepath);
if (value && safewrite(fd, value, strlen(value)) != strlen(value))
ABORT("Unable to write: %s", filepath);
VIR_FORCE_CLOSE(fd);
VIR_FREE(filepath);
}
static int
getrealpath(char **newpath,
const char *path)
{
if (!fakesysfsdir)
init_env();
if (STRPREFIX(path, PCI_SYSFS_PREFIX)) {
if (virAsprintfQuiet(newpath, "%s/%s",
fakesysfsdir,
path + strlen(PCI_SYSFS_PREFIX)) < 0) {
errno = ENOMEM;
return -1;
}
} else {
if (VIR_STRDUP_QUIET(*newpath, path) < 0)
return -1;
}
return 0;
}
/*
* PCI Device functions
*/
static void
pci_device_new_from_stub(const struct pciDevice *data)
{
struct pciDevice *dev;
char *devpath;
char tmp[32];
if (VIR_ALLOC_QUIET(dev) < 0 ||
virAsprintfQuiet(&devpath, "%s/devices/%s", fakesysfsdir, data->id) < 0)
ABORT_OOM();
memcpy(dev, data, sizeof(*dev));
if (virFileMakePath(devpath) < 0)
ABORT("Unable to create: %s", devpath);
make_file(devpath, "config", "some dummy config");
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->vendor) < 0)
ABORT("@tmp overflow");
make_file(devpath, "vendor", tmp);
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->device) < 0)
ABORT("@tmp overflow");
make_file(devpath, "device", tmp);
if (VIR_APPEND_ELEMENT_QUIET(pciDevices, nPciDevices, dev) < 0)
ABORT_OOM();
VIR_FREE(devpath);
}
/*
* Functions to load the symbols and init the environment
*/
static void
init_syms(void)
{
if (realaccess)
return;
# define LOAD_SYM(name) \
do { \
if (!(real ## name = dlsym(RTLD_NEXT, #name))) \
ABORT("Cannot find real '%s' symbol\n", #name); \
} while (0)
# define LOAD_SYM_ALT(name1, name2) \
do { \
if (!(real ## name1 = dlsym(RTLD_NEXT, #name1)) && \
!(real ## name2 = dlsym(RTLD_NEXT, #name2))) \
ABORT("Cannot find real '%s' or '%s' symbol\n", \
#name1, #name2); \
} while (0)
LOAD_SYM(access);
LOAD_SYM_ALT(lstat, __lxstat);
LOAD_SYM(open);
}
static void
init_env(void)
{
if (fakesysfsdir)
return;
if (!(fakesysfsdir = getenv("LIBVIRT_FAKE_SYSFS_DIR")))
ABORT("Missing LIBVIRT_FAKE_SYSFS_DIR env variable\n");
# define MAKE_PCI_DEVICE(Id, Vendor, Device, ...) \
do { \
struct pciDevice dev = {.id = (char *)Id, .vendor = Vendor, \
.device = Device, __VA_ARGS__}; \
pci_device_new_from_stub(&dev); \
} while (0)
MAKE_PCI_DEVICE("0000:00:00.0", 0x8086, 0x0044);
}
/*
*
* Mocked functions
*
*/
int
access(const char *path, int mode)
{
int ret;
init_syms();
if (STRPREFIX(path, PCI_SYSFS_PREFIX)) {
char *newpath;
if (getrealpath(&newpath, path) < 0)
return -1;
ret = realaccess(newpath, mode);
VIR_FREE(newpath);
} else {
ret = realaccess(path, mode);
}
return ret;
}
int
__lxstat(int ver, const char *path, struct stat *sb)
{
int ret;
init_syms();
if (STRPREFIX(path, PCI_SYSFS_PREFIX)) {
char *newpath;
if (getrealpath(&newpath, path) < 0)
return -1;
ret = real__lxstat(ver, newpath, sb);
VIR_FREE(newpath);
} else {
ret = real__lxstat(ver, path, sb);
}
return ret;
}
int
lstat(const char *path, struct stat *sb)
{
int ret;
init_syms();
if (STRPREFIX(path, PCI_SYSFS_PREFIX)) {
char *newpath;
if (getrealpath(&newpath, path) < 0)
return -1;
ret = reallstat(newpath, sb);
VIR_FREE(newpath);
} else {
ret = reallstat(path, sb);
}
return ret;
}
int
open(const char *path, int flags, ...)
{
int ret;
char *newpath = NULL;
init_syms();
if (STRPREFIX(path, PCI_SYSFS_PREFIX) &&
getrealpath(&newpath, path) < 0)
return -1;
if (flags & O_CREAT) {
va_list ap;
mode_t mode;
va_start(ap, flags);
mode = va_arg(ap, mode_t);
va_end(ap);
ret = realopen(newpath ? newpath : path, flags, mode);
} else {
ret = realopen(newpath ? newpath : path, flags);
}
VIR_FREE(newpath);
return ret;
}
#else
/* Nothing to override on non-__linux__ platforms */
#endif

103
tests/virpcitest.c Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Michal Privoznik <mprivozn@redhat.com>
*/
#include <config.h>
#include "testutils.h"
#ifdef __linux__
# include <stdlib.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <virpci.h>
# define VIR_FROM_THIS VIR_FROM_NONE
static int
testVirPCIDeviceNew(const void *opaque ATTRIBUTE_UNUSED)
{
int ret = -1;
virPCIDevicePtr dev;
const char *devName;
if (!(dev = virPCIDeviceNew(0, 0, 0, 0)))
goto cleanup;
devName = virPCIDeviceGetName(dev);
if (STRNEQ(devName, "0000:00:00.0")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"PCI device name mismatch: %s, expecting %s",
devName, "0000:00:00.0");
goto cleanup;
}
ret = 0;
cleanup:
virPCIDeviceFree(dev);
return ret;
}
# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX"
static int
mymain(void)
{
int ret = 0;
char *fakesysfsdir;
if (VIR_STRDUP_QUIET(fakesysfsdir, FAKESYSFSDIRTEMPLATE) < 0) {
fprintf(stderr, "Out of memory\n");
abort();
}
if (!mkdtemp(fakesysfsdir)) {
fprintf(stderr, "Cannot create fakesysfsdir");
abort();
}
setenv("LIBVIRT_FAKE_SYSFS_DIR", fakesysfsdir, 1);
# define DO_TEST(fnc) \
do { \
if (virtTestRun(#fnc, fnc, NULL) < 0) \
ret = -1; \
} while (0)
DO_TEST(testVirPCIDeviceNew);
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
virFileDeleteTree(fakesysfsdir);
VIR_FREE(fakesysfsdir);
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virpcimock.so")
#else
int
main(void)
{
return EXIT_AM_SKIP;
}
#endif