Merged Michael's dbunittestmonitor branch

This commit is contained in:
Andrew Johnson
2016-01-07 13:22:43 -06:00
6 changed files with 245 additions and 1 deletions

View File

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

View File

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

View File

@@ -31,6 +31,13 @@ testHarness_SRCS += arrayOpTest.c
TESTFILES += ../arrayOpTest.db
TESTS += arrayOpTest
TESTPROD_HOST += linkRetargetLinkTest
linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c
linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += linkRetargetLinkTest.c
TESTFILES += ../linkRetargetLink.db
TESTS += linkRetargetLinkTest
TARGETS += $(COMMON_DIR)/asTestIoc.dbd
asTestIoc_DBD += base.dbd
asTestIoc_DBD += asTest.dbd

View File

@@ -15,6 +15,7 @@
int analogMonitorTest(void);
int arrayOpTest(void);
int asTest(void);
int linkRetargetLinkTest(void);
void epicsRunRecordTests(void)
{
@@ -26,5 +27,7 @@ void epicsRunRecordTests(void)
runTest(asTest);
runTest(linkRetargetLinkTest);
epicsExit(0); /* Trigger test harness */
}

View File

@@ -0,0 +1,17 @@
record(ai, "rec:ai") {
field(INP, "0")
}
record(ai, "rec:src1") {
field(VAL, "1")
}
record(ai, "rec:src2") {
field(VAL, "2")
}
record(stringout, "rec:link1") {
field(VAL, "rec:src1")
field(OUT, "rec:ai.INP CA")
}
record(stringout, "rec:link2") {
field(VAL, "rec:src2 CP")
field(OUT, "rec:ai.INP CA")
}

View File

@@ -0,0 +1,86 @@
/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Michael Davidsaver <mdavidsaver@gmail.com>
*
* Test using several stringout records to retarget the link of another record
*/
#include <string.h>
#include "dbAccess.h"
#include "dbUnitTest.h"
#include "errlog.h"
#include "epicsThread.h"
#include "testMain.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void testRetarget(void)
{
testMonitor *lnkmon, *valmon;
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("linkRetargetLink.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0);
valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0);
/* initially rec:ai.INP is CONSTANT */
testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0);
testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "0");
/* rec:ai.INP becomes DB_LINK, but no processing is triggered */
testdbPutFieldOk("rec:link1.PROC", DBF_LONG, 0);
testMonitorWait(lnkmon);
testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0);
testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src1 NPP NMS");
/* trigger a read from rec:ai.INP */
testdbPutFieldOk("rec:ai.PROC", DBF_LONG, 0);
testMonitorWait(valmon);
testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 1.0);
/* rec:ai.INP becomes CA_LINK w/ CP, processing is triggered */
testdbPutFieldOk("rec:link2.PROC", DBF_LONG, 0);
testMonitorWait(lnkmon);
testMonitorWait(valmon);
testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 2.0);
testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src2 CP NMS");
testMonitorDestroy(lnkmon);
testMonitorDestroy(valmon);
testIocShutdownOk();
testdbCleanup();
}
MAIN(linkRetargetLinkTest)
{
testPlan(10);
testRetarget();
return testDone();
}