Files
pvData/testApp/misc/testprinter.cpp
2018-10-29 13:12:01 -07:00

242 lines
8.3 KiB
C++

/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#include <vector>
#include <testMain.h>
#include <epicsString.h>
#include <pv/pvUnitTest.h>
#include <pv/current_function.h>
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/standardField.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
# define USE_JSON
#endif
namespace pvd = epics::pvData;
typedef std::vector<std::string> lines_t;
namespace {
lines_t lines(const std::string& str)
{
lines_t ret;
size_t p = 0;
while(true) {
size_t next = str.find_first_of('\n', p);
ret.push_back(str.substr(p, next-p)); // exclude trailing '\n'
if(next==str.npos)
break;
else
p = next+1;
}
return ret;
}
std::string escape(const std::string& inp) {
size_t N = epicsStrnEscapedFromRawSize(inp.c_str(), inp.size());
std::vector<char> esc(N+1, '\0'); // include space for trailing nil
epicsStrnEscapedFromRaw(&esc[0], esc.size(), inp.c_str(), inp.size());
return std::string(&esc[0]);
}
std::string print(const pvd::PVStructure::Formatter& fmt)
{
std::ostringstream strm;
strm<<fmt;
return strm.str();
}
struct point {
size_t L, R;
};
// really primative diff
// expect -> actual
::detail::testPassx
testDiff(const std::string& expect, const std::string& actual, const std::string& msg = std::string())
{
bool match = expect==actual;
::detail::testPassx ret(match);
ret<<msg<<'\n';
lines_t lhs(lines(expect)), rhs(lines(actual));
size_t L=0, R=0;
while(L<lhs.size() && R<rhs.size()) {
if(lhs[L]==rhs[R]) {
ret<<" "<<escape(lhs[L])<<'\n';
L++;
R++;
} else {
// ugly... diagonalization hardcoded...
static const point search[] = {
{1,0},
{0,1},
{1,1},
{2,1},
{1,2},
{2,2},
};
size_t Lp, Rp;
for(size_t n=0, N=sizeof(search)/sizeof(search[0]); n<N; n++) {
Lp = std::min(L+search[n].L, lhs.size()-1u);
Rp = std::min(R+search[n].R, rhs.size()-1u);
if(lhs[Lp]==rhs[Rp])
break;
}
for(size_t l=L; l<Lp; l++)
ret<<"- "<<escape(lhs[l])<<'\n';
for(size_t r=R; r<Rp; r++)
ret<<"+ "<<escape(rhs[r])<<'\n';
L = Lp;
R = Rp;
// loop around and print matching line
}
}
for(; L<lhs.size(); L++)
ret<<"- "<<escape(lhs[L])<<'\n';
for(; R<rhs.size(); R++)
ret<<"+ "<<escape(rhs[R])<<'\n';
return ret;
}
static const pvd::StructureConstPtr scalarNumeric(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTScalar:1.0")
->add("value", pvd::pvInt)
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTScalarNumeric()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarNumeric));
input->getSubFieldT<pvd::PVScalar>("value")->putFrom(-42);
testDiff("<undefined> -42 \n", print(input->stream()));
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
testDiff("<undefined> -42 MINOR DEVICE FOO \n", print(input->stream()));
}
static const pvd::StructureConstPtr scalarString(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTScalar:1.0")
->add("value", pvd::pvString)
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTScalarString()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarString));
testDiff("<undefined> \n", print(input->stream()));
input->getSubFieldT<pvd::PVString>("value")->put("bar");
testDiff("<undefined> bar \n", print(input->stream()));
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
testDiff("<undefined> bar MINOR DEVICE FOO \n", print(input->stream()));
}
static const pvd::StructureConstPtr everything(pvd::getFieldCreate()->createFieldBuilder()
->setId("omg")
->add("scalar", pvd::pvString)
->addArray("scalarArray", pvd::pvString)
->addNestedStructure("below")
->add("A", pvd::pvInt)
->addNestedUnion("select")
->add("one", pvd::pvInt)
->add("two", pvd::pvInt)
->endNested()
->addNestedUnionArray("arrselect")
->add("foo", pvd::pvInt)
->add("bar", pvd::pvInt)
->endNested()
->addNestedStructureArray("astruct")
->add("red", pvd::pvInt)
->add("blue", pvd::pvInt)
->endNested()
->endNested()
->add("anything", pvd::getFieldCreate()->createVariantUnion())
->add("arrayany", pvd::getFieldCreate()->createVariantUnionArray())
->createStructure());
void testRaw()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(everything));
testDiff("omg \n"
" string scalar \n"
" string[] scalarArray []\n"
" structure below\n"
" int A 0\n"
" union select\n"
" (none)\n"
" union[] arrselect\n"
" structure[] astruct\n"
" any anything\n"
" (none)\n"
" any[] arrayany\n"
, print(input->stream()));
testDiff("omg \n"
" string scalar \n"
" structure below\n"
" int A 0\n"
, print(input->stream().show(pvd::BitSet().set(1).set(4))));
testDiff("omg \n"
"\033[1m string scalar \n"
"\033[0m\033[1m string[] scalarArray []\n"
"\033[0m structure below\n"
"\033[1m int A 0\n"
"\033[0m union select\n"
" (none)\n"
" union[] arrselect\n"
" structure[] astruct\n"
" any anything\n"
" (none)\n"
" any[] arrayany\n"
, print(input->stream()
.mode(pvd::PVStructure::Formatter::ANSI) // force use of escapes
.highlight(pvd::BitSet().set(1).set(2).set(4))
));
}
} // namespace
MAIN(testprinter)
{
testPlan(0);
showNTScalarNumeric();
showNTScalarString();
testRaw();
return testDone();
}