add MonitorFIFO
This commit is contained in:
@ -27,6 +27,9 @@ TESTPROD_HOST += testServerContext
|
||||
testServerContext_SRCS += testServerContext.cpp
|
||||
TESTS += testServerContext
|
||||
|
||||
TESTPROD_HOST += testmonitorfifo
|
||||
testmonitorfifo_SRCS += testmonitorfifo.cpp
|
||||
TESTS += testmonitorfifo
|
||||
|
||||
PROD_HOST += testServer
|
||||
testServer_SRCS += testServer.cpp
|
||||
|
781
testApp/remote/testmonitorfifo.cpp
Normal file
781
testApp/remote/testmonitorfifo.cpp
Normal file
@ -0,0 +1,781 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <testMain.h>
|
||||
#include <epicsMutex.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/current_function.h>
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Tester {
|
||||
// we only have one thread, so no need for sync.
|
||||
enum cb_t {
|
||||
Connect,
|
||||
Event,
|
||||
Unlisten,
|
||||
Close,
|
||||
LowWater,
|
||||
};
|
||||
static const char* name(cb_t cb) {
|
||||
switch(cb) {
|
||||
#define CASE(NAME) case NAME: return #NAME
|
||||
CASE(Connect);
|
||||
CASE(Event);
|
||||
CASE(Unlisten);
|
||||
CASE(Close);
|
||||
CASE(LowWater);
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::vector<cb_t> timeline_t;
|
||||
static timeline_t timeline;
|
||||
|
||||
struct Requester : public pva::MonitorRequester {
|
||||
POINTER_DEFINITIONS(Requester);
|
||||
|
||||
epicsMutex mutex; // not strictly needed, but a good simulation of usage
|
||||
|
||||
virtual ~Requester() {}
|
||||
virtual std::string getRequesterName() OVERRIDE FINAL {return "Tester::Requester";}
|
||||
virtual void channelDisconnect(bool destroy) OVERRIDE FINAL {
|
||||
testDiag("In %s", CURRENT_FUNCTION);
|
||||
Guard G(mutex);
|
||||
Tester::timeline.push_back(Close);
|
||||
}
|
||||
virtual void monitorConnect(epics::pvData::Status const & status,
|
||||
pva::MonitorPtr const & monitor, epics::pvData::StructureConstPtr const & structure) OVERRIDE FINAL {
|
||||
testDiag("In %s", CURRENT_FUNCTION);
|
||||
Guard G(mutex);
|
||||
Tester::timeline.push_back(Connect);
|
||||
}
|
||||
virtual void monitorEvent(pva::MonitorPtr const & monitor) OVERRIDE FINAL {
|
||||
testDiag("In %s", CURRENT_FUNCTION);
|
||||
Guard G(mutex);
|
||||
Tester::timeline.push_back(Event);
|
||||
}
|
||||
virtual void unlisten(pva::MonitorPtr const & monitor) OVERRIDE FINAL {
|
||||
testDiag("In %s", CURRENT_FUNCTION);
|
||||
Guard G(mutex);
|
||||
Tester::timeline.push_back(Unlisten);
|
||||
}
|
||||
};
|
||||
Requester::shared_pointer requester;
|
||||
|
||||
struct Handler : public pva::MonitorFIFO::Source {
|
||||
POINTER_DEFINITIONS(Handler);
|
||||
|
||||
epicsMutex mutex; // not strictly needed, but a good simulation of usage
|
||||
|
||||
std::function<void(pva::MonitorFIFO *mon, size_t)> action;
|
||||
|
||||
virtual ~Handler() {}
|
||||
virtual void freeHighMark(pva::MonitorFIFO *mon, size_t numEmpty) OVERRIDE FINAL {
|
||||
testDiag("In %s", CURRENT_FUNCTION);
|
||||
Guard G(mutex);
|
||||
Tester::timeline.push_back(LowWater);
|
||||
if(action)
|
||||
action(mon, numEmpty);
|
||||
}
|
||||
};
|
||||
Handler::weak_pointer handler;
|
||||
|
||||
void setAction(const std::function<void(pva::MonitorFIFO *mon, size_t)>& action) {
|
||||
Handler::shared_pointer H(handler);
|
||||
|
||||
H->action = action;
|
||||
}
|
||||
|
||||
pva::MonitorFIFO::shared_pointer mon;
|
||||
|
||||
pvd::StructureConstPtr type;
|
||||
|
||||
Tester(const pvd::PVStructure::const_shared_pointer& pvReq,
|
||||
pva::MonitorFIFO::Config *conf)
|
||||
{
|
||||
Handler::shared_pointer H(new Handler);
|
||||
handler = H;
|
||||
requester.reset(new Requester);
|
||||
mon.reset(new pva::MonitorFIFO(requester, pvReq, H, conf));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
timeline.clear();
|
||||
}
|
||||
|
||||
void testTimeline(std::initializer_list<cb_t> l) {
|
||||
size_t i=0;
|
||||
for(auto event : l) {
|
||||
if(i >= timeline.size()) {
|
||||
testFail("timeline ends early. Expected %s", name(event));
|
||||
continue;
|
||||
}
|
||||
testOk(event==timeline[i], "%s == %s", name(event), name(timeline[i]));
|
||||
|
||||
i++;
|
||||
}
|
||||
for(; i<timeline.size(); i++) {
|
||||
testFail("timeline ends late. Unexpected %s", name(timeline[i]));
|
||||
}
|
||||
if(i==0 && timeline.empty())
|
||||
testPass("timeline no events as expected");
|
||||
reset();
|
||||
}
|
||||
|
||||
void connect(pvd::ScalarType t) {
|
||||
testDiag("connect() with %s", pvd::ScalarTypeFunc::name(t));
|
||||
type = pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", t)
|
||||
->createStructure();
|
||||
|
||||
mon->open(type);
|
||||
}
|
||||
|
||||
void close() {
|
||||
testDiag("close()");
|
||||
type.reset();
|
||||
mon->close();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void post(T val)
|
||||
{
|
||||
testShow()<<"post("<<val<<")";
|
||||
assert(!!type);
|
||||
pvd::PVStructurePtr V(pvd::getPVDataCreate()->createPVStructure(type));
|
||||
pvd::PVScalarPtr fld(V->getSubFieldT<pvd::PVScalar>("value"));
|
||||
fld->putFrom(val);
|
||||
pvd::BitSet changed;
|
||||
changed.set(fld->getFieldOffset());
|
||||
mon->post(*V, changed);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tryPost(T val, bool expect, bool force = false)
|
||||
{
|
||||
assert(!!type);
|
||||
pvd::PVStructurePtr V(pvd::getPVDataCreate()->createPVStructure(type));
|
||||
pvd::PVScalarPtr fld(V->getSubFieldT<pvd::PVScalar>("value"));
|
||||
fld->putFrom(val);
|
||||
pvd::BitSet changed;
|
||||
changed.set(fld->getFieldOffset());
|
||||
testEqual(mon->tryPost(*V, changed, pvd::BitSet(), force), expect)<<" value="<<val;
|
||||
}
|
||||
};
|
||||
|
||||
Tester::timeline_t Tester::timeline;
|
||||
|
||||
void testEmpty(pva::Monitor& mon)
|
||||
{
|
||||
pva::MonitorElement::Ref elem(mon);
|
||||
testTrue(!elem)<<"Queue expected empty";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void testPop(pva::Monitor& mon, T expected, bool overrun = false)
|
||||
{
|
||||
pva::MonitorElement::Ref elem(mon);
|
||||
if(!elem) {
|
||||
testFail("Queue unexpected empty");
|
||||
return;
|
||||
}
|
||||
pvd::PVScalarPtr fld(elem->pvStructurePtr->getSubFieldT<pvd::PVScalar>("value"));
|
||||
T actual(fld->getAs<T>());
|
||||
bool changed = elem->changedBitSet->get(fld->getFieldOffset());
|
||||
bool overran = elem->overrunBitSet->get(fld->getFieldOffset());
|
||||
|
||||
bool pop = actual==expected && changed && overran==overrun;
|
||||
testTrue(pop)
|
||||
<<" "<<actual<<" == "<<expected<<" changed="<<changed<<" "<<overran<<"=="<<overrun;
|
||||
}
|
||||
|
||||
static const pvd::PVStructure::const_shared_pointer pvReqEmpty(pvd::getPVDataCreate()->createPVStructure(
|
||||
pvd::getFieldCreate()->createFieldBuilder()
|
||||
->createStructure()));
|
||||
pvd::PVStructure::const_shared_pointer
|
||||
pvReqPipelineBuild() {
|
||||
pvd::PVStructure::shared_pointer ret(pvd::getPVDataCreate()->createPVStructure(
|
||||
pvd::getFieldCreate()->createFieldBuilder()
|
||||
->addNestedStructure("record")
|
||||
->addNestedStructure("_options")
|
||||
->add("pipeline", pvd::pvBoolean)
|
||||
->add("queueSize", pvd::pvUInt)
|
||||
->endNested()
|
||||
->endNested()
|
||||
->createStructure()));
|
||||
ret->getSubFieldT<pvd::PVScalar>("record._options.pipeline")->putFrom<pvd::boolean>(true);
|
||||
ret->getSubFieldT<pvd::PVScalar>("record._options.queueSize")->putFrom<pvd::uint32>(2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const pvd::PVStructure::const_shared_pointer pvReqPipeline(pvReqPipelineBuild());
|
||||
|
||||
// Test the basic life cycle of a FIFO.
|
||||
// post and pop single elements
|
||||
void checkPlain()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
Tester tester(pvReqEmpty, 0);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
tester.testTimeline({});
|
||||
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.post(5);
|
||||
tester.mon->notify();
|
||||
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 5);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// close() while FIFO not empty.
|
||||
void checkAfterClose()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
Tester tester(pvReqEmpty, 0);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
tester.testTimeline({});
|
||||
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.post(5);
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
|
||||
tester.testTimeline({Tester::Event, Tester::Close});
|
||||
|
||||
// FIFO not cleared by close
|
||||
testPop(*tester.mon, 5);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
}
|
||||
|
||||
// close() while FIFO not empty, then re-open
|
||||
void checkReOpenLost()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
Tester tester(pvReqEmpty, 0);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
tester.testTimeline({});
|
||||
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.post(5);
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
// FIFO cleared by connect()
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.mon->start();
|
||||
|
||||
tester.testTimeline({Tester::Event, Tester::Close, Tester::Connect});
|
||||
|
||||
// update 5 was lost!
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
}
|
||||
|
||||
// type change while FIFO is empty
|
||||
void checkTypeChange()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
Tester tester(pvReqEmpty, 0);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->start();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.post(5);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 5);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
|
||||
testDiag("Type change");
|
||||
|
||||
tester.connect(pvd::pvString);
|
||||
tester.mon->start();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.post(std::string("hello"));
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, std::string("hello"));
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// post() until the FIFO is full, and keep going. Check overrun behavour
|
||||
void checkFill()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
pva::MonitorFIFO::Config conf = {4, 2, 0};
|
||||
Tester tester(pvReqEmpty, &conf);
|
||||
|
||||
testEqual(conf.actualCount, 2u);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
|
||||
testEqual(conf.actualCount, tester.mon->freeCount());
|
||||
|
||||
testShow()<<"Empty before "<<*tester.mon;
|
||||
|
||||
// fill up and overflow
|
||||
tester.post(5);
|
||||
testShow()<<"A5 "<<*tester.mon;
|
||||
tester.post(6);
|
||||
testShow()<<"Full "<<*tester.mon;
|
||||
tester.post(7);
|
||||
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testShow()<<"Overrun1 "<<*tester.mon;
|
||||
|
||||
tester.post(8);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
|
||||
testShow()<<"Overrun2 "<<*tester.mon;
|
||||
|
||||
testPop(*tester.mon, 5);
|
||||
tester.testTimeline({});
|
||||
testPop(*tester.mon, 6);
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
testPop(*tester.mon, 8, true);
|
||||
tester.testTimeline({});
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
testShow()<<"Empty again "<<*tester.mon;
|
||||
|
||||
// repeat
|
||||
tester.post(15);
|
||||
tester.post(16);
|
||||
tester.post(17);
|
||||
tester.post(18);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 15);
|
||||
tester.testTimeline({});
|
||||
testPop(*tester.mon, 16);
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
testPop(*tester.mon, 18, true);
|
||||
tester.testTimeline({});
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// post() until past full, then pop() and post() on a partially full queue
|
||||
void checkSaturate()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
pva::MonitorFIFO::Config conf = {4, 2, 0};
|
||||
Tester tester(pvReqEmpty, &conf);
|
||||
|
||||
testEqual(conf.actualCount, 2u);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
|
||||
testShow()<<"Empty before "<<*tester.mon;
|
||||
|
||||
// fill up and overflow
|
||||
tester.post(5);
|
||||
tester.post(6);
|
||||
tester.post(7);
|
||||
tester.post(8);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 5);
|
||||
tester.testTimeline({});
|
||||
tester.post(9);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
|
||||
testPop(*tester.mon, 6);
|
||||
tester.testTimeline({});
|
||||
tester.post(10);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
|
||||
testPop(*tester.mon, 8, true);
|
||||
tester.testTimeline({});
|
||||
tester.post(11);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({});
|
||||
|
||||
testShow()<<"Overrun2 "<<*tester.mon;
|
||||
|
||||
testPop(*tester.mon, 9);
|
||||
tester.testTimeline({});
|
||||
testPop(*tester.mon, 10);
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
testPop(*tester.mon, 11);
|
||||
tester.testTimeline({});
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
testShow()<<"Empty again "<<*tester.mon;
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// check handling of pipeline=true
|
||||
void checkPipeline()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
pva::MonitorFIFO::Config conf = {4, 3, 0};
|
||||
Tester tester(pvReqPipeline, &conf);
|
||||
|
||||
testEqual(conf.actualCount, 2u);
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
tester.mon->start();
|
||||
|
||||
testEqual(tester.mon->freeCount(), 0u);
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
testEqual(tester.mon->freeCount(), 2u);
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
|
||||
// fill up and overflow
|
||||
tester.post(5);
|
||||
tester.post(6);
|
||||
tester.post(7);
|
||||
tester.post(8);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testEqual(tester.mon->freeCount(), 0u);
|
||||
testPop(*tester.mon, 5);
|
||||
testPop(*tester.mon, 6);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
testDiag("ACK 2");
|
||||
testShow()<<"Before ACK "<<*tester.mon;
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
testShow()<<"After ACK "<<*tester.mon;
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
testEqual(tester.mon->freeCount(), 1u);
|
||||
|
||||
// still have the overflow'd element on the queue
|
||||
|
||||
testPop(*tester.mon, 8, true);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
testEqual(tester.mon->freeCount(), 2u);
|
||||
tester.testTimeline({});
|
||||
|
||||
testShow()<<"Empty before re-fill "<<*tester.mon;
|
||||
|
||||
// fill up and overflow
|
||||
tester.post(15);
|
||||
tester.post(16);
|
||||
tester.post(17);
|
||||
tester.post(18);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 15);
|
||||
testPop(*tester.mon, 16);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
testDiag("ACK 1");
|
||||
testShow()<<"Before ACK "<<*tester.mon;
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
testShow()<<"After ACK "<<*tester.mon;
|
||||
tester.testTimeline({});
|
||||
|
||||
// 1 inuse, 1 returned, 1 empty
|
||||
|
||||
tester.post(19);
|
||||
tester.post(20);
|
||||
|
||||
testPop(*tester.mon, 18, true);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.post(21);
|
||||
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
testShow()<<"Before final ACK "<<*tester.mon;
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
|
||||
testPop(*tester.mon, 21, true);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// test the spam counter (keeps FIFO full) with pipeline=true
|
||||
void checkSpam()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
pva::MonitorFIFO::Config conf = {4, 3, 0};
|
||||
Tester tester(pvReqPipeline, &conf);
|
||||
|
||||
pvd::uint32 cnt = 0;
|
||||
|
||||
tester.setAction([&tester, &cnt] (pva::MonitorFIFO *mon, size_t nfree) {
|
||||
testDiag("Spamming %zu", nfree);
|
||||
while(nfree--) {
|
||||
testDiag("nfree=%zu %c", nfree, (nfree>0)?'T':'F');
|
||||
tester.tryPost(cnt++, nfree>0);
|
||||
}
|
||||
});
|
||||
tester.mon->setFreeHighMark(0); // run action when all buffers free
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
testEqual(conf.actualCount, 2u);
|
||||
|
||||
testDiag("prime the pump");
|
||||
tester.mon->reportRemoteQueueStatus(conf.actualCount);
|
||||
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
|
||||
testShow()<<"Before start() "<<*tester.mon<<"\n";
|
||||
tester.mon->start();
|
||||
testShow()<<"After start() "<<*tester.mon<<"\n";
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 0);
|
||||
testPop(*tester.mon, 1);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 2);
|
||||
testPop(*tester.mon, 3);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 4);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 5);
|
||||
testPop(*tester.mon, 6);
|
||||
testEmpty(*tester.mon);
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
// check pipeline=true, watermark, and unlisten.
|
||||
// a sequence with a definite end
|
||||
void checkCountdown()
|
||||
{
|
||||
testDiag("==== %s ====", CURRENT_FUNCTION);
|
||||
pva::MonitorFIFO::Config conf = {4, 3, 0};
|
||||
Tester tester(pvReqPipeline, &conf);
|
||||
|
||||
pvd::int32 cnt = 10;
|
||||
|
||||
tester.setAction([&tester, &cnt] (pva::MonitorFIFO *mon, size_t nfree) {
|
||||
testDiag("Spamming %zu", nfree);
|
||||
while(cnt >= 0 && nfree--) {
|
||||
pvd::int32 c = cnt--;
|
||||
testDiag("Count %d nfree=%zu %c", c, nfree, (nfree>0)?'T':'F');
|
||||
tester.tryPost(c, nfree>0);
|
||||
}
|
||||
if(cnt<0) {
|
||||
testDiag("Finished!");
|
||||
tester.mon->finish();
|
||||
}
|
||||
});
|
||||
tester.mon->setFreeHighMark(0.5); // run action() when one of two buffer elements is free
|
||||
|
||||
tester.connect(pvd::pvInt);
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Connect});
|
||||
|
||||
testEqual(conf.actualCount, 2u);
|
||||
|
||||
testDiag("prime the pump");
|
||||
tester.mon->reportRemoteQueueStatus(conf.actualCount);
|
||||
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::LowWater});
|
||||
|
||||
tester.mon->start();
|
||||
tester.testTimeline({Tester::Event});
|
||||
|
||||
|
||||
testPop(*tester.mon, 10);
|
||||
testPop(*tester.mon, 9);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 8);
|
||||
testPop(*tester.mon, 7);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
tester.testTimeline({}); // nothing happens, watermark not reached
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 6);
|
||||
testPop(*tester.mon, 5);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
tester.testTimeline({});
|
||||
tester.mon->reportRemoteQueueStatus(1);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 4);
|
||||
testPop(*tester.mon, 3);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 2);
|
||||
testPop(*tester.mon, 1);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({});
|
||||
|
||||
tester.mon->reportRemoteQueueStatus(2);
|
||||
tester.testTimeline({Tester::LowWater, Tester::Event});
|
||||
|
||||
testPop(*tester.mon, 0);
|
||||
testEmpty(*tester.mon);
|
||||
tester.testTimeline({Tester::Unlisten});
|
||||
|
||||
tester.mon->stop();
|
||||
tester.close();
|
||||
tester.mon->notify();
|
||||
tester.testTimeline({Tester::Close});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testmonitorfifo)
|
||||
{
|
||||
testPlan(184);
|
||||
checkPlain();
|
||||
checkAfterClose();
|
||||
checkReOpenLost();
|
||||
checkTypeChange();
|
||||
checkFill();
|
||||
checkSaturate();
|
||||
checkPipeline();
|
||||
checkSpam();
|
||||
checkCountdown();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
#else /* c++11 */
|
||||
|
||||
MAIN(testmonitorfifo)
|
||||
{
|
||||
testPlan(1);
|
||||
testSkip(1, "no c++11");
|
||||
return testDone();
|
||||
}
|
||||
|
||||
#endif /* c++11 */
|
Reference in New Issue
Block a user