Merge branch 'database/master' back
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP = ../../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TESTLIBRARY = Recs
|
||||
|
||||
Recs_SRCS += xRecord.c
|
||||
Recs_SRCS += arrRecord.c
|
||||
Recs_LIBS += dbCore ca Com
|
||||
|
||||
PROD_LIBS = Recs dbRecStd dbCore ca Com
|
||||
|
||||
DBDDEPENDS_FILES += filterTest.dbd$(DEP)
|
||||
TARGETS += $(COMMON_DIR)/filterTest.dbd
|
||||
filterTest_DBD += menuGlobal.dbd
|
||||
filterTest_DBD += menuConvert.dbd
|
||||
filterTest_DBD += menuScan.dbd
|
||||
filterTest_DBD += filters.dbd
|
||||
filterTest_DBD += xRecord.dbd
|
||||
filterTest_DBD += arrRecord.dbd
|
||||
TESTFILES += $(COMMON_DIR)/filterTest.dbd
|
||||
|
||||
testHarness_SRCS += filterTest_registerRecordDeviceDriver.cpp
|
||||
|
||||
TESTPROD_HOST += tsTest
|
||||
tsTest_SRCS += tsTest.c
|
||||
tsTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += tsTest.c
|
||||
TESTFILES += ../xRecord.db
|
||||
TESTS += tsTest
|
||||
|
||||
TESTPROD_HOST += dbndTest
|
||||
dbndTest_SRCS += dbndTest.c
|
||||
dbndTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbndTest.c
|
||||
TESTS += dbndTest
|
||||
|
||||
TESTPROD_HOST += arrTest
|
||||
arrTest_SRCS += arrTest.cpp
|
||||
arrTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += arrTest.cpp
|
||||
TESTFILES += ../arrTest.db
|
||||
TESTS += arrTest
|
||||
|
||||
TESTPROD_HOST += syncTest
|
||||
syncTest_SRCS += syncTest.c
|
||||
syncTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += syncTest.c
|
||||
TESTS += syncTest
|
||||
|
||||
# epicsRunFilterTests runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunFilterTests.c
|
||||
|
||||
filterTestHarness_SRCS += $(testHarness_SRCS)
|
||||
filterTestHarness_SRCS_RTEMS += rtemsTestHarness.c
|
||||
|
||||
PROD_vxWorks = filterTestHarness
|
||||
PROD_RTEMS = filterTestHarness
|
||||
|
||||
TESTSPEC_vxWorks = filterTestHarness.munch; epicsRunFilterTests
|
||||
TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
tsTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
dbndTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
syncTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
arrTest$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
@@ -0,0 +1,145 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* arrRecord.c - minimal array record for test purposes: no processing */
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* vaguely implemented like parts of recWaveform.c by Bob Dalesio
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "cantProceed.h"
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "arrRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(struct dbCommon *, int);
|
||||
static long process(struct dbCommon *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
static long cvt_dbaddr(DBADDR *);
|
||||
static long get_array_info(DBADDR *, long *, long *);
|
||||
static long put_array_info(DBADDR *, long);
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
#define get_graphic_double NULL
|
||||
#define get_control_double NULL
|
||||
#define get_alarm_double NULL
|
||||
|
||||
rset arrRSET = {
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset, arrRSET);
|
||||
|
||||
static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
struct arrRecord *prec = (struct arrRecord *)pcommon;
|
||||
|
||||
if (pass == 0) {
|
||||
if (prec->nelm <= 0)
|
||||
prec->nelm = 1;
|
||||
if (prec->ftvl > DBF_ENUM)
|
||||
prec->ftvl = DBF_UCHAR;
|
||||
prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
|
||||
"arr calloc failed");
|
||||
|
||||
if (prec->nelm == 1) {
|
||||
prec->nord = 1;
|
||||
} else {
|
||||
prec->nord = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long process(struct dbCommon *pcommon)
|
||||
{
|
||||
struct arrRecord *prec = (struct arrRecord *)pcommon;
|
||||
|
||||
if(prec->clbk)
|
||||
(*prec->clbk)(prec);
|
||||
prec->pact = TRUE;
|
||||
recGblGetTimeStamp(prec);
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->nelm;
|
||||
paddr->field_type = prec->ftvl;
|
||||
paddr->field_size = dbValueSize(prec->ftvl);
|
||||
paddr->dbr_field_type = prec->ftvl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
*no_elements = prec->nord;
|
||||
*offset = prec->off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long put_array_info(DBADDR *paddr, long nNew)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
prec->nord = nNew;
|
||||
if (prec->nord > prec->nelm)
|
||||
prec->nord = prec->nelm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
include "menuGlobal.dbd"
|
||||
include "menuConvert.dbd"
|
||||
include "menuScan.dbd"
|
||||
recordtype(arr) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL, DBF_NOACCESS) {
|
||||
prompt("Value")
|
||||
special(SPC_DBADDR)
|
||||
pp(TRUE)
|
||||
extra("void *val")
|
||||
}
|
||||
field(NELM, DBF_ULONG) {
|
||||
prompt("Number of Elements")
|
||||
special(SPC_NOMOD)
|
||||
initial("1")
|
||||
}
|
||||
field(FTVL, DBF_MENU) {
|
||||
prompt("Field Type of Value")
|
||||
special(SPC_NOMOD)
|
||||
menu(menuFtype)
|
||||
}
|
||||
field(NORD, DBF_ULONG) {
|
||||
prompt("Number elements read")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(OFF, DBF_ULONG) {
|
||||
prompt("Offset into array")
|
||||
}
|
||||
field(BPTR, DBF_NOACCESS) {
|
||||
prompt("Buffer Pointer")
|
||||
special(SPC_NOMOD)
|
||||
extra("void *bptr")
|
||||
}
|
||||
field(INP, DBF_INLINK) {
|
||||
prompt("Input Link")
|
||||
}
|
||||
field(CLBK, DBF_NOACCESS) {
|
||||
prompt("Processing callback")
|
||||
special(SPC_NOMOD)
|
||||
extra("void (*clbk)(struct arrRecord*)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2003 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to the Software License Agreement
|
||||
* found in the file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
/* using stuff from softIoc.cpp by Andrew Johnson */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "registryFunction.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "envDefs.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
#include "registry.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbAccess.h"
|
||||
#include "asDbLib.h"
|
||||
#include "iocInit.h"
|
||||
#include "iocsh.h"
|
||||
#include "dbChannel.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#include "arrRecord.h"
|
||||
|
||||
extern "C" {
|
||||
void filterTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
}
|
||||
|
||||
#define CA_SERVER_PORT "65535"
|
||||
|
||||
#define PATTERN 0x55
|
||||
|
||||
const char *server_port = CA_SERVER_PORT;
|
||||
|
||||
static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
|
||||
for (int i = 0; i < pfl1->no_elements; i++) {
|
||||
switch (type) {
|
||||
case DBR_DOUBLE:
|
||||
if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
|
||||
testDiag("at index=%d: field log has %g, should be %d",
|
||||
i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case DBR_LONG:
|
||||
if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
|
||||
testDiag("at index=%d: field log has %d, should be %d",
|
||||
i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case DBR_STRING:
|
||||
if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
|
||||
testDiag("at index=%d: field log has '%s', should be '%d'",
|
||||
i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) {
|
||||
ELLNODE *node;
|
||||
char name[80];
|
||||
|
||||
strncpy(name, chan, sizeof(name)-1);
|
||||
strncat(name, json, sizeof(name)-strlen(name)-1);
|
||||
|
||||
testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type);
|
||||
testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no);
|
||||
|
||||
testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened");
|
||||
|
||||
node = ellFirst(&(*pch)->pre_chain);
|
||||
(void) CONTAINER(node, chFilter, pre_node);
|
||||
testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain");
|
||||
|
||||
node = ellFirst(&(*pch)->post_chain);
|
||||
(void) CONTAINER(node, chFilter, post_node);
|
||||
testOk((ellCount(&(*pch)->post_chain) == no),
|
||||
"arr has %d filter(s) in post chain", no);
|
||||
}
|
||||
|
||||
static void testHead (const char *title, const char *typ = "") {
|
||||
const char *line = "------------------------------------------------------------------------------";
|
||||
testDiag("%s", line);
|
||||
testDiag(title, typ);
|
||||
testDiag("%s", line);
|
||||
}
|
||||
|
||||
#define TEST1(Size, Offset, Incr, Text) \
|
||||
testDiag("Offset: %d (%s)", Offset, Text); \
|
||||
off = Offset; \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
|
||||
pfl2 = dbChannelRunPostChain(pch, pfl); \
|
||||
testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
|
||||
testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
|
||||
testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
static void check(short dbr_type) {
|
||||
dbChannel *pch;
|
||||
db_field_log *pfl, *pfl2;
|
||||
dbAddr valaddr;
|
||||
dbAddr offaddr;
|
||||
const char *offname = NULL, *valname = NULL, *typname = NULL;
|
||||
epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19};
|
||||
epicsInt32 *ar10_0_1 = ar;
|
||||
epicsInt32 ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13};
|
||||
epicsInt32 ar5_0_1[10] = {12,13,14,15,16};
|
||||
epicsInt32 ar5_3_1[10] = {15,16,17,18,19};
|
||||
epicsInt32 ar5_5_1[10] = {17,18,19,10,11};
|
||||
epicsInt32 ar5_9_1[10] = {11,12,13,14,15};
|
||||
epicsInt32 ar5_0_2[10] = {12,14,16};
|
||||
epicsInt32 ar5_3_2[10] = {15,17,19};
|
||||
epicsInt32 ar5_5_2[10] = {17,19,11};
|
||||
epicsInt32 ar5_9_2[10] = {11,13,15};
|
||||
epicsInt32 ar5_0_3[10] = {12,15};
|
||||
epicsInt32 ar5_3_3[10] = {15,18};
|
||||
epicsInt32 ar5_5_3[10] = {17,10};
|
||||
epicsInt32 ar5_9_3[10] = {11,14};
|
||||
epicsInt32 off = 0;
|
||||
|
||||
switch (dbr_type) {
|
||||
case DBR_LONG:
|
||||
offname = "x.OFF";
|
||||
valname = "x.VAL";
|
||||
typname = "long";
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
offname = "y.OFF";
|
||||
valname = "y.VAL";
|
||||
typname = "double";
|
||||
break;
|
||||
case DBR_STRING:
|
||||
offname = "z.OFF";
|
||||
valname = "z.VAL";
|
||||
typname = "string";
|
||||
break;
|
||||
default:
|
||||
testDiag("Invalid data type %d", dbr_type);
|
||||
}
|
||||
|
||||
(void) dbNameToAddr(offname, &offaddr);
|
||||
|
||||
(void) dbNameToAddr(valname, &valaddr);
|
||||
(void) dbPutField(&valaddr, DBR_LONG, ar, 10);
|
||||
|
||||
/* Default: should not change anything */
|
||||
|
||||
testHead("Ten %s elements from rec, increment 1, full size (default)", typname);
|
||||
createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
TEST1(10, 0, 1, "no offset");
|
||||
TEST1(10, 4, 1, "wrapped");
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname);
|
||||
createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
TEST1(10, 4, 1, "wrapped");
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname);
|
||||
createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
TEST1(10, 4, 1, "wrapped");
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname);
|
||||
createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
TEST1(10, 4, 1, "wrapped");
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname);
|
||||
createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
TEST1(10, 4, 1, "wrapped");
|
||||
dbChannelDelete(pch);
|
||||
|
||||
#define TEST5(Incr, Left, Right, Type) \
|
||||
testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \
|
||||
createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
|
||||
"(" #Left ":" #Incr ":" #Right ")", &pch, 1); \
|
||||
testOk(pch->final_type == valaddr.field_type, \
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
|
||||
testOk(pch->final_no_elements == 4 / Incr + 1, \
|
||||
"final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
|
||||
TEST1(5, 0, Incr, "no offset"); \
|
||||
TEST1(5, 3, Incr, "from upper block"); \
|
||||
TEST1(5, 5, Incr, "wrapped"); \
|
||||
TEST1(5, 9, Incr, "from lower block"); \
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* Contiguous block of 5 */
|
||||
|
||||
TEST5(1, 2, 6, "regular");
|
||||
TEST5(1, -8, 6, "left side from-end");
|
||||
TEST5(1, 2, -4, "right side from-end");
|
||||
TEST5(1, -8, -4, "both sides from-end");
|
||||
|
||||
/* 5 elements with increment 2 */
|
||||
|
||||
TEST5(2, 2, 6, "regular");
|
||||
TEST5(2, -8, 6, "left side from-end");
|
||||
TEST5(2, 2, -4, "right side from-end");
|
||||
TEST5(2, -8, -4, "both sides from-end");
|
||||
|
||||
/* 5 elements with increment 3 */
|
||||
|
||||
TEST5(3, 2, 6, "regular");
|
||||
TEST5(3, -8, 6, "left side from-end");
|
||||
TEST5(3, 2, -4, "right side from-end");
|
||||
TEST5(3, -8, -4, "both sides from-end");
|
||||
|
||||
/* From buffer (plugin chain) */
|
||||
|
||||
#define TEST5B(Incr, Left, Right, Type) \
|
||||
testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \
|
||||
createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
|
||||
"(" #Left ":" #Incr ":" #Right ")", &pch, 2); \
|
||||
testOk(pch->final_type == valaddr.field_type, \
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
|
||||
testOk(pch->final_no_elements == 4 / Incr + 1, \
|
||||
"final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
|
||||
TEST1(5, 0, Incr, "no offset"); \
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* Contiguous block of 5 */
|
||||
|
||||
TEST5B(1, 2, 6, "regular");
|
||||
TEST5B(1, -8, 6, "left side from-end");
|
||||
TEST5B(1, 2, -4, "right side from-end");
|
||||
TEST5B(1, -8, -4, "both sides from-end");
|
||||
|
||||
/* 5 elements with increment 2 */
|
||||
|
||||
TEST5B(2, 2, 6, "regular");
|
||||
TEST5B(2, -8, 6, "left side from-end");
|
||||
TEST5B(2, 2, -4, "right side from-end");
|
||||
TEST5B(2, -8, -4, "both sides from-end");
|
||||
|
||||
/* 5 elements with increment 3 */
|
||||
|
||||
TEST5B(3, 2, 6, "regular");
|
||||
TEST5B(3, -8, 6, "left side from-end");
|
||||
TEST5B(3, 2, -4, "right side from-end");
|
||||
TEST5B(3, -8, -4, "both sides from-end");
|
||||
}
|
||||
|
||||
MAIN(arrTest)
|
||||
{
|
||||
dbEventCtx evtctx;
|
||||
const chFilterPlugin *plug;
|
||||
char arr[] = "arr";
|
||||
|
||||
testPlan(1402);
|
||||
|
||||
/* Prepare the IOC */
|
||||
|
||||
epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("filterTest.dbd", NULL, NULL);
|
||||
|
||||
filterTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("arrTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
/* Start the IOC */
|
||||
|
||||
evtctx = db_init_events();
|
||||
|
||||
testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly");
|
||||
|
||||
check(DBR_LONG);
|
||||
check(DBR_DOUBLE);
|
||||
check(DBR_STRING);
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
record(arr, "x") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "LONG")
|
||||
}
|
||||
record(arr, "y") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "DOUBLE")
|
||||
}
|
||||
record(arr, "z") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "STRING")
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*************************************************************************\
|
||||
* 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 <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbCommon.h"
|
||||
#include "registry.h"
|
||||
#include "errlog.h"
|
||||
#include "chfPlugin.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbmf.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#define PATTERN 0x55
|
||||
|
||||
void filterTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static db_field_log fl;
|
||||
|
||||
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) {
|
||||
struct dbCommon *prec = dbChannelRecord(chan);
|
||||
|
||||
pfl->ctx = dbfl_context_read;
|
||||
pfl->type = dbfl_type_val;
|
||||
pfl->stat = prec->stat;
|
||||
pfl->sevr = prec->sevr;
|
||||
pfl->time = prec->time;
|
||||
pfl->field_type = dbChannelFieldType(chan);
|
||||
pfl->no_elements = dbChannelElements(chan);
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
static void changeValue(db_field_log *pfl2, long val) {
|
||||
pfl2->u.v.field.dbf_long = val;
|
||||
testDiag("new value: %ld", val);
|
||||
}
|
||||
|
||||
static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
|
||||
db_field_log *pfl;
|
||||
|
||||
changeValue(pfl2, val);
|
||||
testDiag("mode=%s delta=%g filter must pass once", m, d);
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
|
||||
testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data");
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(NULL == pfl, "call 2 drops field_log");
|
||||
}
|
||||
|
||||
static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
|
||||
db_field_log *pfl;
|
||||
|
||||
changeValue(pfl2, val);
|
||||
testDiag("mode=%s delta=%g filter must drop", m, d);
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(NULL == pfl, "call 1 drops field_log");
|
||||
}
|
||||
|
||||
static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
|
||||
db_field_log *pfl;
|
||||
|
||||
changeValue(pfl2, val);
|
||||
testDiag("mode=%s delta=%g filter must pass twice", m, d);
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
|
||||
testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data");
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
|
||||
testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data");
|
||||
}
|
||||
|
||||
static void testHead (char* title) {
|
||||
testDiag("--------------------------------------------------------");
|
||||
testDiag("%s", title);
|
||||
testDiag("--------------------------------------------------------");
|
||||
}
|
||||
|
||||
MAIN(dbndTest)
|
||||
{
|
||||
dbChannel *pch;
|
||||
chFilter *filter;
|
||||
const chFilterPlugin *plug;
|
||||
char dbnd[] = "dbnd";
|
||||
ELLNODE *node;
|
||||
chPostEventFunc *cb_out = NULL;
|
||||
void *arg_out = NULL;
|
||||
db_field_log *pfl2;
|
||||
db_field_log fl1;
|
||||
dbEventCtx evtctx;
|
||||
|
||||
testPlan(59);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("filterTest.dbd", NULL, NULL);
|
||||
|
||||
filterTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
evtctx = db_init_events();
|
||||
|
||||
testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly");
|
||||
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created");
|
||||
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
|
||||
|
||||
memset(&fl, PATTERN, sizeof(fl));
|
||||
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 dbnd opened");
|
||||
node = ellFirst(&pch->pre_chain);
|
||||
filter = CONTAINER(node, chFilter, pre_node);
|
||||
testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL),
|
||||
"dbnd has one filter with argument in pre chain");
|
||||
testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
|
||||
|
||||
/* Field logs of type ref and rec: pass any update */
|
||||
|
||||
testHead("Field logs of type ref and rec");
|
||||
fl1.type = dbfl_type_rec;
|
||||
mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
|
||||
|
||||
fl1.type = dbfl_type_ref;
|
||||
mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
|
||||
|
||||
/* Delta = 0: pass any change */
|
||||
|
||||
testHead("Delta = 0: pass any change");
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "abs", 0., 0);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "abs", 0., 1);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* Delta = -1: pass any update */
|
||||
|
||||
testHead("Delta = -1: pass any update");
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":-1.0}}")), "dbChannel with plugin dbnd (delta=-1) created");
|
||||
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassTwice(pch, pfl2, "abs", -1., 0);
|
||||
mustPassTwice(pch, pfl2, "abs", -1., 1);
|
||||
|
||||
db_delete_field_log(pfl2);
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* Delta = absolute */
|
||||
|
||||
testHead("Delta = absolute");
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":3}}")), "dbChannel with plugin dbnd (delta=3) created");
|
||||
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "abs", 3., 1);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustDrop(pch, pfl2, "abs", 3., 3);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustDrop(pch, pfl2, "abs", 3., 4);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "abs", 3., 5);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* Delta = relative */
|
||||
|
||||
testHead("Delta = relative");
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"m\":\"rel\",\"d\":50}}")),
|
||||
"dbChannel with plugin dbnd (mode=rel, delta=50) created");
|
||||
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened");
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "rel", 50., 1);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "rel", 50., 2);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustDrop(pch, pfl2, "rel", 50., 3);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "rel", 50., 4);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustDrop(pch, pfl2, "rel", 50., 5);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustDrop(pch, pfl2, "rel", 50., 6);
|
||||
|
||||
pfl2 = db_create_read_log(pch);
|
||||
testDiag("new field_log from record");
|
||||
fl_setup(pch, pfl2);
|
||||
|
||||
mustPassOnce(pch, pfl2, "rel", 50., 7);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Run filter tests as a batch.
|
||||
*/
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
#include "epicsExit.h"
|
||||
#include "dbmf.h"
|
||||
|
||||
int tsTest(void);
|
||||
int dbndTest(void);
|
||||
int syncTest(void);
|
||||
int arrTest(void);
|
||||
|
||||
void epicsRunFilterTests(void)
|
||||
{
|
||||
testHarness();
|
||||
|
||||
runTest(tsTest);
|
||||
runTest(dbndTest);
|
||||
runTest(syncTest);
|
||||
runTest(arrTest);
|
||||
|
||||
dbmfFreeChunks();
|
||||
|
||||
epicsExit(0); /* Trigger test harness */
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
extern void epicsRunFilterTests(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
epicsRunFilterTests(); /* calls epicsExit(0) */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
/*************************************************************************\
|
||||
* 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 <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbChannel.h"
|
||||
#include "registry.h"
|
||||
#include "chfPlugin.h"
|
||||
#include "errlog.h"
|
||||
#include "dbmf.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbState.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#define PATTERN 0x55
|
||||
|
||||
void filterTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
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 testHead (char* title) {
|
||||
testDiag("--------------------------------------------------------");
|
||||
testDiag("%s", 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) {
|
||||
db_field_log *pfl;
|
||||
|
||||
testDiag("%s: filter must pass twice", m);
|
||||
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;
|
||||
db_field_log fl1;
|
||||
|
||||
testDiag("Test filter structure and open channel");
|
||||
|
||||
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
|
||||
|
||||
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)
|
||||
{
|
||||
dbChannel *pch;
|
||||
const chFilterPlugin *plug;
|
||||
char myname[] = "sync";
|
||||
db_field_log *pfl[10];
|
||||
int i;
|
||||
dbEventCtx evtctx;
|
||||
|
||||
testPlan(139);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("filterTest.dbd", NULL, NULL);
|
||||
|
||||
filterTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
evtctx = db_init_events();
|
||||
|
||||
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]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* 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]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* 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");
|
||||
|
||||
db_delete_field_log(pfl[2]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* 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");
|
||||
|
||||
db_delete_field_log(pfl[3]);
|
||||
db_delete_field_log(pfl[9]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* 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");
|
||||
|
||||
db_delete_field_log(pfl[5]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
/* 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");
|
||||
|
||||
db_delete_field_log(pfl[6]);
|
||||
db_delete_field_log(pfl[9]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*************************************************************************\
|
||||
* 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 <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "chfPlugin.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "registry.h"
|
||||
#include "dbmf.h"
|
||||
#include "epicsTime.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#define PATTERN 0x55
|
||||
|
||||
void filterTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static db_field_log fl;
|
||||
|
||||
static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
|
||||
return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
|
||||
}
|
||||
|
||||
static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) {
|
||||
db_field_log fl1 = *pfl1;
|
||||
|
||||
fl1.time = pfl2->time;
|
||||
return fl_equal(&fl1, pfl2);
|
||||
}
|
||||
|
||||
MAIN(tsTest)
|
||||
{
|
||||
dbChannel *pch;
|
||||
chFilter *filter;
|
||||
const chFilterPlugin *plug;
|
||||
char ts[] = "ts";
|
||||
ELLNODE *node;
|
||||
chPostEventFunc *cb_out = NULL;
|
||||
void *arg_out = NULL;
|
||||
db_field_log fl1;
|
||||
db_field_log *pfl2;
|
||||
epicsTimeStamp stamp, now;
|
||||
dbEventCtx evtctx;
|
||||
|
||||
testPlan(12);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("filterTest.dbd", NULL, NULL);
|
||||
|
||||
filterTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
evtctx = db_init_events();
|
||||
|
||||
testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly");
|
||||
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created");
|
||||
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
|
||||
|
||||
memset(&fl, PATTERN, sizeof(fl));
|
||||
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 w/o argument");
|
||||
testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type");
|
||||
|
||||
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened");
|
||||
node = ellFirst(&pch->pre_chain);
|
||||
filter = CONTAINER(node, chFilter, pre_node);
|
||||
testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL),
|
||||
"ts has one filter w/o argument in pre chain");
|
||||
testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain");
|
||||
|
||||
memset(&fl, PATTERN, sizeof(fl));
|
||||
fl1 = fl;
|
||||
pfl2 = dbChannelRunPreChain(pch, &fl1);
|
||||
testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log");
|
||||
testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data");
|
||||
|
||||
testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel");
|
||||
stamp = pfl2->time;
|
||||
db_delete_field_log(pfl2);
|
||||
|
||||
pfl2 = dbChannelRunPreChain(pch, &fl1);
|
||||
epicsTimeGetCurrent(&now);
|
||||
testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) >= 0 &&
|
||||
epicsTimeDiffInSeconds(&now, &pfl2->time) >= 0,
|
||||
"ts filter sets time stamp to \"now\"");
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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: Andrew Johnson <anj@aps.anl.gov>
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include "dbAccessDefs.h"
|
||||
#include <recSup.h>
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "xRecord.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
static rset xRSET;
|
||||
epicsExportAddress(rset,xRSET);
|
||||
@@ -0,0 +1,2 @@
|
||||
record(x, x) {}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# This is a combined minimal DBD and DB file
|
||||
|
||||
recordtype(x) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL, DBF_LONG) {
|
||||
prompt("Value")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP = ../../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TESTLIBRARY = dbRecStdTest
|
||||
|
||||
dbRecStdTest_SRCS += asTestLib.c
|
||||
dbRecStdTest_LIBS += dbRecStd dbCore ca Com
|
||||
|
||||
PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com
|
||||
|
||||
TARGETS += $(COMMON_DIR)/recTestIoc.dbd
|
||||
DBDDEPENDS_FILES += recTestIoc.dbd$(DEP)
|
||||
recTestIoc_DBD = base.dbd
|
||||
TESTFILES += $(COMMON_DIR)/recTestIoc.dbd
|
||||
|
||||
testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += asTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
TESTPROD_HOST += arrayOpTest
|
||||
arrayOpTest_SRCS += arrayOpTest.c
|
||||
arrayOpTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += arrayOpTest.c
|
||||
TESTFILES += ../arrayOpTest.db
|
||||
TESTS += arrayOpTest
|
||||
|
||||
TESTPROD_HOST += recMiscTest
|
||||
recMiscTest_SRCS += recMiscTest.c
|
||||
recMiscTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += recMiscTest.c
|
||||
TESTFILES += ../recMiscTest.db
|
||||
TESTS += recMiscTest
|
||||
|
||||
TESTPROD_HOST += linkRetargetLinkTest
|
||||
linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c
|
||||
linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += linkRetargetLinkTest.c
|
||||
TESTFILES += ../linkRetargetLink.db
|
||||
TESTS += linkRetargetLinkTest
|
||||
|
||||
TESTPROD_HOST += linkInitTest
|
||||
linkInitTest_SRCS += linkInitTest.c
|
||||
linkInitTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += linkInitTest.c
|
||||
TESTFILES += ../linkInitTest.db
|
||||
TESTS += linkInitTest
|
||||
|
||||
TESTPROD_HOST += compressTest
|
||||
compressTest_SRCS += compressTest.c
|
||||
compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += compressTest.c
|
||||
TESTFILES += ../compressTest.db
|
||||
TESTS += compressTest
|
||||
|
||||
TESTPROD_HOST += asyncSoftTest
|
||||
asyncSoftTest_SRCS += asyncSoftTest.c
|
||||
asyncSoftTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += asyncSoftTest.c
|
||||
TESTFILES += ../asyncSoftTest.db
|
||||
TESTS += asyncSoftTest
|
||||
|
||||
TESTPROD_HOST += softTest
|
||||
softTest_SRCS += softTest.c
|
||||
softTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += softTest.c
|
||||
TESTFILES += ../softTest.db
|
||||
TESTS += softTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/asTestIoc.dbd
|
||||
DBDDEPENDS_FILES += asTestIoc.dbd$(DEP)
|
||||
asTestIoc_DBD += base.dbd
|
||||
asTestIoc_DBD += asTest.dbd
|
||||
TESTPROD_HOST += asTest
|
||||
asTest_SRCS += asTest.c
|
||||
asTest_SRCS += asTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += asTest.c
|
||||
TESTFILES += $(COMMON_DIR)/asTestIoc.dbd ../asTest.db
|
||||
TESTS += asTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/analogMonitorTest.dbd
|
||||
DBDDEPENDS_FILES += analogMonitorTest.dbd$(DEP)
|
||||
analogMonitorTest_DBD += base.dbd
|
||||
TESTPROD_HOST += analogMonitorTest
|
||||
analogMonitorTest_SRCS += analogMonitorTest.c
|
||||
analogMonitorTest_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += analogMonitorTest.c
|
||||
testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
|
||||
TESTS += analogMonitorTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/regressTest.dbd
|
||||
DBDDEPENDS_FILES += regressTest.dbd$(DEP)
|
||||
regressTest_DBD += base.dbd
|
||||
TESTPROD_HOST += regressTest
|
||||
regressTest_SRCS += regressTest.c
|
||||
regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db ../regressHex.db ../regressLinkMS.db
|
||||
TESTS += regressTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/simmTest.dbd
|
||||
TARGETS += $(COMMON_DIR)/simmTest.db
|
||||
DBDDEPENDS_FILES += simmTest.dbd$(DEP)
|
||||
DBDDEPENDS_FILES += simmTest.db$(DEP)
|
||||
simmTest_DBD += base.dbd
|
||||
TESTPROD_HOST += simmTest
|
||||
simmTest_SRCS += simmTest.c
|
||||
simmTest_SRCS += simmTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += simmTest.c
|
||||
testHarness_SRCS += simmTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/simmTest.dbd $(COMMON_DIR)/simmTest.db
|
||||
TESTS += simmTest
|
||||
|
||||
# epicsRunRecordTests runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunRecordTests.c
|
||||
|
||||
recordTestHarness_SRCS += $(testHarness_SRCS)
|
||||
recordTestHarness_SRCS_RTEMS += rtemsTestHarness.c
|
||||
|
||||
PROD_vxWorks = recordTestHarness
|
||||
PROD_RTEMS = recordTestHarness
|
||||
|
||||
TESTSPEC_vxWorks = recordTestHarness.munch; epicsRunRecordTests
|
||||
TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
@@ -0,0 +1,240 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 ITER Organization.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "registryFunction.h"
|
||||
#include "osiFileName.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMath.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbEvent.h"
|
||||
#include "caeventmask.h"
|
||||
#include "db_field_log.h"
|
||||
#include "chfPlugin.h"
|
||||
#include "iocInit.h"
|
||||
#include "testMain.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Test parameters */
|
||||
|
||||
#define NO_OF_RECORD_TYPES 7
|
||||
#define NO_OF_DEADBANDS 3
|
||||
#define NO_OF_PATTERNS 16
|
||||
#define NO_OF_VALUES_PER_SEQUENCE 2
|
||||
|
||||
void analogMonitorTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
/* Indices for record type, deadband type, deadband value, test number, val in sequence */
|
||||
static int irec, ityp, idbnd, itest, iseq;
|
||||
|
||||
/* Records to test with */
|
||||
static const char t_Record[NO_OF_RECORD_TYPES][10] = {
|
||||
{"ai"}, {"ao"}, {"calc"}, {"calcout"}, {"dfanout"}, {"sel"}, {"sub"},
|
||||
};
|
||||
/* Deadband types to test */
|
||||
static const char t_DbndType[2][6] = { {".MDEL"}, {".ADEL"} };
|
||||
/* Different deadbands to test with */
|
||||
static double t_Deadband[NO_OF_DEADBANDS] = { -1, 0, 1.5 };
|
||||
/* Value sequences for each of the 16 tests */
|
||||
static double t_SetValues[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE];
|
||||
/* Expected updates (1=yes) for each sequence of each test of each deadband */
|
||||
static int t_ExpectedUpdates[NO_OF_DEADBANDS][NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE] = {
|
||||
{ /* deadband = -1 */
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 1},
|
||||
},
|
||||
{ /* deadband = 0 */
|
||||
{1, 1}, {0, 1}, {0, 0}, {0, 0},
|
||||
{1, 1}, {1, 0}, {1, 1}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 0}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 0},
|
||||
},
|
||||
{ /* deadband = 1.5 */
|
||||
{0, 1}, {0, 1}, {0, 0}, {0, 0},
|
||||
{1, 1}, {1, 0}, {1, 1}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 0}, {1, 1},
|
||||
{1, 1}, {1, 1}, {1, 1}, {1, 0},
|
||||
},
|
||||
};
|
||||
static int t_ReceivedUpdates[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE];
|
||||
|
||||
/* Dummy subroutine needed for sub record */
|
||||
|
||||
static long myTestSub(void *p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Minimal pre-chain plugin to divert all monitors back into the test (before they hit the queue) */
|
||||
|
||||
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
double val = *((double*)chan->addr.pfield);
|
||||
|
||||
/* iseq == -1 is the value reset before the test pattern -> do not count */
|
||||
if (iseq >= 0) {
|
||||
t_ReceivedUpdates[itest][iseq] = 1;
|
||||
testOk((val == t_SetValues[itest][iseq]) || (isnan(val) && isnan(t_SetValues[itest][iseq])),
|
||||
"update %d pattern %2d with %s = %2.1f (expected %f, got %f)",
|
||||
iseq, itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd], t_SetValues[itest][iseq], val);
|
||||
}
|
||||
db_delete_field_log(pfl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void channelRegisterPre(dbChannel *chan, void *pvt,
|
||||
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
|
||||
{
|
||||
*cb_out = filter;
|
||||
}
|
||||
|
||||
static chfPluginIf pif = {
|
||||
NULL, /* allocPvt, */
|
||||
NULL, /* freePvt, */
|
||||
NULL, /* parse_error, */
|
||||
NULL, /* parse_ok, */
|
||||
NULL, /* channel_open, */
|
||||
channelRegisterPre,
|
||||
NULL, /* channelRegisterPost, */
|
||||
NULL, /* channel_report, */
|
||||
NULL /* channel_close */
|
||||
};
|
||||
|
||||
|
||||
MAIN(analogMonitorTest)
|
||||
{
|
||||
dbChannel *pch;
|
||||
const chFilterPlugin *plug;
|
||||
const char test[] = "test";
|
||||
dbEventCtx evtctx;
|
||||
dbEventSubscription subscr;
|
||||
unsigned mask;
|
||||
struct dbAddr vaddr, daddr;
|
||||
double val;
|
||||
char chan[50]; /* Channel name */
|
||||
char cval[50]; /* Name for test values */
|
||||
char cdel[50]; /* Name for deadband values */
|
||||
|
||||
/* Test patterns:
|
||||
* 0: step less than deadband (of 1.5)
|
||||
* 1: step larger than deadband (of 1.5)
|
||||
* 2: no change
|
||||
* 3: -0.0 -> +0.0
|
||||
* ... all possible combinations of steps
|
||||
* between: finite / NaN / -inf / +inf
|
||||
*/
|
||||
t_SetValues[ 0][0] = 1.0; t_SetValues[ 0][1] = 2.0;
|
||||
t_SetValues[ 1][0] = 0.0; t_SetValues[ 1][1] = 2.0;
|
||||
t_SetValues[ 2][0] = 0.0; t_SetValues[ 2][1] = 0.0;
|
||||
t_SetValues[ 3][0] = -0.0; t_SetValues[ 3][1] = 0.0;
|
||||
t_SetValues[ 4][0] = epicsNAN; t_SetValues[ 4][1] = 1.0;
|
||||
t_SetValues[ 5][0] = epicsNAN; t_SetValues[ 5][1] = epicsNAN;
|
||||
t_SetValues[ 6][0] = epicsNAN; t_SetValues[ 6][1] = epicsINF;
|
||||
t_SetValues[ 7][0] = epicsNAN; t_SetValues[ 7][1] = -epicsINF;
|
||||
t_SetValues[ 8][0] = epicsINF; t_SetValues[ 8][1] = 1.0;
|
||||
t_SetValues[ 9][0] = epicsINF; t_SetValues[ 9][1] = epicsNAN;
|
||||
t_SetValues[10][0] = epicsINF; t_SetValues[10][1] = epicsINF;
|
||||
t_SetValues[11][0] = epicsINF; t_SetValues[11][1] = -epicsINF;
|
||||
t_SetValues[12][0] = -epicsINF; t_SetValues[12][1] = 1.0;
|
||||
t_SetValues[13][0] = -epicsINF; t_SetValues[13][1] = epicsNAN;
|
||||
t_SetValues[14][0] = -epicsINF; t_SetValues[14][1] = epicsINF;
|
||||
t_SetValues[15][0] = -epicsINF; t_SetValues[15][1] = -epicsINF;
|
||||
|
||||
registryFunctionAdd("myTestSub", (REGISTRYFUNCTION) myTestSub);
|
||||
|
||||
testPlan(1793);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "analogMonitorTest.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Error reading database description 'analogMonitorTest.dbd'");
|
||||
|
||||
analogMonitorTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "analogMonitorTest.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Error reading test database 'analogMonitorTest.db'");
|
||||
|
||||
/* Start the core IOC (no CA) */
|
||||
iocBuildIsolated();
|
||||
|
||||
evtctx = db_init_events();
|
||||
chfPluginRegister(test, &pif, NULL);
|
||||
|
||||
plug = dbFindFilter(test, strlen(test));
|
||||
testOk(!!plug, "interceptor plugin registered");
|
||||
|
||||
/* Loop over all analog record types (one instance each) */
|
||||
for (irec = 0; irec < NO_OF_RECORD_TYPES; irec++) {
|
||||
strcpy(cval, t_Record[irec]);
|
||||
strcpy(chan, cval);
|
||||
strcat(chan, ".VAL{\"test\":{}}");
|
||||
if ((strcmp(t_Record[irec], "sel") == 0)
|
||||
|| (strcmp(t_Record[irec], "calc") == 0)
|
||||
|| (strcmp(t_Record[irec], "calcout") == 0)) {
|
||||
strcat(cval, ".A");
|
||||
} else {
|
||||
strcat(cval, ".VAL");
|
||||
}
|
||||
|
||||
testDiag("--------------------------------------------------------");
|
||||
testDiag("Testing the %s record", t_Record[irec]);
|
||||
testDiag("--------------------------------------------------------");
|
||||
|
||||
pch = dbChannelCreate(chan);
|
||||
testOk(!!pch, "dbChannel with test plugin created");
|
||||
testOk(!dbChannelOpen(pch), "dbChannel opened");
|
||||
|
||||
dbNameToAddr(cval, &vaddr);
|
||||
|
||||
/* Loop over both tested deadband types */
|
||||
for (ityp = 0; ityp < 2; ityp++) {
|
||||
strcpy(cdel, t_Record[irec]);
|
||||
strcat(cdel, t_DbndType[ityp]);
|
||||
dbNameToAddr(cdel, &daddr);
|
||||
mask = (ityp==0?DBE_VALUE:DBE_ARCHIVE);
|
||||
subscr = db_add_event(evtctx, pch, NULL, NULL, mask);
|
||||
db_event_enable(subscr);
|
||||
|
||||
/* Loop over all tested deadband values */
|
||||
for (idbnd = 0; idbnd < NO_OF_DEADBANDS; idbnd++) {
|
||||
testDiag("Test %s%s = %g", t_Record[irec], t_DbndType[ityp], t_Deadband[idbnd]);
|
||||
dbPutField(&daddr, DBR_DOUBLE, (void*) &t_Deadband[idbnd], 1);
|
||||
memset(t_ReceivedUpdates, 0, sizeof(t_ReceivedUpdates));
|
||||
|
||||
/* Loop over all test patterns */
|
||||
for (itest = 0; itest < NO_OF_PATTERNS; itest++) {
|
||||
iseq = -1;
|
||||
val = 0.0;
|
||||
dbPutField(&vaddr, DBR_DOUBLE, (void*) &val, 1);
|
||||
|
||||
/* Loop over the test sequence */
|
||||
for (iseq = 0; iseq < NO_OF_VALUES_PER_SEQUENCE; iseq++) {
|
||||
dbPutField(&vaddr, DBR_DOUBLE, (void*) &t_SetValues[itest][iseq], 1);
|
||||
}
|
||||
/* Check expected vs. actual monitors */
|
||||
testOk( (t_ExpectedUpdates[idbnd][itest][0] == t_ReceivedUpdates[itest][0]) &&
|
||||
(t_ExpectedUpdates[idbnd][itest][1] == t_ReceivedUpdates[itest][1]),
|
||||
"pattern %2d with %s = %2.1f (expected %d-%d, got %d-%d)",
|
||||
itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd],
|
||||
t_ExpectedUpdates[idbnd][itest][0], t_ExpectedUpdates[idbnd][itest][1],
|
||||
t_ReceivedUpdates[itest][0], t_ReceivedUpdates[itest][1]);
|
||||
}
|
||||
}
|
||||
db_cancel_event(subscr);
|
||||
}
|
||||
}
|
||||
iocShutdown();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
record(ai, "ai") {}
|
||||
record(ao, "ao") {}
|
||||
record(calc, "calc") {
|
||||
field(CALC, "A")
|
||||
}
|
||||
record(calcout, "calcout") {
|
||||
field(CALC, "A")
|
||||
}
|
||||
record(dfanout, "dfanout") {}
|
||||
record(sel, "sel") {}
|
||||
record(sub, "sub") {
|
||||
field(SNAM, "myTestSub")
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "dbTest.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "waveformRecord.h"
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static void testGetPutArray(void)
|
||||
{
|
||||
double data[4] = {11, 12, 13, 14};
|
||||
DBADDR addr, save;
|
||||
long nreq;
|
||||
epicsInt32 *pbtr;
|
||||
waveformRecord *prec;
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("arrayOpTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Test dbGet() and dbPut() from/to an array");
|
||||
|
||||
prec = (waveformRecord*)testdbRecordPtr("wfrec");
|
||||
if(!prec || dbNameToAddr("wfrec", &addr))
|
||||
testAbort("Failed to find record wfrec");
|
||||
memcpy(&save, &addr, sizeof(save));
|
||||
|
||||
testDiag("Fetch initial value of %s", prec->name);
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
testOk(prec->nord==3, "prec->nord==3 (got %d)", prec->nord);
|
||||
|
||||
nreq = NELEMENTS(data);
|
||||
if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) {
|
||||
testFail("dbGet fails");
|
||||
testSkip(1, "failed get");
|
||||
} else {
|
||||
testOk(nreq==3, "nreq==3 (got %ld)", nreq);
|
||||
testOk1(data[0]==1.0 && data[1]==2.0 && data[2]==3.0);
|
||||
}
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
addr=save;
|
||||
|
||||
testDiag("Write a new value");
|
||||
|
||||
data[0] = 4.0;
|
||||
data[1] = 5.0;
|
||||
data[2] = 6.0;
|
||||
data[3] = 7.0;
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
testOk1(dbPut(&addr, DBF_DOUBLE, &data, NELEMENTS(data))==0);
|
||||
pbtr = prec->bptr;
|
||||
testOk(prec->nord==4, "prec->nord==4 (got %u)", prec->nord);
|
||||
testOk1(pbtr[0]==4 && pbtr[1]==5 && pbtr[2]==6 && pbtr[3]==7);
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
addr=save;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
testDiag("Reread the value");
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
nreq = NELEMENTS(data);
|
||||
if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL))
|
||||
testFail("dbGet fails");
|
||||
else {
|
||||
testOk1(nreq==NELEMENTS(data));
|
||||
testOk1(data[0]==4.0 && data[1]==5.0 && data[2]==6.0 && data[3]==7.0);
|
||||
}
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
|
||||
testDiag("Test dbGet() and dbPut() from/to an array of size 1");
|
||||
|
||||
prec = (waveformRecord*)testdbRecordPtr("wfrec1");
|
||||
if(!prec || dbNameToAddr("wfrec1", &addr))
|
||||
testAbort("Failed to find record wfrec1");
|
||||
memcpy(&save, &addr, sizeof(save));
|
||||
|
||||
testDiag("Fetch initial value of %s", prec->name);
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
testOk(prec->nord==0, "prec->nord==0 (got %d)", prec->nord);
|
||||
|
||||
nreq = NELEMENTS(data);
|
||||
if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL))
|
||||
testFail("dbGet fails");
|
||||
else {
|
||||
testOk(nreq==0, "nreq==0 (got %ld)", nreq);
|
||||
}
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
addr=save;
|
||||
|
||||
testDiag("Write a new value");
|
||||
|
||||
data[0] = 4.0;
|
||||
data[1] = 5.0;
|
||||
data[2] = 6.0;
|
||||
data[3] = 7.0;
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
testOk1(dbPut(&addr, DBF_DOUBLE, &data, 1)==0);
|
||||
pbtr = prec->bptr;
|
||||
testOk(prec->nord==1, "prec->nord==1 (got %u)", prec->nord);
|
||||
testOk1(pbtr[0]==4);
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
addr=save;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
testDiag("Reread the value");
|
||||
|
||||
dbScanLock(addr.precord);
|
||||
nreq = NELEMENTS(data);
|
||||
if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL))
|
||||
testFail("dbGet fails");
|
||||
else {
|
||||
testOk1(nreq==1);
|
||||
testOk1(data[0]==4.0);
|
||||
}
|
||||
dbScanUnlock(addr.precord);
|
||||
|
||||
testOk1(memcmp(&addr, &save, sizeof(save))==0);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(arrayOpTest)
|
||||
{
|
||||
testPlan(21);
|
||||
testGetPutArray();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
record(waveform, "wfrec") {
|
||||
field(NELM, "10")
|
||||
field(FTVL, "LONG")
|
||||
field(INP, [1, 2, 3])
|
||||
}
|
||||
record(waveform, "wfrec1") {
|
||||
field(NELM, "1")
|
||||
field(FTVL, "LONG")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*
|
||||
* 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 "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 "testMain.h"
|
||||
|
||||
epicsShareFunc void testRestore(void);
|
||||
|
||||
#include "epicsExport.h"
|
||||
|
||||
MAIN(asTest)
|
||||
{
|
||||
testPlan(42);
|
||||
testRestore();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
record(ao, "rec0") {
|
||||
field(DESC, "foobar")
|
||||
field(DTYP, "asTest")
|
||||
field(VAL, "1")
|
||||
field(OUT, "rec0.DISV")
|
||||
}
|
||||
|
||||
record(waveform, "rec1") {
|
||||
field(DTYP, "asTest")
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "5")
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
device(ao, CONSTANT, devAOasTest, "asTest")
|
||||
device(waveform, CONSTANT, devWFasTest, "asTest")
|
||||
@@ -0,0 +1,291 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*
|
||||
* 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 initRec0(aoRecord *prec)
|
||||
{
|
||||
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 initRec1(waveformRecord *prec)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
struct dset6 {
|
||||
dset common;
|
||||
DEVSUPFUN proc;
|
||||
DEVSUPFUN linconv;
|
||||
};
|
||||
|
||||
static long noop() {return 0;}
|
||||
|
||||
static struct dset6 devAOasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec0, NULL}, (DEVSUPFUN)noop, NULL};
|
||||
static struct dset6 devWFasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec1, NULL}, (DEVSUPFUN)noop, NULL};
|
||||
|
||||
epicsExportAddress(dset, devAOasTest);
|
||||
epicsExportAddress(dset, devWFasTest);
|
||||
@@ -0,0 +1,189 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 UChicago Argonne LLC, as operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errlog.h"
|
||||
#include "registryFunction.h"
|
||||
#include "subRecord.h"
|
||||
#include "testMain.h"
|
||||
|
||||
static int startCounter, doneCounter;
|
||||
static epicsEventId asyncEvent, doneEvent;
|
||||
|
||||
static
|
||||
long asyncSubr(subRecord *prec)
|
||||
{
|
||||
testDiag("Processing %s, pact=%d", prec->name, prec->pact);
|
||||
|
||||
if (!prec->pact) {
|
||||
epicsEventTrigger(asyncEvent);
|
||||
prec->pact = 1; /* Make asynchronous */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
long doneSubr(subRecord *prec)
|
||||
{
|
||||
epicsEventTrigger(doneEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void checkAsyncInput(const char *rec, int init, dbCommon *async)
|
||||
{
|
||||
char inp[16], proc[16];
|
||||
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
strcpy(inp, rec);
|
||||
strcat(inp, ".INP");
|
||||
strcpy(proc, rec);
|
||||
strcat(proc, ".PROC");
|
||||
|
||||
if (init) {
|
||||
testdbGetFieldEqual(rec, DBF_LONG, init);
|
||||
|
||||
testdbPutFieldOk(inp, DBF_STRING, "async");
|
||||
}
|
||||
|
||||
testdbPutFieldOk(proc, DBF_CHAR, 1);
|
||||
|
||||
epicsEventWait(asyncEvent);
|
||||
testdbGetFieldEqual("startCounter", DBF_LONG, ++startCounter);
|
||||
testdbGetFieldEqual("doneCounter", DBF_LONG, doneCounter);
|
||||
|
||||
dbScanLock(async);
|
||||
async->rset->process(async);
|
||||
dbScanUnlock(async);
|
||||
|
||||
epicsEventWait(doneEvent);
|
||||
testdbGetFieldEqual("startCounter", DBF_LONG, startCounter);
|
||||
testdbGetFieldEqual("doneCounter", DBF_LONG, ++doneCounter);
|
||||
}
|
||||
|
||||
static
|
||||
void testAsynInputs(dbCommon *async)
|
||||
{
|
||||
const char * records[] = {
|
||||
"ai0", "bi0", "di0", "ii0", "li0", "mi0", "si0", NULL,
|
||||
"bi1", /* bi1 must be first in this group */
|
||||
"ai1", "di1", "ii1", "li1", "mi1", "si1", NULL,
|
||||
};
|
||||
const char ** rec = &records[0];
|
||||
int init = 1; /* bi1 initializes to 1 */
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
startCounter = doneCounter = 0;
|
||||
testdbPutFieldOk("startCounter", DBF_LONG, startCounter);
|
||||
testdbPutFieldOk("doneCounter", DBF_LONG, doneCounter);
|
||||
|
||||
epicsEventTryWait(asyncEvent);
|
||||
epicsEventTryWait(doneEvent);
|
||||
|
||||
while (*rec) { /* 1st group don't need initializing */
|
||||
checkAsyncInput(*rec++, 0, async);
|
||||
}
|
||||
rec++;
|
||||
while (*rec) {
|
||||
checkAsyncInput(*rec++, init, async);
|
||||
init = 9; /* remainder initialize to 9 */
|
||||
}
|
||||
|
||||
testDiag("============= Ending %s =============", EPICS_FUNCTION);
|
||||
}
|
||||
|
||||
static
|
||||
void checkAsyncOutput(const char *rec, dbCommon *async)
|
||||
{
|
||||
char proc[16];
|
||||
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
strcpy(proc, rec);
|
||||
strcat(proc, ".PROC");
|
||||
|
||||
testdbPutFieldOk(proc, DBF_CHAR, 1);
|
||||
|
||||
epicsEventWait(asyncEvent);
|
||||
testdbGetFieldEqual("startCounter", DBF_LONG, ++startCounter);
|
||||
testdbGetFieldEqual("doneCounter", DBF_LONG, doneCounter);
|
||||
|
||||
dbScanLock(async);
|
||||
async->rset->process(async);
|
||||
dbScanUnlock(async);
|
||||
|
||||
epicsEventWait(doneEvent);
|
||||
testdbGetFieldEqual("startCounter", DBF_LONG, startCounter);
|
||||
testdbGetFieldEqual("doneCounter", DBF_LONG, ++doneCounter);
|
||||
}
|
||||
|
||||
static
|
||||
void testAsyncOutputs(dbCommon *async)
|
||||
{
|
||||
const char * records[] = {
|
||||
"ao1", "bo1", "do1", "io1", "lo1", "lso1", "mo1", "so1", NULL,
|
||||
};
|
||||
const char ** rec = &records[0];
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
startCounter = doneCounter = 0;
|
||||
testdbPutFieldOk("startCounter", DBF_LONG, startCounter);
|
||||
testdbPutFieldOk("doneCounter", DBF_LONG, doneCounter);
|
||||
|
||||
epicsEventTryWait(asyncEvent);
|
||||
epicsEventTryWait(doneEvent);
|
||||
|
||||
while (*rec) {
|
||||
checkAsyncOutput(*rec++, async);
|
||||
}
|
||||
|
||||
testDiag("============= Ending %s =============", EPICS_FUNCTION);
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(asyncSoftTest)
|
||||
{
|
||||
dbCommon *async;
|
||||
|
||||
testPlan(128);
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
registryFunctionAdd("asyncSubr", (REGISTRYFUNCTION) asyncSubr);
|
||||
registryFunctionAdd("doneSubr", (REGISTRYFUNCTION) doneSubr);
|
||||
|
||||
testdbReadDatabase("asyncSoftTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
async = testdbRecordPtr("async");
|
||||
asyncEvent = epicsEventCreate(epicsEventEmpty);
|
||||
doneEvent = epicsEventCreate(epicsEventEmpty);
|
||||
|
||||
testAsynInputs(async);
|
||||
testAsyncOutputs(async);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
record(ai, "ai0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(bi, "bi0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(longin, "li0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbbiDirect, "di0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbbi, "mi0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(stringin, "si0") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, "async")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
|
||||
record(ai, "ai1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, {const:9})
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(bi, "bi1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, {const:1})
|
||||
field(FLNK, "done")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, {const:9})
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(longin, "li1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, {const:9})
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbbiDirect, "di1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, {const:9})
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbbi, "mi1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, {const:9})
|
||||
field(FLNK, "done")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(stringin, "si1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(INP, {const:"9"})
|
||||
field(FLNK, "done")
|
||||
}
|
||||
|
||||
record(sub, "async") {
|
||||
field(INPA, "startCounter PP")
|
||||
field(SNAM, "asyncSubr")
|
||||
}
|
||||
record(calc, "startCounter") {
|
||||
field(CALC, "VAL+1")
|
||||
}
|
||||
record(sub, "done") {
|
||||
field(INPA, "doneCounter PP")
|
||||
field(SNAM, "doneSubr")
|
||||
}
|
||||
record(calc, "doneCounter") {
|
||||
field(CALC, "VAL+1")
|
||||
}
|
||||
|
||||
record(ao, "ao1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(bo, "bo1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(longout, "lo1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbboDirect, "do1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
record(mbbo, "mo1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so1") {
|
||||
field(DTYP, "Async Soft Channel")
|
||||
field(OUT, "async.PROC CA")
|
||||
field(FLNK, "done")
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "dbLock.h"
|
||||
#include "errlog.h"
|
||||
#include "dbAccess.h"
|
||||
#include "epicsMath.h"
|
||||
|
||||
#include "aiRecord.h"
|
||||
#include "compressRecord.h"
|
||||
|
||||
#define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B)
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static
|
||||
void checkArrD(const char *pv, long elen, double a, double b, double c, double d)
|
||||
{
|
||||
double buf[4];
|
||||
double expect[4];
|
||||
long nReq = NELEMENTS(buf), i;
|
||||
unsigned match;
|
||||
DBADDR addr;
|
||||
|
||||
expect[0] = a;
|
||||
expect[1] = b;
|
||||
expect[2] = c;
|
||||
expect[3] = d;
|
||||
|
||||
if (dbNameToAddr(pv, &addr))
|
||||
testAbort("Unknown PV '%s'", pv);
|
||||
|
||||
if (dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL))
|
||||
testAbort("Failed to get '%s'", pv);
|
||||
|
||||
match = elen==nReq;
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
match &= fabs(buf[i]-expect[i])<0.01;
|
||||
}
|
||||
testOk(match, "dbGet(\"%s\") matches", pv);
|
||||
if (elen!=nReq)
|
||||
testDiag("lengths don't match %ld != %ld", elen, nReq);
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
if (fabs(buf[i]-expect[i])>=0.01)
|
||||
testDiag("[%ld] -> %f != %f", i, expect[i], buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32 c, epicsInt32 d)
|
||||
{
|
||||
epicsInt32 buf[4];
|
||||
epicsInt32 expect[4];
|
||||
long nReq = NELEMENTS(buf), i;
|
||||
unsigned match;
|
||||
DBADDR addr;
|
||||
|
||||
expect[0] = a;
|
||||
expect[1] = b;
|
||||
expect[2] = c;
|
||||
expect[3] = d;
|
||||
|
||||
if (dbNameToAddr(pv, &addr))
|
||||
testAbort("Unknown PV '%s'", pv);
|
||||
|
||||
if (dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL))
|
||||
testAbort("Failed to get '%s'", pv);
|
||||
|
||||
match = elen==nReq;
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
match &= buf[i]==expect[i];
|
||||
}
|
||||
testOk(match, "dbGet(\"%s\") matches", pv);
|
||||
if (elen!=nReq)
|
||||
testDiag("lengths don't match %ld != %ld", elen, nReq);
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
if(buf[i]!=expect[i])
|
||||
testDiag("[%ld] -> %d != %d", i, (int)expect[i], (int)buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void testFIFOCirc(void)
|
||||
{
|
||||
aiRecord *vrec;
|
||||
compressRecord *crec;
|
||||
double *cbuf;
|
||||
|
||||
testDiag("Test FIFO");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4");
|
||||
|
||||
vrec = (aiRecord*)testdbRecordPtr("val");
|
||||
crec = (compressRecord*)testdbRecordPtr("comp");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
dbScanLock((dbCommon*)crec);
|
||||
cbuf = crec->bptr;
|
||||
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==0);
|
||||
|
||||
testDiag("Push 1.1");
|
||||
vrec->val = 1.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
/* In FIFO mode the valid elements are
|
||||
* cbuf[(off-nuse-1) % size] through cbuf[(off-1) % size]
|
||||
*/
|
||||
testOk1(crec->off==1);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==1);
|
||||
testDEq(cbuf[0], 1.1, 0.1);
|
||||
testDEq(cbuf[1], 0.0, 0.1);
|
||||
testDEq(cbuf[2], 0.0, 0.1);
|
||||
testDEq(cbuf[3], 0.0, 0.1);
|
||||
checkArrD("comp", 1, 1.1, 0, 0, 0);
|
||||
|
||||
testDiag("Push 2.1");
|
||||
vrec->val = 2.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==2);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==2);
|
||||
testDEq(cbuf[0], 1.1, 0.1);
|
||||
testDEq(cbuf[1], 2.1, 0.1);
|
||||
testDEq(cbuf[2], 0.0, 0.1);
|
||||
testDEq(cbuf[3], 0.0, 0.1);
|
||||
checkArrD("comp", 2, 1.1, 2.1, 0, 0);
|
||||
|
||||
testDiag("Push 3.1");
|
||||
vrec->val = 3.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==3);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==3);
|
||||
testDEq(cbuf[0], 1.1, 0.1);
|
||||
testDEq(cbuf[1], 2.1, 0.1);
|
||||
testDEq(cbuf[2], 3.1, 0.1);
|
||||
testDEq(cbuf[3], 0.0, 0.1);
|
||||
checkArrD("comp", 3, 1.1, 2.1, 3.1, 0);
|
||||
|
||||
testDiag("Push 4.1");
|
||||
vrec->val = 4.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 1.1, 0.1);
|
||||
testDEq(cbuf[1], 2.1, 0.1);
|
||||
testDEq(cbuf[2], 3.1, 0.1);
|
||||
testDEq(cbuf[3], 4.1, 0.1);
|
||||
checkArrD("comp", 4, 1.1, 2.1, 3.1, 4.1);
|
||||
|
||||
testDiag("Push 5.1");
|
||||
vrec->val = 5.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==1);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 5.1, 0.1);
|
||||
testDEq(cbuf[1], 2.1, 0.1);
|
||||
testDEq(cbuf[2], 3.1, 0.1);
|
||||
testDEq(cbuf[3], 4.1, 0.1);
|
||||
checkArrD("comp", 4, 2.1, 3.1, 4.1, 5.1);
|
||||
|
||||
testDiag("Push 6.1");
|
||||
vrec->val = 6.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==2);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 5.1, 0.1);
|
||||
testDEq(cbuf[1], 6.1, 0.1);
|
||||
testDEq(cbuf[2], 3.1, 0.1);
|
||||
testDEq(cbuf[3], 4.1, 0.1);
|
||||
checkArrD("comp", 4, 3.1, 4.1, 5.1, 6.1);
|
||||
|
||||
dbScanUnlock((dbCommon*)crec);
|
||||
|
||||
testDiag("Reset");
|
||||
testdbPutFieldOk("comp.RES", DBF_LONG, 0);
|
||||
|
||||
dbScanLock((dbCommon*)crec);
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==0);
|
||||
checkArrD("comp", 0, 0, 0, 0, 0);
|
||||
dbScanUnlock((dbCommon*)crec);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static
|
||||
void testLIFOCirc(void)
|
||||
{
|
||||
aiRecord *vrec;
|
||||
compressRecord *crec;
|
||||
double *cbuf;
|
||||
|
||||
testDiag("Test LIFO");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("compressTest.db", NULL,
|
||||
"ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4");
|
||||
|
||||
vrec = (aiRecord*)testdbRecordPtr("val");
|
||||
crec = (compressRecord*)testdbRecordPtr("comp");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
dbScanLock((dbCommon*)crec);
|
||||
cbuf = crec->bptr;
|
||||
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==0);
|
||||
|
||||
testDiag("Push 1.1");
|
||||
vrec->val = 1.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testDiag("off %u", crec->off);
|
||||
testOk1(crec->off==3);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==1);
|
||||
testDEq(cbuf[0], 0.0, 0.1);
|
||||
testDEq(cbuf[1], 0.0, 0.1);
|
||||
testDEq(cbuf[2], 0.0, 0.1);
|
||||
testDEq(cbuf[3], 1.1, 0.1);
|
||||
checkArrD("comp", 1, 1.1, 0, 0, 0);
|
||||
|
||||
testDiag("Push 2.1");
|
||||
vrec->val = 2.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==2);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==2);
|
||||
testDEq(cbuf[0], 0.0, 0.1);
|
||||
testDEq(cbuf[1], 0.0, 0.1);
|
||||
testDEq(cbuf[2], 2.1, 0.1);
|
||||
testDEq(cbuf[3], 1.1, 0.1);
|
||||
checkArrD("comp", 2, 2.1, 1.1, 0, 0);
|
||||
checkArrI("comp", 2, 2, 1, 0, 0);
|
||||
|
||||
testDiag("Push 3.1");
|
||||
vrec->val = 3.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==1);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==3);
|
||||
testDEq(cbuf[0], 0.0, 0.1);
|
||||
testDEq(cbuf[1], 3.1, 0.1);
|
||||
testDEq(cbuf[2], 2.1, 0.1);
|
||||
testDEq(cbuf[3], 1.1, 0.1);
|
||||
checkArrD("comp", 3, 3.1, 2.1, 1.1, 0);
|
||||
checkArrI("comp", 3, 3, 2, 1, 0);
|
||||
|
||||
testDiag("Push 4.1");
|
||||
vrec->val = 4.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 4.1, 0.1);
|
||||
testDEq(cbuf[1], 3.1, 0.1);
|
||||
testDEq(cbuf[2], 2.1, 0.1);
|
||||
testDEq(cbuf[3], 1.1, 0.1);
|
||||
checkArrD("comp", 4, 4.1, 3.1, 2.1, 1.1);
|
||||
checkArrI("comp", 4, 4, 3, 2, 1);
|
||||
|
||||
testDiag("Push 5.1");
|
||||
vrec->val = 5.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==3);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 4.1, 0.1);
|
||||
testDEq(cbuf[1], 3.1, 0.1);
|
||||
testDEq(cbuf[2], 2.1, 0.1);
|
||||
testDEq(cbuf[3], 5.1, 0.1);
|
||||
checkArrD("comp", 4, 5.1, 4.1, 3.1, 2.1);
|
||||
checkArrI("comp", 4, 5, 4, 3, 2);
|
||||
|
||||
testDiag("Push 6.1");
|
||||
vrec->val = 6.1;
|
||||
dbProcess((dbCommon*)crec);
|
||||
|
||||
testOk1(crec->off==2);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==4);
|
||||
testDEq(cbuf[0], 4.1, 0.1);
|
||||
testDEq(cbuf[1], 3.1, 0.1);
|
||||
testDEq(cbuf[2], 6.1, 0.1);
|
||||
testDEq(cbuf[3], 5.1, 0.1);
|
||||
checkArrD("comp", 4, 6.1, 5.1, 4.1, 3.1);
|
||||
|
||||
dbScanUnlock((dbCommon*)crec);
|
||||
|
||||
testDiag("Reset");
|
||||
testdbPutFieldOk("comp.RES", DBF_LONG, 0);
|
||||
|
||||
dbScanLock((dbCommon*)crec);
|
||||
testOk1(crec->off==0);
|
||||
testOk1(crec->inx==0);
|
||||
testOk1(crec->nuse==0);
|
||||
checkArrD("comp", 0, 0, 0, 0, 0);
|
||||
dbScanUnlock((dbCommon*)crec);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(compressTest)
|
||||
{
|
||||
testPlan(116);
|
||||
testFIFOCirc();
|
||||
testLIFOCirc();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
record(ai, "val") {}
|
||||
record(compress, "comp") {
|
||||
field(INP, "val NPP")
|
||||
field(ALG, "$(ALG)")
|
||||
field(BALG,"$(BALG)")
|
||||
field(NSAM,"$(NSAM)")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Run tests as a batch.
|
||||
*/
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
#include "epicsExit.h"
|
||||
|
||||
int analogMonitorTest(void);
|
||||
int compressTest(void);
|
||||
int recMiscTest(void);
|
||||
int arrayOpTest(void);
|
||||
int asTest(void);
|
||||
int linkRetargetLinkTest(void);
|
||||
int linkInitTest(void);
|
||||
int asyncSoftTest(void);
|
||||
int simmTest(void);
|
||||
|
||||
void epicsRunRecordTests(void)
|
||||
{
|
||||
testHarness();
|
||||
|
||||
runTest(analogMonitorTest);
|
||||
|
||||
runTest(compressTest);
|
||||
|
||||
runTest(recMiscTest);
|
||||
|
||||
runTest(arrayOpTest);
|
||||
|
||||
runTest(asTest);
|
||||
|
||||
runTest(linkRetargetLinkTest);
|
||||
|
||||
runTest(linkInitTest);
|
||||
|
||||
runTest(asyncSoftTest);
|
||||
|
||||
runTest(simmTest);
|
||||
|
||||
epicsExit(0); /* Trigger test harness */
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "alarm.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static void startTestIoc(const char *dbfile)
|
||||
{
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase(dbfile, NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
}
|
||||
|
||||
static void testLongStringInit()
|
||||
{
|
||||
testDiag("testLongStringInit");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
|
||||
{
|
||||
const char buf[] = "!----------------------------------------------!";
|
||||
testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
|
||||
testdbGetFieldEqual("longstr1.VAL", DBR_STRING, "!--------------------------------------");
|
||||
}
|
||||
|
||||
{
|
||||
const char buf[] = "!----------------------------------------------!";
|
||||
testdbGetArrFieldEqual("longstr2.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
|
||||
testdbGetFieldEqual("longstr2.VAL", DBR_STRING, "!--------------------------------------");
|
||||
}
|
||||
|
||||
{
|
||||
const char buf[] = "!----------------------------------------------!";
|
||||
testdbGetArrFieldEqual("longstr3.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
|
||||
testdbGetFieldEqual("longstr3.VAL", DBR_STRING, "!--------------------------------------");
|
||||
}
|
||||
|
||||
testdbGetFieldEqual("longstr4.VAL", DBR_STRING, "One");
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testCalcInit()
|
||||
{
|
||||
testDiag("testCalcInit");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
|
||||
testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
|
||||
testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, INVALID_ALARM);
|
||||
|
||||
testdbPutFieldOk("emptylink.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
|
||||
testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 0.0);
|
||||
testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, INVALID_ALARM);
|
||||
|
||||
testdbPutFieldOk("emptylink1.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 1.0);
|
||||
testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, 0);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testPrintfStrings()
|
||||
{
|
||||
testDiag("testPrintfStrings");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
|
||||
{
|
||||
const char buf1[] = "Test string, exactly 40 characters long";
|
||||
const char buf2[] = "Longer test string, more that 40 characters long";
|
||||
const char buf2t[] = "Longer test string, more that 40 charac";
|
||||
|
||||
/* The FMT field is pp(TRUE), so this put triggers processing */
|
||||
testdbPutFieldOk("printf1.FMT", DBF_STRING, "%s");
|
||||
testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
|
||||
NELEMENTS(buf1), buf1);
|
||||
testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);
|
||||
|
||||
testdbPutFieldOk("printf1.FMT", DBF_STRING, "%ls");
|
||||
testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
|
||||
NELEMENTS(buf1), buf1);
|
||||
testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);
|
||||
|
||||
testdbPutFieldOk("printf2.FMT", DBF_STRING, "%s");
|
||||
testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
|
||||
NELEMENTS(buf2t), buf2t);
|
||||
testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);
|
||||
|
||||
testdbPutFieldOk("printf2.FMT", DBF_STRING, "%ls");
|
||||
testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
|
||||
NELEMENTS(buf2), buf2);
|
||||
testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);
|
||||
|
||||
testdbPutFieldOk("printf2.FMT", DBF_STRING, "%.39ls");
|
||||
testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
|
||||
NELEMENTS(buf2t), buf2t);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testArrayInputs()
|
||||
{
|
||||
epicsInt32 oneToTwelve[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
|
||||
testDiag("testArrayInputs");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
|
||||
testdbGetFieldEqual("aai1.NORD", DBR_LONG, 10);
|
||||
testdbGetFieldEqual("aai1.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("aai2.NORD", DBR_LONG, 10);
|
||||
testdbGetFieldEqual("aai2.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("sa1.NORD", DBR_LONG, 10);
|
||||
testdbGetFieldEqual("sa1.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("sa2.NORD", DBR_LONG, 0);
|
||||
testdbGetFieldEqual("sa2.UDF", DBR_UCHAR, 1);
|
||||
testdbGetFieldEqual("wf1.NORD", DBR_LONG, 10);
|
||||
testdbGetFieldEqual("wf1.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("wf2.NORD", DBR_LONG, 10);
|
||||
testdbGetFieldEqual("wf2.UDF", DBR_UCHAR, 0);
|
||||
|
||||
testdbGetArrFieldEqual("aai1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
|
||||
testdbGetArrFieldEqual("aai2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
|
||||
testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 10, &oneToTwelve[2]);
|
||||
testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 0, NULL);
|
||||
testdbGetArrFieldEqual("wf1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
|
||||
testdbGetArrFieldEqual("wf2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
|
||||
|
||||
testdbPutFieldOk("sa1.INDX", DBF_LONG, 3);
|
||||
testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 9, &oneToTwelve[3]);
|
||||
|
||||
testdbPutFieldOk("sa1.NELM", DBF_LONG, 3);
|
||||
testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 3, &oneToTwelve[3]);
|
||||
|
||||
testdbPutFieldOk("sa2.VAL", DBF_LONG, 1);
|
||||
testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 1, &oneToTwelve[0]);
|
||||
|
||||
testDiag("testScalarInputs");
|
||||
|
||||
testdbGetFieldEqual("li1", DBR_LONG, 1);
|
||||
testdbGetFieldEqual("i64i1", DBR_INT64, 1LL);
|
||||
testdbGetFieldEqual("li2", DBR_LONG, 1);
|
||||
testdbGetFieldEqual("i64i2", DBR_INT64, 1LL);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testEventRecord()
|
||||
{
|
||||
testMonitor *countmon;
|
||||
|
||||
testDiag("testEventRecord");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
countmon = testMonitorCreate("count1.VAL", DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual("ev1.VAL", DBR_STRING, "soft event 1");
|
||||
testdbGetFieldEqual("ev1.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("ev2.VAL", DBR_STRING, "");
|
||||
testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 1);
|
||||
testdbGetFieldEqual("count1.VAL", DBR_LONG, 0);
|
||||
|
||||
testdbPutFieldOk("ev1.PROC", DBF_UCHAR, 1);
|
||||
testMonitorWait(countmon);
|
||||
testdbGetFieldEqual("count1.VAL", DBR_LONG, 1);
|
||||
|
||||
testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
|
||||
testMonitorWait(countmon);
|
||||
testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 0);
|
||||
testdbGetFieldEqual("count1.VAL", DBR_LONG, 2);
|
||||
|
||||
testdbPutFieldOk("count1.EVNT", DBF_STRING, "Tock");
|
||||
testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
|
||||
testMonitorWait(countmon);
|
||||
testdbGetFieldEqual("count1.VAL", DBR_LONG, 3);
|
||||
|
||||
testMonitorDestroy(countmon);
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void testInt64Inputs(void)
|
||||
{
|
||||
testDiag("testInt64Inputs");
|
||||
|
||||
startTestIoc("linkInitTest.db");
|
||||
|
||||
testdbGetFieldEqual("i1.VAL", DBR_INT64, 1234567890123456789LL);
|
||||
testdbGetFieldEqual("i2.VAL", DBR_INT64, 1234567890123456789LL);
|
||||
testdbGetFieldEqual("i3.VAL", DBR_INT64, 1234567890123456789LL);
|
||||
|
||||
testdbGetFieldEqual("i4.NORD", DBR_LONG, 1);
|
||||
testdbGetFieldEqual("i4.VAL", DBR_INT64, 1234567890123456789LL);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
|
||||
MAIN(linkInitTest)
|
||||
{
|
||||
testPlan(77);
|
||||
|
||||
testLongStringInit();
|
||||
testCalcInit();
|
||||
testPrintfStrings();
|
||||
testArrayInputs();
|
||||
testEventRecord();
|
||||
testInt64Inputs();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
record(lsi, "longstr1") {
|
||||
field(SIZV, "100")
|
||||
field(INP, ["!----------------------------------------------!"])
|
||||
}
|
||||
record(lsi, "longstr2") {
|
||||
field(SIZV, "100")
|
||||
field(INP, {const: ["!----------------------------------------------!"]})
|
||||
}
|
||||
record(lsi, "longstr3") {
|
||||
field(SIZV, "100")
|
||||
field(INP, {const: "!----------------------------------------------!"})
|
||||
}
|
||||
record(lsi, "longstr4") {
|
||||
field(SIZV, "100")
|
||||
field(INP, ["One","Two","Three","Four"])
|
||||
}
|
||||
|
||||
record(ai, "emptylink" ) {
|
||||
field(INP, {calc: {expr:"0"}})
|
||||
}
|
||||
record(ai, "emptylink1" ) {
|
||||
field(INP, {calc: {expr:"A", args:[1], time:"a"}})
|
||||
field(TSEL, -2)
|
||||
}
|
||||
|
||||
record(printf, "printf1") {
|
||||
field(SIZV, "100")
|
||||
field(INP0, ["Test string, exactly 40 characters long"])
|
||||
}
|
||||
record(printf, "printf2") {
|
||||
field(SIZV, "100")
|
||||
field(INP0, ["Longer test string, more that 40 characters long"])
|
||||
}
|
||||
|
||||
record(aai, "aai1") {
|
||||
field(NELM, 10)
|
||||
field(FTVL, "LONG")
|
||||
field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
}
|
||||
record(aai, "aai2") {
|
||||
field(NELM, 10)
|
||||
field(FTVL, "LONG")
|
||||
field(INP, {const:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]})
|
||||
}
|
||||
record(subArray, "sa1") {
|
||||
field(FTVL, "LONG")
|
||||
field(MALM, 12)
|
||||
field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
field(INDX, 2)
|
||||
field(NELM, 10)
|
||||
}
|
||||
record(subArray, "sa2") {
|
||||
field(FTVL, "LONG")
|
||||
field(MALM, 10)
|
||||
field(NELM, 1)
|
||||
}
|
||||
record(waveform, "wf1") {
|
||||
field(NELM, 10)
|
||||
field(FTVL, "LONG")
|
||||
field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
}
|
||||
record(waveform, "wf2") {
|
||||
field(NELM, 10)
|
||||
field(FTVL, "LONG")
|
||||
field(INP, {const:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]})
|
||||
}
|
||||
|
||||
record(longin, "li1") {
|
||||
field(INP, 1)
|
||||
}
|
||||
record(int64in, "i64i1") {
|
||||
field(INP, 1)
|
||||
}
|
||||
record(longin, "li2") {
|
||||
field(INP, {const:1})
|
||||
}
|
||||
record(int64in, "i64i2") {
|
||||
field(INP, {const:1})
|
||||
}
|
||||
|
||||
record(longin, "count1" ) {
|
||||
field(INP, {calc: {expr:"VAL+1"}})
|
||||
field(SCAN, "Event")
|
||||
field(EVNT, "soft event 1")
|
||||
}
|
||||
record(event, "ev1") {
|
||||
field(INP, ["soft event 1"])
|
||||
}
|
||||
record(event, "ev2") {
|
||||
field(INP, "count1.EVNT")
|
||||
}
|
||||
|
||||
record(int64in, "i1") {
|
||||
field(INP, [1234567890123456789,])
|
||||
}
|
||||
record(int64in, "i2") {
|
||||
field(INP, {const:1234567890123456789,})
|
||||
}
|
||||
record(int64in, "i3") {
|
||||
field(INP, 1234567890123456789)
|
||||
}
|
||||
record(waveform, "i4") {
|
||||
field(NELM, 1)
|
||||
field(FTVL, "INT64")
|
||||
field(INP, [1234567890123456789,])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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")
|
||||
}
|
||||
|
||||
record(ai, "rec:j1") {
|
||||
field(INP, {calc:{
|
||||
expr:"A+5",
|
||||
args:5
|
||||
}})
|
||||
field(PINI, "YES")
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*************************************************************************\
|
||||
* 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
|
||||
*/
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
|
||||
#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;
|
||||
|
||||
testDiag("In testRetarget");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define testLongStrEq(PV, VAL) testdbGetArrFieldEqual(PV, DBF_CHAR, sizeof(VAL)+2, sizeof(VAL), VAL)
|
||||
#define testPutLongStr(PV, VAL) testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL);
|
||||
|
||||
static void testRetargetJLink(void)
|
||||
{
|
||||
testDiag("In testRetargetJLink");
|
||||
|
||||
testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 10.0);
|
||||
/* minimal args */
|
||||
testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":5}}");
|
||||
|
||||
/* with [] */
|
||||
testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[7]}}");
|
||||
testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1);
|
||||
|
||||
/* with const */
|
||||
testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}");
|
||||
testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 12.0);
|
||||
testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}");
|
||||
}
|
||||
|
||||
MAIN(linkRetargetLinkTest)
|
||||
{
|
||||
testPlan(18);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("linkRetargetLink.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
/* wait for local CA links to be connected or dbPutField() will fail */
|
||||
/* wait for initial CA_CONNECT actions to be processed.
|
||||
* Assume that local CA links deliver callbacks synchronously
|
||||
* eg. that ca_create_channel() will invoke the connection callback
|
||||
* before returning.
|
||||
*/
|
||||
dbCaSync();
|
||||
|
||||
testRetarget();
|
||||
testRetargetJLink();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
void testint64BeforeInit(void)
|
||||
{
|
||||
const char *S;
|
||||
DBENTRY dbent;
|
||||
|
||||
/* check dbGet/PutString */
|
||||
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
dbInitEntryFromRecord(testdbRecordPtr("out64"), &dbent);
|
||||
if(dbFindField(&dbent, "VAL"))
|
||||
testAbort("Failed to find out64.VAL");
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "0")==0, "initial value \"%s\"", S);
|
||||
|
||||
testOk1(dbPutString(&dbent, "0x12345678abcdef00")==0);
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "1311768467750121216")==0, "1311768467750121216 \"%s\"", S);
|
||||
|
||||
dbFinishEntry(&dbent);
|
||||
}
|
||||
|
||||
static
|
||||
void testint64AfterInit(void)
|
||||
{
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
/* check dbGet/PutField and DB links */
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0ULL);
|
||||
testdbGetFieldEqual("out64", DBF_UINT64, 0x12345678abcdef00ULL);
|
||||
|
||||
testdbPutFieldOk("out64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x12345678abcdef00ULL);
|
||||
|
||||
testdbPutFieldOk("out64.VAL", DBF_UINT64, 0x22345678abcdef00ULL);
|
||||
|
||||
testdbPutFieldOk("in64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x22345678abcdef00ULL);
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(recMiscTest)
|
||||
{
|
||||
testPlan(10);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("recMiscTest.db", NULL, NULL);
|
||||
|
||||
testint64BeforeInit();
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testint64AfterInit();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
# check int64in/out
|
||||
|
||||
record(int64in, "in64") {
|
||||
field(INP , "out64 NPP")
|
||||
}
|
||||
|
||||
record(int64out, "out64") {
|
||||
field(OUT , "in64 NPP")
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
record(waveform, "wf") {
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "1")
|
||||
field(FLNK, "co")
|
||||
}
|
||||
record(calcout, "co") {
|
||||
field(CALC, "A")
|
||||
field(INPA, "wf")
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
record(ai, "ai1") {
|
||||
field(INP, 0x10)
|
||||
}
|
||||
record(longin, "li1") {
|
||||
field(INP, 0x10)
|
||||
}
|
||||
record(mbbiDirect, "mi1") {
|
||||
field(INP, 0x10)
|
||||
field(NOBT, 8)
|
||||
}
|
||||
record(aSub, "as1") {
|
||||
field(INPA, 0x10)
|
||||
field(FTA, "CHAR")
|
||||
field(INPB, 0x10)
|
||||
field(FTB, "UCHAR")
|
||||
field(INPC, 0x10)
|
||||
field(FTC, "SHORT")
|
||||
field(INPD, 0x10)
|
||||
field(FTD, "USHORT")
|
||||
field(INPE, 0x10)
|
||||
field(FTE, "LONG")
|
||||
field(INPF, 0x10)
|
||||
field(FTF, "ULONG")
|
||||
field(INPG, 0x10)
|
||||
field(FTG, "FLOAT")
|
||||
field(INPH, 0x10)
|
||||
field(FTH, "DOUBLE")
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
record(ai, "alarm") {
|
||||
field(HIGH, 1)
|
||||
field(HSV, MINOR)
|
||||
field(HIHI, 2)
|
||||
field(HHSV, MAJOR)
|
||||
field(FLNK, "latch")
|
||||
}
|
||||
|
||||
record(calc, "latch") {
|
||||
field(INPA, "alarm NPP MS")
|
||||
field(INPB, "latch NPP MS")
|
||||
field(CALC, "A")
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <dbAccess.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include <calcoutRecord.h>
|
||||
#include <waveformRecord.h>
|
||||
|
||||
/*
|
||||
* Tests for specific regressions
|
||||
*/
|
||||
|
||||
void regressTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static
|
||||
void startRegressTestIoc(const char *dbfile)
|
||||
{
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("regressTest.dbd", NULL, NULL);
|
||||
regressTest_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase(dbfile, NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* https://bugs.launchpad.net/epics-base/+bug/1577108
|
||||
*/
|
||||
static
|
||||
void testArrayLength1(void)
|
||||
{
|
||||
waveformRecord *precwf;
|
||||
calcoutRecord *precco;
|
||||
double *pbuf;
|
||||
|
||||
startRegressTestIoc("regressArray1.db");
|
||||
|
||||
precwf = (waveformRecord*)testdbRecordPtr("wf");
|
||||
precco = (calcoutRecord*)testdbRecordPtr("co");
|
||||
|
||||
dbScanLock((dbCommon*)precwf);
|
||||
pbuf = (double*)precwf->bptr;
|
||||
dbScanUnlock((dbCommon*)precwf);
|
||||
|
||||
testdbPutFieldOk("wf", DBF_DOUBLE, 2.0);
|
||||
|
||||
dbScanLock((dbCommon*)precwf);
|
||||
testOk(precwf->nord==1, "wf.NORD = %u == 1", (unsigned)precwf->nord);
|
||||
testOk(pbuf[0]==2.0, "wf.VAL[0] = %f == 2.0", pbuf[0]);
|
||||
dbScanUnlock((dbCommon*)precwf);
|
||||
|
||||
dbScanLock((dbCommon*)precco);
|
||||
testOk(precco->a==2.0, "co.A = %f == 2.0", precco->a);
|
||||
dbScanUnlock((dbCommon*)precco);
|
||||
|
||||
testdbGetFieldEqual("co", DBF_DOUBLE, 2.0);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
/*
|
||||
* https://bugs.launchpad.net/epics-base/+bug/1699445
|
||||
*/
|
||||
static
|
||||
void testHexConstantLinks(void)
|
||||
{
|
||||
startRegressTestIoc("regressHex.db");
|
||||
|
||||
testdbGetFieldEqual("ai1", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("li1", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("mi1", DBR_LONG, 0x10);
|
||||
testTodoBegin("Needs JSON5 for hex arrays");
|
||||
testdbGetFieldEqual("as1.A", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.B", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.C", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.D", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.E", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.F", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.G", DBR_LONG, 0x10);
|
||||
testdbGetFieldEqual("as1.H", DBR_LONG, 0x10);
|
||||
testTodoEnd();
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static
|
||||
void testLinkMS(void)
|
||||
{
|
||||
startRegressTestIoc("regressLinkMS.db");
|
||||
|
||||
testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0);
|
||||
|
||||
testdbPutFieldOk("alarm", DBF_DOUBLE, 1.5);
|
||||
testdbGetFieldEqual("latch", DBR_DOUBLE, 1.5);
|
||||
testdbGetFieldEqual("latch.SEVR", DBR_LONG, 1);
|
||||
|
||||
testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0);
|
||||
|
||||
testdbPutFieldOk("alarm", DBF_DOUBLE, 2.5);
|
||||
testdbGetFieldEqual("latch", DBR_DOUBLE, 2.5);
|
||||
testdbGetFieldEqual("latch.SEVR", DBR_LONG, 2);
|
||||
|
||||
testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5);
|
||||
testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
|
||||
MAIN(regressTest)
|
||||
{
|
||||
testPlan(31);
|
||||
testArrayLength1();
|
||||
testHexConstantLinks();
|
||||
testLinkMS();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
extern void epicsRunRecordTests(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
epicsRunRecordTests(); /* calls epicsExit(0) */
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# no info
|
||||
record(ai, "ai-0") {
|
||||
}
|
||||
# only scan
|
||||
record(ai, "ai-1") {
|
||||
field(SSCN,"2 second")
|
||||
}
|
||||
# only delay
|
||||
record(ai, "ai-2") {
|
||||
field(SDLY,".234")
|
||||
}
|
||||
# scan and delay
|
||||
record(ai, "ai-3") {
|
||||
field(SSCN,"5 second")
|
||||
field(SDLY,".345")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# siml target doesn't exist
|
||||
record(ao, "ao-0") {
|
||||
field(SIML, "non-exist")
|
||||
field(HIGH, "1.5")
|
||||
field(HSV, "MINOR")
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 ITER Organization
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <dbAccess.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
#include <errlog.h>
|
||||
#include <alarm.h>
|
||||
|
||||
#include "recSup.h"
|
||||
#include "aiRecord.h"
|
||||
#include "aoRecord.h"
|
||||
#include "aaiRecord.h"
|
||||
#include "aaoRecord.h"
|
||||
#include "biRecord.h"
|
||||
#include "boRecord.h"
|
||||
#include "mbbiRecord.h"
|
||||
#include "mbboRecord.h"
|
||||
#include "mbbiDirectRecord.h"
|
||||
#include "mbboDirectRecord.h"
|
||||
#include "longinRecord.h"
|
||||
#include "longoutRecord.h"
|
||||
#include "int64inRecord.h"
|
||||
#include "int64outRecord.h"
|
||||
#include "stringinRecord.h"
|
||||
#include "stringoutRecord.h"
|
||||
#include "lsiRecord.h"
|
||||
#include "lsoRecord.h"
|
||||
#include "eventRecord.h"
|
||||
#include "histogramRecord.h"
|
||||
#include "waveformRecord.h"
|
||||
|
||||
/*
|
||||
* Tests for simulation mode
|
||||
*/
|
||||
|
||||
void simmTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static
|
||||
void startSimmTestIoc(const char *dbfile)
|
||||
{
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("simmTest.dbd", NULL, NULL);
|
||||
simmTest_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase(dbfile, NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
}
|
||||
|
||||
static char *rawSupp[] = {
|
||||
"ai",
|
||||
"bi",
|
||||
"mbbi",
|
||||
"mbbiDirect",
|
||||
};
|
||||
|
||||
static
|
||||
int hasRawSimmSupport(const char *rectype) {
|
||||
int i;
|
||||
for (i = 0; i < (sizeof(rawSupp)/sizeof(rawSupp[0])); i++)
|
||||
if (strcmp(rectype, rawSupp[i]) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PVNAMELENGTH 60
|
||||
static char nameVAL[PVNAMELENGTH];
|
||||
static char nameB0[PVNAMELENGTH];
|
||||
static char nameRVAL[PVNAMELENGTH];
|
||||
static char nameSGNL[PVNAMELENGTH];
|
||||
static char nameSIMM[PVNAMELENGTH];
|
||||
static char nameSIML[PVNAMELENGTH];
|
||||
static char nameSVAL[PVNAMELENGTH];
|
||||
static char nameSIOL[PVNAMELENGTH];
|
||||
static char nameSCAN[PVNAMELENGTH];
|
||||
static char namePROC[PVNAMELENGTH];
|
||||
static char namePACT[PVNAMELENGTH];
|
||||
static char nameSTAT[PVNAMELENGTH];
|
||||
static char nameSEVR[PVNAMELENGTH];
|
||||
static char nameSIMS[PVNAMELENGTH];
|
||||
static char nameTSE[PVNAMELENGTH];
|
||||
static char nameSimmode[PVNAMELENGTH];
|
||||
static char nameSimval[PVNAMELENGTH];
|
||||
static char nameSimvalNORD[PVNAMELENGTH];
|
||||
static char nameSimvalLEN[PVNAMELENGTH];
|
||||
|
||||
#define SETNAME(field) strcpy(name ## field, name); strcat(name ## field, "." #field)
|
||||
static
|
||||
void setNames(const char *name)
|
||||
{
|
||||
SETNAME(VAL); SETNAME(B0); SETNAME(RVAL); SETNAME(SGNL);
|
||||
SETNAME(SVAL); SETNAME(SIMM); SETNAME(SIML); SETNAME(SIOL); SETNAME(SIMS);
|
||||
SETNAME(SCAN); SETNAME(PROC); SETNAME(PACT);
|
||||
SETNAME(STAT); SETNAME(SEVR); SETNAME(TSE);
|
||||
strcpy(nameSimmode, name); strcat(nameSimmode, ":simmode");
|
||||
strcpy(nameSimval, name); strcat(nameSimval, ":simval");
|
||||
strcpy(nameSimvalNORD, name); strcat(nameSimvalNORD, ":simval.NORD");
|
||||
strcpy(nameSimvalLEN, name); strcat(nameSimvalLEN, ":simval.LEN");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsing of info items and xsimm structure setting
|
||||
*/
|
||||
static
|
||||
void testSimmSetup(void)
|
||||
{
|
||||
aiRecord *precai;
|
||||
|
||||
testDiag("##### Simm initialization #####");
|
||||
|
||||
/* no config */
|
||||
precai = (aiRecord*)testdbRecordPtr("ai-0");
|
||||
testOk(precai->simpvt == NULL, "ai-0.SIMPVT = %p == NULL [no callback]", precai->simpvt);
|
||||
testOk(precai->sscn == USHRT_MAX, "ai-0.SSCN = %u == USHRT_MAX (not set)", precai->sscn);
|
||||
testOk(precai->sdly < 0., "ai-0.SDLY = %g < 0.0 (not set)", precai->sdly);
|
||||
|
||||
/* with SCAN */
|
||||
precai = (aiRecord*)testdbRecordPtr("ai-1");
|
||||
testOk(precai->sscn == 5, "ai-1.SSCN = %u == 5 (2 second)", precai->sscn);
|
||||
testOk(precai->sdly < 0., "ai-1.SDLY = %g < 0.0 (not set)", precai->sdly);
|
||||
|
||||
/* with DELAY */
|
||||
precai = (aiRecord*)testdbRecordPtr("ai-2");
|
||||
testOk(precai->sscn == USHRT_MAX, "ai-2.SSCN = %u == USHRT_MAX (not set)", precai->sscn);
|
||||
testOk(precai->sdly == 0.234, "ai-2.SDLY = %g == 0.234", precai->sdly);
|
||||
|
||||
/* with SCAN and DELAY */
|
||||
precai = (aiRecord*)testdbRecordPtr("ai-3");
|
||||
testOk(precai->sscn == 4, "ai-3.SSCN = %u == 4 (5 second)", precai->sscn);
|
||||
testOk(precai->sdly == 0.345, "ai-3.SDLY = %g == 0.345", precai->sdly);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalid SIML link sets LINK/NO_ALARM if in NO_ALARM
|
||||
*/
|
||||
static
|
||||
void testSimlFail(void)
|
||||
{
|
||||
aoRecord *precao;
|
||||
|
||||
testDiag("##### Behavior for failing SIML #####");
|
||||
|
||||
precao = (aoRecord*)testdbRecordPtr("ao-0");
|
||||
/* before anything: UDF INVALID */
|
||||
testOk(precao->stat == UDF_ALARM, "ao-0.STAT = %u [%s] == %u [UDF]",
|
||||
precao->stat, epicsAlarmConditionStrings[precao->stat], UDF_ALARM);
|
||||
testOk(precao->sevr == INVALID_ALARM, "ao-0.SEVR = %u [%s] == %u [INVALID]",
|
||||
precao->sevr, epicsAlarmSeverityStrings[precao->sevr], INVALID_ALARM);
|
||||
|
||||
/* legal value: LINK NO_ALARM */
|
||||
testdbPutFieldOk("ao-0.VAL", DBR_DOUBLE, 1.0);
|
||||
testOk(precao->stat == LINK_ALARM, "ao-0.STAT = %u [%s] == %u [LINK]",
|
||||
precao->stat, epicsAlarmConditionStrings[precao->stat], LINK_ALARM);
|
||||
testOk(precao->sevr == NO_ALARM, "ao-0.SEVR = %u [%s] == %u [NO_ALARM]",
|
||||
precao->sevr, epicsAlarmSeverityStrings[precao->sevr], NO_ALARM);
|
||||
|
||||
/* HIGH/MINOR overrides */
|
||||
testdbPutFieldOk("ao-0.VAL", DBR_DOUBLE, 2.0);
|
||||
testOk(precao->stat == HIGH_ALARM, "ao-0.STAT = %u [%s] == %u [HIGH]",
|
||||
precao->stat, epicsAlarmConditionStrings[precao->stat], HIGH_ALARM);
|
||||
testOk(precao->sevr == MINOR_ALARM, "ao-0.SEVR = %u [%s] == %u [MINOR]",
|
||||
precao->sevr, epicsAlarmSeverityStrings[precao->sevr], MINOR_ALARM);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIMM triggered SCAN swapping, by writing to SIMM and through SIML
|
||||
*/
|
||||
|
||||
static
|
||||
void testSimmToggle(const char *name, epicsEnum16 *psscn)
|
||||
{
|
||||
testDiag("## SIMM toggle and SCAN swapping ##");
|
||||
|
||||
/* SIMM mode by setting the field */
|
||||
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0);
|
||||
testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn);
|
||||
|
||||
testDiag("set SIMM to YES");
|
||||
testdbPutFieldOk(nameSIMM, DBR_STRING, "YES");
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1);
|
||||
testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn);
|
||||
|
||||
/* Change simm:SCAN when simmYES */
|
||||
testdbPutFieldOk(nameSCAN, DBR_USHORT, 3);
|
||||
|
||||
testDiag("set SIMM to NO");
|
||||
testdbPutFieldOk(nameSIMM, DBR_STRING, "NO");
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0);
|
||||
testOk(*psscn == 3, "SSCN = %u == 3 (10 second)", *psscn);
|
||||
*psscn = 1;
|
||||
|
||||
if (hasRawSimmSupport(name)) {
|
||||
testDiag("set SIMM to RAW");
|
||||
testdbPutFieldOk(nameSIMM, DBR_STRING, "RAW");
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1);
|
||||
testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn);
|
||||
|
||||
testDiag("set SIMM to NO");
|
||||
testdbPutFieldOk(nameSIMM, DBR_STRING, "NO");
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0);
|
||||
testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn);
|
||||
} else {
|
||||
testDiag("Record type %s has no support for simmRAW", name);
|
||||
}
|
||||
|
||||
/* SIMM mode through SIML */
|
||||
|
||||
testdbPutFieldOk(nameSIML, DBR_STRING, nameSimmode);
|
||||
|
||||
testDiag("set SIMM (via SIML -> simmode) to YES");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual(nameSIMM, DBR_USHORT, 1);
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1);
|
||||
testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn);
|
||||
|
||||
testDiag("set SIMM (via SIML -> simmode) to NO");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0);
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0);
|
||||
testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn);
|
||||
|
||||
if (hasRawSimmSupport(name)) {
|
||||
testDiag("set SIMM (via SIML -> simmode) to RAW");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 2);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual(nameSIMM, DBR_USHORT, 2);
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1);
|
||||
testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn);
|
||||
|
||||
testDiag("set SIMM (via SIML -> simmode) to NO");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
|
||||
testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0);
|
||||
testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0);
|
||||
testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn);
|
||||
} else {
|
||||
testDiag("Record type %s has no support for simmRAW", name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reading from SVAL (direct write or through SIOL link)
|
||||
*/
|
||||
|
||||
static
|
||||
void testSvalRead(const char *name,
|
||||
const epicsTimeStamp *mytime,
|
||||
const epicsTimeStamp *svtime)
|
||||
{
|
||||
epicsTimeStamp last;
|
||||
|
||||
if (strcmp(name, "histogram") == 0)
|
||||
strcpy(nameVAL, nameSGNL);
|
||||
|
||||
if (strcmp(name, "aai") != 0 &&
|
||||
strcmp(name, "waveform") != 0 &&
|
||||
strcmp(name, "lsi") != 0) {
|
||||
|
||||
testDiag("## Reading from SVAL ##");
|
||||
|
||||
testDiag("in simmNO, SVAL must be ignored");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
testdbPutFieldOk(nameVAL, DBR_LONG, 0);
|
||||
if (strcmp(name, "stringin") == 0)
|
||||
testdbPutFieldOk(nameSVAL, DBR_STRING, "1");
|
||||
else
|
||||
testdbPutFieldOk(nameSVAL, DBR_USHORT, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameVAL, DBR_USHORT, 0);
|
||||
|
||||
testDiag("in simmYES, SVAL is used for VAL");
|
||||
testdbPutFieldOk(nameSIMS, DBR_USHORT, 0);
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameVAL, DBR_USHORT, 1);
|
||||
testDiag("No SIMS setting: STAT/SEVR == NO_ALARM");
|
||||
testdbGetFieldEqual(nameSTAT, DBR_STRING, "NO_ALARM");
|
||||
testdbGetFieldEqual(nameSEVR, DBR_USHORT, 0);
|
||||
|
||||
if (hasRawSimmSupport(name)) {
|
||||
testDiag("in simmRAW, SVAL is used for RVAL");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 2);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1);
|
||||
} else {
|
||||
testDiag("Record type %s has no support for simmRAW", name);
|
||||
}
|
||||
}
|
||||
|
||||
testDiag("## Reading from SIOL->SVAL ##");
|
||||
|
||||
/* Set SIOL link to simval */
|
||||
testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval);
|
||||
|
||||
testDiag("in simmNO, SIOL->SVAL must be ignored");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
testdbPutFieldOk(nameVAL, DBR_LONG, 0);
|
||||
testdbPutFieldOk(nameSimval, DBR_LONG, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameVAL, DBR_USHORT, 0);
|
||||
|
||||
testDiag("in simmYES, SIOL->SVAL is used for VAL");
|
||||
testdbPutFieldOk(nameSIMS, DBR_USHORT, 3);
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameVAL, DBR_USHORT, 1);
|
||||
testDiag("SIMS is INVALID: STAT/SEVR == SIMM/INVALID");
|
||||
testdbGetFieldEqual(nameSTAT, DBR_STRING, "SIMM");
|
||||
testdbGetFieldEqual(nameSEVR, DBR_USHORT, 3);
|
||||
testdbPutFieldOk(nameSIMS, DBR_USHORT, 0);
|
||||
|
||||
if (hasRawSimmSupport(name)) {
|
||||
testDiag("in simmRAW, SIOL->SVAL is used for RVAL");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 2);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1);
|
||||
} else {
|
||||
testDiag("Record type %s has no support for simmRAW", name);
|
||||
}
|
||||
|
||||
/* My timestamp must be later than simval's */
|
||||
testOk(epicsTimeLessThan(svtime, mytime), "simval time < my time [TSE = 0]");
|
||||
|
||||
testDiag("for TSE=-2 (from device) and simmYES, take time stamp from IOC or input link");
|
||||
|
||||
/* Set simmYES */
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
|
||||
/* Set TSE to -2 (from device) and reprocess: timestamps is taken through SIOL from simval */
|
||||
testdbPutFieldOk(nameTSE, DBR_SHORT, -2);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testOk(epicsTimeEqual(svtime, mytime), "simval time == my time [TSE = -2]");
|
||||
last = *mytime;
|
||||
|
||||
/* With TSE=-2 and no SIOL, timestamp is taken from IOC */
|
||||
testdbPutFieldOk(nameSIOL, DBR_STRING, "");
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testOk(epicsTimeLessThan(&last, mytime), "new time stamp from IOC [TSE = -2, no SIOL]");
|
||||
|
||||
/* Reset TSE */
|
||||
testdbPutFieldOk(nameTSE, DBR_SHORT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writing through SIOL link
|
||||
*/
|
||||
|
||||
static
|
||||
void testSiolWrite(const char *name,
|
||||
const epicsTimeStamp *mytime)
|
||||
{
|
||||
epicsTimeStamp now;
|
||||
|
||||
testDiag("## Writing through SIOL ##");
|
||||
|
||||
/* Set SIOL link to simval */
|
||||
testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval);
|
||||
|
||||
testDiag("in simmNO, SIOL must be ignored");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
if (strcmp(name, "mbboDirect") == 0)
|
||||
testdbPutFieldOk(nameB0, DBR_LONG, 1);
|
||||
else
|
||||
testdbPutFieldOk(nameVAL, DBR_LONG, 1);
|
||||
|
||||
if (strcmp(name, "aao") == 0)
|
||||
testdbGetFieldEqual(nameSimvalNORD, DBR_USHORT, 0);
|
||||
else if (strcmp(name, "lso") == 0)
|
||||
testdbGetFieldEqual(nameSimvalLEN, DBR_USHORT, 0);
|
||||
else
|
||||
testdbGetFieldEqual(nameSimval, DBR_USHORT, 0);
|
||||
|
||||
testDiag("in simmYES, SIOL is used to write VAL");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
if (strcmp(name, "mbboDirect") == 0)
|
||||
testdbPutFieldOk(nameB0, DBR_LONG, 1);
|
||||
else
|
||||
testdbPutFieldOk(nameVAL, DBR_LONG, 1);
|
||||
testdbGetFieldEqual(nameSimval, DBR_USHORT, 1);
|
||||
|
||||
/* Set TSE to -2 (from device) and reprocess: timestamp is taken from IOC */
|
||||
epicsTimeGetCurrent(&now);
|
||||
testdbPutFieldOk(nameTSE, DBR_SHORT, -2);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testOk(epicsTimeLessThan(&now, mytime), "new time stamp from IOC [TSE = -2]");
|
||||
|
||||
/* Reset TSE */
|
||||
testdbPutFieldOk(nameTSE, DBR_SHORT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Asynchronous processing using simm:DELAY
|
||||
*/
|
||||
|
||||
static
|
||||
void testSimmDelay(const char *name,
|
||||
epicsFloat64 *psdly,
|
||||
const epicsTimeStamp *mytime)
|
||||
{
|
||||
epicsTimeStamp now;
|
||||
const double delay = 0.01; /* 10 ms */
|
||||
|
||||
testDiag("## Asynchronous processing with simm:DELAY ##");
|
||||
|
||||
/* Set delay to something just long enough */
|
||||
*psdly = delay;
|
||||
|
||||
/* Process in simmNO: synchronous */
|
||||
testDiag("simm:DELAY and simmNO processes synchronously");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 0);
|
||||
epicsTimeGetCurrent(&now);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(namePACT, DBR_USHORT, 0);
|
||||
testOk(epicsTimeLessThan(&now, mytime), "time stamp is recent");
|
||||
|
||||
/* Process in simmYES: asynchronous */
|
||||
testDiag("simm:DELAY and simmYES processes asynchronously");
|
||||
testdbPutFieldOk(nameSimmode, DBR_USHORT, 1);
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(namePACT, DBR_USHORT, 1);
|
||||
epicsTimeGetCurrent(&now);
|
||||
epicsThreadSleep(1.75*delay);
|
||||
testdbGetFieldEqual(namePACT, DBR_USHORT, 0);
|
||||
testOk(epicsTimeLessThan(&now, mytime), "time stamp taken from second pass processing");
|
||||
|
||||
/* Reset delay */
|
||||
*psdly = -1.;
|
||||
}
|
||||
|
||||
#define RUNALLTESTSREAD(type) \
|
||||
testDiag("################################################### Record Type " #type); \
|
||||
setNames(#type); \
|
||||
testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \
|
||||
testSvalRead(#type, &((type ## Record*)testdbRecordPtr(#type))->time, \
|
||||
&((type ## Record*)testdbRecordPtr(#type ":simval"))->time); \
|
||||
testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \
|
||||
&((type ## Record*)testdbRecordPtr(#type))->time)
|
||||
|
||||
#define RUNALLTESTSWRITE(type) \
|
||||
testDiag("################################################### Record Type " #type); \
|
||||
setNames(#type); \
|
||||
testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \
|
||||
testSiolWrite(#type, &((type ## Record*)testdbRecordPtr(#type))->time); \
|
||||
testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \
|
||||
&((type ## Record*)testdbRecordPtr(#type))->time)
|
||||
|
||||
static
|
||||
void testAllRecTypes(void)
|
||||
{
|
||||
RUNALLTESTSREAD(ai);
|
||||
RUNALLTESTSWRITE(ao);
|
||||
RUNALLTESTSREAD(aai);
|
||||
RUNALLTESTSWRITE(aao);
|
||||
RUNALLTESTSREAD(bi);
|
||||
RUNALLTESTSWRITE(bo);
|
||||
RUNALLTESTSREAD(mbbi);
|
||||
RUNALLTESTSWRITE(mbbo);
|
||||
RUNALLTESTSREAD(mbbiDirect);
|
||||
RUNALLTESTSWRITE(mbboDirect);
|
||||
RUNALLTESTSREAD(longin);
|
||||
RUNALLTESTSWRITE(longout);
|
||||
RUNALLTESTSREAD(int64in);
|
||||
RUNALLTESTSWRITE(int64out);
|
||||
RUNALLTESTSREAD(stringin);
|
||||
RUNALLTESTSWRITE(stringout);
|
||||
RUNALLTESTSREAD(lsi);
|
||||
RUNALLTESTSWRITE(lso);
|
||||
RUNALLTESTSREAD(event);
|
||||
RUNALLTESTSREAD(waveform);
|
||||
RUNALLTESTSREAD(histogram);
|
||||
}
|
||||
|
||||
|
||||
MAIN(simmTest)
|
||||
{
|
||||
testPlan(1176);
|
||||
startSimmTestIoc("simmTest.db");
|
||||
|
||||
testSimmSetup();
|
||||
testSimlFail();
|
||||
testAllRecTypes();
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
file "simmTestSimple.template" {
|
||||
{ TYPE="ai" }
|
||||
{ TYPE="ao" }
|
||||
{ TYPE="bi" }
|
||||
{ TYPE="bo" }
|
||||
{ TYPE="mbbi" }
|
||||
{ TYPE="mbbo" }
|
||||
{ TYPE="mbbiDirect" }
|
||||
{ TYPE="mbboDirect" }
|
||||
{ TYPE="longin" }
|
||||
{ TYPE="longout" }
|
||||
{ TYPE="int64in" }
|
||||
{ TYPE="int64out" }
|
||||
{ TYPE="stringin" }
|
||||
{ TYPE="stringout" }
|
||||
{ TYPE="lsi" }
|
||||
{ TYPE="lso" }
|
||||
{ TYPE="event" }
|
||||
}
|
||||
file "simmTestArray.template" {
|
||||
{ TYPE="aai" }
|
||||
{ TYPE="aao" }
|
||||
{ TYPE="waveform" }
|
||||
}
|
||||
file "simmTestHistogram.template" {
|
||||
{ TYPE="histogram" }
|
||||
}
|
||||
file "simmSetup.db" {
|
||||
{}
|
||||
file "simmSimlFail.db" {
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Array type records
|
||||
# Regular simulation mode and simm:SCAN tests
|
||||
record($(TYPE), "$(TYPE)") {
|
||||
field(SSCN,"Event")
|
||||
field(FTVL,"SHORT")
|
||||
field(NELM,"2")
|
||||
}
|
||||
record($(TYPE), "$(TYPE):simval") {
|
||||
field(FTVL,"SHORT")
|
||||
field(NELM,"2")
|
||||
}
|
||||
record(bo, "$(TYPE):simmode") {
|
||||
field(ZNAM,"off")
|
||||
field(ONAM,"on")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Array type records
|
||||
# Regular simulation mode and simm:SCAN tests
|
||||
record($(TYPE), "$(TYPE)") {
|
||||
field(SSCN,"Event")
|
||||
field(NELM,"2")
|
||||
}
|
||||
record(ai, "$(TYPE):simval") {
|
||||
}
|
||||
record(bo, "$(TYPE):simmode") {
|
||||
field(ZNAM,"off")
|
||||
field(ONAM,"on")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Regular simulation mode and simm:SCAN tests
|
||||
record($(TYPE), "$(TYPE)") {
|
||||
field(SSCN,"Event")
|
||||
}
|
||||
record($(TYPE), "$(TYPE):simval") {
|
||||
}
|
||||
record(bo, "$(TYPE):simmode") {
|
||||
field(ZNAM,"off")
|
||||
field(ONAM,"on")
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 UChicago Argonne LLC, as operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errlog.h"
|
||||
#include "registryFunction.h"
|
||||
#include "subRecord.h"
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
void checkDtyp(const char *rec)
|
||||
{
|
||||
char dtyp[16];
|
||||
|
||||
strcpy(dtyp, rec);
|
||||
strcat(dtyp, ".DTYP");
|
||||
|
||||
testdbGetFieldEqual(dtyp, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(dtyp, DBR_STRING, "Soft Channel");
|
||||
}
|
||||
|
||||
static
|
||||
void doProcess(const char *rec)
|
||||
{
|
||||
char proc[16];
|
||||
|
||||
strcpy(proc, rec);
|
||||
strcat(proc, ".PROC");
|
||||
|
||||
testdbPutFieldOk(proc, DBR_CHAR, 1);
|
||||
}
|
||||
|
||||
/* Group 0 are soft-channel input records with INP being a DB or CA link
|
||||
* to the PV 'source'. Their VAL fields all start out with the default
|
||||
* value for the type, i.e. 0 or an empty string. Triggering record
|
||||
* processing reads the value from the 'source' PV.
|
||||
*/
|
||||
|
||||
static
|
||||
void testGroup0(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ai0", "bi0", "di0", "ii0", "li0", "lsi0", "mi0", "si0",
|
||||
"ai0c", "bi0c", "di0c", "ii0c", "li0c", "lsi0c", "mi0c", "si0c",
|
||||
NULL
|
||||
};
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
testdbPutFieldOk("source", DBR_LONG, 1);
|
||||
/* The above put sends CA monitors to all of the CA links, but
|
||||
* doesn't trigger record processing (the links are not CP/CPP).
|
||||
* How could we wait until all of those monitors have arrived,
|
||||
* instead of just waiting for an arbitrary time period?
|
||||
*/
|
||||
epicsThreadSleep(1.0); /* FIXME: Wait here? */
|
||||
for (rec = records; *rec; rec++) {
|
||||
if (strncmp(*rec, "lsi0", 4) != 0)
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, 0);
|
||||
checkDtyp(*rec);
|
||||
doProcess(*rec);
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, 1);
|
||||
}
|
||||
|
||||
testdbPutFieldOk("source", DBR_LONG, 0);
|
||||
epicsThreadSleep(1.0); /* FIXME: Wait here as above */
|
||||
for (rec = records; *rec; rec++) {
|
||||
doProcess(*rec);
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Group 1 are all soft-channel input records with INP being a non-zero
|
||||
* "const" JSON-link, 9 for most records, 1 for the binary. Their VAL
|
||||
* fields should all be initialized to that constant value. Triggering
|
||||
* record processing should succeed, but shouldn't change VAL.
|
||||
*/
|
||||
static
|
||||
void testGroup1(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"bi1",
|
||||
"ai1", "di1", "ii1", "li1", "lsi1", "mi1", "si1", NULL
|
||||
};
|
||||
int init = 1; /* bi1 initializes to 1 */
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, init);
|
||||
doProcess(*rec);
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, init);
|
||||
init = 9; /* other records initialize to 9 */
|
||||
}
|
||||
}
|
||||
|
||||
/* Group 2 are all soft-channel input records with INP being a CONSTANT
|
||||
* link with value 9 for most records, 1 for the binary. Their VAL
|
||||
* fields should all be initialized to that constant value. Triggering
|
||||
* record processing should succeed, but shouldn't change VAL.
|
||||
*/
|
||||
static
|
||||
void testGroup2(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"bi2",
|
||||
"ai2", "di2", "ii2", "li2", "mi2", NULL
|
||||
};
|
||||
int init = 1; /* bi1 initializes to 1 */
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, init);
|
||||
doProcess(*rec);
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, init);
|
||||
init = 9; /* other records initialize to 9 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dest;
|
||||
epicsEventId destEvent;
|
||||
|
||||
static
|
||||
long destSubr(subRecord *prec)
|
||||
{
|
||||
dest = prec->val;
|
||||
prec->val = -1;
|
||||
epicsEventMustTrigger(destEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void checkOutput3(const char *rec, int value)
|
||||
{
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
testdbPutFieldOk(rec, DBR_LONG, value);
|
||||
|
||||
epicsEventMustWait(destEvent);
|
||||
testOk(dest == value, "value %d output -> %d", value, dest);
|
||||
}
|
||||
|
||||
/* Group 3 are all soft-channel output records with OUT being a DB or
|
||||
* local CA link to the subRecord 'dest'; DB links have the PP flag,
|
||||
* for CA links the VAL field is marked PP. Putting a value to the
|
||||
* output record writes that value to 'dest'.
|
||||
*/
|
||||
static
|
||||
void testGroup3(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ao3", "bo3", "io3", "lo3", "lso3", "mo3", "so3",
|
||||
"ao3c", "bo3c", "io3c", "lo3c", "lso3c", "mo3c", "so3c",
|
||||
NULL,
|
||||
};
|
||||
|
||||
destEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput3(*rec, 1);
|
||||
checkDtyp(*rec);
|
||||
}
|
||||
checkOutput3("do3.B0", 1);
|
||||
checkDtyp("do3");
|
||||
checkOutput3("do3c.B0", 1);
|
||||
checkDtyp("do3c");
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput3(*rec, 0);
|
||||
}
|
||||
checkOutput3("do3.B0", 0);
|
||||
checkOutput3("do3c.B0", 0);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void checkOutput4(const char *rec, int value)
|
||||
{
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
testdbPutFieldOk(rec, DBR_LONG, value);
|
||||
}
|
||||
|
||||
/* Group 4 are all soft-channel output records with OUT being empty
|
||||
* (i.e. a CONSTANT link). Putting a value to the record must succeed.
|
||||
*/
|
||||
static
|
||||
void testGroup4(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ao4", "bo4", "do4.B0", "io4", "lo4", "lso4", "mo4", "so4", NULL,
|
||||
};
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput4(*rec, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
|
||||
MAIN(softTest)
|
||||
{
|
||||
testPlan(258);
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
registryFunctionAdd("destSubr", (REGISTRYFUNCTION) destSubr);
|
||||
|
||||
testdbReadDatabase("softTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testGroup0();
|
||||
testGroup1();
|
||||
testGroup2();
|
||||
testGroup3();
|
||||
testGroup4();
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
# Group 0 are all soft-channel input records with INP being a DB link
|
||||
# to the PV 'source'. Their VAL fields all start out with the default
|
||||
# value for the type, i.e. 0 or an empty string. Triggering record
|
||||
# processing should read the integer value from the 'source' PV.
|
||||
|
||||
record(longin, "source") {}
|
||||
|
||||
record(ai, "ai0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source")
|
||||
}
|
||||
record(bi, "bi0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source")
|
||||
}
|
||||
record(longin, "li0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source")
|
||||
}
|
||||
record(mbbiDirect, "di0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source")
|
||||
}
|
||||
record(mbbi, "mi0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lsi, "lsi0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
field(INP, "source")
|
||||
}
|
||||
record(stringin, "si0") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source")
|
||||
}
|
||||
|
||||
record(ai, "ai0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(bi, "bi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(longin, "li0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(mbbiDirect, "di0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(mbbi, "mi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source CA")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lsi, "lsi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(stringin, "si0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
|
||||
|
||||
# Group 1 are all soft-channel input records with INP being a non-zero
|
||||
# "const" JSON-link, 9 for most records, 1 for the binary. Their VAL
|
||||
# fields should all be initialized to that constant value. Triggering
|
||||
# record processing should succeed, but shouldn't change VAL.
|
||||
|
||||
record(ai, "ai1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, {const:9})
|
||||
}
|
||||
record(bi, "bi1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, {const:1})
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, {const:9})
|
||||
}
|
||||
record(longin, "li1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, {const:9})
|
||||
}
|
||||
record(mbbiDirect, "di1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, {const:9})
|
||||
}
|
||||
record(mbbi, "mi1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, {const:9})
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lsi, "lsi1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
field(INP, {const:"9"})
|
||||
}
|
||||
record(stringin, "si1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, {const:"9"})
|
||||
}
|
||||
|
||||
|
||||
# Group 2 are all soft-channel input records with INP being a CONSTANT
|
||||
# link with value 9 for most records, 1 for the binary. Their VAL
|
||||
# fields should all be initialized to that constant value. Triggering
|
||||
# record processing should succeed, but shouldn't change VAL.
|
||||
|
||||
record(ai, "ai2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, 9)
|
||||
}
|
||||
record(bi, "bi2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, 1)
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, 9)
|
||||
}
|
||||
record(longin, "li2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, 9)
|
||||
}
|
||||
record(mbbiDirect, "di2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, 9)
|
||||
}
|
||||
record(mbbi, "mi2") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, 9)
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
|
||||
|
||||
# Group 3 are all soft-channel output records with OUT being a DB or
|
||||
# CA link to the PV 'dest' with PP. Putting a value to the record
|
||||
# under test writes the value to 'dest' and processes it.
|
||||
|
||||
record(sub, "dest") {
|
||||
field(SNAM, "destSubr")
|
||||
field(VAL, -1)
|
||||
}
|
||||
|
||||
record(ao, "ao3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(bo, "bo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(longout, "lo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(mbboDirect, "do3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(mbbo, "mo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest PP")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
|
||||
record(ao, "ao3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(bo, "bo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(longout, "lo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(mbboDirect, "do3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(mbbo, "mo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest CA")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
|
||||
|
||||
# Group 4 are all soft-channel output records with OUT being empty
|
||||
# (i.e. a CONSTANT link). Putting a value to the record must succeed.
|
||||
|
||||
record(ao, "ao4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(bo, "bo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(longout, "lo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(mbboDirect, "do4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
}
|
||||
record(mbbo, "mo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
Reference in New Issue
Block a user