Simplify testConveyor
This commit is contained in:

committed by
mdavidsaver

parent
bf04c69bda
commit
f3486211f5
@ -6,13 +6,13 @@ include $(TOP)/configure/CONFIG
|
||||
# Access to uninstalled headers
|
||||
USR_CPPFLAGS += -I$(TOP)/src/ca
|
||||
|
||||
TESTPROD_HOST += testCaProvider
|
||||
testCaProvider_SRCS += testCaProvider.cpp
|
||||
|
||||
TESTPROD_HOST += testConveyor
|
||||
testConveyor_SRCS += testConveyor.cpp
|
||||
TESTS += testConveyor
|
||||
|
||||
TESTPROD_HOST += testCaProvider
|
||||
testCaProvider_SRCS += testCaProvider.cpp
|
||||
|
||||
PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS)
|
||||
PROD_SYS_LIBS_WIN32 += netapi32 ws2_32
|
||||
|
||||
@ -35,8 +35,8 @@ ifdef BASE_3_16
|
||||
# Ensure EPICS_HOST_ARCH is set in the environment
|
||||
export EPICS_HOST_ARCH
|
||||
|
||||
caTestHarness_SRCS += $(testCaProvider_SRCS)
|
||||
caTestHarness_SRCS += testConveyor.cpp
|
||||
caTestHarness_SRCS += $(testCaProvider_SRCS)
|
||||
caTestHarness_SRCS += pvCaAllTests.c
|
||||
|
||||
# Build harness for vxWorks and RTEMS
|
||||
|
@ -6,14 +6,11 @@
|
||||
|
||||
/*
|
||||
* Tests for notifierConveyor:
|
||||
* 1. Queueing a notification calls the client.
|
||||
* 2. Notifications can be added while notifyClient() is asleep/busy.
|
||||
* 3. A client can re-queue itself from notifyClient().
|
||||
* 4. Queue a Notification to an empty or already-dead client
|
||||
* -- unreference after adding it to the Notification
|
||||
* 5. Notifying can delete an almost-dead client
|
||||
* -- unreference the client in the notifyClient() routine.
|
||||
* 6. Deleting the conveyor in a notifier doesn't crash it.
|
||||
* 1. Basic queueing functionality.
|
||||
* 2. Queue accepts notification while conveyor is busy.
|
||||
* 3. Client's notifier can re-queue itself.
|
||||
* 4. Delete a client inside its own notifier.
|
||||
* 5. Queue Notification to an already-dead client.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
@ -25,6 +22,8 @@
|
||||
|
||||
#include <notifierConveyor.h>
|
||||
|
||||
#define TIMEOUT 5.0
|
||||
|
||||
using namespace epics::pvAccess::ca;
|
||||
|
||||
typedef void (*voidFunc)(void);
|
||||
@ -65,220 +64,165 @@ private:
|
||||
voidFunc destroyFunc;
|
||||
};
|
||||
|
||||
namespace basic {
|
||||
epicsEvent ogone;
|
||||
void odestroy(void) {
|
||||
testPass("Owner's destructor called");
|
||||
ogone.trigger();
|
||||
}
|
||||
|
||||
epicsEvent noted, gone;
|
||||
void notify(void) {
|
||||
testPass("Client's notify() called");
|
||||
noted.trigger();
|
||||
}
|
||||
void cdestroy(void) {
|
||||
testPass("Client's destructor called");
|
||||
gone.trigger();
|
||||
}
|
||||
|
||||
epicsEvent blocking, unblock;
|
||||
void blockingNotify(void) {
|
||||
blocking.trigger();
|
||||
unblock.wait();
|
||||
}
|
||||
|
||||
class requeingClient :
|
||||
public NotifierClient
|
||||
{
|
||||
public:
|
||||
requeingClient(owner *q, NotificationPtr n, int r) :
|
||||
queue(q), myself(n), requeues(r), count(0) {}
|
||||
virtual void notifyClient() {
|
||||
if (++count < requeues) {
|
||||
queue->notifyClient(myself);
|
||||
}
|
||||
else {
|
||||
testPass("Requeued myself %d times", count);
|
||||
done.trigger();
|
||||
}
|
||||
}
|
||||
bool wait(double timeout) {
|
||||
return done.wait(timeout);
|
||||
}
|
||||
private:
|
||||
owner *queue;
|
||||
NotificationPtr myself;
|
||||
int requeues;
|
||||
int count;
|
||||
epicsEvent done;
|
||||
};
|
||||
|
||||
void testOperation(void)
|
||||
{
|
||||
testDiag("*** testOperation ***");
|
||||
{
|
||||
owner o1(&odestroy);
|
||||
|
||||
testDiag("1. Basic queueing functionality");
|
||||
{
|
||||
NotifierClientPtr c1 = NotifierClientPtr(new
|
||||
basicClient(¬ify, &cdestroy));
|
||||
NotificationPtr n1 = NotificationPtr(new Notification);
|
||||
n1->setClient(c1);
|
||||
|
||||
o1.notifyClient(n1);
|
||||
if (!noted.wait(5.0))
|
||||
testFail("Client's notify() not called");
|
||||
|
||||
// c1's destructor should be called here
|
||||
}
|
||||
if (!gone.tryWait())
|
||||
testFail("Client's destructor not called");
|
||||
|
||||
testDiag("2. Queue accepts notification while conveyor is busy");
|
||||
{
|
||||
NotifierClientPtr c2 = NotifierClientPtr(new
|
||||
basicClient(&blockingNotify));
|
||||
NotificationPtr n2 = NotificationPtr(new Notification);
|
||||
n2->setClient(c2);
|
||||
o1.notifyClient(n2);
|
||||
if (!blocking.wait(5.0))
|
||||
testAbort("Conveyor not stalled");
|
||||
|
||||
NotifierClientPtr c3 = NotifierClientPtr(new
|
||||
basicClient(¬ify));
|
||||
NotificationPtr n3 = NotificationPtr(new Notification);
|
||||
n3->setClient(c3);
|
||||
o1.notifyClient(n3);
|
||||
testPass("Notification added to stalled queue");
|
||||
|
||||
unblock.trigger();
|
||||
if (!noted.wait(5.0))
|
||||
testFail("Queue didn't recover from stall!");
|
||||
}
|
||||
|
||||
testDiag("3. Client can re-queue itself from notifyClient()");
|
||||
{
|
||||
NotificationPtr n4 = NotificationPtr(new Notification);
|
||||
requeingClient *rq = new requeingClient(&o1, n4, 100);
|
||||
NotifierClientPtr c4 = NotifierClientPtr(rq);
|
||||
n4->setClient(c4);
|
||||
o1.notifyClient(n4);
|
||||
if (!rq->wait(5.0))
|
||||
testFail("Requeueing client didn't finish?");
|
||||
}
|
||||
|
||||
testDiag("4. Queue Notification to an already-dead client");
|
||||
{
|
||||
NotifierClientPtr c5 = NotifierClientPtr(new
|
||||
basicClient(¬ify, &cdestroy));
|
||||
NotificationPtr n1 = NotificationPtr(new Notification);
|
||||
n1->setClient(c5);
|
||||
|
||||
c5.reset();
|
||||
if (!gone.tryWait())
|
||||
testFail("Client's destructor not called");
|
||||
|
||||
o1.notifyClient(n1);
|
||||
testOk(!noted.wait(2.0), "Client's notify() not called");
|
||||
}
|
||||
|
||||
// o1's destructor gets called here
|
||||
}
|
||||
if (!ogone.wait(5.0))
|
||||
testFail("Owner's destructor not called");
|
||||
}
|
||||
epicsEvent ogone;
|
||||
void odestroy(void) {
|
||||
testPass("Owner's destructor called");
|
||||
ogone.trigger();
|
||||
}
|
||||
|
||||
namespace torture {
|
||||
static epicsEvent ogone;
|
||||
void odestroy(void) {
|
||||
testPass("Owner's destructor called");
|
||||
ogone.trigger();
|
||||
}
|
||||
epicsEvent noted;
|
||||
void notify(void) {
|
||||
testPass("Client's notify() called");
|
||||
noted.trigger();
|
||||
}
|
||||
epicsEvent cgone;
|
||||
void cdestroy(void) {
|
||||
testPass("Client's destructor called");
|
||||
cgone.trigger();
|
||||
}
|
||||
|
||||
static epicsEvent noted, cgone;
|
||||
void notify(void) {
|
||||
testPass("Client's notify() called");
|
||||
noted.trigger();
|
||||
}
|
||||
void cdestroy(void) {
|
||||
testPass("Client's destructor called");
|
||||
cgone.trigger();
|
||||
}
|
||||
epicsEvent blocking, unblock;
|
||||
void blockingNotify(void) {
|
||||
blocking.trigger();
|
||||
unblock.wait();
|
||||
}
|
||||
|
||||
NotifierClientPtr cptr;
|
||||
void dropClient(void) {
|
||||
testPass("dropClient() called");
|
||||
cptr.reset();
|
||||
noted.trigger();
|
||||
}
|
||||
|
||||
std::shared_ptr<owner> optr;
|
||||
void mayhem(void) {
|
||||
testPass("mayhem() called");
|
||||
optr.reset();
|
||||
noted.trigger();
|
||||
}
|
||||
|
||||
void testDestruction(void)
|
||||
{
|
||||
testDiag("*** testDestruction ***");
|
||||
|
||||
testDiag("5. Notifying can delete an almost-dead client");
|
||||
{
|
||||
owner o1(&odestroy);
|
||||
{
|
||||
cptr = NotifierClientPtr(new
|
||||
basicClient(&dropClient, &cdestroy));
|
||||
NotificationPtr n1 = NotificationPtr(new Notification);
|
||||
n1->setClient(cptr);
|
||||
|
||||
o1.notifyClient(n1);
|
||||
if (!noted.wait(5.0))
|
||||
testFail("Client's not notify() called");
|
||||
// c1's destructor should also have been called
|
||||
if (!cgone.wait(5.0))
|
||||
testFail("Client's destructor not called");
|
||||
|
||||
}
|
||||
|
||||
// o1's destructor gets called here
|
||||
class requeingClient :
|
||||
public NotifierClient
|
||||
{
|
||||
public:
|
||||
requeingClient(owner *q, NotificationPtr n, int r) :
|
||||
queue(q), myself(n), requeues(r), count(0) {}
|
||||
virtual void notifyClient() {
|
||||
if (++count < requeues) {
|
||||
queue->notifyClient(myself);
|
||||
}
|
||||
if (!ogone.wait(5.0))
|
||||
else {
|
||||
testPass("Requeued myself %d times", count);
|
||||
done.trigger();
|
||||
}
|
||||
}
|
||||
bool wait(double timeout) {
|
||||
return done.wait(timeout);
|
||||
}
|
||||
private:
|
||||
owner *queue;
|
||||
NotificationPtr myself;
|
||||
int requeues;
|
||||
int count;
|
||||
epicsEvent done;
|
||||
};
|
||||
|
||||
void testOperation(void)
|
||||
{
|
||||
testDiag("*** testOperation ***");
|
||||
{
|
||||
owner o1(&odestroy);
|
||||
NotifierClientPtr c1;
|
||||
NotificationPtr n1;
|
||||
|
||||
testDiag("1. Basic queueing functionality.");
|
||||
c1 = NotifierClientPtr(new basicClient(¬ify, &cdestroy));
|
||||
n1 = NotificationPtr(new Notification(c1));
|
||||
|
||||
o1.notifyClient(n1);
|
||||
if (!noted.wait(TIMEOUT))
|
||||
testFail("Client's notify() not called");
|
||||
|
||||
c1.reset(); // Clean up c1
|
||||
if (!cgone.tryWait())
|
||||
testFail("Client's destructor not called");
|
||||
|
||||
testDiag("2. Queue accepts notification while conveyor is busy.");
|
||||
{
|
||||
c1 = NotifierClientPtr(new basicClient(&blockingNotify));
|
||||
n1->setClient(c1);
|
||||
o1.notifyClient(n1);
|
||||
if (!blocking.wait(TIMEOUT))
|
||||
testAbort("Conveyor not stalled");
|
||||
|
||||
NotifierClientPtr c2 = NotifierClientPtr(new
|
||||
basicClient(¬ify));
|
||||
NotificationPtr n2 = NotificationPtr(new Notification);
|
||||
n2->setClient(c2);
|
||||
o1.notifyClient(n2);
|
||||
testPass("Notification added to stalled queue");
|
||||
|
||||
unblock.trigger();
|
||||
if (!noted.wait(TIMEOUT))
|
||||
testFail("Queue didn't recover from stall!");
|
||||
}
|
||||
|
||||
testDiag("3. Client's notifier can re-queue itself.");
|
||||
{
|
||||
requeingClient *rq = new requeingClient(&o1, n1, 100);
|
||||
c1 = NotifierClientPtr(rq);
|
||||
n1->setClient(c1);
|
||||
o1.notifyClient(n1);
|
||||
if (!rq->wait(TIMEOUT))
|
||||
testFail("Requeueing client didn't finish?");
|
||||
c1.reset();
|
||||
}
|
||||
}
|
||||
if (!ogone.wait(TIMEOUT))
|
||||
testFail("Owner's destructor not called");
|
||||
}
|
||||
|
||||
NotifierClientPtr cptr;
|
||||
void dropClient(void) {
|
||||
testPass("dropClient() called");
|
||||
cptr.reset();
|
||||
noted.trigger();
|
||||
}
|
||||
|
||||
void testDestruction(void)
|
||||
{
|
||||
testDiag("*** testDestruction ***");
|
||||
|
||||
std::tr1::shared_ptr<owner> optr;
|
||||
NotificationPtr n1;
|
||||
|
||||
testDiag("4. Delete a client inside its own notifier.");
|
||||
{
|
||||
optr = std::tr1::shared_ptr<owner> (new owner(&odestroy));
|
||||
cptr = NotifierClientPtr(new basicClient(&dropClient, &cdestroy));
|
||||
n1 = NotificationPtr(new Notification(cptr));
|
||||
|
||||
optr->notifyClient(n1);
|
||||
if (!noted.wait(TIMEOUT))
|
||||
testFail("dropClient() not called");
|
||||
// Client's destructor should also have been called
|
||||
if (!cgone.wait(TIMEOUT))
|
||||
testFail("Client's destructor not called");
|
||||
}
|
||||
|
||||
NotifierClientPtr c1;
|
||||
|
||||
testDiag("5. Queue Notification to an already-dead client.");
|
||||
{
|
||||
c1 = NotifierClientPtr(new basicClient(¬ify, &cdestroy));
|
||||
n1->setClient(c1);
|
||||
|
||||
c1.reset(); // Kill the client n1 pointed to
|
||||
if (!cgone.tryWait())
|
||||
testFail("Client's destructor not called");
|
||||
|
||||
optr->notifyClient(n1); // Should do nothing
|
||||
|
||||
optr.reset(); // Clean up optr
|
||||
if (!ogone.tryWait())
|
||||
testFail("Owner's destructor not called");
|
||||
|
||||
testDiag("6. Deleting the conveyor inside a notifier doesn't crash it.");
|
||||
{
|
||||
optr = std::shared_ptr<owner> (new owner(&odestroy));
|
||||
{
|
||||
NotifierClientPtr c2 = NotifierClientPtr(new
|
||||
basicClient(&mayhem, &cdestroy));
|
||||
NotificationPtr n2 = NotificationPtr(new Notification);
|
||||
n2->setClient(c2);
|
||||
|
||||
// optr's destructor called by conveyor here:
|
||||
optr->notifyClient(n2);
|
||||
if (!noted.wait(5.0))
|
||||
testFail("mayhem() not called");
|
||||
if (!ogone.tryWait())
|
||||
testFail("Owner's destructor not called");
|
||||
|
||||
// c2's destructor gets called here
|
||||
}
|
||||
if (!cgone.wait(5.0))
|
||||
testFail("Client's destructor not called");
|
||||
}
|
||||
testOk(!noted.tryWait(), "Client's notify() NOT called");
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(testConveyor)
|
||||
{
|
||||
testPlan(14);
|
||||
testPlan(11);
|
||||
|
||||
basic::testOperation();
|
||||
torture::testDestruction();
|
||||
testOperation();
|
||||
testDestruction();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
Reference in New Issue
Block a user