Prior to this patch, every test:/// URI has its own event manager,
which means that registering for an event can only ever receive
events from the connection where it issued the API that triggered
the event. But the whole idea of events is to be able to learn
about something where an API call did NOT trigger the action.
In order to actually test asynchronous events, I wanted to be able
to tie multiple test connections to the same state. Use of a file
in a test URI is still per-connection state, but now parallel
connections to test:///default (from the same binary, of course)
now share common state and can affect one another.
The updated testsuite fails without the rest of this patch.
Valgrind didn't report any leaks.
* src/test/test_driver.c (testConnectOpen): Move per-connection
state initialization...
(testOpenFromFile): ...here.
(defaultConn, defaultConnections, defaultLock, testOnceInit): New
shared state.
(testOpenDefault): Only initialize on first connection.
(testConnectClose): Don't clobber state if still shared.
* tests/objecteventtest.c (testDomainStartStopEvent): Enhance to
cover this.
(timeout, mymain): Ensure test fails rather than blocks.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/test/test_driver.c | 88 +++++++++++++++++++++++++++++++++++--------------
tests/objecteventtest.c | 50 ++++++++++++++++++++++++----
2 files changed, 107 insertions(+), 31 deletions(-)
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index a48404a..9696044 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -1,7 +1,7 @@
/*
* test.c: A "mock" hypervisor for use by application unit tests
*
- * Copyright (C) 2006-2013 Red Hat, Inc.
+ * Copyright (C) 2006-2014 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
@@ -105,6 +105,10 @@ struct _testConn {
typedef struct _testConn testConn;
typedef struct _testConn *testConnPtr;
+static testConn defaultConn;
+static int defaultConnections;
+static virMutex defaultLock;
+
#define TEST_MODEL "i686"
#define TEST_MODEL_WORDSIZE 32
#define TEST_EMULATOR "/usr/bin/test-hv"
@@ -125,6 +129,14 @@ static int testConnectClose(virConnectPtr conn);
static void testObjectEventQueue(testConnPtr driver,
virObjectEventPtr event);
+static int
+testOnceInit(void)
+{
+ return virMutexInit(&defaultLock);
+}
+
+VIR_ONCE_GLOBAL_INIT(test)
+
static void testDriverLock(testConnPtr driver)
{
@@ -665,9 +677,15 @@ cleanup:
return ret;
}
-static int testOpenDefault(virConnectPtr conn) {
+
+/* Simultaneous test:///default connections should share the same
+ * common state (among other things, this allows testing event
+ * detection in one connection for an action caused in another). */
+static int
+testOpenDefault(virConnectPtr conn)
+{
int u;
- testConnPtr privconn;
+ testConnPtr privconn = &defaultConn;
virDomainDefPtr domdef = NULL;
virDomainObjPtr domobj = NULL;
virNetworkDefPtr netdef = NULL;
@@ -679,18 +697,26 @@ static int testOpenDefault(virConnectPtr conn) {
virNodeDeviceDefPtr nodedef = NULL;
virNodeDeviceObjPtr nodeobj = NULL;
- if (VIR_ALLOC(privconn) < 0)
- return VIR_DRV_OPEN_ERROR;
+ virMutexLock(&defaultLock);
+ if (defaultConnections++) {
+ conn->privateData = &defaultConn;
+ virMutexUnlock(&defaultLock);
+ return VIR_DRV_OPEN_SUCCESS;
+ }
+
if (virMutexInit(&privconn->lock) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot initialize mutex"));
- VIR_FREE(privconn);
+ defaultConnections--;
+ virMutexUnlock(&defaultLock);
return VIR_DRV_OPEN_ERROR;
}
- testDriverLock(privconn);
conn->privateData = privconn;
+ if (!(privconn->domainEventState = virObjectEventStateNew()))
+ goto error;
+
if (!(privconn->domains = virDomainObjListNew()))
goto error;
@@ -791,7 +817,7 @@ static int testOpenDefault(virConnectPtr conn) {
}
virNodeDeviceObjUnlock(nodeobj);
- testDriverUnlock(privconn);
+ virMutexUnlock(&defaultLock);
return VIR_DRV_OPEN_SUCCESS;
@@ -802,10 +828,12 @@ error:
virStoragePoolObjListFree(&privconn->pools);
virNodeDeviceObjListFree(&privconn->devs);
virObjectUnref(privconn->caps);
- testDriverUnlock(privconn);
+ virObjectEventStateFree(privconn->domainEventState);
+ virMutexDestroy(&privconn->lock);
conn->privateData = NULL;
- VIR_FREE(privconn);
virDomainDefFree(domdef);
+ defaultConnections--;
+ virMutexUnlock(&defaultLock);
return VIR_DRV_OPEN_ERROR;
}
@@ -1327,6 +1355,9 @@ error:
return ret;
}
+
+/* No shared state between simultaneous test connections initialized
+ * from a file. */
static int
testOpenFromFile(virConnectPtr conn, const char *file)
{
@@ -1355,6 +1386,9 @@ testOpenFromFile(virConnectPtr conn, const char *file)
if (!(privconn->xmlopt = testBuildXMLConfig()))
goto error;
+ if (!(privconn->domainEventState = virObjectEventStateNew()))
+ goto error;
+
if (!(doc = virXMLParseFileCtxt(file, &ctxt))) {
goto error;
}
@@ -1398,6 +1432,7 @@ testOpenFromFile(virConnectPtr conn, const char *file)
virInterfaceObjListFree(&privconn->ifaces);
virStoragePoolObjListFree(&privconn->pools);
VIR_FREE(privconn->path);
+ virObjectEventStateFree(privconn->domainEventState);
testDriverUnlock(privconn);
VIR_FREE(privconn);
conn->privateData = NULL;
@@ -1410,10 +1445,12 @@ static virDrvOpenStatus testConnectOpen(virConnectPtr conn,
unsigned int flags)
{
int ret;
- testConnPtr privconn;
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
+ if (testInitialize() < 0)
+ return VIR_DRV_OPEN_ERROR;
+
if (!conn->uri)
return VIR_DRV_OPEN_DECLINED;
@@ -1442,24 +1479,24 @@ static virDrvOpenStatus testConnectOpen(virConnectPtr conn,
if (ret != VIR_DRV_OPEN_SUCCESS)
return ret;
- privconn = conn->privateData;
- testDriverLock(privconn);
-
- privconn->domainEventState = virObjectEventStateNew();
- if (!privconn->domainEventState) {
- testDriverUnlock(privconn);
- testConnectClose(conn);
- return VIR_DRV_OPEN_ERROR;
- }
-
- testDriverUnlock(privconn);
-
return VIR_DRV_OPEN_SUCCESS;
}
static int testConnectClose(virConnectPtr conn)
{
testConnPtr privconn = conn->privateData;
+
+ if (testInitialize() < 0)
+ return -1;
+
+ if (privconn == &defaultConn) {
+ virMutexLock(&defaultLock);
+ if (--defaultConnections) {
+ virMutexUnlock(&defaultLock);
+ return 0;
+ }
+ }
+
testDriverLock(privconn);
virObjectUnref(privconn->caps);
virObjectUnref(privconn->xmlopt);
@@ -1474,7 +1511,10 @@ static int testConnectClose(virConnectPtr conn)
testDriverUnlock(privconn);
virMutexDestroy(&privconn->lock);
- VIR_FREE(privconn);
+ if (privconn == &defaultConn)
+ virMutexUnlock(&defaultLock);
+ else
+ VIR_FREE(privconn);
conn->privateData = NULL;
return 0;
}
diff --git a/tests/objecteventtest.c b/tests/objecteventtest.c
index ae29792..1f43070 100644
--- a/tests/objecteventtest.c
+++ b/tests/objecteventtest.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2014 Red Hat, Inc.
* Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
*
* This library is free software; you can redistribute it and/or
@@ -220,8 +221,10 @@ testDomainStartStopEvent(const void *data)
lifecycleEventCounter counter;
int eventId = VIR_DOMAIN_EVENT_ID_LIFECYCLE;
int id;
- int ret = 0;
+ int ret = -1;
virDomainPtr dom;
+ virConnectPtr conn2 = NULL;
+ virDomainPtr dom2 = NULL;
lifecycleEventCounter_reset(&counter);
@@ -237,20 +240,40 @@ testDomainStartStopEvent(const void *data)
virDomainDestroy(dom);
virDomainCreate(dom);
- if (virEventRunDefaultImpl() < 0) {
- ret = -1;
+ if (virEventRunDefaultImpl() < 0)
goto cleanup;
- }
if (counter.startEvents != 1 || counter.stopEvents != 1 ||
- counter.unexpectedEvents > 0) {
- ret = -1;
+ counter.unexpectedEvents > 0)
+ goto cleanup;
+
+ /* Repeat the test, but this time, trigger the events via an
+ * alternate connection. */
+ if (!(conn2 = virConnectOpen("test:///default")))
+ goto cleanup;
+ if (!(dom2 = virDomainLookupByName(conn2, "test")))
goto cleanup;
- }
+ if (virDomainDestroy(dom2) < 0)
+ goto cleanup;
+ if (virDomainCreate(dom2) < 0)
+ goto cleanup;
+
+ if (virEventRunDefaultImpl() < 0)
+ goto cleanup;
+
+ if (counter.startEvents != 2 || counter.stopEvents != 2 ||
+ counter.unexpectedEvents > 0)
+ goto cleanup;
+
+ ret = 0;
cleanup:
virConnectDomainEventDeregisterAny(test->conn, id);
virDomainFree(dom);
+ if (dom2)
+ virDomainFree(dom2);
+ if (conn2)
+ virConnectClose(conn2);
return ret;
}
@@ -374,14 +397,26 @@ cleanup:
return ret;
}
+static void
+timeout(int id ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED)
+{
+ fputs("test taking too long; giving up", stderr);
+ exit(EXIT_FAILURE);
+}
+
static int
mymain(void)
{
objecteventTest test;
int ret = EXIT_SUCCESS;
+ int timer;
virEventRegisterDefaultImpl();
+ /* Set up a timer to abort this test if it takes 10 seconds. */
+ if ((timer = virEventAddTimeout(10 * 1000, timeout, NULL, NULL)) < 0)
+ return EXIT_FAILURE;
+
if (!(test.conn = virConnectOpen("test:///default")))
return EXIT_FAILURE;
@@ -411,6 +446,7 @@ mymain(void)
virNetworkUndefine(test.net);
virNetworkFree(test.net);
virConnectClose(test.conn);
+ virEventRemoveTimeout(timer);
return ret;
}
--
1.8.4.2