mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 17:34:18 +03: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/virlogtest
|
||||
/tests/virnet*test
|
||||
/tests/virpcitest
|
||||
/tests/virportallocatortest
|
||||
/tests/virshtest
|
||||
/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 = \
|
||||
^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/)
|
||||
|
@ -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
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…
Reference in New Issue
Block a user