Merged Michael's dbunittestmonitor branch
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
17
src/std/rec/test/linkRetargetLink.db
Normal file
17
src/std/rec/test/linkRetargetLink.db
Normal 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")
|
||||
}
|
||||
86
src/std/rec/test/linkRetargetLinkTest.c
Normal file
86
src/std/rec/test/linkRetargetLinkTest.c
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user