Merge branch 'master' into PSI

This commit is contained in:
zimoch committed 2026-09-25 15:23:11 +02:00
commit 8a5a6fcd27
19 files changed
+333 -130

No files matched your search

+17 -16
View File
@@ -1,5 +1,5 @@
# .appveyor.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# Appveyor configuration for EPICS 7 submodules
# (see also https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
@@ -33,24 +33,28 @@ configuration:
- dynamic-debug
- static-debug
# Environment variables: compiler toolchain, base version, setup file, ...
# Default build worker image
# Despite the Appveyor doc's, don't use this image for vs2015
image: Visual Studio 2019
# Environment variables: base version, compiler toolchain, etc.
environment:
# common / default variables for all jobs
SETUP_PATH: .ci-local
SETUP_PATH: .ci-local:.ci
EPICS_TEST_IMPRECISE_TIMING: YES
BASE: 7.0
# Verbose builds don't seem to fail, and might be faster!
VV: 1
matrix:
- CMP: vs2019
BASE: 3.15
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- CMP: vs2019
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- BASE: 3.15
CMP: vs2019
- CMP: vs2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- CMP: vs2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- CMP: gcc
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
# TODO: static linking w/ readline isn't working. Bypass auto-detect
COMMANDLINE_LIBRARY: EPICS
@@ -71,8 +75,9 @@ matrix:
#---------------------------------#
install:
- cmd: set PATH=C:\Python38-x64;%PATH%
- cmd: git submodule update --init --recursive
- cmd: pip install git+https://github.com/mdavidsaver/ci-core-dumper#egg=ci-core-dumper
- cmd: python -m pip install git+https://github.com/mdavidsaver/ci-core-dumper#egg=ci-core-dumper
- cmd: python .ci/cue.py prepare
build_script:
@@ -93,9 +98,9 @@ on_failure:
# debugging #
#---------------------------------#
## To connect by remote desktop to a failed build, uncomment the lines below.
## To connect by remote desktop to a failing build, uncomment the lines below.
## You must connect within the usual build timeout limit (60 minutes),
## so adjust the build matrix above to just build the config of interest.
## so adjust the build matrix above to build only the config of interest.
#on_failure:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
@@ -106,8 +111,4 @@ on_failure:
#---------------------------------#
notifications:
- provider: Email
to:
- core-talk@aps.anl.gov
on_build_success: false
- provider: GitHubPullRequest
+1 -1
Submodule .ci updated: 20f8e05393...fbbfced900.
+1 -2
View File
@@ -7,11 +7,10 @@
/include/
/templates/
/configure/*.local
/configure/RELEASE.*
/configure/CONFIG_SITE.*
O.*/
/QtC-*
envPaths
*.orig
*.log
.*.swp
.iocsh_history
+31 -3
View File
@@ -1,3 +1,7 @@
# SPDX-FileCopyrightText: 1997 Argonne National Laboratory
#
# SPDX-License-Identifier: EPICS
# CONFIG - Load build configuration data
#
# Do not make changes to this file!
@@ -8,9 +12,19 @@ RULES = $(EPICS_BASE)
# RELEASE files point to other application tops
include $(TOP)/configure/RELEASE
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
ifdef T_A
-include $(TOP)/configure/RELEASE.Common.$(T_A)
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-include $(TOP)/configure/RELEASE.Common.$(T_A)
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
endif
# Check EPICS_BASE is set properly
ifneq (file,$(origin EPICS_BASE))
$(error EPICS_BASE must be set in a configure/RELEASE file)
else
ifeq ($(wildcard $(EPICS_BASE)/configure/CONFIG_BASE),)
$(error EPICS_BASE does not point to an EPICS installation)
endif
endif
CONFIG = $(RULES)/configure
@@ -19,11 +33,25 @@ include $(CONFIG)/CONFIG
# Override the Base definition:
INSTALL_LOCATION = $(TOP)
# CONFIG_SITE files contain other build configuration settings
# CONFIG_SITE files contain local build configuration settings
include $(TOP)/configure/CONFIG_SITE
# Host-arch specific settings
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
ifdef T_A
# Target-arch specific settings
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
# Host & target specific settings
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
endif
# Load our cfg/CONFIG* files if installing elsewhere
ifneq ($(INSTALL_LOCATION),$(TOP))
TOP_CFG_CONFIGS := $(wildcard $(INSTALL_CFG)/CONFIG*)
ifneq ($(TOP_CFG_CONFIGS),)
include $(TOP_CFG_CONFIGS)
endif
endif
+27 -3
View File
@@ -28,6 +28,8 @@
#include <pv/thread.h>
#include <pv/pvData.h>
#include "factoryPvt.h"
using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
@@ -36,7 +38,6 @@ namespace epics { namespace pvData {
size_t Field::num_instances;
struct Field::Helper {
static unsigned hash(Field *fld) {
std::ostringstream key;
@@ -1488,6 +1489,7 @@ static int decodeScalar(int8 code)
FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl* control) const
{
RecurseGuard RG;
control->ensureData(1);
int8 code = buffer->getByte();
if (code == -1)
@@ -1576,7 +1578,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
throw std::invalid_argument("fixed and bounded structure array not supported");
// Type type = Type.structureArray;
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
StructureConstPtr elementStructure = std::tr1::dynamic_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
if(!elementStructure)
throw std::invalid_argument("Expected Structure");
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
Helper::cache(this, sp);
@@ -1589,7 +1593,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
throw std::invalid_argument("fixed and bounded structure array not supported");
// Type type = Type.unionArray;
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
UnionConstPtr elementUnion = std::tr1::dynamic_pointer_cast<const Union>(control->cachedDeserialize(buffer));
if(!elementUnion)
throw std::invalid_argument("Expected Union");
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
Helper::cache(this, sp);
@@ -1612,6 +1618,7 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
namespace detail {
struct field_factory {
FieldCreatePtr fieldCreate;
epicsThreadPrivate<unsigned> deserDepth;
field_factory() :fieldCreate(new FieldCreate()) {
registerRefCounter("Field", &Field::num_instances);
registerRefCounter("Thread", &Thread::num_instances);
@@ -1622,6 +1629,23 @@ struct field_factory {
static detail::field_factory* field_factory_s;
static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT;
#if defined(__rtems__) || defined(vxWorks)
// lower limit on embedded targets
const unsigned maxDepth = 10;
#else
const unsigned maxDepth = 20;
#endif
RecurseGuard::RecurseGuard() {
size_t current = (size_t)field_factory_s->deserDepth.get();
if(current>=maxDepth)
throw std::logic_error("FieldCreateFactory recursion depth exceeded");
field_factory_s->deserDepth.set((unsigned*)(current+1));
}
RecurseGuard::~RecurseGuard() {
size_t current = (size_t)field_factory_s->deserDepth.get();
field_factory_s->deserDepth.set((unsigned*)(current-1));
}
static void field_factory_init(void*)
{
try {
+3
View File
@@ -18,6 +18,8 @@
#include <pv/factory.h>
#include <pv/bitSet.h>
#include "factoryPvt.h"
using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
@@ -224,6 +226,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
void PVStructure::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
RecurseGuard G;
size_t fieldsSize = pvFields.size();
for(size_t i = 0; i<fieldsSize; i++)
pvFields[i]->deserialize(pbuffer, pcontrol);
+3
View File
@@ -16,6 +16,8 @@
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include "factoryPvt.h"
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -158,6 +160,7 @@ void PVStructureArray::serialize(ByteBuffer *pbuffer,
void PVStructureArray::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
RecurseGuard G;
svector data(reuse());
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
+15 -7
View File
@@ -18,6 +18,8 @@
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include "factoryPvt.h"
using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
@@ -143,6 +145,7 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol)
{
RecurseGuard G;
if (variant)
{
FieldConstPtr field = pcontrol->cachedDeserialize(pbuffer);
@@ -158,21 +161,26 @@ void PVUnion::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol)
}
else
{
int32 previousSelector = selector;
selector = static_cast<int32>(SerializeHelper::readSize(pbuffer, pcontrol));
if (selector != UNDEFINED_INDEX)
int32 index = static_cast<int32>(SerializeHelper::readSize(pbuffer, pcontrol));
if (index == UNDEFINED_INDEX)
{
if (selector != previousSelector)
selector = index;
value.reset();
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
throw std::runtime_error("union index out of bounds");
else
{
if (selector != index)
{
FieldConstPtr field = unionPtr->getField(selector);
selector = index;
FieldConstPtr field = unionPtr->getField(index);
// try to reuse existing field instance
if (!value.get() || *value->getField() != *field)
value = pvDataCreate->createPVField(field);
}
value->deserialize(pbuffer, pcontrol);
}
else
value.reset();
}
}
+3
View File
@@ -16,6 +16,8 @@
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include "factoryPvt.h"
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -157,6 +159,7 @@ void PVUnionArray::serialize(ByteBuffer *pbuffer,
void PVUnionArray::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
RecurseGuard G;
svector data(reuse());
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
+14
View File
@@ -0,0 +1,14 @@
#ifndef FACTORYPVT_H
#define FACTORYPVT_H
namespace epics { namespace pvData {
// use to bound recursion in FieldCreate and PVDataCreator singletons
struct RecurseGuard {
RecurseGuard();
~RecurseGuard();
};
}} // namespace epics::pvData
#endif // FACTORYPVT_H
+23 -2
View File
@@ -434,12 +434,33 @@ std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& forma
return strm;
case scalarArray:
{
strm<<format::indent();
printTimeT(strm, format.xtop);
printAlarmT(strm, format.xtop);
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
return strm;
const PVScalarArray* pvArray = static_cast<const PVScalarArray*>(value.get());
ScalarArrayConstPtr scalarArray = pvArray->getScalarArray();
// Stringify byte arrays if requested
if (format.xarrAsStr && scalarArray && scalarArray->getElementType() == pvByte) {
shared_vector<const int8_t> byteArray;
pvArray->getAs(byteArray);
if (!byteArray.empty()) {
const char* data = reinterpret_cast<const char*>(byteArray.data());
size_t slen = byteArray.size();
if (!data[slen-1])
--slen; // Trim trailing null so we don't end up with "mystring\0"
strm<<escape(std::string(data, slen));
}
} else {
strm<<std::setprecision(6);
strm<<*pvArray;
}
strm<<'\n';
return strm;
}
case structure:
if(printEnumT(strm, format.xtop, true)) {
strm<<'\n';
+4 -6
View File
@@ -337,17 +337,15 @@ namespace epics { namespace pvData {
}
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
size_t size = SerializeHelper::readSize(buffer, control);
uint32 bytes = (size == (size_t) -1) ? 0 : static_cast<uint32>(size);
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
// Validate availability before allocating storage
control->ensureData(bytes);
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
words.resize(wordsInUse);
if (wordsInUse == 0)
return;
control->ensureData(bytes);
uint32 i = 0;
uint32 longs = bytes / 8;
while (i < longs)
+40 -42
View File
@@ -69,8 +69,6 @@ inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l,
*
* Helper functions for writing unit tests.
*
@include unittest.cpp
*
* @{
*/
@@ -78,34 +76,34 @@ inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l,
*
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
* Thus constructor and destructor can be used for common test setup and tear down.
@code
namespace { // anon
struct MyTest {
MyTest() { } // setup
~MyTest() { } // tear down
void test1() {}
void test2() {}
};
} // namespace anon
MAIN(somename) {
testPlan(0);
TEST_METHOD(MyTest, test1)
TEST_METHOD(MyTest, test2)
return testDone();
}
@endcode
* @code
* namespace { // anon
* struct MyTest {
* MyTest() { } // setup
* ~MyTest() { } // tear down
* void test1() {}
* void test2() {}
* };
* } // namespace anon
* MAIN(somename) {
* testPlan(0);
* TEST_METHOD(MyTest, test1)
* TEST_METHOD(MyTest, test2)
* return testDone();
* }
* @endcode
*/
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
/** Compare equality. print left and right hand values and expression strings
*
@code
int x=5;
testEqual(x, 5);
// prints "ok 1 - x (5) == 5 (5)\n"
testEqual(x, 6)<<" oops";
// prints "not ok 1 - x (5) == 6 (6) oops\n"
@endcode
* @code
* int x=5;
* testEqual(x, 5);
* // prints "ok 1 - x (5) == 5 (5)\n"
* testEqual(x, 6)<<" oops";
* // prints "not ok 1 - x (5) == 6 (6) oops\n"
* @endcode
*/
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
@@ -113,38 +111,38 @@ inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l,
/** Pass/fail from boolean
*
@code
bool y=true;
testTrue(y);
// prints "ok 1 - y\n"
testTrue(!y)<<" oops";
// prints "not ok 1 - !y oops\n"
@endcode
* @code
* bool y=true;
* testTrue(y);
* // prints "ok 1 - y\n"
* testTrue(!y)<<" oops";
* // prints "not ok 1 - !y oops\n"
* @endcode
*/
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
/** Test that a given block throws an exception
*
@code
testThrows(std::runtime_error, somefunc(5))
@endcode
* @code
* testThrows(std::runtime_error, somefunc(5))
* @endcode
*/
#define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
/** Print test output w/o testing
*
@code
testShow()<<"Foo";
@endcode
* @code
* testShow()<<"Foo";
* @endcode
*/
#define testShow() ::detail::testPassx()
/** Compare value of PVStructure field
*
@code
PVStructurePtr x(.....);
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
@endcode
* @code
* PVStructurePtr x(.....);
* testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
* @endcode
*/
template<typename PVD>
::detail::testPassx
+17 -7
View File
@@ -50,7 +50,7 @@ namespace epics {
*
* @param[in] buffer deserialization buffer.
* @param[in] control the DeserializableControl.
* @returns array size.
* @returns array size, or (size_t) -1.
*/
static std::size_t readSize(ByteBuffer* buffer,
DeserializableControl* control);
@@ -80,20 +80,22 @@ namespace epics {
/**
* std::string deserialization helper method.
* TODO This method cannot return "null", but Java implementation
* could have serialized "null" value as well. We need to decide
* how to deserialize "null".
*
* @param[in] buffer deserialization buffer
* @param[in] control control
* @param[in] max length limit
* @returns deserialized string
*
* @todo This method cannot return "null", but Java implementation
* could have serialized "null" value as well. We need to decide
* how to deserialize "null".
* could have serialized "null" value as well. This implementation
* returns an empty string in that event.
*
* @since UNRELEASED Added the max argument.
* Test for the DESERIALIZE_STRING_HAS_LIMIT macro to detect.
* Retroactive default is 2 GiB
*/
static std::string deserializeString(ByteBuffer* buffer,
DeserializableControl* control);
DeserializableControl* control, std::size_t max = 0x7fffffff);
private:
SerializeHelper() {};
@@ -112,4 +114,12 @@ namespace epics {
}
}
/** Feature test macro for max argument
*
* of SerializeHelper::deserializeString()
*
* @since UNRELEASED added
*/
#define DESERIALIZE_STRING_HAS_LIMIT
#endif /* SERIALIZEHELPER_H_ */
+33 -39
View File
@@ -47,12 +47,13 @@ namespace epics {
DeserializableControl* control) {
control->ensureData(1);
int8 b = buffer->getByte();
if(b==-1)
return -1;
else if(b==-2) {
if (b == -1)
return -1; // NULL
else if (b == -2) {
control->ensureData(sizeof(int32));
int32 s = buffer->getInt();
if(s<0) THROW_BASE_EXCEPTION("negative size");
if (s < 0)
THROW_BASE_EXCEPTION("negative size");
return s;
}
else
@@ -100,44 +101,37 @@ namespace epics {
}
string SerializeHelper::deserializeString(ByteBuffer* buffer,
DeserializableControl* control) {
DeserializableControl* control, std::size_t max) {
std::size_t size = SerializeHelper::readSize(buffer, control);
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
{
if (buffer->getRemaining()>=size)
{
// entire string is in buffer, simply create a string out of it (copy)
std::size_t pos = buffer->getPosition();
string str(buffer->getBuffer()+pos, size);
buffer->setPosition(pos+size);
return str;
}
else
{
string str;
str.reserve(size);
try {
std::size_t i = 0;
while(true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getBuffer()+pos, toRead);
buffer->setPosition(pos+toRead);
i += toRead;
if(i<size)
control->ensureData(1); // at least one
else
break;
}
return str;
} catch(...) {
throw;
}
}
if (size == (std::size_t) -1)
return std::string(); // NULL from old implementations
if (size > max)
THROW_BASE_EXCEPTION("string too long");
if (buffer->getRemaining() >= size) { // All present
std::size_t pos = buffer->getPosition();
string str(buffer->getBuffer() + pos, size);
buffer->setPosition(pos + size);
return str;
}
else
return std::string();
string str;
str.reserve(size);
std::size_t i = 0;
while (true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getBuffer() + pos, toRead);
buffer->setPosition(pos + toRead);
i += toRead;
if (i < size)
control->ensureData(1); // at least one
else
break;
}
return str;
}
}
}
+2
View File
@@ -74,6 +74,8 @@ void Status::deserialize(ByteBuffer *buffer, DeserializableControl *flusher)
}
else
{
if(typeCode < 0 || typeCode > STATUSTYPE_FATAL)
typeCode = STATUSTYPE_FATAL;
m_statusType = (StatusType)typeCode;
m_message = SerializeHelper::deserializeString(buffer, flusher);
m_stackDump = SerializeHelper::deserializeString(buffer, flusher);
+4
View File
@@ -888,6 +888,7 @@ public:
const BitSet* xhighlight;
mode_t xmode;
format_t xfmt;
bool xarrAsStr;
public:
explicit Formatter(const PVStructure& top)
:xtop(top)
@@ -895,6 +896,7 @@ public:
,xhighlight(0)
,xmode(Auto)
,xfmt(NT)
,xarrAsStr(false)
{}
// those fields (and their parents) to be printed. non-NT mode.
@@ -906,6 +908,8 @@ public:
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
FORCE_INLINE Formatter& arrayAsString(bool b) { xarrAsStr = b; return *this; }
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
};
+43 -1
View File
@@ -303,15 +303,57 @@ static void testSerialize()
#undef TOFRO
}
static void testDeserializeOversize()
{
testDiag("testDeserializeOversize");
// Size field declares INT32_MAX bytes but no payload follows: 0xFE
// long-form marker + big-endian int32. deserialize() must reject this
// via ensureData() before allocating a ~2GB backing store.
const char msg[] = {(char)0xFE, 0x7F, (char)0xFF, (char)0xFF, (char)0xFF};
BitSet dut;
ByteBuffer buf((char*)msg, sizeof(msg), EPICS_ENDIAN_BIG);
try {
deserializeFromBuffer(&dut, buf);
testFail("oversized BitSet size was not rejected");
} catch(std::exception& e) {
testPass("oversized BitSet size rejected: %s", e.what());
}
}
static void testDeserializeNull()
{
testDiag("testDeserializeNull");
// A NULL size is encoded on the wire as the single byte 0xFF. Without
// the null check in deserialize() this would be cast to UINT32_MAX bytes
// and overflow the allocation size; it must instead yield an empty BitSet.
const char msg[] = {(char)0xFF};
BitSet dut;
dut.set(5); // ensure deserialize actually clears/replaces existing bits
ByteBuffer buf((char*)msg, sizeof(msg), EPICS_ENDIAN_BIG);
try {
deserializeFromBuffer(&dut, buf);
testPass("NULL BitSet size accepted");
} catch(std::exception& e) {
testFail("NULL BitSet size rejected: %s", e.what());
}
testOk1(dut.isEmpty());
testOk(buf.getRemaining()==0, "buffer remaining 0 == %u",
(unsigned)buf.getRemaining());
}
} // namespace
MAIN(testBitSet)
{
testPlan(90);
testPlan(94);
testInitialize();
testGetSetClearFlip();
testOperators();
testLogical();
testSerialize();
testDeserializeOversize();
testDeserializeNull();
return testDone();
}
+52 -1
View File
@@ -849,11 +849,60 @@ void testFromString(int byteOrder)
testOk1(_data->getSubFieldT<PVString>("Y")->get()=="testing");
}
void testDeserializeTypeFail(const char *buf, size_t buflen)
{
std::vector<char> bytes(buf, buf + buflen);
ByteBuffer B(&bytes[0], bytes.size(), EPICS_ENDIAN_BIG);
try{
FieldConstPtr ptr(getFieldCreate()->deserialize(&B, control));
}catch(std::exception& e) {
testPass("Expected exception: %s", e.what());
return;
}
testFail("Unexpected success? %d", __LINE__);
}
void testMalformed()
{
testDiag("testMalformed()");
#define CASE(STR) testDeserializeTypeFail(STR, sizeof(STR)-1)
// a structure array where the element type is not a structure
CASE("\x88\x83\x2b\x01\x01");
// a union array where ...
CASE("\x89\x83\x2b\x01\x01");
#undef CASE
{
std::vector<char> inp(500u);
for(size_t i=0; i<inp.size(); i+=5) {
inp[i+0] = '\x80'; // Struct
inp[i+1] = '\xff'; // null ID String
inp[i+2] = '\x01'; // 1 member field
inp[i+3] = '\x01'; // member name length 1
inp[i+4] = 'x' ; // member name
}
testDeserializeTypeFail(&inp[0], inp.size());
}
{
std::vector<char> inp(500u);
for(size_t i=0; i<inp.size(); i+=5) {
inp[i+0] = '\x81'; // Union
inp[i+1] = '\xff'; // null ID String
inp[i+2] = '\x01'; // 1 member field
inp[i+3] = '\x01'; // member name length 1
inp[i+4] = 'x' ; // member name
}
testDeserializeTypeFail(&inp[0], inp.size());
}
}
} // end namespace
MAIN(testSerialization) {
testPlan(234);
testPlan(238);
flusher = new SerializableControlImpl();
control = new DeserializableControlImpl();
@@ -880,6 +929,8 @@ MAIN(testSerialization) {
testFromString(EPICS_ENDIAN_BIG);
testFromString(EPICS_ENDIAN_LITTLE);
testMalformed();
delete buffer;
delete control;
delete flusher;