From d155487ddad6f255051f8515206834ba3cb5594f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 May 2018 13:13:28 -0400 Subject: [PATCH 1/6] testCa: Remove extraneous bi record, fix bo.OUT link --- testCa/testCaProvider.db | 6 ------ 1 file changed, 6 deletions(-) diff --git a/testCa/testCaProvider.db b/testCa/testCaProvider.db index d66e322..647c0c5 100644 --- a/testCa/testCaProvider.db +++ b/testCa/testCaProvider.db @@ -131,12 +131,6 @@ record(stringin,"DBRstringin") { record(bo,"DBRbinaryout") { field(DESC, "bo") - field(OUT, "DBRbi.VAL PP NMS") - field(ZNAM,"zero") - field(ONAM,"one") -} -record(bi,"DBRbinaryin") { - field(DESC, "bi") field(ZNAM,"zero") field(ONAM,"one") } From 3e0748d488f308e986030cc035f9051580c93425 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 May 2018 13:23:04 -0400 Subject: [PATCH 2/6] testCa: Adjust test output The DEBUG flag must be 0 when running under a test harness to remove extraneous output which can confuse the output parser. This commit also converts some throws into tests, and adds more test diagnostic messages. --- testCa/testCaProvider.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 4f623aa..52c37ac 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -26,6 +26,9 @@ #include #include +// DEBUG must be 0 to run under the automated test harness +#define DEBUG 0 + using namespace epics::pvData; using namespace epics::pvAccess; using namespace epics::pvAccess::ca; @@ -584,7 +587,7 @@ void TestClient::getDone( testOk(pvStructure->getSubField("value")!=NULL,"value not null"); testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); - std::cout << testChannel->getChannelName() + " TestClient::getDone" + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::getDone" << " bitSet " << *bitSet << " pvStructure\n" << pvStructure << "\n"; waitForGet.signal(); @@ -599,35 +602,38 @@ void TestClient::monitorEvent( PVStructure::shared_pointer const & pvStructure, BitSet::shared_pointer const & bitSet) { - std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" << " bitSet " << *bitSet << " pvStructure\n" << pvStructure << "\n"; } void TestClient::get() { -cout << "TestClient::get() calling get\n"; + testDiag("TestClient::get %s", + testChannel->getChannelName().c_str()); testChannelGet->get(); -cout << "TestClient::get() calling waitGet\n"; + if (DEBUG) cout << "TestClient::get() calling waitGet\n"; waitGet(5.0); } void TestClient::waitGet(double timeout) { - if(waitForGet.wait(timeout)) return; - throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitGet failed "); + testOk(waitForGet.wait(timeout), + "waitGet(%s) succeeded", testChannel->getChannelName().c_str()); } void TestClient::put(string const & value) { + testDiag("TestClient::put %s := %s", + testChannel->getChannelName().c_str(), value.c_str()); testChannelPut->put(value); waitPut(5.0); } void TestClient::waitPut(double timeout) { - if(waitForPut.wait(timeout)) return; - throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitPut failed "); + testOk(waitForPut.wait(timeout), + "waitPut(%s) succeeded", testChannel->getChannelName().c_str()); } void TestClient::stopEvents() @@ -686,7 +692,7 @@ MAIN(testCaProvider) TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(56); + testPlan(84); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); From 872004f75ff0aff9997ba21f16ec7e117d7eeeae Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 00:31:32 -0400 Subject: [PATCH 3/6] testCa: Run IOC using dbUnitTest API where possible Essential for VxWorks and RTEMS which don't support system(). Build embedded IOC tests only against Base-3.15 and later. --- testCa/Makefile | 43 ++++++++++++++++++++++------------- testCa/testCaProvider.cpp | 47 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index 4300613..da17a17 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -3,34 +3,45 @@ TOP = .. include $(TOP)/configure/CONFIG +# Need access to caProviderPvt.h USR_CPPFLAGS += -I$(TOP)/src/ca -CAPROVIDER_TEST = $(TOP)/testCa - -PROD_LIBS += pvAccess pvAccessCA pvData ca Com +PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS) TESTPROD_HOST += testCaProvider testCaProvider_SRCS += testCaProvider.cpp -testHarness_SRCS += testCaProvider.cpp +caTestHarness_SRCS += testCaProvider.cpp TESTS += testCaProvider -testHarness_SRCS += pvCaAllTests.c +ifdef BASE_3_15 + testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp + REGRDDFLAGS = -l +else + # testCaProvider needs EPICS_HOST_ARCH set in the environment + export EPICS_HOST_ARCH +endif -# testCaProvider needs EPICS_HOST_ARCH set in the environment -export EPICS_HOST_ARCH +ifdef BASE_3_15 + # Embedded OSes need dbUnitTest, Base-3.15 and higher only -# Name the application caTestHarness -caTestHarness_SRCS = $(testHarness_SRCS) + # The test collection is caTestHarness + caTestHarness_SRCS += pvCaAllTests.c -# Build for vxWorks -PROD_vxWorks = caTestHarness -TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + # Build for vxWorks + PROD_vxWorks = caTestHarness + TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests -# Build for RTEMS, with harness code & configuration -PROD_RTEMS += caTestHarness -caTestHarness_SRCS_RTEMS += rtemsTestHarness.c -TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + # Build for RTEMS, with harness code & configuration + PROD_RTEMS += caTestHarness + caTestHarness_SRCS_RTEMS += rtemsTestHarness.c + TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests +endif # Build test scripts for hosts TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES + +ifdef BASE_3_15 + $(COMMON_DIR)/testIoc.dbd: $(EPICS_BASE)/dbd/softIoc.dbd + $(CP) $< $@ +endif diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 52c37ac..abd4822 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -13,7 +13,21 @@ #include #include +#include + #if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) + #define USE_DBUNITTEST + // USE_TYPED_RSET prevents deprecation warnings + #define USE_TYPED_RSET + #define EXIT_TESTS 0 + #include + #include + #include + + extern "C" int testIoc_registerRecordDeviceDriver(struct dbBase *pbase); +#else + #define EXIT_TESTS 1 +#endif #include #include @@ -651,8 +665,11 @@ public: virtual void run(); static TestIocPtr create(); void start(); + void shutdown(); private: +#ifndef USE_DBUNITTEST std::auto_ptr thread; +#endif }; TestIocPtr TestIoc::create() @@ -662,16 +679,30 @@ TestIocPtr TestIoc::create() void TestIoc::start() { +#ifdef USE_DBUNITTEST + testdbPrepare(); + testdbReadDatabase("testIoc.dbd", NULL, NULL); + testIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("testCaProvider.db", NULL, NULL); + eltc(0); + testIocInitOk(); + eltc(1); +#else thread = std::auto_ptr(new epicsThread( *this, "testIoc", epicsThreadGetStackSize(epicsThreadStackSmall), epicsThreadPriorityLow)); thread->start(); +#endif } void TestIoc::run() { +#ifndef USE_DBUNITTEST + // Base-3.14 doesn't provide the dbUnitTest APIs. + // This code only works on workstation targets, it runs the + // softIoc from Base as a separate process, using system(). char * base; base = getenv("EPICS_BASE"); if(base==NULL) throw std::runtime_error("TestIoc::run $EPICS_BASE not defined"); @@ -685,6 +716,15 @@ void TestIoc::run() message += "/softIoc -d ../testCaProvider.db not started"; throw std::runtime_error(message); } +#endif +} + +void TestIoc::shutdown() +{ +#ifdef USE_DBUNITTEST + testIocShutdownOk(); + testdbCleanup(); +#endif } MAIN(testCaProvider) @@ -692,12 +732,12 @@ MAIN(testCaProvider) TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(84); + testPlan(84 + EXIT_TESTS); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); - ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); try{ + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); if(!channelProvider) { throw std::runtime_error(" provider ca not registered"); } @@ -788,15 +828,18 @@ MAIN(testCaProvider) client->put("1"); client->get(); client->stopEvents(); +#ifndef USE_DBUNITTEST // put to record that makes IOC exit channelName = "DBRexit"; client = TestClient::create(channelName,pvRequest); if(!client) throw std::runtime_error(channelName + " client null"); client->put("1"); client->stopEvents(); +#endif }catch(std::exception& e){ testFail("caught un-expected exception: %s", e.what()); } + testIoc->shutdown(); return testDone();; } From db15689e1cc159286045c44d55e8ebbdc80e4eb3 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 00:33:18 -0400 Subject: [PATCH 4/6] testCa: Use the softIoc's built-in exit subrecord --- testCa/testCaProvider.cpp | 4 ++-- testCa/testCaProvider.db | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index abd4822..fc1be44 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -709,7 +709,7 @@ void TestIoc::run() char * arch; arch = getenv("EPICS_HOST_ARCH"); if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); - if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -d ../testCaProvider.db")!=0) { + if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { string message(base); message += "/bin/"; message += arch; @@ -830,7 +830,7 @@ MAIN(testCaProvider) client->stopEvents(); #ifndef USE_DBUNITTEST // put to record that makes IOC exit - channelName = "DBRexit"; + channelName = "test:exit"; client = TestClient::create(channelName,pvRequest); if(!client) throw std::runtime_error(channelName + " client null"); client->put("1"); diff --git a/testCa/testCaProvider.db b/testCa/testCaProvider.db index 647c0c5..04cdcf7 100644 --- a/testCa/testCaProvider.db +++ b/testCa/testCaProvider.db @@ -39,12 +39,6 @@ record(calc, "DBRcalcin") field(LLSV, "MAJOR") } -record(sub, "DBRexit") -{ - field(DESC, "exit") - field(SNAM, "exit") -} - record(longin,"DBRlongin") { field(DESC, "longin") field(EGU, "volts") From c57b1352359eb83250405ac420d2244986bbe4ba Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 01:14:47 -0400 Subject: [PATCH 5/6] testCa: Configure CA to search localhost only --- testCa/testCaProvider.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index fc1be44..3437d61 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -709,6 +709,8 @@ void TestIoc::run() char * arch; arch = getenv("EPICS_HOST_ARCH"); if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); + setenv("EPICS_CA_ADDR_LIST", "localhost", 1); + setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1); if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { string message(base); message += "/bin/"; From 2cddd3359e3c538e8ada837175e59aa411599fb6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 01:25:25 -0400 Subject: [PATCH 6/6] testCa: Replace duplicated test code with a subroutine Also uses testAbort() instead of throwing in the event of a problem. --- testCa/testCaProvider.cpp | 141 ++++++++++++-------------------------- 1 file changed, 44 insertions(+), 97 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 3437d61..15dfc16 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -15,7 +15,7 @@ #include #include - #if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) +#if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) #define USE_DBUNITTEST // USE_TYPED_RSET prevents deprecation warnings #define USE_TYPED_RSET @@ -729,119 +729,66 @@ void TestIoc::shutdown() #endif } +void checkClient(const string &channelName, const string &putValue) +{ + string request("value,alarm,timeStamp"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); + client->put(putValue); + client->get(); + client->stopEvents(); +} + MAIN(testCaProvider) { + testPlan(84 + EXIT_TESTS); TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(84 + EXIT_TESTS); + testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); - try{ + try { ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); - if(!channelProvider) { - throw std::runtime_error(" provider ca not registered"); - } - string channelName; - string request("value,alarm,timeStamp"); - PVStructurePtr pvRequest(createRequest(request)); - TestClientPtr client; + if (!channelProvider) + testAbort("Channel provider 'ca' not registered"); + + checkClient("DBRlongout", "5"); + checkClient("DBRdoubleout", "1.5"); + checkClient("DBRstringout", "test"); + checkClient("DBRbyteArray", "1 2 3"); + checkClient("DBRshortArray", "1 2 3"); + checkClient("DBRintArray", "1 2 3"); + checkClient("DBRubyteArray", "1 2 3"); + checkClient("DBRushortArray", "1 2 3"); + checkClient("DBRuintArray", "1 2 3"); + checkClient("DBRfloatArray", "1 2 3"); + checkClient("DBRdoubleArray", "1 2 3"); + checkClient("DBRstringArray", "aa bb cc"); + checkClient("DBRmbbout", "2"); + checkClient("DBRbinaryout", "1"); - channelName = "DBRlongout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("5"); - client->get(); - client->stopEvents(); - channelName = "DBRdoubleout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1.5"); - client->get(); - client->stopEvents(); - channelName = "DBRstringout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("test"); - client->get(); - client->stopEvents(); - channelName = "DBRbyteArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRshortArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRintArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - channelName = "DBRubyteArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRushortArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRuintArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - channelName = "DBRfloatArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRdoubleArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRstringArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("aa bb cc"); - client->get(); - client->stopEvents(); - channelName = "DBRmbbout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("2"); - client->get(); - client->stopEvents(); - channelName = "DBRbinaryout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1"); - client->get(); - client->stopEvents(); #ifndef USE_DBUNITTEST // put to record that makes IOC exit - channelName = "test:exit"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); + string channelName = "test:exit"; + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); client->put("1"); client->stopEvents(); #endif - }catch(std::exception& e){ - testFail("caught un-expected exception: %s", e.what()); } + catch (std::exception& e) { + testAbort("caught un-expected exception: %s", e.what()); + } + testIoc->shutdown(); + return testDone();; }