afafa09547
ioc: check for mis-matched onStartSubscription()/onDisableSubscription() ioc: fix subscription lifetime ioc: catch exceptions in dbEvent callbacks ioc: avoid unnecessary virtual ioc: minor ioc: fix qsrv -S ioc: qsrvGroupSourceInit() catch+log ioc: runOnServer avoid std::function ioc: cleanup and simplifications. Avoid some redundant std::map lookups. Make Group partially const to prevent implicit ctor. ioc: avoid typedefs only used once ioc: overhaul Group::show(). shows triggers ioc: MappingType ioc: pvxsgl -> pvxgl ioc: separate group config singleton from server singleton ioc: remove unnecessary forward declarations ioc: restructure pvxsInitHook ioc: qsrv runtime disable by default ioc: compat w/ older Base ioc: link pvxsIoc w/ DB libs ioc: Channel proper detection of invalid PV ioc: no need to keep vector<dbCommon*> around ioc: fix initial group update for mappings w/o dbChannel ioc: redo testing split out group tests, only run with Base >= 7.0 ioc: minor ioc: loc_bad_alloc ioc: avoid symbol/DTYP clash with pva2pva ioc: test record alias in group json ioc: test put failure when SPC_NOMOD and DISP=1 ioc: test channel filters ioc: unnecessary capture ioc: avoid sharing Value between multiple subscriptions It is possible to create two subscriptions through the same channel. ioc: group subscription include queueSize ioc: eliminate unused atomicMonitor ioc: consolidate GroupSource::get() avoid some indirection ioc: pvRequest override of atomicPutGet ioc: fix group non-atomic put ioc: test asTrap hooks ioc: test putOrder also sets field order ioc: simplify GroupConfigProcessor::loadConfigFiles() Also ensure that groupMapMutex is held ioc: testqgroup cover JSON def. ioc: dbLoadGroup() use macros ioc: pvxsl() take integer argument ioc: display.form and info(Q:form ioc: "NO_ALARM" -> "" ioc: use dbServer at least for informational callbacks. ioc: consolidate createRequestAndSubscriptionHandlers() ioc: eliminate ChannelAndLock properties dbChannel doesn't need a separate DBManyLock ioc: test that putOrder also controls field order ioc: MappingType -> MappingInfo Handle info(Q:time:tag Add +type:"const" ioc: cleanup includes ioc: test dbNotifyCancel() ioc: inline checkForTrailingCommentsAtEnd()
122 lines
3.2 KiB
C++
122 lines
3.2 KiB
C++
/**
|
|
* 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 TESTIOC_H
|
|
#define TESTIOC_H
|
|
|
|
#include <pvxs/log.h>
|
|
#include <pvxs/client.h>
|
|
#include <pvxs/server.h>
|
|
#include <pvxs/unittest.h>
|
|
#include <pvxs/iochooks.h>
|
|
|
|
#include <epicsEvent.h>
|
|
#include <dbUnitTest.h>
|
|
#include <dbChannel.h>
|
|
|
|
class TestIOC {
|
|
bool running = false;
|
|
public:
|
|
TestIOC() {
|
|
testdbPrepare();
|
|
}
|
|
void init() {
|
|
if(!running) {
|
|
testIocInitOk();
|
|
running = true;
|
|
}
|
|
}
|
|
void shutdown() {
|
|
if(running) {
|
|
pvxs::ioc::testShutdown();
|
|
testIocShutdownOk();
|
|
}
|
|
}
|
|
~TestIOC() {
|
|
this->shutdown();
|
|
testdbCleanup();
|
|
}
|
|
};
|
|
|
|
struct TestClient : pvxs::client::Context
|
|
{
|
|
TestClient() : pvxs::client::Context(pvxs::ioc::server().clientConfig().build()) {}
|
|
};
|
|
|
|
struct TestSubscription
|
|
{
|
|
epicsEvent evt;
|
|
const std::shared_ptr<pvxs::client::Subscription> sub;
|
|
TestSubscription(pvxs::client::MonitorBuilder b)
|
|
:sub(b.event([this](pvxs::client::Subscription& subscription) {
|
|
testDiag("%s update event occurred", subscription.name().c_str());
|
|
evt.signal();
|
|
}).exec())
|
|
{}
|
|
pvxs::Value waitForUpdate() {
|
|
while (true) {
|
|
if (auto value = sub->pop()) {
|
|
testDiag("Update %s", sub->name().c_str());
|
|
return value;
|
|
} else if (!evt.wait(5.0)) {
|
|
testFail("timeout waiting for event for %s", sub->name().c_str());
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
void testEmpty() {
|
|
while (true) {
|
|
if (auto value = sub->pop()) {
|
|
testTrue(false)<<" Unexpected update for "<<sub->name()<<"\n"
|
|
<<value.format().delta().arrayLimit(5u);
|
|
return;
|
|
} else if (!evt.wait(1.0)) {
|
|
testPass("Not updates for %s", sub->name().c_str());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
void testFldEq(const pvxs::Value& top, const char* fldname, const T& expect)
|
|
{
|
|
if(auto fld = top[fldname]) {
|
|
T actual;
|
|
if(fld.as<T>(actual)) {
|
|
testEq(expect, actual);
|
|
} else {
|
|
testFalse(false)<<" unable to convert "<<fldname<<" to "<<typeid(T).name()<<"\n"
|
|
<<top.format();
|
|
}
|
|
} else {
|
|
testFalse(false)<<" Missing field: "<<fldname<<"\n"<<top.format();
|
|
}
|
|
}
|
|
|
|
#if EPICS_VERSION_INT < VERSION_INT(3, 16, 1, 0)
|
|
static
|
|
void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf)
|
|
{
|
|
dbChannel *chan = dbChannelCreate(pv);
|
|
long status = -1;
|
|
|
|
if(!chan || (status=dbChannelOpen(chan))) {
|
|
testFail("Channel error (%p, %ld) : %s", chan, status, pv);
|
|
goto done;
|
|
}
|
|
|
|
status = dbChannelPutField(chan, dbrType, pbuf, count);
|
|
|
|
testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status);
|
|
|
|
done:
|
|
if(chan)
|
|
dbChannelDelete(chan);
|
|
}
|
|
#endif
|
|
|
|
#endif // TESTIOC_H
|