Completely rework Validator API
This commit is contained in:

committed by
mdavidsaver

parent
9923459c4b
commit
f22b5d5b7b
@ -41,7 +41,6 @@ LIBSRCS += ntcontinuum.cpp
|
||||
LIBSRCS += nthistogram.cpp
|
||||
LIBSRCS += nturi.cpp
|
||||
LIBSRCS += ntndarrayAttribute.cpp
|
||||
LIBSRCS += validator.cpp
|
||||
|
||||
LIBRARY = nt
|
||||
|
||||
|
246
src/ntfield.cpp
246
src/ntfield.cpp
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include "validator.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/ntfield.h>
|
||||
@ -31,198 +32,103 @@ NTField::NTField()
|
||||
{
|
||||
}
|
||||
|
||||
Result& NTField::isEnumerated(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("index")
|
||||
.has<ScalarArray>("choices");
|
||||
}
|
||||
|
||||
bool NTField::isEnumerated(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=2) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("index")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("choices")!=0) return false;
|
||||
if(f->getType()!=scalarArray) return false;
|
||||
ScalarConstPtr sa = static_pointer_cast<const Scalar>(f);
|
||||
if(sa->getScalarType()!=pvString) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isEnumerated(result).valid();
|
||||
}
|
||||
|
||||
Result& NTField::isTimeStamp(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("secondsPastEpoch")
|
||||
.has<Scalar>("nanoseconds")
|
||||
.has<Scalar>("userTag");
|
||||
}
|
||||
|
||||
bool NTField::isTimeStamp(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=3) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("secondsPastEpoch")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvLong) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("nanoseconds")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[2];
|
||||
if(names[2].compare("userTag")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isTimeStamp(result).valid();
|
||||
}
|
||||
|
||||
Result& NTField::isAlarm(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("severity")
|
||||
.has<Scalar>("status")
|
||||
.has<Scalar>("message");
|
||||
}
|
||||
|
||||
bool NTField::isAlarm(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=3) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("severity")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("status")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[2];
|
||||
if(names[2].compare("message")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvString) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isAlarm(result).valid();
|
||||
}
|
||||
|
||||
Result& NTField::isDisplay(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("limitLow")
|
||||
.has<Scalar>("limitHigh")
|
||||
.has<Scalar>("description")
|
||||
.has<Scalar>("format")
|
||||
.has<Scalar>("units");
|
||||
|
||||
}
|
||||
|
||||
bool NTField::isDisplay(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=5) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("limitLow")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("limitHigh")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[2];
|
||||
if(names[2].compare("description")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvString) return false;
|
||||
f = fields[3];
|
||||
if(names[3].compare("format")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvString) return false;
|
||||
f = fields[4];
|
||||
if(names[4].compare("units")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvString) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isDisplay(result).valid();
|
||||
}
|
||||
|
||||
Result& NTField::isAlarmLimit(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("active")
|
||||
.has<Scalar>("lowAlarmLimit")
|
||||
.has<Scalar>("lowWarningLimit")
|
||||
.has<Scalar>("highWarningLimit")
|
||||
.has<Scalar>("highAlarmLimit")
|
||||
.has<Scalar>("lowAlarmSeverity")
|
||||
.has<Scalar>("lowWarningSeverity")
|
||||
.has<Scalar>("highWarningSeverity")
|
||||
.has<Scalar>("highAlarmSeverity")
|
||||
.has<Scalar>("hysteresis");
|
||||
}
|
||||
|
||||
bool NTField::isAlarmLimit(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=10) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("active")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvBoolean) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("lowAlarmLimit")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[2];
|
||||
if(names[2].compare("lowWarningLimit")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[3];
|
||||
if(names[3].compare("highWarningLimit")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[4];
|
||||
if(names[4].compare("highAlarmLimit")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[5];
|
||||
if(names[5].compare("lowAlarmSeverity")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[6];
|
||||
if(names[6].compare("lowWarningSeverity")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[7];
|
||||
if(names[7].compare("highWarningSeverity")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[8];
|
||||
if(names[8].compare("highAlarmSeverity")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvInt) return false;
|
||||
f = fields[9];
|
||||
if(names[9].compare("hysteresis")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isAlarmLimit(result).valid();
|
||||
}
|
||||
|
||||
Result& NTField::isControl(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<Scalar>("limitLow")
|
||||
.has<Scalar>("limitHigh")
|
||||
.has<Scalar>("minStep");
|
||||
}
|
||||
|
||||
bool NTField::isControl(FieldConstPtr const & field)
|
||||
{
|
||||
if(field->getType()!=structure) return false;
|
||||
StructureConstPtr structurePtr = static_pointer_cast<const Structure>(field);
|
||||
FieldConstPtrArray fields = structurePtr->getFields();
|
||||
StringArray names = structurePtr->getFieldNames();
|
||||
size_t n = structurePtr->getNumberFields();
|
||||
if(n!=3) return false;
|
||||
FieldConstPtr f = fields[0];
|
||||
if(names[0].compare("limitLow")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
ScalarConstPtr s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[1];
|
||||
if(names[1].compare("limitHigh")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
f = fields[2];
|
||||
if(names[2].compare("minStep")!=0) return false;
|
||||
if(f->getType()!=scalar) return false;
|
||||
s = static_pointer_cast<const Scalar>(f);
|
||||
if(s->getScalarType()!=pvDouble) return false;
|
||||
return true;
|
||||
Result result(field);
|
||||
return isControl(result).valid();
|
||||
}
|
||||
|
||||
StructureConstPtr NTField::createEnumerated()
|
||||
|
@ -175,50 +175,75 @@ bool NTNDArray::is_a(PVStructurePtr const & pvStructure)
|
||||
return is_a(pvStructure->getStructure());
|
||||
}
|
||||
|
||||
static Validator* validator;
|
||||
static epicsThreadOnceId validator_once = EPICS_THREAD_ONCE_INIT;
|
||||
namespace {
|
||||
Result& isValue(Result& result)
|
||||
{
|
||||
result.is<Union>(Union::defaultId());
|
||||
|
||||
static void validator_init(void *)
|
||||
{
|
||||
StructureConstPtr structure(
|
||||
NTNDArray::createBuilder()->
|
||||
addDescriptor()->
|
||||
addAlarm()->
|
||||
addDisplay()->
|
||||
addTimeStamp()->
|
||||
createStructure());
|
||||
for (int i = pvBoolean; i < pvString; ++i) {
|
||||
ScalarType type = static_cast<ScalarType>(i);
|
||||
string name(ScalarTypeFunc::name(type));
|
||||
result.has<ScalarArray>(name + "Value");
|
||||
}
|
||||
|
||||
std::set<Field const *> optional;
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: these should be all getFieldT
|
||||
optional.insert(structure->getField("descriptor").get());
|
||||
optional.insert(structure->getField("alarm").get());
|
||||
optional.insert(structure->getField("timeStamp").get());
|
||||
optional.insert(structure->getField("display").get());
|
||||
Result& isCodec(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>("codec_t")
|
||||
.has<Scalar>("name")
|
||||
.has<Union>("parameters");
|
||||
}
|
||||
|
||||
// TODO: the following should come from a helper in NTNDArrayAttribute
|
||||
StructureConstPtr attribute(
|
||||
std::static_pointer_cast<const StructureArray>(
|
||||
structure->getField("attribute")
|
||||
)->getStructure()
|
||||
);
|
||||
Result& isDimension(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<StructureArray>("dimension_t[]")
|
||||
.has<Scalar>("size")
|
||||
.has<Scalar>("offset")
|
||||
.has<Scalar>("fullSize")
|
||||
.has<Scalar>("binning")
|
||||
.has<Scalar>("reverse");
|
||||
}
|
||||
|
||||
optional.insert(attribute->getField("tags").get());
|
||||
optional.insert(attribute->getField("alarm").get());
|
||||
optional.insert(attribute->getField("timeStamp").get());
|
||||
|
||||
validator = new Validator(structure, optional);
|
||||
// TODO: move to NTNDArrayAttribute
|
||||
Result& isAttribute(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<StructureArray>()
|
||||
.has<Scalar>("name")
|
||||
.has<Union>("value")
|
||||
.maybeHas<ScalarArray>("tags")
|
||||
.has<Scalar>("descriptor")
|
||||
.maybeHas<&NTField::isAlarm>("alarm")
|
||||
.maybeHas<&NTField::isTimeStamp>("timeStamp");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: I want to deprecate this and replace it with one that accepts
|
||||
// "Field const &" so nullptr shouldn't have to be checked for here
|
||||
bool NTNDArray::isCompatible(StructureConstPtr const &structure)
|
||||
{
|
||||
if(!structure.get()) return false;
|
||||
if (!structure)
|
||||
return false;
|
||||
|
||||
epicsThreadOnce(&validator_once, &validator_init, 0);
|
||||
Result result(structure);
|
||||
|
||||
return validator->isCompatible(*structure);
|
||||
return result
|
||||
.is<Structure>()
|
||||
.has<&isValue>("value")
|
||||
.has<&isCodec>("codec")
|
||||
.has<Scalar>("compressedSize")
|
||||
.has<Scalar>("uncompressedSize")
|
||||
.has<&isDimension>("dimension")
|
||||
.has<Scalar>("uniqueId")
|
||||
.has<&NTField::isTimeStamp>("dataTimeStamp")
|
||||
.has<&isAttribute>("attribute")
|
||||
.maybeHas<Scalar>("descriptor")
|
||||
.maybeHas<&NTField::isAlarm>("alarm")
|
||||
.maybeHas<&NTField::isTimeStamp>("timeStamp")
|
||||
.maybeHas<&NTField::isDisplay>("display")
|
||||
.valid();
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
namespace epics { namespace nt {
|
||||
|
||||
class Result;
|
||||
|
||||
class NTField;
|
||||
typedef std::tr1::shared_ptr<NTField> NTFieldPtr;
|
||||
|
||||
@ -149,6 +151,16 @@ private:
|
||||
NTField();
|
||||
epics::pvData::FieldCreatePtr fieldCreate;
|
||||
epics::pvData::StandardFieldPtr standardField;
|
||||
|
||||
// These won't be public just yet
|
||||
static Result& isEnumerated(Result&);
|
||||
static Result& isTimeStamp(Result&);
|
||||
static Result& isAlarm(Result&);
|
||||
static Result& isDisplay(Result&);
|
||||
static Result& isAlarmLimit(Result&);
|
||||
static Result& isControl(Result&);
|
||||
|
||||
friend class NTNDArray;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,272 +0,0 @@
|
||||
/* ntvalidator.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
|
||||
#include "validator.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace epics { namespace nt {
|
||||
|
||||
using epics::pvData::Type;
|
||||
using epics::pvData::Field;
|
||||
using epics::pvData::Union;
|
||||
using epics::pvData::UnionArray;
|
||||
using epics::pvData::Structure;
|
||||
using epics::pvData::StructureConstPtr;
|
||||
using epics::pvData::StructureArray;
|
||||
using epics::pvData::StringArray;
|
||||
using epics::pvData::FieldConstPtr;
|
||||
using epics::pvData::FieldConstPtrArray;
|
||||
using epics::pvData::BitSet;
|
||||
|
||||
using std::string;
|
||||
using std::set;
|
||||
|
||||
struct Validator::Helper {
|
||||
Validator const & validator;
|
||||
std::deque<string> path;
|
||||
Validator::Result result;
|
||||
|
||||
Helper(Validator const & validator);
|
||||
bool isOptional(Field const & field) const;
|
||||
string getCurrentPath(void) const;
|
||||
|
||||
void appendError(Validator::ErrorType type);
|
||||
void appendError(Validator::ErrorType type, string const & field_name);
|
||||
void appendError(Validator::ErrorType type, string const & ref_field_name,
|
||||
string const & field_name);
|
||||
|
||||
bool validate(Union const & ref, Union const & un);
|
||||
bool validate(Structure const & ref, Structure const & struc);
|
||||
bool validate(Field const & reference, Field const & field);
|
||||
bool validate(Field const & field);
|
||||
};
|
||||
|
||||
Validator::Helper::Helper(Validator const & validator) : validator(validator) {}
|
||||
|
||||
bool Validator::Helper::isOptional(Field const & field) const
|
||||
{
|
||||
return validator.optional.find(&field) != validator.optional.end();
|
||||
}
|
||||
|
||||
string Validator::Helper::getCurrentPath(void) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
// TODO: is there a better string join?
|
||||
std::deque<string>::const_iterator it;
|
||||
for (it = path.cbegin(); it != path.cend(); ++it) {
|
||||
if (it != path.cbegin())
|
||||
os << ".";
|
||||
os << *it;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void Validator::Helper::appendError(Validator::ErrorType type)
|
||||
{
|
||||
result.errors.push_back(Validator::Error(getCurrentPath(), type));
|
||||
}
|
||||
|
||||
void Validator::Helper::appendError(Validator::ErrorType type, string const & field_name)
|
||||
{
|
||||
path.push_back(field_name);
|
||||
appendError(type);
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
void Validator::Helper::appendError(Validator::ErrorType type, string const & ref_field_name,
|
||||
string const & field_name)
|
||||
{
|
||||
path.push_back(ref_field_name);
|
||||
string ref_field_path = getCurrentPath();
|
||||
path.pop_back();
|
||||
|
||||
path.push_back(field_name);
|
||||
string field_path(getCurrentPath());
|
||||
path.pop_back();
|
||||
|
||||
result.errors.push_back(Validator::Error(ref_field_path, field_path, type));
|
||||
}
|
||||
|
||||
bool Validator::Helper::validate(Union const & ref, Union const & un)
|
||||
{
|
||||
if (&un == &ref)
|
||||
return true;
|
||||
|
||||
if (ref.isVariant() != un.isVariant()) {
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ref.isVariant())
|
||||
return true;
|
||||
|
||||
StringArray const & rnames = ref.getFieldNames();
|
||||
StringArray const & unames = un.getFieldNames();
|
||||
|
||||
FieldConstPtrArray const & rfields = ref.getFields();
|
||||
FieldConstPtrArray const & ufields = un.getFields();
|
||||
|
||||
size_t numRefFields = ref.getNumberFields();
|
||||
size_t numUnFields = un.getNumberFields();
|
||||
|
||||
// Extra fields are OK
|
||||
bool ok = numRefFields <= numUnFields;
|
||||
|
||||
size_t i = 0;
|
||||
size_t N = std::min(numRefFields, numUnFields);
|
||||
for (; i < N; ++i) {
|
||||
string const & rname(rnames[i]);
|
||||
string const & uname(unames[i]);
|
||||
|
||||
if (rname != uname) {
|
||||
appendError(Validator::ErrorType::MISMATCHED_NAME, rname, uname);
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
path.push_back(rname);
|
||||
ok = ok && validate(*rfields[i], *ufields[i]);
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
for (; i < numRefFields; ++i)
|
||||
appendError(Validator::ErrorType::MISSING_FIELD, rnames[i]);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Validator::Helper::validate(Structure const &ref, Structure const &struc)
|
||||
{
|
||||
if (&struc == &ref)
|
||||
return true;
|
||||
|
||||
StringArray const &rnames = ref.getFieldNames();
|
||||
StringArray const &snames = struc.getFieldNames();
|
||||
|
||||
// TODO: This is naive O(N^2), replace with better algorithm (cache?)
|
||||
// TODO: This assumes getField doesn't fail (there's no Field::getFieldT (yet?))
|
||||
bool ok = true;
|
||||
StringArray::const_iterator ri;
|
||||
for (ri = rnames.cbegin(); ri != rnames.cend(); ++ri) {
|
||||
FieldConstPtr rfield(ref.getField(*ri));
|
||||
|
||||
if (std::find(snames.cbegin(), snames.cend(), *ri) == snames.end()) {
|
||||
if (isOptional(*rfield))
|
||||
continue;
|
||||
|
||||
appendError(Validator::ErrorType::MISSING_FIELD, *ri);
|
||||
ok = false;
|
||||
} else {
|
||||
path.push_back(*ri);
|
||||
if (!validate(*rfield, *struc.getField(*ri)))
|
||||
{
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE, *ri);
|
||||
ok = false;
|
||||
}
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Validator::Helper::validate(Field const &reference, Field const &field)
|
||||
{
|
||||
Type referenceType = reference.getType();
|
||||
if (referenceType != field.getType())
|
||||
return false;
|
||||
|
||||
switch (referenceType) {
|
||||
case Type::scalar:
|
||||
case Type::scalarArray:
|
||||
return true;
|
||||
|
||||
case Type::structure:
|
||||
return validate(
|
||||
static_cast<Structure const &>(reference),
|
||||
static_cast<Structure const &>(field));
|
||||
|
||||
case Type::structureArray:
|
||||
return validate(
|
||||
*static_cast<StructureArray const &>(reference).getStructure(),
|
||||
*static_cast<StructureArray const &>(field).getStructure());
|
||||
|
||||
case Type::union_:
|
||||
return validate(
|
||||
static_cast<Union const &>(reference),
|
||||
static_cast<Union const &>(field));
|
||||
|
||||
case Type::unionArray:
|
||||
return validate(
|
||||
*static_cast<UnionArray const &>(reference).getUnion(),
|
||||
*static_cast<UnionArray const &>(field).getUnion());
|
||||
|
||||
default:
|
||||
throw std::logic_error("Unknown Field type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Validator::Helper::validate(Field const &field)
|
||||
{
|
||||
return validate(*validator.reference, field);
|
||||
}
|
||||
|
||||
std::ostream& Validator::Error::dump(std::ostream& o) const {
|
||||
switch (type) {
|
||||
case ErrorType::MISSING_FIELD:
|
||||
return o << "Missing field '" << ref_path << "'";
|
||||
case ErrorType::INCORRECT_TYPE:
|
||||
return o << "Field '" << ref_path << "' has incorrect type";
|
||||
case ErrorType::MISMATCHED_NAME:
|
||||
return o << "Expected field '" << ref_path
|
||||
<< "' in Union, got '" << path << "'";
|
||||
default:
|
||||
return o << "Unknown error " << type << " in field '"
|
||||
<< ref_path << "'";
|
||||
}
|
||||
}
|
||||
|
||||
Validator::Validator(FieldConstPtr const & reference)
|
||||
: reference(reference) {
|
||||
if (!reference)
|
||||
throw std::logic_error("reference structure must not be NULL");
|
||||
}
|
||||
|
||||
Validator::Validator(FieldConstPtr const & reference,
|
||||
set<Field const *> const & optional)
|
||||
: reference(reference), optional(optional) {
|
||||
if (!reference)
|
||||
throw std::logic_error("reference structure must not be NULL");
|
||||
}
|
||||
|
||||
|
||||
Validator::Result Validator::validate(Field const & field) const
|
||||
{
|
||||
Helper helper(*this);
|
||||
helper.validate(field);
|
||||
return helper.result;
|
||||
}
|
||||
|
||||
bool Validator::isCompatible(Field const & field) const
|
||||
{
|
||||
return Helper(*this).validate(field);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Validator::Error& error)
|
||||
{
|
||||
return error.dump(o);
|
||||
}
|
||||
|
||||
}}
|
171
src/validator.h
171
src/validator.h
@ -20,61 +20,152 @@ namespace epics { namespace nt {
|
||||
* @author bsm
|
||||
*/
|
||||
|
||||
class Validator {
|
||||
public:
|
||||
enum ErrorType { MISSING_FIELD, INCORRECT_TYPE, MISMATCHED_NAME };
|
||||
|
||||
struct Helper;
|
||||
|
||||
struct Result {
|
||||
struct Error {
|
||||
std::string ref_path;
|
||||
std::string path;
|
||||
ErrorType type;
|
||||
enum Type {
|
||||
MissingField,
|
||||
IncorrectType,
|
||||
IncorrectId,
|
||||
} type;
|
||||
|
||||
Error(std::string const & ref_path, ErrorType type)
|
||||
: ref_path(ref_path), type(type) {}
|
||||
|
||||
Error(std::string const & ref_path, std::string const & path, ErrorType type)
|
||||
: ref_path(ref_path), path(path), type(type) {}
|
||||
Error(std::string const & ref_path, Type type)
|
||||
: path(), type(type) {}
|
||||
|
||||
bool operator==(const Error& other) const {
|
||||
return type == other.type &&
|
||||
ref_path == other.ref_path &&
|
||||
path == other.path;
|
||||
return type == other.type && path == other.path;
|
||||
}
|
||||
|
||||
std::ostream& dump(std::ostream&) const;
|
||||
std::ostream& dump(std::ostream& os) const {
|
||||
os << "Error(path=" << (path.empty() ? "<root>" : path) << ": ";
|
||||
|
||||
switch(type) {
|
||||
case MissingField: os << "missing"; break;
|
||||
case IncorrectType: os << "incorrect type"; break;
|
||||
case IncorrectId: os << "incorrect ID"; break;
|
||||
}
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
struct Result {
|
||||
std::vector<Error> errors;
|
||||
bool valid(void) const { return errors.empty(); }
|
||||
std::ostream& dump(std::ostream&) const;
|
||||
};
|
||||
const epics::pvData::FieldConstPtr f;
|
||||
const std::string path;
|
||||
std::vector<Error> errors;
|
||||
|
||||
Validator(epics::pvData::FieldConstPtr const & reference);
|
||||
enum result_t {
|
||||
Pass,
|
||||
Fail,
|
||||
} result;
|
||||
|
||||
Validator(epics::pvData::FieldConstPtr const & reference,
|
||||
std::set<epics::pvData::Field const *> const & optional);
|
||||
explicit Result(const epics::pvData::FieldConstPtr& f, const std::string& path = std::string())
|
||||
: f(f), path(path), errors(), result(Pass) {}
|
||||
|
||||
Result validate(epics::pvData::Field const & field) const;
|
||||
Result& operator|=(const Result& other) {
|
||||
result = std::max(result, other.result);
|
||||
errors.insert(errors.end(), other.errors.begin(), other.errors.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a Field is compatible with a NT definition.
|
||||
* @param definition the definition of the reference structure
|
||||
* @param field the Field being tested for compatibility
|
||||
* @return true if field is compatible, false otherwise
|
||||
*/
|
||||
bool isCompatible(epics::pvData::Field const & field) const;
|
||||
bool valid(void) const {
|
||||
return result == Pass;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result& is(void) {
|
||||
if (!dynamic_cast<T const *>(f.get())) {
|
||||
result = Fail;
|
||||
errors.push_back(Error(path, Error::IncorrectType));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result& is(const std::string& id) {
|
||||
T const *s = dynamic_cast<T const *>(f.get());
|
||||
if (!s) {
|
||||
result = Fail;
|
||||
errors.push_back(Error(path, Error::IncorrectType));
|
||||
} else if (s->getID() != id) {
|
||||
result = Fail;
|
||||
errors.push_back(Error(path, Error::IncorrectId));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<Result& (*fn)(Result&)>
|
||||
Result& has(const std::string& name) {
|
||||
return has<epics::pvData::Field>(name, false, fn);
|
||||
}
|
||||
|
||||
template<Result& (*fn)(Result&)>
|
||||
Result& maybeHas(const std::string& name) {
|
||||
return has<epics::pvData::Field>(name, true, fn);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result& has(const std::string& name) {
|
||||
return has<T>(name, false, NULL);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result& maybeHas(const std::string& name) {
|
||||
return has<T>(name, true, NULL);
|
||||
}
|
||||
|
||||
std::ostream& dump(std::ostream& os) const {
|
||||
os << "Result(valid=" << valid() << ", errors=[ ";
|
||||
|
||||
std::vector<Error>::const_iterator it;
|
||||
for (it = errors.cbegin(); it != errors.cend(); ++it) {
|
||||
(*it).dump(os);
|
||||
os << " ";
|
||||
}
|
||||
os << "])";
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct Helper;
|
||||
epics::pvData::FieldConstPtr const reference;
|
||||
std::set<epics::pvData::Field const *> optional;
|
||||
template<typename T>
|
||||
Result& has(const std::string& name, bool optional, Result& (*check)(Result&) = NULL) {
|
||||
epics::pvData::FieldConstPtr field;
|
||||
|
||||
switch(f->getType()) {
|
||||
case epics::pvData::Type::structure:
|
||||
field = static_cast<epics::pvData::Structure const *>(f.get())->getField(name);
|
||||
break;
|
||||
case epics::pvData::Type::structureArray:
|
||||
field = static_cast<epics::pvData::StructureArray const *>(f.get())->getStructure()->getField(name);
|
||||
break;
|
||||
case epics::pvData::Type::union_:
|
||||
field = static_cast<epics::pvData::Union const *>(f.get())->getField(name);
|
||||
break;
|
||||
case epics::pvData::Type::unionArray:
|
||||
field = static_cast<epics::pvData::UnionArray const *>(f.get())->getUnion()->getField(name);
|
||||
break;
|
||||
default:
|
||||
// Expected a structure-like Field
|
||||
result = Fail;
|
||||
errors.push_back(Error(path, Error::Type::IncorrectType));
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!field) {
|
||||
if (!optional) {
|
||||
result = Fail;
|
||||
errors.push_back(Error(path + "." + name, Error::Type::MissingField));
|
||||
}
|
||||
} else if (!dynamic_cast<T const *>(field.get())) {
|
||||
result = Fail;
|
||||
errors.push_back(Error(path + "." + name, Error::Type::IncorrectType));
|
||||
} else if (check) {
|
||||
Result r(field, path + "." + name);
|
||||
*this |= check(r);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Validator::Error& error);
|
||||
|
||||
}}
|
||||
|
||||
#endif /* VALIDATOR_H */
|
||||
#endif
|
@ -12,156 +12,311 @@
|
||||
|
||||
using namespace epics::nt;
|
||||
using epics::pvData::StructureConstPtr;
|
||||
using epics::pvData::UnionConstPtr;
|
||||
using epics::pvData::Field;
|
||||
using epics::pvData::ScalarType;
|
||||
using epics::pvData::ScalarTypeFunc::name;
|
||||
using epics::pvData::FieldConstPtr;
|
||||
using epics::pvData::FieldBuilder;
|
||||
using epics::pvData::FieldBuilderPtr;
|
||||
using epics::pvData::Scalar;
|
||||
using epics::pvData::ScalarArray;
|
||||
using epics::pvData::Structure;
|
||||
using epics::pvData::Union;
|
||||
|
||||
static epics::pvData::FieldCreatePtr FC;
|
||||
|
||||
void test_result()
|
||||
void test_is()
|
||||
{
|
||||
testDiag("test_result");
|
||||
testDiag("test_is");
|
||||
|
||||
Validator::Result result;
|
||||
testOk(result.valid(), "Result with no errors means valid");
|
||||
// Result::is<Scalar> must be valid for Scalars of any type
|
||||
for(int i = ScalarType::pvBoolean; i <= ScalarType::pvString; ++i) {
|
||||
ScalarType t = static_cast<ScalarType>(i);
|
||||
testOk(Result(FC->createScalar(t)).is<Scalar>().valid(),
|
||||
"Result(Scalar<%s>).is<Scalar>().valid()", name(t));
|
||||
}
|
||||
|
||||
result.errors.push_back(Validator::Error("a.b.c", Validator::ErrorType::MISSING_FIELD));
|
||||
testOk(!result.valid(), "Result with one error means invalid");
|
||||
}
|
||||
|
||||
void test_scalar()
|
||||
{
|
||||
testDiag("test_scalar");
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalar(ScalarType::pvBoolean));
|
||||
FieldConstPtr field(FC->createScalar(ScalarType::pvBoolean));
|
||||
testOk(Validator(def).isCompatible(*field), "isCompatible(Scalar<bool>, Scalar<bool>)");
|
||||
// Result::is<ScalarArray> must be valid for ScalarArray of any type
|
||||
for(int i = ScalarType::pvBoolean; i <= ScalarType::pvString; ++i) {
|
||||
ScalarType t = static_cast<ScalarType>(i);
|
||||
testOk(Result(FC->createScalarArray(t)).is<ScalarArray>().valid(),
|
||||
"Result(ScalarArray<%s>).is<ScalarArray>().valid()", name(t));
|
||||
}
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalar(ScalarType::pvBoolean));
|
||||
FieldConstPtr field(FC->createScalar(ScalarType::pvInt));
|
||||
testOk(Validator(def).isCompatible(*field), "isCompatible(Scalar<bool>, Scalar<int>)");
|
||||
// Result::is<Scalar> must be invalid for non-Scalar
|
||||
Result result(FC->createScalarArray(ScalarType::pvInt));
|
||||
result.is<Scalar>();
|
||||
testOk(!result.valid(), "!Result(ScalarArray<pvInt>).is<Scalar>.valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalar(ScalarType::pvString));
|
||||
FieldConstPtr field(FC->createScalar(ScalarType::pvFloat));
|
||||
testOk(Validator(def).isCompatible(*field), "isCompatible(Scalar<string>, Scalar<float>)");
|
||||
}
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalarArray(ScalarType::pvString));
|
||||
FieldConstPtr field(FC->createScalarArray(ScalarType::pvFloat));
|
||||
testOk(Validator(def).isCompatible(*field), "isCompatible(ScalarArray<string>, ScalarArray<float>)");
|
||||
}
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalarArray(ScalarType::pvByte));
|
||||
FieldConstPtr field(FC->createScalar(ScalarType::pvByte));
|
||||
testOk(!Validator(def).isCompatible(*field), "!isCompatible(ScalarArray<byte>, Scalar<byte>)");
|
||||
}
|
||||
|
||||
{
|
||||
FieldConstPtr def(FC->createScalar(ScalarType::pvByte));
|
||||
FieldConstPtr field(FC->createScalarArray(ScalarType::pvByte));
|
||||
testOk(!Validator(def).isCompatible(*field), "!isCompatible(Scalar<byte>, ScalarArray<byte>)");
|
||||
// Result::is<ScalarArray> must be invalid for non-ScalarArray
|
||||
Result result(FC->createScalar(ScalarType::pvInt));
|
||||
result.is<ScalarArray>();
|
||||
testOk(!result.valid(), "!Result(ScalarArray<pvInt>).is<Scalar>.valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
}
|
||||
|
||||
void test_union()
|
||||
void test_is_id()
|
||||
{
|
||||
testDiag("test_union");
|
||||
testDiag("test_is_id");
|
||||
|
||||
FieldBuilderPtr FB(FieldBuilder::begin());
|
||||
|
||||
FieldConstPtr unionVar(FC->createVariantUnion());
|
||||
FieldConstPtr unionABC(FB->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvInt)->
|
||||
add("C", ScalarType::pvInt)->
|
||||
createUnion());
|
||||
|
||||
FieldConstPtr unionABC2(FB->
|
||||
add("A", ScalarType::pvFloat)->
|
||||
add("B", ScalarType::pvDouble)->
|
||||
add("C", ScalarType::pvString)->
|
||||
createUnion());
|
||||
|
||||
FieldConstPtr unionBAC(FB->
|
||||
add("B", ScalarType::pvInt)->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("C", ScalarType::pvInt)->
|
||||
createUnion());
|
||||
|
||||
FieldConstPtr unionAB(FB->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvInt)->
|
||||
createUnion());
|
||||
|
||||
FieldConstPtr unionNested1(FB->
|
||||
add("A", unionABC)->
|
||||
add("B", unionABC)->
|
||||
createUnion());
|
||||
|
||||
FieldConstPtr unionNested2(FB->
|
||||
add("A", unionABC)->
|
||||
add("B", unionBAC)->
|
||||
createUnion());
|
||||
|
||||
testOk(Validator(unionVar).isCompatible(*unionVar),
|
||||
"isCompatible(VarUnion, VarUnion)");
|
||||
testOk(!Validator(unionVar).isCompatible(*unionABC),
|
||||
"!isCompatible(VarUnion, Union{A:int, B:int, C:int})");
|
||||
testOk(!Validator(unionABC).isCompatible(*unionVar),
|
||||
"!isCompatible(Union{A:int, B:int, C:int}, VarUnion)");
|
||||
testOk(Validator(unionABC).isCompatible(*unionABC),
|
||||
"isCompatible(Union{A:int, B:int, C:int}, Union{A:int, B:int, C:int})");
|
||||
|
||||
testOk(Validator(unionABC).isCompatible(*unionABC2),
|
||||
"isCompatible(Union{A:int, B:int, C:int}, Union{A:float, B:double, C:string})");
|
||||
testOk(Validator(unionABC2).isCompatible(*unionABC),
|
||||
"isCompatible(Union{A:float, B:double, C:string}, Union{A:int, B:int, C:int})");
|
||||
|
||||
testOk(!Validator(unionABC).isCompatible(*unionBAC),
|
||||
"!isCompatible(Union{A:int, B:int, C:int}, Union{B:int, A:int, C:int})");
|
||||
|
||||
testOk(Validator(unionAB).isCompatible(*unionABC), "Extra Union field");
|
||||
testOk(!Validator(unionABC).isCompatible(*unionAB), "Missing Union field");
|
||||
|
||||
{
|
||||
Validator::Result result = Validator(unionABC).validate(*unionAB);
|
||||
testOk(result.errors.size() == 1 &&
|
||||
result.errors[0] == Validator::Error("C", Validator::ErrorType::MISSING_FIELD),
|
||||
"Missing Union field Error");
|
||||
// Both type and ID match for Structure
|
||||
Result result(FB->setId("TEST_ID")->createStructure());
|
||||
result.is<Structure>("TEST_ID");
|
||||
testOk(result.valid(), "Result(Structure['TEST_ID']).is<Structure>('TEST_ID').valid()");
|
||||
}
|
||||
|
||||
testOk(!Validator(unionNested1).isCompatible(*unionNested2), "Nested Unions");
|
||||
{
|
||||
// Both type and ID match for Union
|
||||
UnionConstPtr un(FB->
|
||||
setId("TEST_ID")->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvString)->
|
||||
createUnion()
|
||||
);
|
||||
Result result(un);
|
||||
result.is<Union>("TEST_ID");
|
||||
testOk(result.valid(), "Result(Union{A:int,B:string}['TEST_ID']).is<Union>('TEST_ID').valid()");
|
||||
}
|
||||
|
||||
{
|
||||
// Both type and ID match for Variant Union
|
||||
Result result(FB-> createUnion());
|
||||
result.is<Union>(Union::ANY_ID);
|
||||
testOk(result.valid(), "Result(Union).is<Union>('%s').valid()", Union::ANY_ID.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
// ID matches, type doesn't
|
||||
Result result(FB->setId("TEST_ID")->createStructure());
|
||||
result.is<Union>("TEST_ID");
|
||||
testOk(!result.valid(), "!Result(Union['TEST_ID']).is<Structure>('TEST_ID').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
|
||||
{
|
||||
// Type matches, ID doesn't
|
||||
Result result(FB->setId("WRONG_ID")->createStructure());
|
||||
result.is<Structure>("TEST_ID");
|
||||
testOk(!result.valid(), "!Result(Structure['WRONG_ID']).is<Structure>('TEST_ID').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectId));
|
||||
}
|
||||
|
||||
{
|
||||
// Neither type nor ID match (ID is not even checked in this case since it doesn't exist)
|
||||
Result result(FC->createScalar(ScalarType::pvDouble));
|
||||
result.is<Structure>("SOME_ID");
|
||||
testOk(!result.valid(), "!Result(Scalar).is<Structure>('SOME_ID').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
}
|
||||
|
||||
void test_isCompatible()
|
||||
void test_has()
|
||||
{
|
||||
testDiag("test_isCompatible");
|
||||
testDiag("test_has");
|
||||
|
||||
std::set<Field const *> opt;
|
||||
StructureConstPtr ref(NTNDArray::createBuilder()->addAlarm()->createStructure());
|
||||
opt.insert(ref->getField("alarm").get());
|
||||
FieldBuilderPtr FB(FieldBuilder::begin());
|
||||
|
||||
StructureConstPtr struc(NTNDArray::createBuilder()->createStructure());
|
||||
StructureConstPtr struc(FB->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvString)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
testOk1(Validator(ref, opt).isCompatible(*struc));
|
||||
std::string strucRepr("Structure{A:int,B:String}");
|
||||
|
||||
{
|
||||
// Test that struc has both A and B, both being Scalars
|
||||
Result result(struc);
|
||||
result
|
||||
.has<Scalar>("A")
|
||||
.has<Scalar>("B");
|
||||
|
||||
testOk(result.valid(),
|
||||
"Result(%s).has<Scalar>('A').has<Scalar>('B').valid()",
|
||||
strucRepr.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
// Test that struc does not have a field B of type ScalarArray
|
||||
Result result(struc);
|
||||
result
|
||||
.has<Scalar>("A")
|
||||
.has<ScalarArray>("B");
|
||||
testOk(!result.valid(),
|
||||
"!Result(%s).has<Scalar>('A').has<ScalarArray>('B').valid()",
|
||||
strucRepr.c_str());
|
||||
testOk1(result.errors.at(0) == Result::Error("B", Result::Error::IncorrectType));
|
||||
}
|
||||
|
||||
{
|
||||
// Test that struc does not have a field C
|
||||
Result result(struc);
|
||||
result
|
||||
.has<Scalar>("A")
|
||||
.has<Scalar>("C");
|
||||
testOk(!result.valid(),
|
||||
"!Result(%s).has<Scalar>('A').has<Scalar>('C').valid()",
|
||||
strucRepr.c_str());
|
||||
testOk1(result.errors.at(0) == Result::Error("C", Result::Error::MissingField));
|
||||
}
|
||||
|
||||
{
|
||||
// Test that 'has' fails for non-structure-like Fields
|
||||
Result result(FC->createScalar(ScalarType::pvByte));
|
||||
result.has<Scalar>("X");
|
||||
testOk(!result.valid(), "!Result(Scalar<pvByte>).has<Scalar>('X').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
}
|
||||
|
||||
void test_maybe_has()
|
||||
{
|
||||
testDiag("test_maybe_has");
|
||||
|
||||
FieldBuilderPtr FB(FieldBuilder::begin());
|
||||
|
||||
StructureConstPtr struc(FB->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvString)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
std::string strucRepr("Structure{A:int,B:String}");
|
||||
|
||||
{
|
||||
// Test that struc maybe has A and B, both being Scalars
|
||||
Result result(struc);
|
||||
result
|
||||
.maybeHas<Scalar>("A")
|
||||
.maybeHas<Scalar>("B");
|
||||
|
||||
testOk(result.valid(),
|
||||
"Result(%s).maybeHas<Scalar>('A').maybeHas<Scalar>('B').valid()",
|
||||
strucRepr.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
// Test that if struc has a field B, it must be of type ScalarArray
|
||||
Result result(struc);
|
||||
result
|
||||
.maybeHas<Scalar>("A")
|
||||
.maybeHas<ScalarArray>("B");
|
||||
testOk(!result.valid(),
|
||||
"!Result(%s).maybeHas<Scalar>('A').maybeHas<ScalarArray>('B').valid()",
|
||||
strucRepr.c_str());
|
||||
testOk1(result.errors.at(0) == Result::Error("B", Result::Error::IncorrectType));
|
||||
}
|
||||
|
||||
{
|
||||
// Test that struc maybe has A (which it does) and B (which it doesn't)
|
||||
Result result(struc);
|
||||
result
|
||||
.maybeHas<Scalar>("A")
|
||||
.maybeHas<Scalar>("C");
|
||||
testOk(result.valid(),
|
||||
"Result(%s).maybeHas<Scalar>('A').maybeHas<Scalar>('C').valid()",
|
||||
strucRepr.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
// Test that 'maybeHas' fails for non-structure-like Fields
|
||||
Result result(FC->createScalar(ScalarType::pvByte));
|
||||
result.maybeHas<Scalar>("X");
|
||||
testOk(!result.valid(), "!Result(Scalar<pvByte>).maybeHas<Scalar>('X').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
|
||||
}
|
||||
}
|
||||
|
||||
Result& isStructABC(Result& result)
|
||||
{
|
||||
return result
|
||||
.is<Structure>("ABC")
|
||||
.has<Scalar>("A")
|
||||
.has<ScalarArray>("B")
|
||||
.maybeHas<Scalar>("C");
|
||||
}
|
||||
|
||||
void test_has_fn()
|
||||
{
|
||||
testDiag("test_has_fn");
|
||||
FieldBuilderPtr FB(FieldBuilder::begin());
|
||||
|
||||
{
|
||||
StructureConstPtr inner(FB->
|
||||
setId("ABC")->
|
||||
add("A", ScalarType::pvInt)->
|
||||
addArray("B", ScalarType::pvDouble)->
|
||||
add("C", ScalarType::pvString)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
Result result(FB->add("inner", inner)->createStructure());
|
||||
result.has<&isStructABC>("inner");
|
||||
|
||||
testOk(result.valid(), "Result({inner:<valid structABC>}).has<&isStructAbc>('inner').valid()");
|
||||
}
|
||||
|
||||
{
|
||||
StructureConstPtr inner(FB->
|
||||
setId("ABC")->
|
||||
add("A", ScalarType::pvInt)->
|
||||
addArray("B", ScalarType::pvDouble)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
Result result(FB->add("inner", inner)->createStructure());
|
||||
result.has<&isStructABC>("inner");
|
||||
|
||||
testOk(result.valid(), "Result({inner:<valid structABC w/o C>}).has<&isStructAbc>('inner').valid()");
|
||||
}
|
||||
|
||||
{
|
||||
StructureConstPtr inner(FB->
|
||||
setId("XYZ")->
|
||||
add("A", ScalarType::pvInt)->
|
||||
addArray("B", ScalarType::pvDouble)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
Result result(FB->add("inner", inner)->createStructure());
|
||||
result.has<&isStructABC>("inner");
|
||||
|
||||
testOk(!result.valid(), "!Result({inner:<structABC wrong id>}).has<&isStructAbc>('inner').valid()");
|
||||
testOk1(result.errors.at(0) == Result::Error("inner", Result::Error::IncorrectId));
|
||||
}
|
||||
|
||||
{
|
||||
StructureConstPtr inner(FB->
|
||||
setId("XYZ")->
|
||||
add("A", ScalarType::pvInt)->
|
||||
add("B", ScalarType::pvDouble)->
|
||||
createStructure()
|
||||
);
|
||||
|
||||
Result result(FB->add("inner", inner)->createStructure());
|
||||
result.has<&isStructABC>("inner");
|
||||
|
||||
testOk(!result.valid(), "!Result({inner:<structABC wrong id and fields>}).has<&isStructAbc>('inner').valid()");
|
||||
testOk1(result.errors.size() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(testValidator) {
|
||||
testPlan(0);
|
||||
testPlan(56);
|
||||
FC = epics::pvData::getFieldCreate();
|
||||
test_result();
|
||||
test_scalar();
|
||||
test_union();
|
||||
test_isCompatible();
|
||||
test_is();
|
||||
test_is_id();
|
||||
test_has();
|
||||
test_maybe_has();
|
||||
test_has_fn();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user