/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #include #include #include #include #include "utilpvt.h" #include "pvaproto.h" #include "dataimpl.h" using namespace pvxs; namespace { template testCase testBytes(const std::vector& actual, const char(&buf)[N]) { bool ok = actual.size()==(N-1) && std::equal(actual.begin(), actual.end(), (const uint8_t*)buf); testCase ret(ok); ret<<"Expect: \""< void testToBytes(bool be, Fn&& fn, const char(&expect)[N]) { std::vector buf; VectorOutBuf S(be, buf); fn(S); buf.resize(buf.size()-S.size()); testBytes(buf, expect); } template void testFromBytes(bool be, const char(&input)[N], Fn&& fn) { std::vector buf(input, input+N-1); FixedBuf S(be, buf); fn(S); testCase(S.good() && S.empty())<<"Deserialize \""<(), 0xdeadbeef); } { TypeStore ctxt; auto val = nt::NTScalar{TypeCode::UInt32}.build().create(); testFromBytes(true, "\x02 \x01\x0bhello world\x00\x00\x00\xab", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["timeStamp.nanoseconds"].isMarked()); testOk1(!!val["alarm.message"].isMarked()); testEq(val["value"].as(), 0u); testEq(val["timeStamp.nanoseconds"].as(), 0xabu); testEq(val["alarm.message"].as(), "hello world"); } } TypeDef simpledef(TypeCode::Struct, "simple_t", { Member(TypeCode::Float64A, "value"), Member(TypeCode::Struct, "timeStamp", "time_t", { Member(TypeCode::UInt64, "secondsPastEpoch"), Member(TypeCode::UInt32, "nanoseconds"), }), Member(TypeCode::Struct, "arbitrary", { Member(TypeCode::StructA, "sarr", { Member(TypeCode::UInt32, "value"), }), }), Member(TypeCode::Any, "any"), Member(TypeCode::AnyA, "anya"), Member(TypeCode::Union, "choice", { Member(TypeCode::Float32, "a"), Member(TypeCode::String, "b"), }), Member(TypeCode::UnionA, "achoice", { Member(TypeCode::String, "x"), Member(TypeCode::String, "y"), }), }); void testSimpleDef() { testDiag("%s", __func__); auto val = simpledef.create(); testEq(std::string(SB()<<"\n"< 10 [10] any -> 7 [7] anya -> 8 [8] arbitrary -> 5 [5] arbitrary.sarr -> 6 [6] choice -> 9 [9] timeStamp -> 2 [2] timeStamp.nanoseconds -> 4 [4] timeStamp.secondsPastEpoch -> 3 [3] value -> 1 [1] value : 1 [1] timeStamp : 2 [2] arbitrary : 5 [5] any : 7 [7] anya : 8 [8] choice : 9 [9] achoice : 10 [10] [1] double[] parent=[0] [1:2) [2] struct time_t parent=[0] [2:5) nanoseconds -> 2 [4] secondsPastEpoch -> 1 [3] secondsPastEpoch : 1 [3] nanoseconds : 2 [4] [3] uint64_t parent=[2] [3:4) [4] uint32_t parent=[2] [4:5) [5] struct parent=[0] [5:7) sarr -> 1 [6] sarr : 1 [6] [6] struct[] parent=[5] [6:7) [0] struct parent=[0] [0:2) value -> 1 [1] value : 1 [1] [1] uint32_t parent=[0] [1:2) [7] any parent=[0] [7:8) [8] any[] parent=[0] [8:9) [9] union parent=[0] [9:10) a -> 0 [0] b -> 1 [1] a : 0 [0] [0] float parent=[0] [0:1) b : 1 [1] [0] string parent=[0] [0:1) [10] union[] parent=[0] [10:11) [0] union parent=[0] [0:1) x -> 0 [0] y -> 1 [1] x : 0 [0] [0] string parent=[0] [0:1) y : 1 [1] [0] string parent=[0] [0:1) )out")<<"Actual:\n"< arr(3); arr[0] = fld.allocMember(); arr[1] = fld.allocMember(); // leave [2] as null arr[0]["value"] = 0xdeadbeef; arr[1]["value"] = 0x1badface; fld = arr.freeze().castTo(); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x01@\x03\x01\xde\xad\xbe\xef\x01\x1b\xad\xfa\xce\x00"); } { auto val = simpledef.create(); val["choice->b"] = "test"; testOk1(!!val["choice"].isMarked()); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x02\x00\x02\x01\x04test"); } { auto val = simpledef.create(); auto fld = val["achoice"]; shared_array arr(3); arr[0] = fld.allocMember(); arr[1] = fld.allocMember(); // leave [2] as null arr[0]["->x"] = "theX"; arr[1]["->y"] = "theY"; fld = arr.freeze().castTo(); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x02\x00\x04\x03\x01\x00\x04theX\x01\x01\x04theY\x00"); } // Any { auto val = simpledef.create(); auto v = TypeDef(TypeCode::UInt32).create(); v = 0x600df00d; val["any"].from(v); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x01\x80\x26\x60\x0d\xf0\x0d"); } // Any { auto val = simpledef.create(); val["any"].mark(); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x01\x80\xff"); } // Any[] { auto val = simpledef.create(); auto fld = val["anya"]; shared_array arr(3); arr[0] = TypeDef(TypeCode::UInt32).create(); arr[1] = TypeDef(TypeCode::Struct, {Member(TypeCode::String, "q")}).create(); // leave [2] as null arr[0] = 0x7b; arr[1]["q"] = "theq"; fld = arr.freeze().castTo(); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); }, "\x02\x00\x01\x03\x01\x26\x00\x00\x00\x7b\x01\x80\x00\x01\x01q\x60\x04theq\x00"); } } void testDeserialize2() { testDiag("%s", __func__); { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x01@\x03\x01\xde\xad\xbe\xef\x01\x1b\xad\xfa\xce\x00", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["arbitrary.sarr"].isMarked()); testEq(val["arbitrary.sarr"].as>().size(), 3u*sizeof(Value)); testEq(val["arbitrary.sarr[0]value"].as(), 0xdeadbeef); testEq(val["arbitrary.sarr[1]value"].as(), 0x1badfaceu); testEq(val["arbitrary.sarr[2]value"].type(), TypeCode::Null); } { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x02\x00\x02\x01\x04test", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["choice"].isMarked()); testEq(val["choice"].as(), "test"); } { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x02\x00\x04\x03\x01\x00\x04theX\x01\x01\x04theY\x00", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["achoice"].isMarked()); testEq(val["achoice"].as>().size(), 3u*sizeof(Value)); testEq(val["achoice[0]"].as(), "theX"); testEq(val["achoice[1]"].as(), "theY"); testEq(val["achoice[2]"].type(), TypeCode::Null); } { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x01\x80\x26\x60\x0d\xf0\x0d", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["any"].isMarked()); testEq(val["any"].as(), 0x600df00du); } { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x01\x80\xff", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["any"].isMarked()); //testEq(val["any"].type(), TypeCode::Null); determine type _inside_ Any? } { TypeStore ctxt; auto val = simpledef.create(); testFromBytes(true, "\x02\x00\x01\x03\x01\x26\x00\x00\x00\x7b\x01\x80\x00\x01\x01q\x60\x04theq\x00", [&val, &ctxt](Buffer& buf) { from_wire_valid(buf, ctxt, val); }); testOk1(!val["value"].isMarked()); testOk1(!!val["anya"].isMarked()); testEq(val["anya"].as>().size(), 3u*sizeof(Value)); testEq(val["anya[0]"].as(), 0x7bu); testEq(val["anya[1]q"].as(), "theq"); testEq(val["anya[2]"].type(), TypeCode::Null); } } void testTraverse() { testDiag("%s", __func__); auto top = nt::NTScalar{TypeCode::Int32, true}.create(); testOk1(!top["<"].valid()); { auto top2 = top["value<"]; testOk1(top.compareType(top2)); testOk1(top.compareInst(top2)); } { auto sevr1 = top["alarm.severity"]; auto sevr2 = top["value([&val]() { val.nameOf(val); }); } } // namespace MAIN(testdata) { testPlan(70); testSerialize1(); testDeserialize1(); testSimpleDef(); testSerialize2(); testDeserialize2(); testTraverse(); testAssign(); testName(); cleanup_for_valgrind(); return testDone(); }