/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * * Test the hooks that autosave uses during initialization */ #include "string.h" #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" #include "link.h" #include "recSup.h" #include "iocsh.h" #include "dbAccess.h" #include "dbConvert.h" #include "dbStaticLib.h" #include "registry.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "osiFileName.h" #include "initHooks.h" #include "devSup.h" #include "errlog.h" #include "aoRecord.h" #include "waveformRecord.h" #include "epicsExport.h" static unsigned iran; static int checkGetString(DBENTRY *pent, const char *expect) { dbCommon *prec = pent->precnode->precord; const char *actual = dbGetString(pent); int ret = strcmp(actual, expect); testOk(ret==0, "dbGetString(\"%s.%s\") -> '%s' == '%s'", prec->name, pent->pflddes->name, actual, expect); return ret; } static void hookPass0(initHookState state) { DBENTRY entry; if(state!=initHookAfterInitDevSup) return; testDiag("initHookAfterInitDevSup"); dbInitEntry(pdbbase, &entry); testDiag("restore integer pass0"); /* rec0.VAL is initially 1, set it to 2 */ if(dbFindRecord(&entry, "rec0.VAL")==0) { aoRecord *prec = entry.precnode->precord; testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); checkGetString(&entry, "1"); testOk1(dbPutString(&entry, "2")==0); testOk(prec->val==2, "VAL %d==2", (int)prec->val); checkGetString(&entry, "2"); } else { testFail("Missing rec0"); testSkip(4, "missing record"); } testDiag("restore string pass0"); if(dbFindRecord(&entry, "rec0.DESC")==0) { aoRecord *prec = entry.precnode->precord; testOk1(strcmp(prec->desc, "foobar")==0); checkGetString(&entry, "foobar"); testOk1(dbPutString(&entry, "hello")==0); testOk1(strcmp(prec->desc, "hello")==0); checkGetString(&entry, "hello"); } else { testFail("Missing rec0"); testSkip(4, "missing record"); } if(dbFindRecord(&entry, "rec1.DESC")==0) { aoRecord *prec = entry.precnode->precord; testOk1(strcmp(prec->desc, "")==0); checkGetString(&entry, ""); testOk1(dbPutString(&entry, "world")==0); testOk1(strcmp(prec->desc, "world")==0); checkGetString(&entry, "world"); } else { testFail("Missing rec1"); testSkip(4, "missing record"); } testDiag("restore link pass0"); /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ if(dbFindRecord(&entry, "rec0.OUT")==0) { aoRecord *prec = entry.precnode->precord; if(prec->out.type==CONSTANT) testOk(strcmp(prec->out.text,"rec0.DISV")==0, "%s==rec0.DISV (initial value from .db)", prec->out.text); else testFail("Wrong link type: %d", (int)prec->out.type); checkGetString(&entry, "rec0.DISV"); testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* rec0.SDIS is initially NULL, set it to "rec0.STAT" */ if(dbFindRecord(&entry, "rec0.SDIS")==0) { aoRecord *prec = entry.precnode->precord; if(prec->sdis.type==CONSTANT) testOk1(prec->sdis.value.constantStr==NULL); else testFail("Wrong link type: %d", (int)prec->sdis.type); testOk1(dbPutString(&entry, "rec0.STAT")==0); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* can't restore array field in pass0 */ dbFinishEntry(&entry); } static long initRecAO(dbCommon *pr) { aoRecord *prec = (aoRecord *) pr; DBLINK *plink = &prec->out; testDiag("init_record(%s)", prec->name); testOk(prec->val==2, "VAL %d==2 (pass0 value)", (int)prec->val); prec->val = 3; testOk(prec->val==3, "VAL %d==3", (int)prec->val); testOk1(plink->type==DB_LINK); if(plink->type==DB_LINK) testOk(strcmp(plink->value.pv_link.pvname,"rec0.SEVR")==0, "%s==rec0.SEVR (pass0 value)", plink->value.pv_link.pvname); else testFail("Wrong link type"); plink = &prec->sdis; testOk1(plink->type==DB_LINK); if(plink->type==DB_LINK) testOk(strcmp(plink->value.pv_link.pvname,"rec0.STAT")==0, "%s==rec0.STAT (pass0 value)", plink->value.pv_link.pvname); else testFail("Wrong link type"); iran |= 1; return 2; /* we set .VAL, so don't use RVAL */ } static long initRecWF(dbCommon *pr) { waveformRecord *prec = (waveformRecord *) pr; testDiag("init_record(%s)", prec->name); testOk(prec->nord==0, "NORD %d==0", (int)prec->nord); iran |= 2; return 0; } static double values[] = {1,2,3,4,5}; static void hookPass1(initHookState state) { DBENTRY entry; DBADDR addr; if(state!=initHookAfterInitDatabase) return; testDiag("initHookAfterInitDatabase"); dbInitEntry(pdbbase, &entry); if(dbFindRecord(&entry, "rec0.VAL")==0) { aoRecord *prec = entry.precnode->precord; testOk(prec->val==3, "VAL %d==3 (init_record value)", (int)prec->val); testOk1(dbPutString(&entry, "4")==0); testOk(prec->val==4, "VAL %d==4", (int)prec->val); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* Can't restore links in pass 1 */ if(dbNameToAddr("rec1.VAL", &addr)) { testFail("missing rec1"); testSkip(3, "missing record"); } else { rset *prset = dbGetRset(&addr); dbfType ftype = addr.field_type; long count=-1, offset=-1, maxcount = addr.no_elements; testOk1(prset && prset->get_array_info && prset->put_array_info); testOk1((*prset->get_array_info)(&addr, &count, &offset)==0); /* count is ignored */ testOk1((*dbPutConvertRoutine[DBF_DOUBLE][ftype])(&addr, values, NELEMENTS(values), maxcount,offset)==0); testOk1((*prset->put_array_info)(&addr, NELEMENTS(values))==0); } dbFinishEntry(&entry); } #if defined(__rtems__) || defined(vxWorks) void asTestIoc_registerRecordDeviceDriver(struct dbBase *); #endif epicsShareFunc void testRestore(void) { aoRecord *rec0; waveformRecord *rec1; testDiag("test Restore"); initHookRegister(hookPass0); initHookRegister(hookPass1); testdbPrepare(); testdbReadDatabase("asTestIoc.dbd", NULL, NULL); /* since this test has device support it must appear in a * DLL for windows dynamic builds. * However, the rRDD function is in the executable, * and not accessible here. So use iocsh. * For rtems/vxworks the test harness clears * iocsh registrations, so iocsh can't work here. */ #if defined(__rtems__) || defined(vxWorks) asTestIoc_registerRecordDeviceDriver(pdbbase); #else iocshCmd("asTestIoc_registerRecordDeviceDriver(pdbbase)"); #endif testdbReadDatabase("asTest.db", NULL, NULL); rec0 = (aoRecord*)testdbRecordPtr("rec0"); rec1 = (waveformRecord*)testdbRecordPtr("rec1"); eltc(0); testIocInitOk(); eltc(1); testDiag("Post initialization"); testOk1(iran==3); testOk1(rec0->val==4); testOk1(rec1->nord==5); { double *buf = rec1->bptr; testOk(buf[0]==1, "buf[0] %f==1", buf[0]); testOk1(buf[1]==2); testOk1(buf[2]==3); testOk1(buf[3]==4); testOk1(buf[4]==5); } testIocShutdownOk(); /* recSup doesn't cleanup after itself */ free(rec1->bptr); testdbCleanup(); } static long write_ao(struct aoRecord *prec) {return 0;} static long read_wf(struct waveformRecord *prec) {return 0;} static struct aodset devAOasTest = { {6, NULL, NULL, initRecAO, NULL}, write_ao, NULL }; static struct wfdset devWFasTest = { {5, NULL, NULL, initRecWF, NULL}, read_wf }; epicsExportAddress(dset, devAOasTest); epicsExportAddress(dset, devWFasTest);