mirror of
https://github.com/resiprocate/resiprocate.git
synced 2026-01-12 00:05:02 +08:00
688 lines
16 KiB
C++
688 lines
16 KiB
C++
#include <list>
|
|
|
|
#include "cppunit/TestCase.h"
|
|
#include "rutil/BaseException.hxx"
|
|
#include "rutil/Logger.hxx"
|
|
#include "tfm/Sequence.hxx"
|
|
#include "tfm/SequenceSet.hxx"
|
|
|
|
using namespace resip;
|
|
using namespace std;
|
|
|
|
#define RESIPROCATE_SUBSYSTEM resip::Subsystem::TEST
|
|
|
|
std::shared_ptr<SequenceSet> SequenceClass::CPUSequenceSet;
|
|
bool SequenceClass::CPUSequenceSetCleanup = true;
|
|
|
|
void SequenceClass::CPUSequenceSetup()
|
|
{
|
|
if (SequenceClass::CPUSequenceSetCleanup)
|
|
{
|
|
// get rid of the old SequenceSet
|
|
if (SequenceClass::CPUSequenceSet)
|
|
{
|
|
//.dcm. -- should rationalize clear and release
|
|
SequenceClass::CPUSequenceSet->clear();
|
|
SequenceClass::CPUSequenceSet->release();
|
|
}
|
|
SequenceSet* sset = new SequenceSet();
|
|
SequenceClass::CPUSequenceSet = sset->getHandle();
|
|
// reuse this SequenceSet until executed
|
|
SequenceClass::CPUSequenceSetCleanup = false;
|
|
}
|
|
}
|
|
|
|
int
|
|
SequenceClass::size()
|
|
{
|
|
return (int)mExpects.size();
|
|
}
|
|
|
|
bool
|
|
SequenceClass::isFailed() const
|
|
{
|
|
return mFailed || !mExpects.empty();
|
|
}
|
|
|
|
void
|
|
SequenceClass::setParent(SequenceClass* parent)
|
|
{
|
|
mParent = parent;
|
|
}
|
|
|
|
SequenceClass*
|
|
SequenceClass::getParent() const
|
|
{
|
|
return mParent;
|
|
}
|
|
|
|
|
|
const SequenceClass&
|
|
SequenceClass::getRoot() const
|
|
{
|
|
if (this->getParent() == 0)
|
|
{
|
|
return *this;
|
|
}
|
|
return this->getParent()->getRoot();
|
|
}
|
|
|
|
void
|
|
SequenceClass::setBranchCount(unsigned int count)
|
|
{
|
|
resip_assert(count);
|
|
resip_assert(mBranchCount == 0);
|
|
|
|
mBranchCount = count;
|
|
removeFromActiveSet();
|
|
}
|
|
|
|
void
|
|
SequenceClass::setAfterAction(ActionBase* action)
|
|
{
|
|
delete mAfterAction;
|
|
mAfterAction = action;
|
|
}
|
|
|
|
bool
|
|
SequenceClass::decrementBranches()
|
|
{
|
|
resip_assert(mBranchCount);
|
|
if (!(--mBranchCount))
|
|
{
|
|
if (mAfterAction)
|
|
{
|
|
InfoLog(<< "All branches satisfied: triggering after action");
|
|
// .dlb. what if this throws?
|
|
mAfterAction->exec();
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
SequenceClass::fail(const resip::Data& message)
|
|
{
|
|
// keep first fail info
|
|
if (!getSequenceSet()->mFailed)
|
|
{
|
|
getSequenceSet()->mFailed = true;
|
|
Data errorMessage;
|
|
{
|
|
DataStream str(errorMessage);
|
|
str << message << endl << "Sequence state:" << endl;
|
|
str << " ";
|
|
bool previousActive = false;
|
|
this->getRoot().prettyPrint(str, previousActive);
|
|
str << endl;
|
|
}
|
|
getSequenceSet()->mExplanation = errorMessage;
|
|
getSequenceSet()->mLineNumber = mLineNumber;
|
|
}
|
|
mFailed = true;
|
|
}
|
|
|
|
void
|
|
SequenceClass::prettyPrint(EncodeStream& str, bool& previousActive, int ind) const
|
|
{
|
|
if (find(getSequenceSet()->mSequences.begin(),
|
|
getSequenceSet()->mSequences.end(), this) != getSequenceSet()->mSequences.end())
|
|
{
|
|
str << "Seq(";
|
|
str << mAction->toString() << ", ";
|
|
}
|
|
else
|
|
{
|
|
str << "Sub(";
|
|
}
|
|
|
|
bool first = true;
|
|
ind += 4;
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mUsedExpects.begin();
|
|
i != mUsedExpects.end(); i++)
|
|
{
|
|
if (!first)
|
|
{
|
|
str << ", ";
|
|
}
|
|
str << endl;
|
|
str << indent(ind);
|
|
(*i)->prettyPrint(str, previousActive, ind);
|
|
}
|
|
|
|
if (!mUsedExpects.empty() && !mExpects.empty())
|
|
{
|
|
str << ", " << endl;
|
|
}
|
|
|
|
first = true;
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
if (!first)
|
|
{
|
|
str << ",";
|
|
}
|
|
str << endl;
|
|
|
|
if (!previousActive)
|
|
{
|
|
str << "===> " << indent(ind - 5);
|
|
(*i)->prettyPrint(str, previousActive, ind);
|
|
}
|
|
else
|
|
{
|
|
str << indent(ind);
|
|
(*i)->prettyPrint(str, previousActive, ind);
|
|
}
|
|
first = false;
|
|
previousActive = true;
|
|
}
|
|
str << ")";
|
|
str.flush();
|
|
}
|
|
|
|
bool
|
|
SequenceClass::isMatch(std::shared_ptr<Event> event) const
|
|
{
|
|
const auto to = std::dynamic_pointer_cast<OptionalTimeoutEvent>(event);
|
|
for (std::list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
DebugLog(<< "expect matching: " << **i);
|
|
if (to)
|
|
{
|
|
if (to->getExpect() == *i)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if ((*i)->isMatch(event))
|
|
{
|
|
return true;
|
|
}
|
|
else if (!(*i)->isOptional())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SequenceClass::handleEvent(std::shared_ptr<Event> event)
|
|
{
|
|
TestEndPoint* user = event->getEndPoint();
|
|
|
|
InfoLog(<< "inner handleEvent: " << " to " << user << " " << *this << endl << event);
|
|
next:
|
|
if (!mExpects.empty())
|
|
{
|
|
TestEndPoint::ExpectBase* expect = mExpects.front();
|
|
|
|
mUsedExpects.push_back(mExpects.front());
|
|
mExpects.pop_front();
|
|
|
|
const auto ot = std::dynamic_pointer_cast<OptionalTimeoutEvent>(event);
|
|
if (ot && ot->getExpect() == expect)
|
|
{
|
|
mTimerId=-7;
|
|
scheduleTimeout();
|
|
return;
|
|
}
|
|
|
|
if (!expect->isMatch(event))
|
|
{
|
|
if (expect->isOptional())
|
|
{
|
|
//DebugLog("handleEvent -- skipping optional: " << *expect);
|
|
cancelTimeout();
|
|
scheduleTimeout();
|
|
goto next;
|
|
}
|
|
else
|
|
{
|
|
DebugLog(<< "Failed expect: " << expect->explainMismatch(event));
|
|
|
|
fail("Received " + event->briefString() + " expected " + expect->getMsgTypeString());
|
|
return;
|
|
}
|
|
}
|
|
DebugLog(<< "Matched");
|
|
|
|
cancelTimeout();
|
|
scheduleTimeout();
|
|
try
|
|
{
|
|
if(user)
|
|
{
|
|
expect->onEvent(*user, event);
|
|
}
|
|
else
|
|
{
|
|
resip_assert(0);
|
|
}
|
|
}
|
|
catch (const BaseException& e)
|
|
{
|
|
InfoLog(<< "Failed expect action with: " << e << " for " << event->briefString());
|
|
Data errorMessage;
|
|
{
|
|
DataStream str(errorMessage);
|
|
str << "Failed expect action: " << e << ", Event: " << event->toString();
|
|
}
|
|
fail(errorMessage);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
InfoLog (<< "Failed expect action with: " << e.what() << " for " << event->briefString());
|
|
Data errorMessage;
|
|
{
|
|
DataStream str(errorMessage);
|
|
str << "Failed expect action: " << e.what() << ", Event: " << event->toString();
|
|
}
|
|
fail(errorMessage);
|
|
}
|
|
catch (...)
|
|
{
|
|
InfoLog(<< "Failed expect action with an unknown exception, Event: " << event->briefString());
|
|
fail("Failed expect action with an unknown exception, Event: " + event->toString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InfoLog(<< "Failed expect: received " << event->briefString() << " expected no more messages");
|
|
fail("Unexpected " + event->toString());
|
|
}
|
|
}
|
|
|
|
void
|
|
SequenceClass::addToActiveSet()
|
|
{
|
|
//DebugLog( << "Adding sequence: " << this << " to activeSet");
|
|
getSequenceSet()->mActiveSet.insert(this);
|
|
scheduleTimeout();
|
|
}
|
|
|
|
bool
|
|
SequenceClass::removeFromActiveSet()
|
|
{
|
|
//DebugLog(<< "removeFromActiveSet: " << *this);
|
|
set<SequenceClass*>::iterator i = getSequenceSet()->mActiveSet.find(this);
|
|
if (i != getSequenceSet()->mActiveSet.end())
|
|
{
|
|
getSequenceSet()->mActiveSet.erase(i);
|
|
cancelTimeout();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SequenceClass::cancelTimeout()
|
|
{
|
|
if (mTimerId != -7)
|
|
{
|
|
InfoLog(<< "SequenceClass::cancelTimeout(" << mTimerId << ") " << this);
|
|
if(!getSequenceSet()->mEventFifo.cancel(mTimerId))
|
|
{
|
|
// Race condition; it is likely that the timer is sitting, unprocessed,
|
|
// in our message queue, right as we satisfy the expect that the timer
|
|
// was waiting for. It would be nice to figure out a way to fix this.
|
|
resip_assert(0);
|
|
}
|
|
mTimerId = -7;
|
|
}
|
|
}
|
|
|
|
void SequenceClass::scheduleTimeout()
|
|
{
|
|
if (mExpects.empty())
|
|
{
|
|
// schedule sequence done event
|
|
InfoLog(<< "Queuing Sequence done: hangAroundTime: " <<mHangAroundTimeMs);
|
|
mTimingOut = true;
|
|
getSequenceSet()->enqueue(std::make_shared<SequenceDoneEvent>(this), mHangAroundTimeMs);
|
|
}
|
|
else
|
|
{
|
|
TestEndPoint::ExpectBase* f = mExpects.front();
|
|
if (f->isOptional())
|
|
{
|
|
mTimerId = getSequenceSet()->enqueue(std::make_shared<OptionalTimeoutEvent>(*this, f),
|
|
f->getTimeout());
|
|
InfoLog(<< "SequenceClass::scheduleTimeout(" << mTimerId << ") optional");
|
|
}
|
|
else
|
|
{
|
|
mTimerId = getSequenceSet()->enqueue(std::make_shared<TimeoutEvent>(*this),
|
|
f->getTimeout());
|
|
InfoLog(<< "SequenceClass::scheduleTimeout(" << mTimerId << ") for " << *f << " with timeout "<< f->getTimeout());
|
|
}
|
|
}
|
|
}
|
|
|
|
// execute the starting Action
|
|
// (called only from Execute)
|
|
void
|
|
SequenceClass::start()
|
|
{
|
|
mTimerId = -7;
|
|
addToActiveSet();
|
|
mAction->exec();
|
|
}
|
|
|
|
bool
|
|
SequenceClass::queue()
|
|
{
|
|
if (!mExpects.empty() && mExpects.front()->queue(this))
|
|
{
|
|
mUsedExpects.push_back(mExpects.front());
|
|
mExpects.pop_front();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SequenceClass::addExpect(TestEndPoint::ExpectBase* expect)
|
|
{
|
|
//DebugLog(<< "addExpect " << expect);
|
|
|
|
mExpects.push_back(expect);
|
|
}
|
|
|
|
#include "SequenceClassConstructorDefns.hxx"
|
|
|
|
SequenceClass::~SequenceClass()
|
|
{
|
|
delete mAction;
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
delete *i;
|
|
}
|
|
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mUsedExpects.begin();
|
|
i != mUsedExpects.end(); i++)
|
|
{
|
|
delete *i;
|
|
}
|
|
|
|
delete mAfterAction;
|
|
|
|
mSet.reset();
|
|
}
|
|
|
|
EncodeStream&
|
|
operator<<(EncodeStream& s, const SequenceClass& sequence)
|
|
{
|
|
s << "Sequence(" << &sequence << ", line: " << sequence.mLineNumber << ")[";
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = sequence.mExpects.begin();
|
|
i != sequence.mExpects.end(); i++)
|
|
{
|
|
s << **i << ", ";
|
|
}
|
|
s << "]";
|
|
s.flush();
|
|
return s;
|
|
}
|
|
|
|
bool
|
|
operator<(const SequenceClass& lhs, const SequenceClass& rhs)
|
|
{
|
|
return &lhs < &rhs;
|
|
}
|
|
|
|
Box
|
|
SequenceClass::layout() const
|
|
{
|
|
//InfoLog(<< "SequenceClass::layout");
|
|
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
// layout alone (0,0) corner
|
|
Box sub = (*i)->layout();
|
|
|
|
// push down to position in sequence
|
|
sub = (*i)->displaceDown(mBox);
|
|
//InfoLog(<< "after displace down: " << sub);
|
|
mBox.boxUnion(sub);
|
|
}
|
|
|
|
// center each in the widest
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
unsigned displace = mBox.mWidth/2 - (*i)->mBox.mWidth/2;
|
|
//InfoLog(<< "displace: " << displace);
|
|
(*i)->displaceRight(Box(0, 0, displace, 0));
|
|
}
|
|
|
|
//InfoLog(<< "SequenceClass::layout: " << mBox);
|
|
|
|
return mBox;
|
|
}
|
|
|
|
void
|
|
SequenceClass::render(CharRaster& out) const
|
|
{
|
|
//InfoLog(<< "SequenceClass::render");
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
(*i)->render(out);
|
|
}
|
|
|
|
// pad the | after the sequence
|
|
for (unsigned int i = mBox.mY +mBox.mHeight; i < mContainerBottom; i++)
|
|
{
|
|
out[i][mBox.mX + mBox.mWidth/2+1] = '|';
|
|
}
|
|
}
|
|
|
|
Box&
|
|
SequenceClass::displaceDown(const Box& box)
|
|
{
|
|
for (list<TestEndPoint::ExpectBase*>::iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
(*i)->displaceDown(box);
|
|
}
|
|
|
|
AsciiGraphic::displaceDown(box);
|
|
mContainerBottom += box.mY + box.mHeight;
|
|
return mBox;
|
|
}
|
|
|
|
Box&
|
|
SequenceClass::displaceRight(const Box& box)
|
|
{
|
|
for (list<TestEndPoint::ExpectBase*>::iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
(*i)->displaceRight(box);
|
|
}
|
|
|
|
AsciiGraphic::displaceRight(box);
|
|
return mBox;
|
|
}
|
|
|
|
TimeoutEvent::TimeoutEvent(SequenceClass& sequence)
|
|
: Event(0),
|
|
mSequence(sequence)
|
|
{
|
|
}
|
|
|
|
TimeoutEvent::~TimeoutEvent()
|
|
{
|
|
}
|
|
|
|
SequenceClass&
|
|
TimeoutEvent::getSequence()
|
|
{
|
|
return mSequence;
|
|
}
|
|
|
|
resip::Data
|
|
TimeoutEvent::toString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<TimeoutEvent> " << mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
resip::Data
|
|
TimeoutEvent::briefString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<TimeoutEvent> " << &mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
OptionalTimeoutEvent::OptionalTimeoutEvent(SequenceClass& sequence,
|
|
TestEndPoint::ExpectBase* expect)
|
|
: Event(0),
|
|
mSequence(sequence),
|
|
mExpect(expect)
|
|
{
|
|
}
|
|
|
|
OptionalTimeoutEvent::~OptionalTimeoutEvent()
|
|
{
|
|
}
|
|
|
|
SequenceClass&
|
|
OptionalTimeoutEvent::getSequence() const
|
|
{
|
|
return mSequence;
|
|
}
|
|
|
|
TestEndPoint::ExpectBase*
|
|
OptionalTimeoutEvent::getExpect() const
|
|
{
|
|
return mExpect;
|
|
}
|
|
|
|
resip::Data
|
|
OptionalTimeoutEvent::toString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<OptionalTimeoutEvent> " << mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
resip::Data
|
|
OptionalTimeoutEvent::briefString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<OptionalTimeoutEvent> " << &mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
SequenceDoneEvent::SequenceDoneEvent(SequenceClass* sequence)
|
|
: Event(0),
|
|
mSequence(sequence)
|
|
{
|
|
}
|
|
|
|
SequenceClass*
|
|
SequenceDoneEvent::getSequence()
|
|
{
|
|
return mSequence;
|
|
}
|
|
|
|
resip::Data
|
|
SequenceDoneEvent::toString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<SequenceDoneEvent> " << mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
resip::Data
|
|
SequenceDoneEvent::briefString() const
|
|
{
|
|
Data s;
|
|
{
|
|
DataStream str(s);
|
|
str << "<SequenceDoneEvent> " << &mSequence;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void
|
|
SequenceClass::setSequenceSet(std::shared_ptr<SequenceSet> set)
|
|
{
|
|
mSet = set;
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mExpects.begin();
|
|
i != mExpects.end(); i++)
|
|
{
|
|
(*i)->setSequenceSet(set);
|
|
}
|
|
|
|
for (list<TestEndPoint::ExpectBase*>::const_iterator i = mUsedExpects.begin();
|
|
i != mUsedExpects.end(); i++)
|
|
{
|
|
(*i)->setSequenceSet(set);
|
|
}
|
|
}
|
|
|
|
#include "SeqDefns.hxx"
|
|
#include "SubDefns.hxx"
|
|
|
|
// Copyright 2005 Purplecomm, Inc.
|
|
/*
|
|
Copyright (c) 2005, PurpleComm, Inc.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
* Neither the name of PurpleComm, Inc. nor the names of its contributors may
|
|
be used to endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|