Merge branch 'epics-base:7.0' into add-simm-to-ao-records
This commit is contained in:
@@ -117,6 +117,13 @@ testHarness_SRCS += dbCACTest.cpp
|
||||
TESTS += dbCaLinkTest
|
||||
TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db
|
||||
|
||||
TESTPROD_HOST += dbDbLinkTest
|
||||
dbDbLinkTest_SRCS += dbDbLinkTest.c
|
||||
dbDbLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbDbLinkTest.c
|
||||
TESTS += dbDbLinkTest
|
||||
TESTFILES += ../dbDbLinkTest.db
|
||||
|
||||
TESTPROD_HOST += scanIoTest
|
||||
scanIoTest_SRCS += scanIoTest.c
|
||||
scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -200,7 +207,9 @@ include $(TOP)/configure/RULES
|
||||
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h
|
||||
dbDbLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
dbPutGetTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
devx$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
|
||||
@@ -54,7 +54,7 @@ typedef struct myPvt {
|
||||
int resultFail;
|
||||
} myPvt;
|
||||
|
||||
epicsEventId finished;
|
||||
static epicsEventId finished;
|
||||
|
||||
static void myCallback(epicsCallback *pCallback)
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ typedef struct myPvt {
|
||||
int resultFail;
|
||||
} myPvt;
|
||||
|
||||
epicsEventId finished;
|
||||
static epicsEventId finished;
|
||||
|
||||
|
||||
static void myCallback(epicsCallback *pCallback)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "dbAccess.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "dbEvent.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
/* Declarations from cadef.h and db_access.h which we can't include here */
|
||||
typedef void * chid;
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2020 Michael Davidsaver
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <errlog.h>
|
||||
#include <epicsTime.h>
|
||||
|
||||
#include <dbLock.h>
|
||||
#include <dbAccess.h>
|
||||
#include <recGbl.h>
|
||||
#include <alarm.h>
|
||||
|
||||
#include "xRecord.h"
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static
|
||||
void checkTime(void)
|
||||
{
|
||||
epicsTimeStamp stamp;
|
||||
epicsUTag tag;
|
||||
|
||||
dbCommon* target = testdbRecordPtr("target");
|
||||
dbCommon* src = testdbRecordPtr("src");
|
||||
|
||||
testDiag("checkTime()");
|
||||
|
||||
dbScanLock(target);
|
||||
target->time.secPastEpoch = 0x12345678;
|
||||
target->time.nsec = 0x9abcdef0;
|
||||
target->utag = 0xdeadbeef;
|
||||
dbScanUnlock(target);
|
||||
|
||||
dbScanLock(src);
|
||||
testOk1(0==dbGetTimeStamp(dbGetDevLink(src), &stamp));
|
||||
dbScanUnlock(src);
|
||||
|
||||
testOk1(stamp.secPastEpoch==0x12345678);
|
||||
testOk1(stamp.nsec==0x9abcdef0);
|
||||
stamp.secPastEpoch = 0;
|
||||
stamp.nsec = 0;
|
||||
|
||||
dbScanLock(src);
|
||||
testOk1(0==dbGetTimeStampTag(dbGetDevLink(src), &stamp, &tag));
|
||||
dbScanUnlock(src);
|
||||
|
||||
testOk1(stamp.secPastEpoch==0x12345678);
|
||||
testOk1(stamp.nsec==0x9abcdef0);
|
||||
testOk1(tag==0xdeadbeef);
|
||||
}
|
||||
|
||||
static
|
||||
void alarmProc(xRecord *prec)
|
||||
{
|
||||
recGblSetSevrMsg(prec, READ_ALARM, MAJOR_ALARM, "a %s", "message");
|
||||
prec->val = 0;
|
||||
}
|
||||
|
||||
static
|
||||
void checkAlarm(void)
|
||||
{
|
||||
epicsEnum16 stat, sevr;
|
||||
|
||||
xRecord* target = (xRecord*)testdbRecordPtr("target");
|
||||
dbCommon* src = testdbRecordPtr("src");
|
||||
|
||||
char amsg[sizeof(src->amsg)];
|
||||
|
||||
testDiag("checkAlarm()");
|
||||
|
||||
dbScanLock((dbCommon*)target);
|
||||
target->clbk = &alarmProc;
|
||||
dbProcess((dbCommon*)target);
|
||||
target->clbk = NULL;
|
||||
dbScanUnlock((dbCommon*)target);
|
||||
|
||||
dbScanLock(src);
|
||||
testOk1(0==dbGetAlarm(dbGetDevLink(src), &stat, &sevr));
|
||||
dbScanUnlock(src);
|
||||
|
||||
testOk1(stat==READ_ALARM);
|
||||
testOk1(sevr==MAJOR_ALARM);
|
||||
stat = sevr = 0;
|
||||
|
||||
dbScanLock(src);
|
||||
testOk1(0==dbGetAlarmMsg(dbGetDevLink(src), &stat, &sevr, amsg, sizeof(amsg)));
|
||||
dbScanUnlock(src);
|
||||
|
||||
testOk1(stat==READ_ALARM);
|
||||
testOk1(sevr==MAJOR_ALARM);
|
||||
testOk1(strcmp(amsg, "a message")==0);
|
||||
stat = sevr = 0;
|
||||
memset(amsg, 0, sizeof(amsg));
|
||||
|
||||
dbScanLock(src);
|
||||
testOk1(0==dbGetAlarmMsg(dbGetDevLink(src), &stat, &sevr, amsg, 5));
|
||||
dbScanUnlock(src);
|
||||
|
||||
testOk1(stat==READ_ALARM);
|
||||
testOk1(sevr==MAJOR_ALARM);
|
||||
testOk1(strcmp(amsg, "a me")==0);
|
||||
}
|
||||
|
||||
MAIN(dbDbLinkTest)
|
||||
{
|
||||
testPlan(18);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbDbLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
checkTime();
|
||||
checkAlarm();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
record(x, "target") {
|
||||
}
|
||||
|
||||
record(x, "src") {
|
||||
field(INP, "target.VAL")
|
||||
}
|
||||
@@ -7,12 +7,186 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <errlog.h>
|
||||
#include <alarm.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbStaticLib.h>
|
||||
#include <dbStaticPvt.h>
|
||||
#include <dbChannel.h>
|
||||
#include <dbEvent.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include "xRecord.h"
|
||||
|
||||
typedef struct {
|
||||
DBRstatus
|
||||
DBRamsg
|
||||
DBRunits
|
||||
DBRprecision
|
||||
DBRtime
|
||||
DBRutag
|
||||
DBRgrDouble
|
||||
DBRctrlDouble
|
||||
DBRalDouble
|
||||
} dbMetaDouble;
|
||||
|
||||
enum {dbMetaDoubleMask = DBR_STATUS | DBR_AMSG | DBR_UNITS | DBR_PRECISION | DBR_TIME | DBR_UTAG | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE};
|
||||
|
||||
static
|
||||
void testdbMetaDoubleSizes(void)
|
||||
{
|
||||
dbMetaDouble meta;
|
||||
size_t pos=0;
|
||||
|
||||
testDiag("dbMetaDouble may not have padding");
|
||||
#define testOffset(FLD) do {\
|
||||
testOk(offsetof(dbMetaDouble, FLD)==pos, "offset(meta, " #FLD "), %u == %u", (unsigned)offsetof(dbMetaDouble, FLD), (unsigned)pos); \
|
||||
pos += sizeof(meta.FLD); \
|
||||
}while(0)
|
||||
|
||||
testOffset(status);
|
||||
testOffset(severity);
|
||||
testOffset(acks);
|
||||
testOffset(ackt);
|
||||
testOffset(amsg);
|
||||
testOffset(units);
|
||||
testOffset(precision);
|
||||
testOffset(time);
|
||||
testOffset(utag);
|
||||
testOffset(upper_disp_limit);
|
||||
testOffset(lower_disp_limit);
|
||||
testOffset(upper_ctrl_limit);
|
||||
testOffset(lower_ctrl_limit);
|
||||
testOffset(upper_alarm_limit);
|
||||
testOffset(upper_warning_limit);
|
||||
testOffset(lower_warning_limit);
|
||||
testOffset(lower_alarm_limit);
|
||||
#undef testOffset
|
||||
testOk(sizeof(dbMetaDouble)==pos, "sizeof(dbMetaDouble), %u == %u", (unsigned)sizeof(dbMetaDouble), (unsigned)pos);
|
||||
}
|
||||
|
||||
static
|
||||
void checkDoubleGet(dbChannel *chan, db_field_log* pfl)
|
||||
{
|
||||
dbMetaDouble meta;
|
||||
long options = (long)dbMetaDoubleMask;
|
||||
long nReq = 0;
|
||||
long status;
|
||||
|
||||
status=dbChannelGet(chan, DBF_DOUBLE, &meta, &options, &nReq, pfl);
|
||||
testOk(status==0, "dbGet OTST : %ld", status);
|
||||
|
||||
testOk1(meta.severity==INVALID_ALARM);
|
||||
testOk1(meta.status==UDF_ALARM);
|
||||
testOk1(meta.acks==MAJOR_ALARM);
|
||||
testOk1(meta.ackt==1);
|
||||
testOk1(strncmp(meta.amsg, "oops", DB_UNITS_SIZE)==0);
|
||||
testOk1(meta.time.secPastEpoch==0x12345678);
|
||||
testOk1(meta.time.nsec==0x90abcdef);
|
||||
testOk1(meta.utag==0x10203040);
|
||||
testOk1(meta.precision.dp==0x12345678);
|
||||
testOk1(strncmp(meta.units, "arbitrary", DB_UNITS_SIZE)==0);
|
||||
#define limitEq(UL, FL, VAL) testOk(meta.UL ## _ ## FL ## _limit == (VAL), #UL "_" #FL "_limit (%f) == %f", meta.UL ## _ ## FL ## _limit, VAL)
|
||||
limitEq(lower, disp, 10000000.0-1.0);
|
||||
limitEq(upper, disp, 10000000.0+1.0);
|
||||
limitEq(lower, ctrl, 10000000.0-2.0);
|
||||
limitEq(upper, ctrl, 10000000.0+2.0);
|
||||
limitEq(lower, alarm, 10000000.0-3.0);
|
||||
limitEq(lower, warning, 10000000.0-4.0);
|
||||
limitEq(upper, warning, 10000000.0+4.0);
|
||||
limitEq(upper, alarm, 10000000.0+3.0);
|
||||
#undef limitEq
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
void testdbMetaDoubleGet(void)
|
||||
{
|
||||
xRecord* prec = (xRecord*)testdbRecordPtr("recmeta");
|
||||
dbChannel *chan = dbChannelCreate("recmeta.OTST");
|
||||
db_field_log *pfl;
|
||||
evSubscrip evsub;
|
||||
long status;
|
||||
dbMetaDouble meta;
|
||||
|
||||
STATIC_ASSERT(sizeof(meta.amsg)==sizeof(prec->amsg));
|
||||
STATIC_ASSERT(sizeof(meta.amsg)==sizeof(pfl->amsg));
|
||||
|
||||
if(!chan)
|
||||
testAbort("Missing recmeta OTST");
|
||||
if((status=dbChannelOpen(chan))!=0)
|
||||
testAbort("can't open recmeta OTST : %ld", status);
|
||||
|
||||
dbScanLock((dbCommon*)prec);
|
||||
/* ensure that all meta-data has different non-zero values */
|
||||
prec->otst = 10000000.0;
|
||||
prec->sevr = INVALID_ALARM;
|
||||
prec->stat = UDF_ALARM;
|
||||
strcpy(prec->amsg, "oops");
|
||||
prec->acks = MAJOR_ALARM;
|
||||
prec->time.secPastEpoch = 0x12345678;
|
||||
prec->time.nsec = 0x90abcdef;
|
||||
prec->utag = 0x10203040;
|
||||
|
||||
testDiag("dbGet directly from record");
|
||||
checkDoubleGet(chan, NULL);
|
||||
|
||||
testDiag("dbGet from field log");
|
||||
|
||||
/* bare minimum init for db_create_event_log() */
|
||||
memset(&evsub, 0, sizeof(evsub));
|
||||
evsub.chan = chan;
|
||||
evsub.useValque = 1;
|
||||
pfl = db_create_event_log(&evsub);
|
||||
/* spoil things which should now come from field log */
|
||||
prec->sevr = 0;
|
||||
prec->stat = 0;
|
||||
strcpy(prec->amsg, "invalid");
|
||||
prec->time.secPastEpoch = 0xdeadbeef;
|
||||
prec->time.nsec = 0xdeadbeef;
|
||||
prec->utag = 0xdeadbeef;
|
||||
|
||||
/* never any filters, so skip pre/post */
|
||||
checkDoubleGet(chan, pfl);
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
dbScanUnlock((dbCommon*)prec);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
DBRstatus
|
||||
DBRamsg
|
||||
DBRtime
|
||||
DBRutag
|
||||
DBRenumStrs
|
||||
} dbMetaEnum;
|
||||
|
||||
static
|
||||
void testdbMetaEnumSizes(void)
|
||||
{
|
||||
dbMetaEnum meta;
|
||||
size_t pos=0;
|
||||
|
||||
testDiag("dbMetaEnum may not have implicit padding");
|
||||
#define testOffset(FLD) do {\
|
||||
testOk(offsetof(dbMetaEnum, FLD)==pos, "offset(meta, " #FLD "), %u == %u", (unsigned)offsetof(dbMetaEnum, FLD), (unsigned)pos); \
|
||||
pos += sizeof(meta.FLD); \
|
||||
}while(0)
|
||||
|
||||
testOffset(status);
|
||||
testOffset(severity);
|
||||
testOffset(acks);
|
||||
testOffset(ackt);
|
||||
testOffset(amsg);
|
||||
testOffset(time);
|
||||
testOffset(utag);
|
||||
testOffset(no_str);
|
||||
testOffset(padenumStrs);
|
||||
testOffset(strs);
|
||||
#undef testOffset
|
||||
testOk(sizeof(dbMetaEnum)==pos, "sizeof(dbMetaEnum), %u == %u", (unsigned)sizeof(dbMetaEnum), (unsigned)pos);
|
||||
}
|
||||
|
||||
static
|
||||
void testdbGetStringEqual(const char *pv, const char *expected)
|
||||
{
|
||||
@@ -143,9 +317,12 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(dbPutGet)
|
||||
{
|
||||
testPlan(44);
|
||||
testPlan(111);
|
||||
testdbPrepare();
|
||||
|
||||
testdbMetaDoubleSizes();
|
||||
testdbMetaEnumSizes();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbPutGetTest.db", NULL, NULL);
|
||||
@@ -158,6 +335,8 @@ MAIN(dbPutGet)
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testdbMetaDoubleGet();
|
||||
|
||||
testLongLink();
|
||||
testLongAttr();
|
||||
testLongField();
|
||||
|
||||
@@ -44,3 +44,5 @@ record(arr, "arr") {
|
||||
field(FTVL, "ULONG")
|
||||
field(NELM, "10")
|
||||
}
|
||||
|
||||
record(x, "recmeta") {}
|
||||
|
||||
@@ -30,6 +30,7 @@ int dbLockTest(void);
|
||||
int dbPutLinkTest(void);
|
||||
int dbStaticTest(void);
|
||||
int dbCaLinkTest(void);
|
||||
int dbDbLinkTest(void);
|
||||
int testDbChannel(void);
|
||||
int chfPluginTest(void);
|
||||
int arrShorthandTest(void);
|
||||
@@ -52,6 +53,7 @@ void epicsRunDbTests(void)
|
||||
runTest(dbPutLinkTest);
|
||||
runTest(dbStaticTest);
|
||||
runTest(dbCaLinkTest);
|
||||
runTest(dbDbLinkTest);
|
||||
runTest(testDbChannel);
|
||||
runTest(arrShorthandTest);
|
||||
runTest(recGblCheckDeadbandTest);
|
||||
|
||||
@@ -76,7 +76,82 @@ static long process(struct dbCommon *pcommon)
|
||||
return ret;
|
||||
}
|
||||
|
||||
long get_units(struct dbAddr *paddr, char *units)
|
||||
{
|
||||
if(dbGetFieldIndex(paddr)==xRecordOTST) {
|
||||
strncpy(units, "arbitrary", DB_UNITS_SIZE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long get_precision(const struct dbAddr *paddr, long *precision)
|
||||
{
|
||||
if(dbGetFieldIndex(paddr)==xRecordOTST) {
|
||||
*precision = 0x12345678;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long get_graphic_double(struct dbAddr *paddr, struct dbr_grDouble *p)
|
||||
{
|
||||
xRecord *prec = (xRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==xRecordOTST) {
|
||||
p->lower_disp_limit = prec->otst-1.0;
|
||||
p->upper_disp_limit = prec->otst+1.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long get_control_double(struct dbAddr *paddr, struct dbr_ctrlDouble *p)
|
||||
{
|
||||
xRecord *prec = (xRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==xRecordOTST) {
|
||||
p->lower_ctrl_limit = prec->otst-2.0;
|
||||
p->upper_ctrl_limit = prec->otst+2.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
long get_alarm_double(struct dbAddr *paddr, struct dbr_alDouble *p)
|
||||
{
|
||||
xRecord *prec = (xRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==xRecordOTST) {
|
||||
p->lower_alarm_limit = prec->otst-3.0;
|
||||
p->lower_warning_limit = prec->otst-4.0;
|
||||
p->upper_warning_limit = prec->otst+4.0;
|
||||
p->upper_alarm_limit = prec->otst+3.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
|
||||
static rset xRSET = {
|
||||
RSETNUMBER, NULL, NULL, init_record, process
|
||||
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,xRSET);
|
||||
|
||||
@@ -46,4 +46,8 @@ recordtype(x) {
|
||||
special(SPC_NOMOD)
|
||||
extra("void (*clbk)(struct xRecord*)")
|
||||
}
|
||||
field(OTST, DBF_DOUBLE) {
|
||||
prompt("dbGet() options test")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +175,12 @@ dbHeaderTestxx_SRCS += dbHeaderTestxx.cpp
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
|
||||
# Unfortunately hangs too often on CI systems:
|
||||
ifndef CI
|
||||
TESTS += netget
|
||||
endif
|
||||
endif
|
||||
|
||||
# epicsRunRecordTests runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunRecordTests.c
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
epicsShareFunc void testRestore(void);
|
||||
|
||||
#include "epicsExport.h"
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use if $^O eq 'MSWin32', "Win32::Process";
|
||||
use if $^O eq 'MSWin32', "Win32";
|
||||
|
||||
use lib '@TOP@/lib/perl';
|
||||
|
||||
use Test::More tests => 3;
|
||||
use EPICS::IOC;
|
||||
|
||||
# Set to 1 to echo all IOC and client communications
|
||||
my $debug = 1;
|
||||
my $debug = 0;
|
||||
|
||||
$ENV{HARNESS_ACTIVE} = 1 if scalar @ARGV && shift eq '-tap';
|
||||
|
||||
@@ -35,7 +38,7 @@ $ioc->debug($debug);
|
||||
|
||||
$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = sub {
|
||||
$ioc->exit;
|
||||
BAIL_OUT('Caught signal');
|
||||
BAIL_OUT("Caught signal: $_[0]");
|
||||
};
|
||||
|
||||
|
||||
@@ -50,11 +53,21 @@ sub kill_bail {
|
||||
}
|
||||
|
||||
sub watchdog (&$$) {
|
||||
my ($do, $timeout, $abort) = @_;
|
||||
$SIG{ALRM} = $abort;
|
||||
alarm $timeout;
|
||||
&$do;
|
||||
alarm 0;
|
||||
my ($code, $timeout, $fail) = @_;
|
||||
my $bark = "Woof $$\n";
|
||||
my $result;
|
||||
eval {
|
||||
local $SIG{__DIE__};
|
||||
local $SIG{ALRM} = sub { die $bark };
|
||||
alarm $timeout;
|
||||
$result = &$code;
|
||||
alarm 0;
|
||||
};
|
||||
if ($@) {
|
||||
die if $@ ne $bark;
|
||||
$result = &$fail;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,19 +103,6 @@ like($version, qr/^ \d+ \. \d+ \. \d+ /x,
|
||||
"Got BaseVersion '$version' from iocsh");
|
||||
|
||||
|
||||
# Client Tests
|
||||
|
||||
my $client = EPICS::IOC->new;
|
||||
$client->debug($debug);
|
||||
|
||||
sub close_client {
|
||||
my $doing = shift;
|
||||
return sub {
|
||||
diag("Timeout $doing");
|
||||
$client->close;
|
||||
}
|
||||
}
|
||||
|
||||
# Channel Access
|
||||
|
||||
SKIP: {
|
||||
@@ -119,17 +119,9 @@ SKIP: {
|
||||
|
||||
# CA Client test
|
||||
|
||||
watchdog {
|
||||
$client->start($caget, '-w5', $pv);
|
||||
my $caVersion = $client->_getline;
|
||||
like($caVersion, qr/^ $pv \s+ \Q$version\E $/x,
|
||||
'Got same BaseVersion from caget');
|
||||
my @errors = $client->_geterrors;
|
||||
note("Errors from caget:\n",
|
||||
map(" $_\n", @errors))
|
||||
if scalar @errors;
|
||||
$client->close;
|
||||
} 15, close_client('doing caget');
|
||||
my $caVersion = qx_timeout(15, "$caget -w5 $pv");
|
||||
like($caVersion, qr/^ $pv \s+ \Q$version\E $/x,
|
||||
'Got same BaseVersion from caget');
|
||||
}
|
||||
|
||||
|
||||
@@ -152,17 +144,71 @@ SKIP: {
|
||||
|
||||
# PVA Client test
|
||||
|
||||
watchdog {
|
||||
$client->start($pvget, '-w5', $pv);
|
||||
my $pvaVersion = $client->_getline;
|
||||
like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x,
|
||||
'Got same BaseVersion from pvget');
|
||||
my @errors = $client->_geterrors;
|
||||
note("Errors from pvget:\n",
|
||||
map(" $_\n", @errors))
|
||||
if scalar @errors;
|
||||
$client->close;
|
||||
} 10, close_client('doing pvget');
|
||||
my $pvaVersion = qx_timeout(15, "$pvget -w5 $pv");
|
||||
like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x,
|
||||
'Got same BaseVersion from pvget');
|
||||
}
|
||||
|
||||
$ioc->exit;
|
||||
|
||||
|
||||
# Process timeout utilities
|
||||
|
||||
sub system_timeout {
|
||||
my ($timeout, $cmdline) = @_;
|
||||
my $status;
|
||||
if ($^O eq 'MSWin32') {
|
||||
my $proc;
|
||||
(my $app) = split ' ', $cmdline;
|
||||
if (! Win32::Process::Create($proc, $app, $cmdline,
|
||||
1, &Win32::Process::NORMAL_PRIORITY_CLASS, '.')) {
|
||||
my $err = Win32::FormatMessage(Win32::GetLastError());
|
||||
die "Can't create Process for '$cmdline': $err\n";
|
||||
}
|
||||
if (! $proc->Wait(1000 * $timeout)) {
|
||||
$proc->Kill(1);
|
||||
note("Timed out '$cmdline' after $timeout seconds\n");
|
||||
}
|
||||
my $status;
|
||||
$proc->GetExitCode($status);
|
||||
return $status;
|
||||
}
|
||||
else {
|
||||
my $pid;
|
||||
$status = watchdog {
|
||||
$pid = fork();
|
||||
die "Can't fork: $!\n"
|
||||
unless defined $pid;
|
||||
exec $cmdline
|
||||
or die "Can't exec: $!\n"
|
||||
unless $pid;
|
||||
waitpid $pid, 0;
|
||||
return $? >> 8;
|
||||
} $timeout, sub {
|
||||
kill 9, $pid if $pid;
|
||||
note("Timed out '$cmdline' after $timeout seconds\n");
|
||||
return -2;
|
||||
};
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
sub qx_timeout {
|
||||
my ($timeout, $cmdline) = @_;
|
||||
open(my $stdout, '>&STDOUT')
|
||||
or die "Can't save STDOUT: $!\n";
|
||||
my $outfile = "stdout-$$.txt";
|
||||
unlink $outfile;
|
||||
open STDOUT, '>', $outfile;
|
||||
my $text;
|
||||
if (system_timeout($timeout, $cmdline) == 0 && -r $outfile) {
|
||||
open(my $file, '<', $outfile)
|
||||
or die "Can't open $outfile: $!\n";
|
||||
$text = join '', <$file>;
|
||||
close $file;
|
||||
}
|
||||
open(STDOUT, '>&', $stdout)
|
||||
or die "Can't restore STDOUT: $!\n";
|
||||
unlink $outfile;
|
||||
return $text;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
#include <testMain.h>
|
||||
#include <dbAccess.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
#include <errlog.h>
|
||||
#include <alarm.h>
|
||||
#include <callback.h>
|
||||
|
||||
#include "recSup.h"
|
||||
#include "aiRecord.h"
|
||||
@@ -428,6 +430,15 @@ void testSiolWrite(const char *name,
|
||||
* Asynchronous processing using simm:DELAY
|
||||
*/
|
||||
|
||||
static void
|
||||
ping(CALLBACK *pcb)
|
||||
{
|
||||
epicsEventId ev;
|
||||
callbackGetUser(ev, pcb);
|
||||
|
||||
epicsEventMustTrigger(ev);
|
||||
}
|
||||
|
||||
static
|
||||
void testSimmDelay(const char *name,
|
||||
epicsFloat64 *psdly,
|
||||
@@ -436,6 +447,14 @@ void testSimmDelay(const char *name,
|
||||
epicsTimeStamp now;
|
||||
const double delay = 0.01; /* 10 ms */
|
||||
double diff;
|
||||
epicsEventId poked;
|
||||
CALLBACK cb;
|
||||
|
||||
memset(&cb, 0, sizeof(CALLBACK));
|
||||
poked = epicsEventMustCreate(epicsEventEmpty);
|
||||
callbackSetCallback(ping, &cb);
|
||||
callbackSetPriority(priorityLow, &cb);
|
||||
callbackSetUser(poked, &cb);
|
||||
|
||||
testDiag("## Asynchronous processing with simm:DELAY ##");
|
||||
|
||||
@@ -457,14 +476,11 @@ void testSimmDelay(const char *name,
|
||||
testdbPutFieldOk(namePROC, DBR_LONG, 0);
|
||||
testdbGetFieldEqual(namePACT, DBR_USHORT, 1);
|
||||
epicsTimeGetCurrent(&now);
|
||||
epicsThreadSleep(1.75*delay);
|
||||
if(testImpreciseTiming())
|
||||
testTodoBegin("imprecise");
|
||||
callbackRequestDelayed(&cb, 1.5 * delay);
|
||||
epicsEventWait(poked);
|
||||
testdbGetFieldEqual(namePACT, DBR_USHORT, 0);
|
||||
diff = epicsTimeDiffInSeconds(mytime, &now);
|
||||
testOk(diff >= 0.0, "time stamp is recent (%.9f sec)", diff);
|
||||
if(testImpreciseTiming())
|
||||
testTodoEnd();
|
||||
|
||||
/* Reset delay */
|
||||
*psdly = -1.;
|
||||
|
||||
@@ -30,10 +30,12 @@ is_deeply $menu->choice(2), undef, 'Third choice undefined';
|
||||
|
||||
like $menu->toDeclaration, qr/ ^
|
||||
\s* \# \s* ifndef \s+ test_NUM_CHOICES \s* \n
|
||||
\s* \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* typedef \s+ enum \s+ \{ \s* \n
|
||||
\s* ch1 \s+ \/\* [^*]* \*\/, \s* \n
|
||||
\s* ch2 \s+ \/\* [^*]* \*\/ \s* \n
|
||||
\s* ch1 \s+ \/\*\* [^*]* \*\/, \s* \n
|
||||
\s* ch2 \s+ \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* \} \s* test \s* ; \s* \n
|
||||
\s* \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* \# \s* define \s+ test_NUM_CHOICES \s+ 2 \s* \n
|
||||
\s* \# \s* endif \s* \n
|
||||
\s* $ /x, 'C declaration';
|
||||
|
||||
Reference in New Issue
Block a user