From 8d044c558a5025d8a2a7e63d5b2819600e0a5f6a Mon Sep 17 00:00:00 2001 From: Sang-Woo Kim Date: Mon, 12 Oct 2026 07:54:48 +0900 Subject: [PATCH] dbNotify: honour processRequest in processNotifyCommon (#988) * dbNotify: honour processRequest in processNotifyCommon dbNotify.h:132-137 documents that a process request is issued when the requester asked for one and the record is passive, but 056304409 added that rule and the enumerator while testing only processGetRequest, so processRequest has always yielded no put, no process, and notifyOK. --- modules/database/src/ioc/db/dbNotify.c | 3 +- modules/database/test/ioc/db/Makefile | 7 + modules/database/test/ioc/db/dbNotifyTest.c | 144 ++++++++++++++++++ modules/database/test/ioc/db/dbNotifyTest.db | 7 + .../database/test/ioc/db/epicsRunDbTests.c | 2 + 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 modules/database/test/ioc/db/dbNotifyTest.c create mode 100644 modules/database/test/ioc/db/dbNotifyTest.db diff --git a/modules/database/src/ioc/db/dbNotify.c b/modules/database/src/ioc/db/dbNotify.c index 22f993555..60f7ef31b 100644 --- a/modules/database/src/ioc/db/dbNotify.c +++ b/modules/database/src/ioc/db/dbNotify.c @@ -245,7 +245,8 @@ static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) doProcess = 1; else - if (ppn->requestType == processGetRequest && + if ((ppn->requestType == processRequest || + ppn->requestType == processGetRequest) && precord->scan == 0) doProcess = 1; diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile index f6f009de7..43e73ad15 100644 --- a/modules/database/test/ioc/db/Makefile +++ b/modules/database/test/ioc/db/Makefile @@ -49,6 +49,13 @@ dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbScanTest.c TESTS += dbScanTest +TESTPROD_HOST += dbNotifyTest +dbNotifyTest_SRCS += dbNotifyTest.c +dbNotifyTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbNotifyTest.c +TESTFILES += ../dbNotifyTest.db +TESTS += dbNotifyTest + TESTPROD_HOST += dbShutdownTest dbShutdownTest_SRCS += dbShutdownTest.c dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp diff --git a/modules/database/test/ioc/db/dbNotifyTest.c b/modules/database/test/ioc/db/dbNotifyTest.c new file mode 100644 index 000000000..477c12e57 --- /dev/null +++ b/modules/database/test/ioc/db/dbNotifyTest.c @@ -0,0 +1,144 @@ +/*************************************************************************\ +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Which requestType leads dbProcessNotify() to process the record, per the + * three rules stated in dbNotify.h. + */ + +#include + +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsEvent.h" +#include "errlog.h" + +#include "dbUnitTest.h" +#include "testMain.h" + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +typedef struct { + processNotify notify; + epicsEventId done; + int puts; /* putCallback calls */ + int gets; /* getCallback calls */ + int didPut; /* what putCallback reports back */ + /* Read in doneCallback: dbNotifyCancel() overwrites notify.status. */ + notifyStatus status; + int wasProcessed; +} request; + +static int putCallback(processNotify *ppn, notifyPutType type) +{ + request *r = (request *) ppn->usrPvt; + epicsUInt8 one = 1; + + r->puts++; + if (!r->didPut) + return 0; + if (dbChannelPut(ppn->chan, DBR_UCHAR, &one, 1)) + ppn->status = notifyError; + return 1; +} + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + request *r = (request *) ppn->usrPvt; + epicsUInt8 scratch; + long count = 1; + + r->gets++; + dbChannelGet(ppn->chan, DBR_UCHAR, &scratch, NULL, &count, NULL); +} + +static void doneCallback(processNotify *ppn) +{ + request *r = (request *) ppn->usrPvt; + + r->status = ppn->status; + r->wasProcessed = ppn->wasProcessed; + epicsEventMustTrigger(r->done); +} + +/* One complete request against pv, run to its doneCallback. */ +static void run(request *r, const char *what, const char *pv, + notifyRequestType type, int didPut) +{ + memset(r, 0, sizeof(*r)); + r->didPut = didPut; + r->done = epicsEventMustCreate(epicsEventEmpty); + r->notify.requestType = type; + r->notify.putCallback = putCallback; + r->notify.getCallback = getCallback; + r->notify.doneCallback = doneCallback; + r->notify.usrPvt = r; + r->notify.chan = dbChannelCreate(pv); + if (!r->notify.chan || dbChannelOpen(r->notify.chan)) + testAbort("dbChannelCreate(\"%s\") failed", pv); + + testDiag("%s: %s", what, pv); + dbProcessNotify(&r->notify); + testOk(epicsEventWaitWithTimeout(r->done, 10.0) == epicsEventWaitOK, + "doneCallback ran"); + + dbNotifyCancel(&r->notify); + dbChannelDelete(r->notify.chan); + epicsEventDestroy(r->done); +} + +MAIN(dbNotifyTest) +{ + request r; + + testPlan(19); + + testdbPrepare(); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbNotifyTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + run(&r, "rule 1, process request on a passive record", + "passive.VAL", processRequest, 0); + testOk1(r.wasProcessed == 1); + testOk1(r.status == notifyOK); + testOk(r.puts == 0 && r.gets == 0, + "processRequest calls neither the put nor the get callback"); + + run(&r, "rule 1 does not apply when the record is not passive", + "scanned.VAL", processRequest, 0); + testOk1(r.wasProcessed == 0); + testOk1(r.status == notifyOK); + + run(&r, "rule 2, a put through PROC", + "passive.PROC", putProcessRequest, 1); + testOk1(r.wasProcessed == 1); + testOk1(r.puts == 1); + + run(&r, "rule 2 needs the put to have happened", + "passive.PROC", putProcessRequest, 0); + testOk1(r.wasProcessed == 0); + testOk1(r.puts == 1); + + run(&r, "rule 2 needs PROC or a process-passive field", + "passive.VAL", putProcessRequest, 1); + testOk1(r.wasProcessed == 0); + testOk1(r.puts == 1); + + run(&r, "rule 3, processGet on a passive record", + "passive.VAL", processGetRequest, 0); + testOk1(r.wasProcessed == 1); + testOk1(r.gets == 1); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/ioc/db/dbNotifyTest.db b/modules/database/test/ioc/db/dbNotifyTest.db new file mode 100644 index 000000000..0e7d2bf07 --- /dev/null +++ b/modules/database/test/ioc/db/dbNotifyTest.db @@ -0,0 +1,7 @@ +record(x, "passive") { +} + +record(x, "scanned") { + field(SCAN, "Event") + field(EVNT, "neverTriggered") +} diff --git a/modules/database/test/ioc/db/epicsRunDbTests.c b/modules/database/test/ioc/db/epicsRunDbTests.c index 6c7ce84c9..8d68ecf03 100644 --- a/modules/database/test/ioc/db/epicsRunDbTests.c +++ b/modules/database/test/ioc/db/epicsRunDbTests.c @@ -25,6 +25,7 @@ int dbServerTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); int dbScanTest(void); +int dbNotifyTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); @@ -48,6 +49,7 @@ void epicsRunDbTests(void) runTest(dbCaStatsTest); runTest(dbShutdownTest); runTest(dbScanTest); + runTest(dbNotifyTest); runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest);