Merge remote-tracking branch 'launchpad/3.15'

* launchpad/3.15:
  db/test: dbPutGetTest check dbGet() of long string field
  db: dbGet() ensure long string nil and actual string length
  db/test: dbPutGetTest check for dbGet() attribute crash
  db: fix dbGet() for attributes as long string
  db/test: dbPutGetTest add test for lp:1678494
  db: fix dbGet() of link fields as DBF_CHAR
  dbUnitTest: add testdbGetArrFieldEqual()
  db/test: add dbPutGetTest
  Adjusted .gitignore patterns
  Move .bzrignore to .gitignore
This commit is contained in:
Michael Davidsaver
2017-04-06 20:32:48 -04:00
6 changed files with 321 additions and 20 deletions

View File

@@ -739,28 +739,34 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
{
dbCommon *precord = paddr->precord;
dbFldDes *pfldDes = paddr->pfldDes;
/* size of pbuf storage in bytes, including space for trailing nil */
int maxlen;
DBENTRY dbEntry;
long status;
long nReq = nRequest ? *nRequest : 1;
/* dbFindRecord() below will always succeed as we have a
* valid DBADDR, so no point to check again.
* Request for zero elements always succeeds
*/
if(!nReq)
return 0;
switch (dbrType) {
case DBR_STRING:
maxlen = MAX_STRING_SIZE - 1;
if (nRequest && *nRequest > 1) *nRequest = 1;
maxlen = MAX_STRING_SIZE;
nReq = 1;
break;
case DBR_DOUBLE: /* Needed for dbCa links */
if (nRequest && *nRequest) *nRequest = 1;
if (nRequest) *nRequest = 1;
*(double *)pbuf = epicsNAN;
return 0;
case DBR_CHAR:
case DBR_UCHAR:
if (nRequest && *nRequest > 0) {
maxlen = *nRequest - 1;
break;
}
/* else fall through ... */
maxlen = nReq;
break;
default:
return S_db_badDbrtype;
}
@@ -769,10 +775,13 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
status = dbFindRecord(&dbEntry, precord->name);
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
if (!status) {
char *rtnString = dbGetString(&dbEntry);
const char *rtnString = dbGetString(&dbEntry);
strncpy(pbuf, rtnString, --maxlen);
pbuf[maxlen] = 0;
strncpy(pbuf, rtnString, maxlen-1);
pbuf[maxlen-1] = 0;
if(dbrType!=DBR_STRING)
nReq = strlen(pbuf)+1;
if(nRequest) *nRequest = nReq;
}
dbFinishEntry(&dbEntry);
return status;
@@ -782,28 +791,31 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
char *pbuf, long *nRequest)
{
int maxlen;
long nReq = nRequest ? *nRequest : 1;
if (!paddr->pfield) return S_db_badField;
switch (dbrType) {
case DBR_STRING:
maxlen = MAX_STRING_SIZE - 1;
if (nRequest && *nRequest > 1) *nRequest = 1;
maxlen = MAX_STRING_SIZE;
nReq = 1;
break;
case DBR_CHAR:
case DBR_UCHAR:
if (nRequest && *nRequest > 0) {
maxlen = *nRequest - 1;
break;
}
maxlen = nReq;
break;
/* else fall through ... */
default:
return S_db_badDbrtype;
}
strncpy(pbuf, paddr->pfield, --maxlen);
pbuf[maxlen] = 0;
strncpy(pbuf, paddr->pfield, maxlen-1);
pbuf[maxlen-1] = 0;
if(dbrType!=DBR_STRING)
nReq = strlen(pbuf)+1;
if(nRequest) *nRequest = nReq;
return 0;
}
@@ -931,6 +943,15 @@ long dbGet(DBADDR *paddr, short dbrType,
localAddr.pfield = (char *) pfl->u.r.field;
status = convert(&localAddr, pbuf, n, capacity, offset);
}
if(!status && dbrType==DBF_CHAR && nRequest &&
paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)
{
/* long string ensure nil and truncate to actual length */
long nReq = *nRequest;
pbuf[nReq-1] = '\0';
*nRequest = strlen(pbuf)+1;
}
}
done:
paddr->pfield = pfieldsave;

View File

