/** * 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 #include "utilpvt.h" #include "pvaproto.h" #include "dataimpl.h" using namespace pvxs; namespace { void testTraverse() { testDiag("%s", __func__); auto top = nt::NTScalar{TypeCode::Int32, true}.create(); testOk1(!top["<"].valid()); testThrows([&top](){ top.lookup("<"); }); { auto top2 = top["value<"]; testOk1(top.equalType(top2)); testOk1(top.equalInst(top2)); } { auto sevr1 = top["alarm.severity"]; auto sevr2 = top["value(), INVALID_ALARM); } void testAssignArray() { testDiag("%s", __func__); auto val = TypeDef(TypeCode::Int32A).create(); val = shared_array({1, 2, 3}); val = shared_array({4, 5, 6}); // with implied conversion } void testAssignUnion() { testDiag("%s", __func__); auto val = TypeDef(TypeCode::Union, { members::UInt16("u16"), members::String("s"), }).create(); val["->u16"] = 42; testEq(val.as(), "42"); val["->s"] = "test"; testEq(val.as(), "test"); testEq(val.nameOf(val["->"]), "s"); val = unselect; testFalse(val["->"].valid()); testThrows([&val](){ val["->u16"] = "hello"; }); // previous selection succeeds, but assignment fails testTrue(val["->"].valid()); } void testAssignAny() { testDiag("%s", __func__); auto val = TypeDef(TypeCode::Any).create(); // check the simple self assignment is an error. testThrows([&val](){ val.from(val); }); val = 42; testThrows([&val](){ val.from(val); }); } void testName() { testDiag("%s", __func__); auto def = nt::NTScalar{TypeCode::String}.build(); auto val = def.create(); testEq(val.nameOf(val["value"]), "value"); testEq(val.nameOf(val["alarm.status"]), "alarm.status"); testThrows([&val]() { val.nameOf(val); }); } void testIterStruct() { testDiag("%s", __func__); auto def = nt::NTScalar{TypeCode::String}.build(); auto val = def.create(); unsigned i=0; for(auto fld : val.iall()) { testDiag("field %s", val.nameOf(fld).c_str()); i++; } testEq(i, 9u)<<"# of descendant fields"; i=0; for(auto fld : val.ichildren()) { testDiag("field %s", val.nameOf(fld).c_str()); i++; } testEq(i, 3u)<<"# of child fields"; auto testMarked = [&val](unsigned expect) -> testCase { unsigned i=0; for(auto fld : val.imarked()) { testDiag("field %s", val.nameOf(fld).c_str()); i++; } return testEq(i, expect); }; testMarked(0u)<<"no descendant fields"; val["alarm.status"].mark(); testMarked(1u)<<"mark one field"; val.unmark(); val["alarm"].mark(); testMarked(4u)<<"mark alarm sub-struct"; val.unmark(); val["value"].mark(); // 1 field val["alarm.status"].mark(); // 1 field val["timeStamp"].mark(); // 4 fields (struct node and 3x leaves) testMarked(6u)<<"mark multiple sub-struct"; val["timeStamp.nanoseconds"].unmark(true); testMarked(2u)<<"mark multiple sub-struct"; } void testIterUnion() { testDiag("%s", __func__); auto top = TypeDef(TypeCode::Union, { members::UInt32("A"), members::String("B"), }).create(); { auto it = top.iall().begin(); auto end = top.iall().end(); if(testOk1(it!=end)) testEq(top.nameOf(*it), "A"); ++it; if(testOk1(it!=end)) testEq(top.nameOf(*it), "B"); ++it; testOk1(it==end); } { auto it = top.ichildren().begin(); auto end = top.ichildren().end(); if(testOk1(it!=end)) testEq(top.nameOf(*it), "A"); ++it; if(testOk1(it!=end)) testEq(top.nameOf(*it), "B"); ++it; testOk1(it==end); } testOk(top.imarked().begin()==top.imarked().end(), "imarked() empty"); top["->A"] = 42; { auto it = top.imarked().begin(); auto end = top.imarked().end(); if(testOk1(it!=end)) testEq(top.nameOf(*it), "A"); ++it; testOk1(it==end); } top["->B"] = "test"; { auto it = top.imarked().begin(); auto end = top.imarked().end(); if(testOk1(it!=end)) testEq(top.nameOf(*it), "B"); ++it; testOk1(it==end); } } template void testConvertScalar(const Store& store, const Inout& inout) { testShow()<<__func__<<"("< store_t; auto cont = TypeDef(store_t::code).create(); try { cont.from(inout); }catch(std::exception& e){ testCase(false)<<"Error storing as "<())<())<"< void testConvertScalar2(const Store& store, const In& in, const Out& out) { testShow()<<__func__<<"("< store_t; auto cont = TypeDef(store_t::code).create(); try { cont.from(in); }catch(std::exception& e){ testCase(false)<<"Error storing as "<())<())<"<(), 4.0); val1.unmark(); val2["display.description"] = "blah"; testThrows([&val1, &val2]() { val1.assign(val2); }); } } void testUnionMagicAssign() { testShow()<<__func__; using namespace members; auto val(TypeDef(TypeCode::Union, { UInt16("x"), Float64("y"), }).create()); val = 5; testEq(val.nameOf(val["->"]), "x"); testEq(val.as(), "5"); val = unselect; testThrows([&val](){ val = "invalid"; }); } void testExtract() { testShow()<<__func__; auto top = nt::NTScalar{TypeCode::Int32}.create(); top["value"] = 42; testEq(top["value"].as(), 42); { int32_t val = -1; testTrue(top["value"].as(val)); testEq(val, 42); } { std::string val("canary"); testTrue(top["value"].as(val)); testEq(val, "42"); } { bool ran = false; top["value"].as([&ran](const int32_t& v) { testEq(v, 42); ran = true; }); testTrue(ran); } } void testClear() { testShow()<<__func__; auto val = TypeDef(TypeCode::Struct, { members::UInt32("int"), members::String("string"), members::UInt32A("arr"), members::Any("any"), }).create(); val["int"] = 0x12345678; val["string"] = "testing"; val["arr"] = shared_array({1,2,3}); val["any"].assign(nt::NTScalar{TypeCode::UInt32}.create()); val["any->value"] = 0x01020304; testEq(val["int"].as(), 0x12345678u); testEq(val["string"].as(), std::string("testing")); testEq(val["arr"].as>().size(), 3u); testTrue(!!val["any->"]); testEq(val["any->value"].as(), 0x01020304u); testTrue(val.isMarked(true, true)); val.clear(); testEq(val["int"].as(), 0u); testEq(val["string"].as(), std::string("")); testEq(val["arr"].as>().size(), 0u); testFalse(val["any->"]); testFalse(val.isMarked(true, true)); } } // namespace MAIN(testdata) { testPlan(148); testSetup(); testTraverse(); testAssign(); testAssignArray(); testAssignUnion(); testAssignAny(); testName(); testIterStruct(); testIterUnion(); testConvertScalar(true, true); testConvertScalar(true, 1u); testConvertScalar(true, 1); //testConvertScalar(true, 1.0); // TODO: define double -> bool testConvertScalar(true, "true"); testConvertScalar(false, "false"); testConvertScalar(1.0, true); testConvertScalar(0.0, false); testConvertScalar(5.0, 5); testConvertScalar(5.0, 5); testConvertScalar(-5.0, -5); testConvertScalar(-5.0, -5.0); testConvertScalar(-5.0, "-5"); testConvertScalar(1, true); testConvertScalar(0, false); testConvertScalar(5, 5); testConvertScalar(5, 5); testConvertScalar(-5, -5); testConvertScalar(-5, -5.0); testConvertScalar(-5, "-5"); testConvertScalar(0xffffffff, -1); testConvertScalar(0xffffffff, -1); testConvertScalar(0xffff, 0xffff); testConvertScalar(0x80000000, -2147483648); testConvertScalar("true", true); testConvertScalar("false", false); testConvertScalar("5", 5); testConvertScalar("5", 5); testConvertScalar("-5", -5); testConvertScalar("-5", -5.0); testConvertScalar("-5", "-5"); #ifdef _MSC_VER // MSVC reads back 2147483648 testTodoBegin("MSVC differs"); #endif testConvertScalar2(-2147483648, 0x80000000, -2147483648); testTodoEnd(); testConvertScalar2(0, 0x100000000llu, -0); testAssignSimilar(); testUnionMagicAssign(); testExtract(); testClear(); cleanup_for_valgrind(); return testDone(); }