diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index 23ebe705e..3ce50d9f2 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -33,6 +33,12 @@ arrTest_LIBS += dbStaticIoc OBJS_IOC_vxWorks += arrTest TESTS += arrTest +TESTPROD_HOST += syncTest +syncTest_SRCS += syncTest.c +syncTest_LIBS += dbStaticHost +OBJS_IOC_vxWorks += syncTest +TESTS += syncTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/filters/test/syncTest.c b/src/ioc/db/filters/test/syncTest.c new file mode 100644 index 000000000..11ecbe04e --- /dev/null +++ b/src/ioc/db/filters/test/syncTest.c @@ -0,0 +1,356 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "dbState.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_syncInitialize)(void); + +static db_field_log fl; +static dbStateId red; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { + struct dbCommon *prec = dbChannelRecord(chan); + + pfl->ctx = dbfl_context_event; + pfl->type = dbfl_type_val; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = DBF_LONG; + pfl->no_elements = 1; + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pfl->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); + pfl->u.v.field.dbf_long = val; +} + +static void changeValue(db_field_log *pfl2, long val) { + pfl2->u.v.field.dbf_long = val; +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "filter drops field_log (%s)", m); +} + +static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { + testDiag("%s: filter must pass twice", m); + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); +} + +static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(old == pfl, "filter passes previous field log (%s)", m); +} + +static void mustPass(dbChannel *pch, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(cur == pfl, "filter passes field_log (%s)", m); +} + +static void checkCtxRead(dbChannel *pch, dbStateId id) { + fl.ctx = dbfl_context_read; + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + dbStateSet(id); + mustPassTwice(pch, &fl, "ctx='read', state=TRUE"); + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + fl.ctx = dbfl_context_event; +} + +static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { + ELLNODE *node; + chFilter *filter; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + + testDiag("Test filter structure and open channel"); + + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + db_field_log fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin sync opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), + "sync has one filter with argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain"); + + checkCtxRead(pch, red); +} + +//MAIN(syncTest) +main() +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char myname[] = "sync"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log *pfl[10]; + db_field_log fl1; + int i; + + testPlan(0); + + db_init_events(); + + testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + (*pvar_func_syncInitialize)(); /* manually initialize plugin */ + + testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); + testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); + + /* nonexisting state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"blue\"}}")), + "dbChannel with sync (m='while' s='blue') (nonex state) failed"); + /* missing state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\"}}")), + "dbChannel with sync (m='while') (no state) failed"); + /* missing mode */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"s\":\"red\"}}")), + "dbChannel with sync (s='red') (no mode) failed"); + + /* mode WHILE */ + + testHead("Mode WHILE (m='while', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='while' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustPass(pch, pfl[4], "state=TRUE, log4"); + mustPass(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode UNLESS */ + + testHead("Mode UNLESS (m='unless', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"unless\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='unless' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustPass(pch, pfl[0], "state=FALSE, log0"); + mustPass(pch, pfl[1], "state=FALSE, log1"); + mustPass(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustPass(pch, pfl[7], "state=FALSE, log7"); + mustPass(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode BEFORE */ + + testHead("Mode BEFORE (m='before', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"before\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='before' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + mustDrop(pch, pfl[6], "state=TRUE, log6"); + dbStateClear(red); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode FIRST */ + + testHead("Mode FIRST (m='first', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"first\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='first' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode LAST */ + + testHead("Mode LAST (m='last', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"last\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='last' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode AFTER */ + + testHead("Mode AFTER (m='after', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"after\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='after' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +}