/** * 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 #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 testAssignSimilarNDArray() { testShow()<<__func__; Value val1, val2; using namespace pvxs::members; { auto def(nt::NTNDArray{}.build()); def += { StructA("dimensions", { Int32("val1"), }), }; val1 = def.create(); } { auto def(nt::NTNDArray{}.build()); def += { StructA("dimensions", { Int32("val2"), }), }; val2 = def.create(); } testFalse(val1.equalType(val2)); testTrue(val1["dimension"].equalType(val2["dimension"])); val1.from(val2); // nothing marked, so a no-op shared_array pixels({1,2,3,4,5,6}); val1["value->ubyteValue"] = pixels; shared_array dims;//(2); dims.resize(2); dims[0] = val1["dimension"].allocMember() .update("size", 12); dims[1] = dims[0].cloneEmpty() .update("size", 34); val1["dimension"] = dims.freeze(); val2.from(val1); testTrue(val2["value"].isMarked(false,false)); testTrue(val2["dimension"].isMarked(false,false)); testFalse(val2["timeStamp"].isMarked(true, true)); testArrEq(pixels, val2["value"].as>()); testEq(val2["dimension[0].size"].as(), 12u); testEq(val2["dimension[1].size"].as(), 34u); } 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(172); 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); testTodoBegin("UB"); // test non-finite -> integer casts // copy in testConvertScalar2(0, std::numeric_limits::quiet_NaN(), 0); testConvertScalar2(0x7fffffffffffffff, std::numeric_limits::infinity(), 0x7fffffff); testConvertScalar2(0x8000000000000000, -std::numeric_limits::infinity(), 0x80000000); testConvertScalar2(0xffffffffffffffff, std::numeric_limits::infinity(), 0xffffffff); // copy out testConvertScalar2(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), 0); testConvertScalar2(std::numeric_limits::infinity(), std::numeric_limits::infinity(), 0x7fffffff); testConvertScalar2(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), 0x80000000); testConvertScalar2(std::numeric_limits::infinity(), std::numeric_limits::infinity(), 0xffffffff); testTodoEnd(); testAssignSimilar(); testAssignSimilarNDArray(); testUnionMagicAssign(); testExtract(); testClear(); cleanup_for_valgrind(); return testDone(); }