/** * 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 #include #include #include "testioc.h" #include "utilpvt.h" #include "capturestd.h" extern "C" { extern int testioc_registerRecordDeviceDriver(struct dbBase*); } using namespace pvxs; namespace { std::atomic testTimeSec{12345678}; int testTimeCurrent(epicsTimeStamp *pDest) { pDest->secPastEpoch = testTimeSec; pDest->nsec = 102030; return 0; } void checkUTAG(Value& v, int32_t expect, const char *fld="timeStamp.userTag") { #ifdef DBR_UTAG auto utag = v[fld]; int32_t tag = -1; if(!utag.isMarked() || (tag = utag.as())!=expect) testFail("userTag not set (%d != %d)", int(expect), int(tag)); utag.tryFrom(0); utag.unmark(); #endif } void testTable() { testDiag("%s", __func__); TestClient ctxt; auto val(ctxt.get("tbl:Tbl").exec()->wait(5.0)); checkUTAG(val, 0); testStrEq(std::string(SB()<wait(5.0); checkUTAG(val, 0); testStrEq(std::string(SB()< colA({1.0, 2.0, 3.0}); shared_array colB({4.0, 5.0, 6.0}); testTimeSec++; ctxt.put("tbl:Tbl").set("value.A", colA).set("value.B", colB).exec()->wait(5.0); val = sub.waitForUpdate(); checkUTAG(val, 0); testStrEq(std::string(SB()<wait(5.0); checkUTAG(val, 0); testStrEq(std::string(SB()<wait(5.0)); checkUTAG(val, 0); testStrEq(std::string(SB()<wait(5.0); testdbGetFieldEqual("enm:ENUM:INDEX", DBR_LONG, 0); // ntenum.db defines no +trigger so only implied self-trigger. // aka. not timeStamp expected val = sub.waitForUpdate(); testStrEq(std::string(SB()<([&ctxt]{ shared_array choices({"foo"}); ctxt.put("enm:ENUM").set("value.choices", choices).exec()->wait(5.0); }); const char expect[2][MAX_STRING_SIZE] = {"ZERO", "ONE"}; testdbGetArrFieldEqual("enm:ENUM:CHOICES", DBR_STRING, 2, 2, expect); } testDiag("attempt to write both index and choices list"); { shared_array choices({"foo"}); ctxt.put("enm:ENUM") .record("process", false) // no update posted .set("value.index", 1) .set("value.choices", choices) .exec()->wait(5.0); const char expect[2][MAX_STRING_SIZE] = {"ZERO", "ONE"}; testdbGetArrFieldEqual("enm:ENUM:CHOICES", DBR_STRING, 2, 2, expect); testdbGetFieldEqual("enm:ENUM:INDEX", DBR_LONG, 1); } sub.testEmpty(); } void testImage() { testDiag("%s", __func__); TestClient ctxt; auto val(ctxt.get("img:Array").exec()->wait(5.0)); checkUTAG(val, 0); checkUTAG(val, 0, "x.timeStamp.userTag"); testStrEq(std::string(SB()< uint16_t = 0\n" "attribute[0].alarm.severity int32_t = 0\n" "attribute[0].alarm.status int32_t = 0\n" "attribute[0].alarm.message string = \"\"\n" "attribute[0].timeStamp.secondsPastEpoch int64_t = 0\n" "attribute[0].timeStamp.nanoseconds int32_t = 0\n" "attribute[0].timeStamp.userTag int32_t = 0\n" "attribute[1] struct\n" "attribute[1].name string = \"\"\n" "attribute[1].value any\n" "attribute[1].value-> uint16_t = 0\n" "attribute[1].alarm.severity int32_t = 3\n" "attribute[1].alarm.status int32_t = 2\n" "attribute[1].alarm.message string = \"UDF\"\n" "attribute[1].timeStamp.secondsPastEpoch int64_t = 631152000\n" "attribute[1].timeStamp.nanoseconds int32_t = 0\n" "attribute[1].timeStamp.userTag int32_t = 0\n" "x.alarm.severity int32_t = 0\n" "x.alarm.status int32_t = 0\n" "x.alarm.message string = \"\"\n" "x.timeStamp.secondsPastEpoch int64_t = 643497678\n" "x.timeStamp.nanoseconds int32_t = 102030\n" "dimension struct[]\n" "dimension[0] struct\n" "dimension[0].size int32_t = 100\n" "dimension[1] struct\n" "dimension[1].size int32_t = 100\n" "value any\n" "value-> uint16_t[] = {10000}[0, 655, 1310, 1966, 2621, ...]\n"); TestSubscription sub(ctxt.monitor("img:Array2")); val = sub.waitForUpdate(); checkUTAG(val, 0); checkUTAG(val, 0, "x.timeStamp.userTag"); testStrEq(std::string(SB()< uint16_t = 0\n" "attribute[0].alarm.severity int32_t = 0\n" "attribute[0].alarm.status int32_t = 0\n" "attribute[0].alarm.message string = \"\"\n" "attribute[0].timeStamp.secondsPastEpoch int64_t = 0\n" "attribute[0].timeStamp.nanoseconds int32_t = 0\n" "attribute[0].timeStamp.userTag int32_t = 0\n" "attribute[1] struct\n" "attribute[1].name string = \"\"\n" "attribute[1].value any\n" "attribute[1].value-> uint16_t = 0\n" "attribute[1].alarm.severity int32_t = 3\n" "attribute[1].alarm.status int32_t = 2\n" "attribute[1].alarm.message string = \"UDF\"\n" "attribute[1].timeStamp.secondsPastEpoch int64_t = 631152000\n" "attribute[1].timeStamp.nanoseconds int32_t = 0\n" "attribute[1].timeStamp.userTag int32_t = 0\n" "value any\n" "value-> uint16_t[] = {10000}[0, 655, 1310, 1966, 2621, ...]\n" "x.alarm.severity int32_t = 0\n" "x.alarm.status int32_t = 0\n" "x.alarm.message string = \"\"\n" "x.timeStamp.secondsPastEpoch int64_t = 643497678\n" "x.timeStamp.nanoseconds int32_t = 102030\n" "dimension struct[]\n" "dimension[0] struct\n" "dimension[0].size int32_t = 100\n" "dimension[1] struct\n" "dimension[1].size int32_t = 100\n"); testTimeSec++; testdbPutFieldOk("img:ArrayData_.PROC", DBR_LONG, 0); val = sub.waitForUpdate(); checkUTAG(val, 0); checkUTAG(val, 0, "x.timeStamp.userTag"); testStrEq(std::string(SB()< uint16_t = 0\n" "attribute[0].alarm.severity int32_t = 0\n" "attribute[0].alarm.status int32_t = 0\n" "attribute[0].alarm.message string = \"\"\n" "attribute[0].timeStamp.secondsPastEpoch int64_t = 0\n" "attribute[0].timeStamp.nanoseconds int32_t = 0\n" "attribute[0].timeStamp.userTag int32_t = 0\n" "attribute[1] struct\n" "attribute[1].name string = \"\"\n" "attribute[1].value any\n" "attribute[1].value-> uint16_t = 0\n" "attribute[1].alarm.severity int32_t = 3\n" "attribute[1].alarm.status int32_t = 2\n" "attribute[1].alarm.message string = \"UDF\"\n" "attribute[1].timeStamp.secondsPastEpoch int64_t = 631152000\n" "attribute[1].timeStamp.nanoseconds int32_t = 0\n" "attribute[1].timeStamp.userTag int32_t = 0\n" "value any\n" "value-> uint16_t[] = {10000}[0, 655, 1310, 1966, 2621, ...]\n" "x.alarm.severity int32_t = 0\n" "x.alarm.status int32_t = 0\n" "x.alarm.message string = \"\"\n" "x.timeStamp.secondsPastEpoch int64_t = 643497681\n" "x.timeStamp.nanoseconds int32_t = 102030\n" "dimension struct[]\n" "dimension[0] struct\n" "dimension[0].size int32_t = 100\n" "dimension[1] struct\n" "dimension[1].size int32_t = 100\n"); sub.testEmpty(); { shared_array arr({1, 2, 3}); ctxt.put("img:Array").set("value", arr).pvRequest("record[process=false]").exec()->wait(1111115.0); testdbGetArrFieldEqual("img:ArrayData", DBR_LONG, arr.size(), arr.size(), arr.data()); } } void testIQ() { testDiag("%s", __func__); TestClient ctxt; auto val(ctxt.get("iq:iq").exec()->wait(5.0)); checkUTAG(val, 1, "I.timeStamp.userTag"); checkUTAG(val, 1, "Q.timeStamp.userTag"); testStrEq(std::string(SB()<wait(5.0)); testStrEq(std::string(SB()<wait(5.0); auto ret(ctxt.get("tst:b:").exec()->wait(5.0)); testEq(ret["SUM.value"].as(), 3); } void testiocsh() { testDiag("%s", __func__); { CaptureStd cap([](){ iocshCmd("pvxgl 5 \"\""); }); testStrEq(cap.err(), ""); testStrMatch(".*enm:ENUM.*enm:ENUM:INDEX.VAL has triggers.*", cap.out()); } } void testDbLoadGroup() { testDiag("%s", __func__); TestClient ctxt; auto val(ctxt.get("tst:fromFile").exec()->wait(5.0)); testStrEq(std::string(SB()<