event: share state driver between test:///default connections

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@redhat.com>
This commit is contained in:
Eric Blake 2014-01-01 09:30:12 -07:00
parent d847792f86
commit fc967c3ec9
2 changed files with 105 additions and 30 deletions

View File

@ -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;
}

View File

@ -265,8 +265,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);
@ -282,20 +284,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;
}
@ -419,14 +441,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;
@ -460,6 +494,7 @@ mymain(void)
virNetworkUndefine(test.net);
virNetworkFree(test.net);
virConnectClose(test.conn);
virEventRemoveTimeout(timer);
return ret;
}