512 lines
15 KiB
C++
512 lines
15 KiB
C++
/**
|
|
* Copyright - See the COPYRIGHT that is included with this distribution.
|
|
* EPICS pvData is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
*/
|
|
/*
|
|
* testSerialization.cpp
|
|
*
|
|
* Created on: Oct 25, 2010
|
|
* Author: Miha Vitorovic
|
|
*/
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
#include <epicsUnitTest.h>
|
|
#include <testMain.h>
|
|
#include <dbDefs.h> // for NELEMENTS
|
|
|
|
#include <epicsExit.h>
|
|
#include <pv/pvIntrospect.h>
|
|
#include <pv/pvData.h>
|
|
#include <pv/serialize.h>
|
|
#include <pv/noDefaultMethods.h>
|
|
#include <pv/byteBuffer.h>
|
|
#include <pv/convert.h>
|
|
|
|
#include <pv/standardField.h>
|
|
|
|
#include <limits>
|
|
|
|
#define BYTE_MAX_VALUE std::numeric_limits<int8>::max()
|
|
#define BYTE_MIN_VALUE std::numeric_limits<int8>::min()
|
|
#define UBYTE_MAX_VALUE std::numeric_limits<uint8>::max()
|
|
#define SHORT_MAX_VALUE std::numeric_limits<int16>::max()
|
|
#define SHORT_MIN_VALUE std::numeric_limits<int16>::min()
|
|
#define USHORT_MAX_VALUE std::numeric_limits<uint16>::max()
|
|
#define INT_MAX_VALUE std::numeric_limits<int32>::max()
|
|
#define INT_MIN_VALUE std::numeric_limits<int32>::min()
|
|
#define UINT_MAX_VALUE std::numeric_limits<uint32>::max()
|
|
#define LONG_MAX_VALUE std::numeric_limits<int64>::max()
|
|
#define LONG_MIN_VALUE std::numeric_limits<int64>::min()
|
|
#define ULONG_MAX_VALUE std::numeric_limits<uint64>::max()
|
|
#define FLOAT_MAX_VALUE std::numeric_limits<float>::max()
|
|
#define FLOAT_MIN_VALUE std::numeric_limits<float>::min()
|
|
#define DOUBLE_MAX_VALUE std::numeric_limits<double>::max()
|
|
#define DOUBLE_MIN_VALUE std::numeric_limits<double>::min()
|
|
|
|
using namespace epics::pvData;
|
|
|
|
namespace {
|
|
|
|
static SerializableControl* flusher;
|
|
static DeserializableControl* control;
|
|
static ByteBuffer* buffer;
|
|
|
|
|
|
class SerializableControlImpl : public SerializableControl,
|
|
public NoDefaultMethods {
|
|
public:
|
|
virtual void flushSerializeBuffer() {
|
|
}
|
|
|
|
virtual void ensureBuffer(std::size_t /*size*/) {
|
|
}
|
|
|
|
virtual void alignBuffer(std::size_t alignment) {
|
|
buffer->align(alignment);
|
|
}
|
|
|
|
virtual bool directSerialize(ByteBuffer */*existingBuffer*/, const char* /*toSerialize*/,
|
|
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual void cachedSerialize(std::tr1::shared_ptr<const Field> const & field, ByteBuffer* buffer)
|
|
{
|
|
field->serialize(buffer, this);
|
|
}
|
|
|
|
SerializableControlImpl() {
|
|
}
|
|
|
|
virtual ~SerializableControlImpl() {
|
|
}
|
|
};
|
|
|
|
class DeserializableControlImpl : public DeserializableControl,
|
|
public NoDefaultMethods {
|
|
public:
|
|
virtual void ensureData(size_t /*size*/) {
|
|
}
|
|
|
|
virtual void alignData(size_t alignment) {
|
|
buffer->align(alignment);
|
|
}
|
|
|
|
virtual bool directDeserialize(ByteBuffer */*existingBuffer*/, char* /*deserializeTo*/,
|
|
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual std::tr1::shared_ptr<const Field> cachedDeserialize(ByteBuffer* buffer)
|
|
{
|
|
return getFieldCreate()->deserialize(buffer, this);
|
|
}
|
|
|
|
DeserializableControlImpl() {
|
|
}
|
|
|
|
virtual ~DeserializableControlImpl() {
|
|
}
|
|
};
|
|
|
|
void serializationTest(PVFieldPtr const & field) {
|
|
buffer->clear();
|
|
|
|
// serialize
|
|
field->serialize(buffer, flusher);
|
|
|
|
buffer->flip();
|
|
|
|
// create new instance and deserialize
|
|
PVFieldPtr deserializedField = getPVDataCreate()->createPVField(field->getField());
|
|
deserializedField->deserialize(buffer, control);
|
|
|
|
// must equal
|
|
if(*field==*deserializedField)
|
|
testPass("Serialization round trip OK");
|
|
else {
|
|
testFail("Serialization round trip did not match!");
|
|
std::string buf;
|
|
field->toString(&buf);
|
|
testDiag("Expected: %s", buf.c_str());
|
|
buf.clear();
|
|
deserializedField->toString(&buf);
|
|
testDiag("Found: %s", buf.c_str());
|
|
}
|
|
}
|
|
|
|
void testEquals() {
|
|
testDiag("Testing equals...");
|
|
PVDataCreatePtr factory = getPVDataCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
// be sure all is covered
|
|
for (int i = pvBoolean; i < pvString; i++)
|
|
{
|
|
ScalarType scalarType = static_cast<ScalarType>(i);
|
|
|
|
PVScalarPtr scalar1 = factory->createPVScalar(scalarType);
|
|
PVScalarPtr scalar2 = factory->createPVScalar(scalarType);
|
|
testOk1((*scalar1)==(*scalar2));
|
|
|
|
PVScalarArrayPtr array1 = factory->createPVScalarArray(scalarType);
|
|
PVScalarArrayPtr array2 = factory->createPVScalarArray(scalarType);
|
|
testOk1((*array1)==(*array2));
|
|
}
|
|
|
|
// and a structure
|
|
PVStructurePtr structure1 = factory->createPVStructure(getStandardField()->timeStamp());
|
|
PVStructurePtr structure2 = factory->createPVStructure(getStandardField()->timeStamp());
|
|
testOk1((*structure1)==(*structure2));
|
|
|
|
// and a structure array
|
|
PVStructureArrayPtr structureArray1 = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure1->getStructure()));
|
|
PVStructureArrayPtr structureArray2 = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure2->getStructure()));
|
|
testOk1((*structureArray1)==(*structureArray2));
|
|
}
|
|
|
|
template<typename PVT>
|
|
void testScalarType()
|
|
{
|
|
typedef typename PVT::value_type value_type;
|
|
|
|
testDiag("type %s", ScalarTypeFunc::name(PVT::typeCode));
|
|
|
|
typename PVT::shared_pointer pv = std::tr1::static_pointer_cast<PVT>(getPVDataCreate()->createPVScalar(PVT::typeCode));
|
|
|
|
pv->put(0);
|
|
serializationTest(pv);
|
|
pv->put(42);
|
|
serializationTest(pv);
|
|
pv->put(std::numeric_limits<value_type>::max()-1);
|
|
serializationTest(pv);
|
|
pv->put(std::numeric_limits<value_type>::max());
|
|
serializationTest(pv);
|
|
|
|
if(std::numeric_limits<value_type>::min()!=0) {
|
|
pv->put(-42);
|
|
serializationTest(pv);
|
|
pv->put(std::numeric_limits<value_type>::min()+1);
|
|
serializationTest(pv);
|
|
pv->put(std::numeric_limits<value_type>::min());
|
|
serializationTest(pv);
|
|
}
|
|
|
|
if(std::numeric_limits<value_type>::has_infinity) {
|
|
pv->put(std::numeric_limits<value_type>::infinity());
|
|
serializationTest(pv);
|
|
}
|
|
}
|
|
|
|
void testScalar() {
|
|
testDiag("Testing scalars...");
|
|
PVDataCreatePtr factory = getPVDataCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
testDiag("type %s", ScalarTypeFunc::name(pvBoolean));
|
|
PVBooleanPtr pvBoolean =
|
|
std::tr1::static_pointer_cast<PVBoolean>(factory->createPVScalar(epics::pvData::pvBoolean));
|
|
pvBoolean->put(false);
|
|
serializationTest(pvBoolean);
|
|
pvBoolean->put(true);
|
|
serializationTest(pvBoolean);
|
|
|
|
testScalarType<PVByte>();
|
|
testScalarType<PVUByte>();
|
|
testScalarType<PVShort>();
|
|
testScalarType<PVUShort>();
|
|
testScalarType<PVInt>();
|
|
testScalarType<PVUInt>();
|
|
testScalarType<PVLong>();
|
|
testScalarType<PVULong>();
|
|
testScalarType<PVFloat>();
|
|
testScalarType<PVDouble>();
|
|
|
|
|
|
testDiag("type %s", ScalarTypeFunc::name(pvString));
|
|
PVStringPtr pvString =
|
|
std::tr1::static_pointer_cast<PVString>(factory->createPVScalar(epics::pvData::pvString));
|
|
pvString->put("");
|
|
serializationTest(pvString);
|
|
pvString->put("s");
|
|
serializationTest(pvString);
|
|
pvString->put("string");
|
|
serializationTest(pvString);
|
|
pvString->put("string with spaces");
|
|
serializationTest(pvString);
|
|
pvString->put("string with spaces and special characters\f\n");
|
|
serializationTest(pvString);
|
|
|
|
// huge string test
|
|
pvString->put(String(10000, 'a'));
|
|
serializationTest(pvString);
|
|
}
|
|
|
|
template<typename PVT>
|
|
void testArrayType(const typename PVT::value_type* rdata, size_t len)
|
|
{
|
|
typedef typename PVT::value_type value_type;
|
|
|
|
typename PVT::svector empty(0), data(len);
|
|
|
|
std::copy(rdata, rdata+len, data.begin());
|
|
|
|
testDiag("type %s", ScalarTypeFunc::name(PVT::typeCode));
|
|
|
|
typename PVT::shared_pointer pv = std::tr1::static_pointer_cast<PVT>(getPVDataCreate()->createPVScalarArray(PVT::typeCode));
|
|
|
|
pv->replace(freeze(empty));
|
|
serializationTest(pv);
|
|
pv->replace(freeze(data));
|
|
serializationTest(pv);
|
|
}
|
|
|
|
static const boolean bdata[] = {0, 1, 0, 1, 1};
|
|
|
|
static const int8 i8data[] = { 0, 1, 2, -1, BYTE_MAX_VALUE, BYTE_MAX_VALUE-1,
|
|
BYTE_MIN_VALUE+1, BYTE_MIN_VALUE };
|
|
static const uint8 u8data[] = { 0, 1, 2, -1, UBYTE_MAX_VALUE, UBYTE_MAX_VALUE-1 };
|
|
|
|
static const int16 i16data[] = { 0, 1, 2, -1, SHORT_MAX_VALUE, SHORT_MAX_VALUE-1,
|
|
SHORT_MIN_VALUE+1, SHORT_MIN_VALUE };
|
|
static const uint16 u16data[] = { 0, 1, 2, -1, USHORT_MAX_VALUE, USHORT_MAX_VALUE-1 };
|
|
|
|
static const int32 i32data[] = { 0, 1, 2, -1, INT_MAX_VALUE, INT_MAX_VALUE-1,
|
|
INT_MIN_VALUE+1, INT_MIN_VALUE };
|
|
static const uint32 u32data[] = { 0, 1, 2, -1, UINT_MAX_VALUE, UINT_MAX_VALUE-1 };
|
|
|
|
static const int64 i64data[] = { 0, 1, 2, -1, LONG_MAX_VALUE, LONG_MAX_VALUE-1,
|
|
LONG_MIN_VALUE+1, LONG_MIN_VALUE };
|
|
static const uint64 u64data[] = { 0, 1, 2, -1, ULONG_MAX_VALUE, ULONG_MAX_VALUE-1 };
|
|
|
|
static const double ddata[] = { (double)0.0, (double)1.1, (double)2.3, (double)-1.4,
|
|
DOUBLE_MAX_VALUE, DOUBLE_MAX_VALUE-(double)123456.789,
|
|
DOUBLE_MIN_VALUE+(double)1.1, DOUBLE_MIN_VALUE };
|
|
|
|
static const float fdata[] = { (float)0.0, (float)1.1, (float)2.3, (float)-1.4,
|
|
FLOAT_MAX_VALUE, FLOAT_MAX_VALUE-(float)123456.789,
|
|
FLOAT_MIN_VALUE+(float)1.1, FLOAT_MIN_VALUE };
|
|
|
|
static const String sdata[] = {
|
|
"",
|
|
"a",
|
|
"a b",
|
|
" ",
|
|
"test",
|
|
"smile",
|
|
"this is a little longer string... maybe a little but longer... this makes test better",
|
|
String(10000, 'b')
|
|
};
|
|
|
|
void testArray() {
|
|
testDiag("Testing arrays...");
|
|
|
|
testArrayType<PVBooleanArray>(bdata, NELEMENTS(bdata));
|
|
|
|
testArrayType<PVByteArray>(i8data, NELEMENTS(i8data));
|
|
testArrayType<PVUByteArray>(u8data, NELEMENTS(u8data));
|
|
testArrayType<PVShortArray>(i16data, NELEMENTS(i16data));
|
|
testArrayType<PVUShortArray>(u16data, NELEMENTS(u16data));
|
|
testArrayType<PVIntArray>(i32data, NELEMENTS(i32data));
|
|
testArrayType<PVUIntArray>(u32data, NELEMENTS(u32data));
|
|
testArrayType<PVLongArray>(i64data, NELEMENTS(i64data));
|
|
testArrayType<PVULongArray>(u64data, NELEMENTS(u64data));
|
|
|
|
testArrayType<PVDoubleArray>(ddata, NELEMENTS(ddata));
|
|
testArrayType<PVFloatArray>(fdata, NELEMENTS(fdata));
|
|
|
|
testArrayType<PVStringArray>(sdata, NELEMENTS(sdata));
|
|
}
|
|
|
|
void testNonInitialized() {
|
|
testDiag("Testing non-initialized...");
|
|
PVDataCreatePtr factory = getPVDataCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
// be sure all is covered
|
|
for (int i = pvBoolean; i < pvString; i++)
|
|
{
|
|
ScalarType scalarType = static_cast<ScalarType>(i);
|
|
|
|
PVScalarPtr scalar = factory->createPVScalar(scalarType);
|
|
serializationTest(scalar);
|
|
|
|
PVScalarArrayPtr array = factory->createPVScalarArray(scalarType);
|
|
serializationTest(array);
|
|
}
|
|
|
|
// and a structure
|
|
PVStructurePtr structure = factory->createPVStructure(getStandardField()->timeStamp());
|
|
serializationTest(structure);
|
|
|
|
// and a structure array
|
|
PVStructureArrayPtr structureArray = factory->createPVStructureArray(getFieldCreate()->createStructureArray(structure->getStructure()));
|
|
serializationTest(structureArray);
|
|
}
|
|
|
|
void testStructure() {
|
|
testDiag("Testing structure...");
|
|
|
|
PVDataCreatePtr factory = getPVDataCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
testDiag("\tSimple structure serialization");
|
|
PVStructurePtr pvStructure = factory->createPVStructure(getStandardField()->timeStamp());
|
|
pvStructure->getLongField("secondsPastEpoch")->put(123);
|
|
pvStructure->getIntField("nanoSeconds")->put(456);
|
|
pvStructure->getIntField("userTag")->put(789);
|
|
|
|
serializationTest(pvStructure);
|
|
|
|
testDiag("\tComplex structure serialization");
|
|
pvStructure = factory->createPVStructure(
|
|
getStandardField()->structureArray(
|
|
getStandardField()->timeStamp(), "alarm,control,display,timeStamp")
|
|
);
|
|
// TODO fill with data
|
|
serializationTest(pvStructure);
|
|
}
|
|
|
|
void testStructureArray() {
|
|
testDiag("Testing structure array...");
|
|
|
|
PVDataCreatePtr factory = getPVDataCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
StructureArrayConstPtr tstype(
|
|
getFieldCreate()->createStructureArray(getStandardField()->alarm()));
|
|
PVStructureArrayPtr pvArr = getPVDataCreate()->createPVStructureArray(tstype);
|
|
|
|
testDiag("empty array");
|
|
serializationTest(pvArr);
|
|
|
|
pvArr->setLength(10);
|
|
|
|
testDiag("All NULLs");
|
|
serializationTest(pvArr);
|
|
|
|
PVStructureArray::svector data(5);
|
|
|
|
data[1] = getPVDataCreate()->createPVStructure(getStandardField()->alarm());
|
|
data[4] = getPVDataCreate()->createPVStructure(getStandardField()->alarm());
|
|
|
|
pvArr->replace(freeze(data));
|
|
|
|
testDiag("Some NULLs");
|
|
serializationTest(pvArr);
|
|
}
|
|
|
|
|
|
void testStructureId() {
|
|
testDiag("Testing structureID...");
|
|
|
|
FieldCreatePtr fieldCreate = getFieldCreate();
|
|
|
|
StringArray fieldNames;
|
|
fieldNames.push_back("longField");
|
|
fieldNames.push_back("intField");
|
|
|
|
FieldConstPtrArray fields;
|
|
fields.push_back(fieldCreate->createScalar(pvLong));
|
|
fields.push_back(fieldCreate->createScalar(pvInt));
|
|
|
|
StructureConstPtr structureWithNoId = fieldCreate->createStructure(fieldNames, fields);
|
|
StructureConstPtr structure1 = fieldCreate->createStructure("id1", fieldNames, fields);
|
|
StructureConstPtr structure2 = fieldCreate->createStructure("id2", fieldNames, fields);
|
|
|
|
|
|
testOk1(structureWithNoId!=structure1);
|
|
testOk1(structure1!=structure2);
|
|
|
|
//serializationTest(structure1);
|
|
|
|
PVStructurePtr pvStructure = getPVDataCreate()->createPVStructure(structure1);
|
|
serializationTest(pvStructure);
|
|
}
|
|
|
|
void serializatioTest(FieldConstPtr const & field)
|
|
{
|
|
buffer->clear();
|
|
|
|
// serialize
|
|
field->serialize(buffer, flusher);
|
|
|
|
// deserialize
|
|
buffer->flip();
|
|
|
|
FieldConstPtr deserializedField = getFieldCreate()->deserialize(buffer, control);
|
|
|
|
// must equal
|
|
testOk1(*field == *deserializedField);
|
|
}
|
|
|
|
void testIntrospectionSerialization()
|
|
{
|
|
testDiag("Testing introspection serialization...");
|
|
|
|
FieldCreatePtr factory = getFieldCreate();
|
|
testOk1(factory.get()!=NULL);
|
|
|
|
// be sure all is covered
|
|
for (int i = pvBoolean; i < pvString; i++)
|
|
{
|
|
ScalarType scalarType = static_cast<ScalarType>(i);
|
|
|
|
ScalarConstPtr scalar = factory->createScalar(scalarType);
|
|
serializatioTest(scalar);
|
|
|
|
ScalarArrayConstPtr array = factory->createScalarArray(scalarType);
|
|
serializatioTest(array);
|
|
}
|
|
|
|
// and a structure
|
|
StructureConstPtr structure = getStandardField()->timeStamp();
|
|
serializatioTest(structure);
|
|
|
|
// and a structure array
|
|
StructureArrayConstPtr structureArray = factory->createStructureArray(structure);
|
|
serializatioTest(structureArray);
|
|
}
|
|
|
|
void testStringCopy() {
|
|
String s1 = "abc";
|
|
String s2 = s1;
|
|
if (s1.c_str() != s2.c_str())
|
|
testDiag("implementation of epics::pvData::String assignment operator does not share content");
|
|
}
|
|
|
|
} // end namespace
|
|
|
|
MAIN(testSerialization) {
|
|
|
|
testPlan(175);
|
|
|
|
flusher = new SerializableControlImpl();
|
|
control = new DeserializableControlImpl();
|
|
buffer = new ByteBuffer(1<<16);
|
|
|
|
testStringCopy();
|
|
|
|
testIntrospectionSerialization();
|
|
testEquals();
|
|
testNonInitialized();
|
|
|
|
testScalar();
|
|
testArray();
|
|
testStructure();
|
|
testStructureArray();
|
|
|
|
|
|
delete buffer;
|
|
delete control;
|
|
delete flusher;
|
|
|
|
epicsExitCallAtExits();
|
|
return testDone();
|
|
}
|
|
|