From 1d182cb031dff343a8fb3844baf30c79230992e7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Feb 2026 09:40:50 +0000 Subject: [PATCH] test iocsh functions --- ioc/group.cpp | 4 ++- test/Makefile | 3 +++ test/capturestd.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++ test/capturestd.h | 27 ++++++++++++++++++++ test/testpvalink.cpp | 25 ++++++++++++++++++- test/testqgroup.cpp | 17 ++++++++++++- test/testqsingle.cpp | 54 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 test/capturestd.cpp create mode 100644 test/capturestd.h diff --git a/ioc/group.cpp b/ioc/group.cpp index f4182c1..f519f47 100644 --- a/ioc/group.cpp +++ b/ioc/group.cpp @@ -8,7 +8,6 @@ */ #include -#include #include #include @@ -16,6 +15,9 @@ #include "group.h" #include "utilpvt.h" +// define printf macro after __attribute__ in event2.h +#include + namespace pvxs { namespace ioc { diff --git a/test/Makefile b/test/Makefile index 80b62a1..d12c615 100644 --- a/test/Makefile +++ b/test/Makefile @@ -119,6 +119,7 @@ benchdata_SRCS += benchdata.cpp TESTPROD_HOST += testpvalink testpvalink_SRCS += testpvalink.cpp +testpvalink_SRCS += capturestd.cpp testpvalink_SRCS += testioc_registerRecordDeviceDriver.cpp testpvalink_LIBS += pvxsIoc pvxs $(EPICS_BASE_IOC_LIBS) TESTS += testpvalink @@ -134,6 +135,7 @@ TESTFILES += $(COMMON_DIR)/testioc.dbd TESTPROD_HOST += testqsingle testqsingle_SRCS += testqsingle +testqsingle_SRCS += capturestd.cpp testqsingle_SRCS += testioc_registerRecordDeviceDriver.cpp testqsingle_LIBS = pvxsIoc pvxs $(EPICS_BASE_IOC_LIBS) TESTFILES += ../testqsingle.db @@ -148,6 +150,7 @@ ifdef BASE_7_0 TESTPROD_HOST += testqgroup testqgroup_SRCS += testqgroup +testqgroup_SRCS += capturestd.cpp testqgroup_SRCS += testioc_registerRecordDeviceDriver.cpp testqgroup_LIBS = pvxsIoc pvxs $(EPICS_BASE_IOC_LIBS) TESTFILES += ../table.db diff --git a/test/capturestd.cpp b/test/capturestd.cpp new file mode 100644 index 0000000..5829d3c --- /dev/null +++ b/test/capturestd.cpp @@ -0,0 +1,59 @@ +/** + * 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 "capturestd.h" + +#include +#include + +static +std::string readFile(const std::string& filename) +{ + std::ifstream t(filename.c_str()); + std::stringstream buffer; + + if (!t.is_open()) { + throw std::invalid_argument("Could not open filename " + filename); + } + + buffer << t.rdbuf(); + return buffer.str(); +} + +CaptureStd::CaptureStd(const std::function &fn) { + std::shared_ptr + out(fopen("testiocsh.out", "w+b"), + [](FILE* fp){ + (void)fclose(fp); + }), + err(fopen("testiocsh.err", "w+b"), + [](FILE* fp){ + (void)fclose(fp); + }); + + if(!out || !err) + testAbort("Unable to open/create testiocsh.out / .err"); + epicsSetThreadStdout(out.get()); + epicsSetThreadStderr(err.get()); + + try { + fn(); + }catch(...){ + epicsSetThreadStdout(nullptr); + epicsSetThreadStderr(nullptr); + throw; + } + out.reset(); + err.reset(); + + m_out = readFile("testiocsh.out"); + m_err = readFile("testiocsh.err"); +} diff --git a/test/capturestd.h b/test/capturestd.h new file mode 100644 index 0000000..c0bfd19 --- /dev/null +++ b/test/capturestd.h @@ -0,0 +1,27 @@ +/** + * 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. + */ +#ifndef CAPTURESTD_H +#define CAPTURESTD_H + +#include +#include + +struct CaptureStd +{ + std::string m_out, m_err; + + CaptureStd(const std::function& fn); + + const std::string& out() const { + return m_out; + } + const std::string& err() const { + return m_err; + } +}; + + +#endif // CAPTURESTD_H diff --git a/test/testpvalink.cpp b/test/testpvalink.cpp index 5e1cf04..a486937 100644 --- a/test/testpvalink.cpp +++ b/test/testpvalink.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "dblocker.h" #include "qsrvpvt.h" #include "pvalink.h" +#include "capturestd.h" using namespace pvxs::ioc; using namespace pvxs; @@ -604,6 +606,26 @@ namespace { ntndarray.close(); } + void testiocsh() + { + testDiag("==== %s ====", __func__); + + { + CaptureStd cap([](){ + iocshCmd("dbpvar \"\" 5"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*PVA links in all records.*async:target conn=T.*", cap.out()); + } + { + CaptureStd cap([](){ + iocshCmd("dbjlr \"\" 5"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*\'pva\': testToFromString:str1.*", cap.out()); + } + } + } // namespace @@ -611,7 +633,7 @@ extern "C" void testioc_registerRecordDeviceDriver(struct dbBase *); MAIN(testpvalink) { - testPlan(102); + testPlan(106); testSetup(); pvxs::logger_config_env(); @@ -643,6 +665,7 @@ MAIN(testpvalink) testAtomic(); testEnum(); testNTNDArray(); + testiocsh(); } catch (std::exception &e) { diff --git a/test/testqgroup.cpp b/test/testqgroup.cpp index c6699d5..bfb5800 100644 --- a/test/testqgroup.cpp +++ b/test/testqgroup.cpp @@ -19,6 +19,7 @@ #include "testioc.h" #include "utilpvt.h" +#include "capturestd.h" extern "C" { extern int testioc_registerRecordDeviceDriver(struct dbBase*); @@ -731,6 +732,19 @@ void testBatch() 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__); @@ -753,7 +767,7 @@ void testDbLoadGroup() MAIN(testqgroup) { - testPlan(39); + testPlan(41); testSetup(); { generalTimeRegisterCurrentProvider("test", 1, &testTimeCurrent); @@ -778,6 +792,7 @@ MAIN(testqgroup) testConst(); testBatch(); testDbLoadGroup(); + testiocsh(); } // call epics atexits explicitly to handle older base w/o de-init hooks epicsExitCallAtExits(); diff --git a/test/testqsingle.cpp b/test/testqsingle.cpp index f07436f..7b66e89 100644 --- a/test/testqsingle.cpp +++ b/test/testqsingle.cpp @@ -18,11 +18,14 @@ #include #include #include +#include #include "dblocker.h" #include "testioc.h" #include "utilpvt.h" +#include "capturestd.h" + #if EPICS_VERSION_INT >= VERSION_INT(3, 15, 0, 2) # define HAVE_lsi #endif @@ -947,11 +950,59 @@ void testMonitorDBE(TestClient& ctxt) testEq(val["value"].as(), 43); } +void testiocsh(TestClient& ctxt) +{ + testDiag("%s", __func__); + + TestSubscription sub(ctxt.monitor("test:ai") + .maskConnected(true) + .maskDisconnected(true)); + + auto val(sub.waitForUpdate()); + + { + CaptureStd cap([](){ + iocshCmd("pvxsr 5"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*EPICS_PVAS_AUTO_BEACON_ADDR_LIST=NO.*", cap.out()); + testStrMatch(".*Source: __server.*", cap.out()); + testStrMatch(".*test:nsec.*", cap.out()); + testStrMatch(".*State: Running TCP_Port:.*", cap.out()); + testStrMatch(".*test:ai TX=.*", cap.out()); + } + { + CaptureStd cap([](){ + iocshCmd("pvxsi"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*Toolchain.*", cap.out()); + testStrMatch(".*epicsThreadGetCPUs.*", cap.out()); + testStrMatch(".*EPICS_PVAS_AUTO_BEACON_ADDR_LIST=.*", cap.out()); + } + { + CaptureStd cap([](){ + iocshCmd("pvxrefshow"); + iocshCmd("pvxrefsave"); + iocshCmd("pvxrefdiff"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*StructTop.*", cap.out()); + } + { + CaptureStd cap([](){ + iocshCmd("pvxsl 5"); + }); + testStrEq(cap.err(), ""); + testStrMatch(".*RECORDS.*test:ai.*", cap.out()); + } +} + } // namespace MAIN(testqsingle) { - testPlan(99); + testPlan(113); testSetup(); pvxs::logger_config_env(); generalTimeRegisterCurrentProvider("test", 1, &testTimeCurrent); @@ -996,6 +1047,7 @@ MAIN(testqsingle) testMonitorAIFilt(mctxt); testMonitorDBEAlarm(mctxt); testMonitorDBE(mctxt); + testiocsh(mctxt); } timeSim = false; testPutBlock();