@@ -222,6 +222,71 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
}
}
void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbuf)
{
DBADDR addr;
const long vSize = dbValueSize(dbfType);
const long nStore = vSize * nRequest;
long status;
void *gbuf, *gstore;
if(dbNameToAddr(pv, &addr)) {
testFail("Missing PV \"%s\"", pv);
return;
}
gbuf = gstore = malloc(nStore);
if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */
testFail("Allocation failed esize=%ld total=%ld", vSize, nStore);
return;
}
status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
if (status) {
testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
} else {
unsigned match = nRequest==cnt;
long n, N = nRequest < cnt ? nRequest : cnt;
if(!match)
testDiag("Length mis-match. expected=%lu actual=%lu", cnt, nRequest);
for(n=0; n<N; n++, gbuf+=vSize, pbuf+=vSize) {
switch(dbfType) {
case DBR_STRING: {
const char *expect = (const char*)pbuf,
*actual = (const char*)gbuf;
/* expected (pbuf) is allowed to omit storage for trailing nils for last element */
unsigned int eq = strncmp(expect, actual, MAX_STRING_SIZE)==0 && actual[MAX_STRING_SIZE-1]=='\0';
match &= eq;
if(!eq)
testDiag("[%lu] = expected=\"%s\" actual=\"%s\"", n, expect, actual);
break;
}
#define OP(DBR,Type,pat) case DBR: {Type expect = *(Type*)pbuf, actual = *(Type*)gbuf; assert(vSize==sizeof(Type)); match &= expect==actual; \
if(expect!=actual) testDiag("[%lu] expected=" pat " actual=" pat, n, expect, actual); break;}
OP(DBR_CHAR, char, "%c");
OP(DBR_UCHAR, unsigned char, "%u");
OP(DBR_SHORT, short, "%d");
OP(DBR_USHORT, unsigned short, "%u");
OP(DBR_LONG, int, "%d");
OP(DBR_ULONG, unsigned int, "%u");
OP(DBR_FLOAT, float, "%e");
OP(DBR_DOUBLE, double, "%e");
OP(DBR_ENUM, int, "%d");
#undef OP
}
}
testOk(match, "dbGetField(\"%s\", dbrType=%d, nRequest=%ld ...) match", pv, dbfType, nRequest);
}
free(gstore);
}
dbCommon* testdbRecordPtr(const char* pv)
{
DBADDR addr;

View File

@@ -55,6 +55,23 @@ epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap);
epicsShareFunc void testdbGetFieldEqual(const char* pv, short dbrType, ...);
epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
/**
* @param pv PV name string
* @param dbfType One of the DBF_* macros from dbAccess.h
* @param nRequest Number of elements to request from pv
* @param pbufcnt Number of elements pointed to be pbuf
* @param pbuf Expected value buffer
*
* Execute dbGet() of nRequest elements and compare the result with
* pbuf (pbufcnt is an element count).
* Element size is derived from dbfType.
*
* nRequest > pbufcnt will detect truncation.
* nRequest < pbufcnt always fails.
* nRequest ==pbufcnt checks prefix (actual may be longer than expected)
*/
epicsShareFunc void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long pbufcnt, const void *pbuf);
epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
typedef struct testMonitor testMonitor;

View File

@@ -152,7 +152,14 @@ recGblCheckDeadbandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += recGblCheckDeadbandTest.c
TESTS += recGblCheckDeadbandTest
# The testHarness runs all the test programs in a known working order.
TESTPROD_HOST += testPutGetTest
testPutGetTest_SRCS += dbPutGetTest.c
testPutGetTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbPutGetTest.db
TESTFILES += ../dbPutGetTest.db
TESTS += testPutGetTest
# This runs all the test programs in a known working order:
testHarness_SRCS += epicsRunDbTests.c
dbTestHarness_SRCS += $(testHarness_SRCS)

View File

