Add unit test for hostdev common library. Current tests are based on
virpcimock,
limited to pci related APIs only.
The USB and SCSI logic is similar, so PCI tests do help those somewhat.
USB and SCSI tests could be added later if no one objects.
BTW, this should be patch 2 in the series, following the introduction of
the virhostdev interface. Or maybe included in patch 1? But I think it
is common to add tests as a separate patch, after the introduction of
the new code/feature.
Signed-off-by: Chunyan Liu <cyliu(a)suse.com>
---
tests/Makefile.am | 5 +
tests/virhostdevtest.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/virpcimock.c | 45 +++++-
3 files changed, 530 insertions(+), 1 deletions(-)
create mode 100644 tests/virhostdevtest.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 568b7a0..3e66d8c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -141,6 +141,7 @@ test_programs = virshtest sockettest \
virportallocatortest \
sysinfotest \
virstoragetest \
+ virhostdevtest \
$(NULL)
if WITH_REMOTE
@@ -754,6 +755,10 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS)
vircgroupmock_la_LDFLAGS = -module -avoid-version \
-rpath /evil/libtool/hack/to/force/shared/lib/creation
+virhostdevtest_SOURCES = \
+ virhostdevtest.c testutils.h testutils.c
+virhostdevtest_LDADD = $(LDADDS)
+
virpcitest_SOURCES = \
virpcitest.c testutils.h testutils.c
virpcitest_LDADD = $(LDADDS)
diff --git a/tests/virhostdevtest.c b/tests/virhostdevtest.c
new file mode 100644
index 0000000..5b45548
--- /dev/null
+++ b/tests/virhostdevtest.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ *
+ * 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: Chunyan Liu <cyliu(a)suse.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 <virhostdev.h>
+
+# define VIR_FROM_THIS VIR_FROM_NONE
+
+# define CHECK_LIST_COUNT(list, cnt) \
+ if ((count = virPCIDeviceListCount(list)) != cnt) { \
+ virReportError(VIR_ERR_INTERNAL_ERROR, \
+ "Unexpected count of items in " #list ": %d,
" \
+ "expecting %zu", count, (size_t) cnt); \
+ goto cleanup; \
+ }
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define TEST_STATE_DIR abs_builddir "/hostdevmgr"
+static const char *drv_name = "test_driver";
+static const char *dom_name = "test_domain";
+static const unsigned char *uuid =
+ (unsigned char *)("f92360b0-2541-8791-fb32-d1f838811541");
+static int nhostdevs = 3;
+static virDomainHostdevDefPtr hostdevs[] = {NULL, NULL, NULL};
+static virPCIDevicePtr dev[] = {NULL, NULL, NULL};
+static virHostdevManagerPtr mgr = NULL;
+
+static void
+myCleanup(void)
+{
+ size_t i;
+ for (i = 0; i < nhostdevs; i++) {
+ virPCIDeviceFree(dev[i]);
+ virDomainHostdevDefFree(hostdevs[i]);
+ }
+
+ if (mgr) {
+ virObjectUnref(mgr->activePciHostdevs);
+ virObjectUnref(mgr->inactivePciHostdevs);
+ virObjectUnref(mgr->activeUsbHostdevs);
+ VIR_FREE(mgr->stateDir);
+ VIR_FREE(mgr);
+ }
+}
+
+static int
+myInit(void)
+{
+ size_t i;
+
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevSubsys subsys;
+ hostdevs[i] = virDomainHostdevDefAlloc();
+ if (!hostdevs[i])
+ goto cleanup;
+ hostdevs[i]->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
+ subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+ subsys.u.pci.addr.domain = 0;
+ subsys.u.pci.addr.bus = 0;
+ subsys.u.pci.addr.slot = i + 1;
+ subsys.u.pci.addr.function = 0;
+ subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM;
+ hostdevs[i]->source.subsys = subsys;
+ }
+
+ for (i = 0; i < nhostdevs; i++) {
+ if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)) ||
+ virPCIDeviceSetStubDriver(dev[i], "pci-stub") < 0)
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC(mgr) < 0)
+ goto cleanup;
+ if ((mgr->activePciHostdevs = virPCIDeviceListNew()) == NULL)
+ goto cleanup;
+ if ((mgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL)
+ goto cleanup;
+ if ((mgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL)
+ goto cleanup;
+ if ((mgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL)
+ goto cleanup;
+ if (VIR_STRDUP(mgr->stateDir, TEST_STATE_DIR) < 0)
+ goto cleanup;
+ if (virFileMakePath(mgr->stateDir) < 0) {
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ myCleanup();
+ return -1;
+}
+
+static int
+testVirHostdevPreparePciHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1, count2;
+
+ for (i = 0; i < nhostdevs; i++) {
+ hostdevs[i]->managed = false;
+ }
+
+ /* Test invalid args */
+ DPRINTF("Test hostdev mgr=NULL\n");
+ if (!virHostdevPreparePciHostdevs(NULL, drv_name, dom_name, uuid,
+ hostdevs, nhostdevs, 0))
+ goto cleanup;
+
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+ count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs);
+
+ /* Test normal functionality */
+ DPRINTF("Test 0 hostdevs\n");
+ if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid,
+ NULL, 0, 0) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ /* Test unmanaged hostdevs */
+ DPRINTF("Test >=1 unmanaged hostdevs\n");
+ if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid,
+ hostdevs, nhostdevs, 0) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3);
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2 - 3);
+
+ /* Test conflict */
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+ count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs);
+ DPRINTF("Test: prepare same hostdevs for same driver/domain again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid,
+ &hostdevs[0], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2);
+
+ DPRINTF("Test: prepare same hostdevs for same driver, diff domain
again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, drv_name, "test_domain1", uuid,
+ &hostdevs[1], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2);
+
+ DPRINTF("Test: prepare same hostdevs for diff driver/domain again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, "test_driver1", dom_name, uuid,
+ &hostdevs[2], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2);
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevReAttachPciHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1, count2;
+
+ for (i = 0; i < nhostdevs; i++) {
+ if (hostdevs[i]->managed != false) {
+ DPRINTF("invalid test\n");
+ return -1;
+ }
+ }
+
+ DPRINTF("Test hostdev mgr=NULL\n");
+ virHostdevReAttachPciHostdevs(NULL, drv_name, dom_name,
+ hostdevs, nhostdevs);
+
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+ count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs);
+
+ DPRINTF("Test 0 hostdevs\n");
+ virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, NULL, 0);
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ DPRINTF("Test >=1 unmanaged hostdevs\n");
+ virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, hostdevs, nhostdevs);
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 - 3);
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2 + 3);
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevPreparePciHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1;
+
+ for (i = 0; i < nhostdevs; i++) {
+ hostdevs[i]->managed = true;
+ }
+
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+
+ /* Test normal functionality */
+ DPRINTF("Test >=1 hostdevs\n");
+ if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid,
+ hostdevs, nhostdevs, 0) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3);
+
+ /* Test conflict */
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+ DPRINTF("Test: prepare same hostdevs for same driver/domain again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid,
+ &hostdevs[0], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ DPRINTF("Test: prepare same hostdevs for same driver, diff domain
again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, drv_name, "test_domain1", uuid,
+ &hostdevs[1], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ DPRINTF("Test: prepare same hostdevs for diff driver/domain again\n");
+ if (!virHostdevPreparePciHostdevs(mgr, "test_driver1", dom_name, uuid,
+ &hostdevs[2], 1, 0))
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevReAttachPciHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1;
+
+ for (i = 0; i < nhostdevs; i++) {
+ if (hostdevs[i]->managed != true) {
+ DPRINTF("invalid test\n");
+ return -1;
+ }
+ }
+
+ DPRINTF("Test hostdev mgr=NULL\n");
+ virHostdevReAttachPciHostdevs(NULL, drv_name, dom_name,
+ hostdevs, nhostdevs);
+
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+
+ DPRINTF("Test 0 hostdevs\n");
+ virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, NULL, 0);
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ DPRINTF("Test >=1 hostdevs\n");
+ virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, hostdevs, nhostdevs);
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 - 3);
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevDetachPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1;
+
+ if (!virHostdevPciNodeDeviceDetach(NULL, dev[0]))
+ goto cleanup;
+
+ if (!virHostdevPciNodeDeviceDetach(mgr, NULL))
+ goto cleanup;
+
+ for (i = 0; i < nhostdevs; i++) {
+ count1 = virPCIDeviceListCount(mgr->inactivePciHostdevs);
+ if (virHostdevPciNodeDeviceDetach(mgr, dev[i]) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count1 + 1);
+ }
+
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+static int
+testVirHostdevResetPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+
+ if (!virHostdevPciNodeDeviceReset(NULL, dev[0]))
+ goto cleanup;
+
+ if (!virHostdevPciNodeDeviceReAttach(mgr, NULL))
+ goto cleanup;
+
+ for (i = 0; i < nhostdevs; i++) {
+ if (virHostdevPciNodeDeviceReset(mgr, dev[i]) < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevReAttachPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ size_t i;
+ int count, count1;
+
+ if (!virHostdevPciNodeDeviceReAttach(NULL, dev[0]))
+ goto cleanup;
+
+ if (!virHostdevPciNodeDeviceReAttach(mgr, NULL))
+ goto cleanup;
+
+ for (i = 0; i < nhostdevs; i++) {
+ count1 = virPCIDeviceListCount(mgr->inactivePciHostdevs);
+ if (virHostdevPciNodeDeviceReAttach(mgr, dev[i]) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count1 - 1);
+ }
+
+ ret = 0;
+
+cleanup:
+ return ret;
+
+}
+
+static int
+testVirHostdevUpdateActivePciHostdevs(const void *oaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ int count, count1;
+
+ DPRINTF("Test hostdev mgr=NULL\n");
+ if (!virHostdevUpdateActivePciHostdevs(NULL, hostdevs, nhostdevs,
+ drv_name, dom_name))
+ goto cleanup;
+
+ count1 = virPCIDeviceListCount(mgr->activePciHostdevs);
+
+ DPRINTF("Test 0 hostdevs\n");
+ if (virHostdevUpdateActivePciHostdevs(mgr, NULL, 0,
+ drv_name, dom_name) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1);
+
+ DPRINTF("Test >=1 hostdevs\n");
+ if (virHostdevUpdateActivePciHostdevs(mgr, hostdevs, nhostdevs,
+ drv_name, dom_name) < 0)
+ goto cleanup;
+ CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3);
+
+ ret = 0;
+
+cleanup:
+ 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 { \
+ DPRINTF("\nTesting: %s", #fnc); \
+ if (virtTestRun(#fnc, fnc, NULL) < 0) \
+ ret = -1; \
+ } while (0)
+
+ if (myInit() < 0)
+ fprintf(stderr, "Init data structures failed.");
+
+ DO_TEST(testVirHostdevDetachPciNodeDevice);
+ if (virHostdevHostSupportsPassthroughKVM()) {
+ /* following tests would check KVM support */
+ DO_TEST(testVirHostdevPreparePciHostdevs_unmanaged);
+ DO_TEST(testVirHostdevReAttachPciHostdevs_unmanaged);
+ }
+ DO_TEST(testVirHostdevResetPciNodeDevice);
+ DO_TEST(testVirHostdevReAttachPciNodeDevice);
+ if (virHostdevHostSupportsPassthroughKVM()) {
+ /* following tests would check KVM support */
+ DO_TEST(testVirHostdevPreparePciHostdevs_managed);
+ DO_TEST(testVirHostdevReAttachPciHostdevs_managed);
+ }
+ DO_TEST(testVirHostdevUpdateActivePciHostdevs);
+
+ myCleanup();
+
+ 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
diff --git a/tests/virpcimock.c b/tests/virpcimock.c
index a5cef46..147eec0 100644
--- a/tests/virpcimock.c
+++ b/tests/virpcimock.c
@@ -29,6 +29,8 @@
# include <fcntl.h>
# include <sys/stat.h>
# include <stdarg.h>
+# include <sys/types.h>
+# include <dirent.h>
# include "viralloc.h"
# include "virstring.h"
# include "virfile.h"
@@ -42,6 +44,7 @@ static int (*real__xstat)(int ver, const char *path, struct stat *sb);
static char *(*realcanonicalize_file_name)(const char *path);
static int (*realopen)(const char *path, int flags, ...);
static int (*realclose)(int fd);
+static DIR *(*realopendir)(const char *name);
/* Don't make static, since it causes problems with clang
* when passed as an arg to virAsprintf()
@@ -146,7 +149,7 @@ static int pci_driver_handle_bind(const char *path);
static int pci_driver_handle_unbind(const char *path);
static int pci_driver_handle_new_id(const char *path);
static int pci_driver_handle_remove_id(const char *path);
-
+static int pci_handle_drivers_probe(const char *path);
/*
* Helper functions
@@ -578,6 +581,8 @@ pci_driver_handle_change(int fd ATTRIBUTE_UNUSED, const char *path)
} else if (STREQ(file, "remove_id")) {
/* handle write to remove_id */
ret = pci_driver_handle_remove_id(path);
+ } else if (STREQ(file, "drivers_probe")) {
+ ret = pci_handle_drivers_probe(path);
} else {
/* yet not handled write */
ABORT("Not handled write to: %s", path);
@@ -717,6 +722,16 @@ cleanup:
return ret;
}
+static int
+pci_handle_drivers_probe(const char *path)
+{
+ struct pciDevice *dev = pci_device_find_by_content(path);
+
+ if (pci_device_autobind(dev) < 0)
+ ABORT("Unable to do driver reprobe.");
+
+ return 0;
+}
/*
* Functions to load the symbols and init the environment
@@ -746,12 +761,16 @@ init_syms(void)
LOAD_SYM_ALT(stat, __xstat);
LOAD_SYM(canonicalize_file_name);
LOAD_SYM(open);
+ LOAD_SYM(opendir);
LOAD_SYM(close);
}
static void
init_env(void)
{
+ int fd = -1;
+ char *filepath;
+
if (fakesysfsdir)
return;
@@ -776,6 +795,12 @@ init_env(void)
MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044);
MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046);
MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048);
+
+ /* make file drivers_probe */
+ if (virAsprintfQuiet(&filepath, "%s/drivers_probe", fakesysfsdir) <
0)
+ ABORT_OOM();
+ if ((fd = realopen(filepath, O_CREAT|O_WRONLY, 0200)) < 0)
+ ABORT("Unable to create: %s", filepath);
}
@@ -899,6 +924,24 @@ canonicalize_file_name(const char *path)
return ret;
}
+DIR *
+opendir(const char *path)
+{
+ DIR *dir = NULL;
+ char *newpath = NULL;
+
+ init_syms();
+
+ if (STRPREFIX(path, PCI_SYSFS_PREFIX) &&
+ getrealpath(&newpath, path) < 0)
+ return -1;
+
+ dir = realopendir(newpath ? newpath : path);
+ VIR_FREE(newpath);
+ return dir;
+}
+
+
int
open(const char *path, int flags, ...)
{