mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-01 17:35:17 +00:00
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:
parent
629aff378f
commit
d770618842
1
.gitignore
vendored
1
.gitignore
vendored
@ -212,6 +212,7 @@
|
|||||||
/tests/virlockspacetest
|
/tests/virlockspacetest
|
||||||
/tests/virlogtest
|
/tests/virlogtest
|
||||||
/tests/virnet*test
|
/tests/virnet*test
|
||||||
|
/tests/virpcitest
|
||||||
/tests/virportallocatortest
|
/tests/virportallocatortest
|
||||||
/tests/virshtest
|
/tests/virshtest
|
||||||
/tests/virstoragetest
|
/tests/virstoragetest
|
||||||
|
2
cfg.mk
2
cfg.mk
@ -946,7 +946,7 @@ exclude_file_name_regexp--sc_bindtextdomain = ^(tests|examples)/
|
|||||||
exclude_file_name_regexp--sc_copyright_usage = \
|
exclude_file_name_regexp--sc_copyright_usage = \
|
||||||
^COPYING(|\.LESSER)$$
|
^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 = \
|
exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \
|
||||||
^(src/rpc/gendispatch\.pl$$|tests/)
|
^(src/rpc/gendispatch\.pl$$|tests/)
|
||||||
|
@ -123,6 +123,7 @@ test_programs = virshtest sockettest \
|
|||||||
virauthconfigtest \
|
virauthconfigtest \
|
||||||
virbitmaptest \
|
virbitmaptest \
|
||||||
vircgrouptest \
|
vircgrouptest \
|
||||||
|
virpcitest \
|
||||||
virendiantest \
|
virendiantest \
|
||||||
viridentitytest \
|
viridentitytest \
|
||||||
virkeycodetest \
|
virkeycodetest \
|
||||||
@ -306,6 +307,7 @@ test_libraries = libshunload.la \
|
|||||||
libvirportallocatormock.la \
|
libvirportallocatormock.la \
|
||||||
virnetserverclientmock.la \
|
virnetserverclientmock.la \
|
||||||
vircgroupmock.la \
|
vircgroupmock.la \
|
||||||
|
virpcimock.la \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
if WITH_QEMU
|
if WITH_QEMU
|
||||||
test_libraries += libqemumonitortestutils.la
|
test_libraries += libqemumonitortestutils.la
|
||||||
@ -742,6 +744,17 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS)
|
|||||||
vircgroupmock_la_LDFLAGS = -module -avoid-version \
|
vircgroupmock_la_LDFLAGS = -module -avoid-version \
|
||||||
-rpath /evil/libtool/hack/to/force/shared/lib/creation
|
-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
|
if WITH_DBUS
|
||||||
virdbustest_SOURCES = \
|
virdbustest_SOURCES = \
|
||||||
virdbustest.c testutils.h testutils.c
|
virdbustest.c testutils.h testutils.c
|
||||||
|
312
tests/virpcimock.c
Normal file
312
tests/virpcimock.c
Normal 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
103
tests/virpcitest.c
Normal 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
|
Loading…
x
Reference in New Issue
Block a user