@@ -0,0 +1,150 @@
#include <string.h>
#include <errlog.h>
#include <dbAccess.h>
#include <dbStaticLib.h>
#include <dbStaticPvt.h>
#include <dbUnitTest.h>
#include <testMain.h>
static
void testdbGetStringEqual(const char *pv, const char *expected)
{
const char *actual;
int ok;
DBENTRY ent;
dbInitEntry(pdbbase, &ent);
if(dbFindRecord(&ent, pv)) {
testFail("Failed to find record '%s'", pv);
return;
}
actual = dbGetString(&ent);
ok = (!actual && !expected)
|| (actual && expected && strcmp(actual, expected)==0);
testOk(ok, "dbGetString(\"%s\") -> \"%s\" == \"%s\"", pv, actual, expected);
dbFinishEntry(&ent);
}
static
void testGetString(void)
{
testDiag("testGetString()");
testdbGetStringEqual("recempty.DTYP", "Soft Channel");
testdbGetStringEqual("recempty.DESC", "");
testdbGetStringEqual("recempty.PHAS", "0");
testdbGetStringEqual("recempty.TSE" , "0");
testdbGetStringEqual("recempty.DISA", "0");
testdbGetStringEqual("recempty.DISV", "0");
testdbGetStringEqual("recoverwrite.DTYP", "Soft Channel");
testdbGetStringEqual("recoverwrite.DESC", "");
testdbGetStringEqual("recoverwrite.PHAS", "0");
testdbGetStringEqual("recoverwrite.TSE" , "0");
testdbGetStringEqual("recoverwrite.DISA", "0");
testdbGetStringEqual("recoverwrite.DISV", "0");
}
static
void testStringMax(void)
{
testDiag("testStringMax()");
testdbGetStringEqual("recmax.DISA", "-1");
}
static
void testLongLink(void)
{
testDiag("testLonkLink()");
testdbGetFieldEqual("lnktest.INP", DBR_STRING, "lnktarget NPP NMS");
testdbGetFieldEqual("lnktest.INP$", DBR_STRING, "lnktarget NPP NMS");
testDiag("dbGet() w/ nRequest==1 gets only trailing nil");
testdbGetFieldEqual("lnktest.INP$", DBR_CHAR, '\0');
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 19, 18, "lnktarget NPP NMS");
testDiag("get w/ truncation");
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 0, 0, NULL);
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 1, 1, "");
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 2, 2, "l");
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 3, 3, "ln");
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 17, 17, "lnktarget NPP NM");
testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 18, 18, "lnktarget NPP NMS");
testdbGetArrFieldEqual("lnktest.INP", DBR_STRING, 2, 1, "lnktarget NPP NMS");
}
static
void testLongAttr(void)
{
testDiag("testLongAttr()");
testdbGetFieldEqual("lnktest.RTYP", DBR_STRING, "x");
testdbGetFieldEqual("lnktest.RTYP$", DBR_STRING, "x");
testDiag("dbGet() w/ nRequest==1 gets only trailing nil");
testdbGetFieldEqual("lnktest.RTYP$", DBR_CHAR, '\0');
testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 4, 2, "x");
testDiag("get w/ truncation");
testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 0, 0, NULL);
testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 1, 1, "");
testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 2, 2, "x");
}
static
void testLongField(void)
{
testDiag("testLongField()");
testdbGetFieldEqual("lnktest.NAME", DBR_STRING, "lnktest");
testdbGetFieldEqual("lnktest.NAME$", DBR_STRING, "108");
testDiag("dbGet() w/ nRequest==1 gets only trailing nil");
testdbGetFieldEqual("lnktest.NAME$", DBR_CHAR, '\0');
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 10, 8, "lnktest");
testDiag("get w/ truncation");
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 0, 0, NULL);
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 1, 1, "");
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 2, 2, "l");
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 3, 3, "ln");
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 7, 7, "lnktes");
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 8, 8, "lnktest");
}
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
MAIN(dbPutGet)
{
testPlan(41);
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbPutGetTest.db", NULL, NULL);
testGetString();
testStringMax();
eltc(0);
testIocInitOk();
eltc(1);
testLongLink();
testLongAttr();
testLongField();
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,41 @@
record(x, "recempty") {
# empty string is the same "0" for numeric fields
field(DTYP, "")
field(DESC, "")
field(PHAS, "")
field(TSE , "")
field(DISA, "")
field(DISV, "")
}
record(x, "recoverwrite") {
# first with non-default values
# field(DTYP, "Scan I/O")
field(DESC, "hello")
field(PHAS, "2")
field(TSE , "5")
field(DISA, "6")
field(DISV, "7")
}
record(x, "recoverwrite") {
# now restore default values
field(DTYP, "")
field(DESC, "")
field(PHAS, "")
field(TSE , "")
field(TSEL, "")
field(DISA, "")
field(DISV, "")
}
record(x, "recmax") {
field(DISA, "0xffffffff")
}
record(x, "lnktarget") {}
record(x, "lnktest") {
field(INP, "lnktarget NPP NMS")
}