diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 48d92a7fd..8e23ab691 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -16,18 +16,33 @@ #include "epicsUnitTest.h" #include "osiFileName.h" #include "registry.h" +#include "epicsEvent.h" #define epicsExportSharedSymbols #include "dbAccess.h" #include "dbBase.h" +#include "dbChannel.h" +#include "dbEvent.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "initHooks.h" #include "iocInit.h" +static dbEventCtx testEvtCtx; +static epicsMutexId testEvtLock; +static ELLLIST testEvtList; /* holds testMonitor::node */ + +struct testMonitor { + ELLNODE node; + dbEventSubscription sub; + epicsEventId event; + unsigned count; +}; + void testdbPrepare(void) { - /* No-op at the moment */ + if(!testEvtLock) + testEvtLock = epicsMutexMustCreate(); } void testdbReadDatabase(const char* file, @@ -46,10 +61,21 @@ void testIocInitOk(void) { if(iocBuildIsolated() || iocRun()) testAbort("Failed to start up test database"); + if(!(testEvtCtx=db_init_events())) + testAbort("Failed to initialize test dbEvent context"); + if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow)) + testAbort("Failed to start test dbEvent context"); } void testIocShutdownOk(void) { + epicsMutexMustLock(testEvtLock); + if(ellCount(&testEvtList)) + testDiag("Warning, testing monitors still active at testIocShutdownOk()"); + epicsMutexUnlock(testEvtLock); + + db_close_events(testEvtCtx); + testEvtCtx = NULL; if(iocShutdown()) testAbort("Failed to shutdown test database"); } @@ -199,3 +225,88 @@ dbCommon* testdbRecordPtr(const char* pv) return addr.precord; } + +static +void testmonupdate(void *user_arg, struct dbChannel *chan, + int eventsRemaining, struct db_field_log *pfl) +{ + testMonitor *mon = user_arg; + + epicsMutexMustLock(testEvtLock); + mon->count++; + epicsMutexUnlock(testEvtLock); + epicsEventMustTrigger(mon->event); +} + +testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt) +{ + long status; + testMonitor *mon; + dbChannel *chan; + assert(testEvtCtx); + + mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate"); + + mon->event = epicsEventMustCreate(epicsEventEmpty); + + chan = dbChannelCreate(pvname); + if(!chan) + testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname); + if(!!(status=dbChannelOpen(chan))) + testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status); + + mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask); + if(!mon->sub) + testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname); + + db_event_enable(mon->sub); + + epicsMutexMustLock(testEvtLock); + ellAdd(&testEvtList, &mon->node); + epicsMutexUnlock(testEvtLock); + + return mon; +} + +void testMonitorDestroy(testMonitor *mon) +{ + if(!mon) return; + + db_event_disable(mon->sub); + + epicsMutexMustLock(testEvtLock); + ellDelete(&testEvtList, &mon->node); + epicsMutexUnlock(testEvtLock); + + db_cancel_event(mon->sub); + + epicsEventDestroy(mon->event); + + free(mon); +} + +void testMonitorWait(testMonitor *mon) +{ + switch(epicsEventWaitWithTimeout(mon->event, 10.0)) + { + case epicsEventOK: + return; + case epicsEventWaitTimeout: + default: + testAbort("testMonitorWait() exceeds timeout"); + } +} + +unsigned testMonitorCount(testMonitor *mon, unsigned reset) +{ + unsigned count; + epicsMutexMustLock(testEvtLock); + count = mon->count; + if(reset) { + mon->count = 0; + epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */ + } + epicsMutexUnlock(testEvtLock); + return count; +} + diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index 80867dc9d..f6145d505 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -57,6 +57,26 @@ epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); +typedef struct testMonitor testMonitor; + +/* Begin monitoring the named PV for changes */ +epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt); +/* End monitoring */ +epicsShareFunc void testMonitorDestroy(testMonitor*); +/* Return immediately if it has been updated since create, last wait, + * or reset (count w/ reset=1). + * Otherwise, block until the value of the target PV is updated. + */ +epicsShareFunc void testMonitorWait(testMonitor*); +/* Return the number of monitor events which have occured since create, + * or a pervious reset (called reset=1). + * Calling w/ reset=0 only returns the count. + * Calling w/ reset=1 resets the count to zero and ensures that the next + * wait will block unless subsequent events occur. Returns the previous + * count. + */ +epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset); + #ifdef __cplusplus } #endif