reSIProcate/stack  9694
Classes | Namespaces | Defines | Functions | Variables
testTransactionFSM.cxx File Reference
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <list>
#include <iostream>
#include <fstream>
#include <string>
#include "rutil/Data.hxx"
#include "rutil/DataStream.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Socket.hxx"
#include "resip/stack/test/TestSupport.hxx"
#include "resip/stack/MethodTypes.hxx"
#include "resip/stack/SipStack.hxx"
#include "resip/stack/SipMessage.hxx"
#include "resip/stack/Symbols.hxx"
#include "resip/stack/Transport.hxx"
#include "rutil/ParseBuffer.hxx"
Include dependency graph for testTransactionFSM.cxx:

Go to the source code of this file.

Classes

class  resip::TestFSM
struct  WaitNode

Namespaces

namespace  resip
 

SERNonceHelper implements the makeNonce function in the same way as SIP Express Router (SER) - http://www.iptel.org/ser.


Defines

#define RESIPROCATE_SUBSYSTEM   Subsystem::TEST
#define PORT   5060

Functions

int sendto (int s, const void *msg, size_t len, int flags, const struct sockaddr *to, int tolen)
void exitusage ()
void processTimeouts (int arg)
void processInject ()
void processExpect ()
void processDelays ()
bool processClause ()
int main (int argc, char *argv[])

Variables

char * ProgramName = 0
char * TestSpecBuf = 0
ParseBufferTestSpecParseBuf = 0
list< WaitNode * > WaitQueue
SipStackclient = 0
FdSet clientFdSet
struct sockaddr_in clientSa
int clientFd
Fifo< SipMessagefakeTxFifo
int errorCount = 0

Define Documentation

#define PORT   5060

Definition at line 73 of file testTransactionFSM.cxx.

Referenced by main().

#define RESIPROCATE_SUBSYSTEM   Subsystem::TEST

Definition at line 72 of file testTransactionFSM.cxx.


Function Documentation

void exitusage ( )

Definition at line 128 of file testTransactionFSM.cxx.

References ProgramName.

Referenced by main().

{
        cerr << "Usage: " << ProgramName << " ";
        cerr << "<testfileofsipmessagesandstuff>" << endl;
        exit(1);
}
int main ( int  argc,
char *  argv[] 
)

Definition at line 519 of file testTransactionFSM.cxx.

References resip::SipStack::addTransport(), client, clientFd, clientSa, DebugLog, errno, errorCount, exitusage(), PORT, processClause(), processTimeouts(), ProgramName, strerror(), TestSpecBuf, TestSpecParseBuf, resip::UDP, and WaitQueue.

{
    ProgramName = argv[0];

    if (NULL == argv[1]) {
        exitusage();
    }

    struct stat buf;
    if (stat(argv[1], &buf) < 0)
    {
        cerr << "Error: " << strerror(errno) << endl;
        exitusage();
    }

    ifstream testSpec;
    testSpec.open(argv[1], ifstream::in);
    if (!testSpec.is_open())
    {
        cerr << "Error: could not open "<< argv[1] << endl;
        exitusage();
    }

    TestSpecBuf = new char[buf.st_size+1];
    assert(TestSpecBuf);
    testSpec.read(TestSpecBuf, buf.st_size);
    TestSpecParseBuf = new ParseBuffer(TestSpecBuf, buf.st_size);
    assert(TestSpecParseBuf);

    int clientFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    clientSa.sin_family = PF_INET;
    clientSa.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientSa.sin_port = htons(PORT);
    int clientFdFlags = fcntl(clientFd, F_GETFL, 0);
    fcntl(clientFd, F_SETFL, clientFdFlags | O_NONBLOCK);

    client = new SipStack();
    assert(client);
    client->addTransport(UDP, PORT);

    signal(SIGALRM, processTimeouts);

    // Cause a signal to be generated with setitimer for its resolution
    struct itimerval timer;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 100000; // 100 ms resolution
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 100000; // 100 ms resolution
    setitimer(ITIMER_REAL, &timer, NULL);

    while (processClause())
    {
    }

    // Catch any remaining events.
    processTimeouts(0);
    if (!WaitQueue.empty())
    {
        DebugLog( << "Warning: ending with expect clauses outstanding"); 
        ++errorCount;
    }

    if (errorCount > 0)
    {
        cerr << "FAIL" << endl;
    }
    else
    {
        cerr << "PASS" << endl;
    }

    return errorCount;
}

Here is the call graph for this function:

bool processClause ( )

Definition at line 487 of file testTransactionFSM.cxx.

References DebugLog, resip::ParseBuffer::eof(), processDelays(), processExpect(), processInject(), resip::ParseBuffer::skipChar(), resip::ParseBuffer::skipToChar(), resip::ParseBuffer::skipToOneOf(), resip::ParseBuffer::skipWhitespace(), and TestSpecParseBuf.

Referenced by main().

{
    TestSpecParseBuf->skipWhitespace();

    // Look for 'i'/'e'/'d' for inject... or expect... or delay respectively.
    const char* now = TestSpecParseBuf->skipToOneOf("ied#");
    switch (*now)
    {
    case 'i':
        processInject();
        break;
    case 'e':
        processExpect();
        break;
    case 'd':
        processDelays();
        break;
    case '#':
        TestSpecParseBuf->skipToChar('\n');
        TestSpecParseBuf->skipChar();
        break;
    default:
        DebugLog(<< "Warning: error parsing test specification");
        TestSpecParseBuf->skipToChar('}');
        TestSpecParseBuf->skipChar();
    }

    TestSpecParseBuf->skipWhitespace();
    return !TestSpecParseBuf->eof();
}

Here is the call graph for this function:

void processDelays ( )

Definition at line 462 of file testTransactionFSM.cxx.

References DebugLog, resip::ParseBuffer::integer(), resip::ParseBuffer::skipChar(), resip::ParseBuffer::skipToChar(), resip::ParseBuffer::skipToOneOf(), resip::ParseBuffer::skipWhitespace(), and TestSpecParseBuf.

Referenced by processClause().

{
    TestSpecParseBuf->skipToChar('{');
    TestSpecParseBuf->skipChar();
    TestSpecParseBuf->skipWhitespace();

    TestSpecParseBuf->skipToOneOf("0123456789");
    int sleepLength = TestSpecParseBuf->integer();

    DebugLog( << "Pausing for " << sleepLength << " ms");

    // We sleep this way to avoid conflict with SIGALRM from alarm().
    struct timespec ts, remainder;
    ts.tv_sec = sleepLength / 1000;
    ts.tv_nsec = (sleepLength % 1000) * 1000000;
    while (nanosleep(&ts, &remainder) < 0)
    {
        ts = remainder;
    }

    TestSpecParseBuf->skipToChar('}');
    TestSpecParseBuf->skipChar();
}

Here is the call graph for this function:

void processExpect ( )

Definition at line 375 of file testTransactionFSM.cxx.

References DebugLog, resip::ParseBuffer::integer(), WaitNode::mExpiry, WaitNode::mIsRequest, WaitNode::mIsTransport, WaitNode::mMethod, WaitNode::mResponseCode, resip::ParseBuffer::position(), resip::ParseBuffer::skipChar(), resip::ParseBuffer::skipToChar(), resip::ParseBuffer::skipToOneOf(), resip::ParseBuffer::skipWhitespace(), strncasecmp(), TestSpecParseBuf, WaitQueue, and resip::ParseBuffer::Whitespace.

Referenced by processClause().

{
    const char* start = TestSpecParseBuf->position();
    const char* now;
    unsigned int expireTime = 1;
    WaitNode* thisWait = new WaitNode;
    assert(thisWait);
    thisWait->mResponseCode = 0;

    if (!strncasecmp(start, "expect_wire", strlen("expect_wire")))
    {
        thisWait->mIsTransport = true;
    }
    else if (!strncasecmp(start, "expect_tu", strlen("expect_tu")))
    {
        thisWait->mIsTransport = false;
    }
    else
    {
        DebugLog(<< "Warning: error parsing test specification"); 
        TestSpecParseBuf->skipToChar('}');
        TestSpecParseBuf->skipChar();
        delete thisWait;
        return;
    }

    TestSpecParseBuf->skipToChar('{');
    TestSpecParseBuf->skipChar();
    TestSpecParseBuf->skipWhitespace();
    start = TestSpecParseBuf->position();

    // We will want to get two of these in an expect_ clause.
    for (int i = 0; i < 2; i++)
    {
        TestSpecParseBuf->skipToChar('=');
        TestSpecParseBuf->skipChar();
        TestSpecParseBuf->skipWhitespace();
        if (!strncasecmp(start, "method", strlen("method")))
        {
            start = TestSpecParseBuf->position();
            now = TestSpecParseBuf->skipToOneOf(ParseBuffer::Whitespace);
            thisWait->mIsRequest = true;
            thisWait->mMethod = getMethodType(start, now-start);
        }
        else if (!strncasecmp(start, "status", strlen("status")))
        {
            TestSpecParseBuf->skipToOneOf("0123456789");
            thisWait->mIsRequest = false;
            thisWait->mResponseCode = TestSpecParseBuf->integer();
        }
        else if (!strncasecmp(start, "timeout", strlen("timeout")))
        {
            TestSpecParseBuf->skipToOneOf("0123456789");
            expireTime = TestSpecParseBuf->integer();
        }
        else
        {
            DebugLog(<< "Warning: error parsing test specification"); 
            TestSpecParseBuf->skipToChar('}');
            TestSpecParseBuf->skipChar();
            delete thisWait;
            return;
        }
        TestSpecParseBuf->skipWhitespace();
        start = TestSpecParseBuf->position();
    }

    assert(thisWait);

    gettimeofday(&thisWait->mExpiry, NULL);
    thisWait->mExpiry.tv_sec += expireTime / 1000;
    thisWait->mExpiry.tv_usec += (expireTime % 1000) * 1000;
    WaitQueue.push_front(thisWait);

    TestSpecParseBuf->skipToChar('}');
    TestSpecParseBuf->skipChar();

    /*
    cerr << "-> Expecting " << endl;
    cerr << "   mIsTransport = " << (thisWait->mIsTransport == true) << endl;
    cerr << "   mIsRequest = " << (thisWait->mIsRequest == true) << endl;
    cerr << "   mResponseCode = " << (thisWait->mResponseCode) << endl;
    cerr << "   mExpiry = " << (thisWait->mExpiry.tv_sec) << endl;
    */
}

Here is the call graph for this function:

void processInject ( )

Definition at line 325 of file testTransactionFSM.cxx.

References resip::TestFSM::addMessage(), client, DebugLog, resip::TestSupport::makeMessage(), resip::ParseBuffer::position(), resip::SipStack::send(), resip::ParseBuffer::skipChar(), resip::ParseBuffer::skipToChar(), resip::ParseBuffer::skipWhitespace(), strncasecmp(), and TestSpecParseBuf.

Referenced by processClause().

{
    const char* start = TestSpecParseBuf->position();
    const char* now;
    bool isWireInject = false;

    if (!strncasecmp(start, "inject_wire", strlen("inject_wire")))
    {
        isWireInject = true;
    }
    else if (!strncasecmp(start, "inject_tu", strlen("inject_tu")))
    {
        isWireInject = false;
    }
    else
    {
        DebugLog(<< "Warning: error parsing test specification.");
        TestSpecParseBuf->skipToChar('}');
        TestSpecParseBuf->skipChar();
        return;
    }

    TestSpecParseBuf->skipToChar('{');
    TestSpecParseBuf->skipChar();
    TestSpecParseBuf->skipWhitespace();

    start = TestSpecParseBuf->position();
    now = TestSpecParseBuf->skipToChar('}');
    *const_cast<char*>(now) = 0;
    DebugLog(<< "Injecting (isWireInject=" << isWireInject << "): " << start);
    TestSpecParseBuf->skipChar();
    if (isWireInject)
    {
        // sendToWire() is a helper function for TestTransport stuff.
        // sendToWire(start);
        SipMessage* message = TestSupport::makeMessage(start, true);
        assert(message);

        TestFSM::addMessage(client,message); //  does a client->mStateMacFifo.add(message);
        
    }
    else
    {
        SipMessage* message = TestSupport::makeMessage(start, false);
        assert(message);
        client->send(*message);
    }
}

Here is the call graph for this function:

void processTimeouts ( int  arg)

Definition at line 138 of file testTransactionFSM.cxx.

References resip::SipStack::buildFdSet(), client, clientFdSet, DebugLog, errorCount, fakeTxFifo, resip::RequestLine::getMethod(), resip::SipMessage::header(), resip::SipMessage::isRequest(), resip::SipMessage::isResponse(), resip::SipStack::process(), resip::SipStack::receive(), and WaitQueue.

Referenced by main().

{
    client->buildFdSet(clientFdSet);
    client->process(clientFdSet);

    if (WaitQueue.empty())
    {
        return;
    }
    SipMessage* message = 0;


#if defined(UGLY) || 1
    // This should:
    // 1. Take all messages from the "wire" and "tu" fifos
    // 2. For each message, look through the WaitQueue and see
    //    if the new message matches something we were waiting for.
    //    If yes, through away the queue entry, else raise a warning.
    // 3. When all messages from the "wire" and "tu" have been
    //    examined, see if anything in the queue has expired.
    //    If yes, warn, else just continue.

    // First go through the "wire" data
    while (fakeTxFifo.messageAvailable())
    {
        message = fakeTxFifo.getNext();
        for (list<WaitNode*>::iterator i = WaitQueue.begin();
             i != WaitQueue.end();
             /* don't increment */)
        {
            if ((*i)->mIsRequest && message->isRequest())
            {
                if ((*i)->mMethod == message->header(h_RequestLine).getMethod())
                {
                    // We matched something we expected.
                    delete message;
                    message = 0;
                    delete *i;
                    WaitQueue.erase(i++);
                    break;
                }
                else
                {
                    ++i;
                }
            }
            else if (!(*i)->mIsRequest && message->isResponse())
            {
                if ((*i)->mResponseCode ==
                    message->header(h_StatusLine).responseCode())
                {
                    // We matched something we expected.
                    delete message;
                    message = 0;
                    delete *i;
                    WaitQueue.erase(i++);
                    break;
                }
                else
                {
                    ++i;
                }
            }
            else
            {
                ++i;
            }
        }
        if (message)
        {
            DebugLog( << "Warning: unexpected message seen at the transport: " 
                      << message);
        }
        else
        {
            DebugLog( << "Success: expected message seen at the transport");
        }
        delete message;
    }

    // Now go through the data at the TU.
    while (0 != (message = client->receive()))
    {
        for (list<WaitNode*>::iterator i = WaitQueue.begin();
             i != WaitQueue.end();
             /* don't increment */)
        {
            if ((*i)->mIsRequest && message->isRequest())
            {
                if ((*i)->mMethod ==
                    message->header(h_RequestLine).getMethod())
                {
                    // We matched something we expected.
                    delete message;
                    message = 0;
                    delete *i;
                    WaitQueue.erase(i++);
                    break;
                }
                else
                {
                    ++i;
                }
            }
            else if (!(*i)->mIsRequest && message->isResponse())
            {
                if ((*i)->mResponseCode ==
                    message->header(h_StatusLine).responseCode())
                {
                    // We matched something we expected.
                    delete message;
                    message = 0;
                    delete *i;
                    WaitQueue.erase(i++);
                    break;
                }
                else
                {
                    ++i;
                }
            }
            else
            {
                ++i;
            }
        }
        if (message)
        {
            DebugLog( << "Warning: unexpected message seen at the TU: "
                      << *message);
            delete message;
        }
        else
        {
            DebugLog( << "Success: expected message seen at TU");
        }
    }

    // Print the list of expected events that have failed to happen withing
    // the specified timeout.
    for (list<WaitNode*>::iterator i = WaitQueue.begin();
         i != WaitQueue.end();
         /* don't increment */)
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        if ((*i)->mExpiry.tv_sec < tv.tv_sec ||
           ((*i)->mExpiry.tv_sec == tv.tv_sec &&
            (*i)->mExpiry.tv_usec < tv.tv_usec))
        {
            if ((*i)->mIsRequest)
            {
                DebugLog(<< "Error: timeout waiting for "
                         << getMethodName((*i)->mMethod) << " method");
                ++errorCount;
            }
            else
            {
                DebugLog(<< "Error: timeout waiting for "
                         << (*i)->mResponseCode << " status code");
                ++errorCount;
            }
            delete *i;
            WaitQueue.erase(i++);
        }
        else
        {
            /*
            cerr << "Still waiting for: ";
            if ((*i)->mIsRequest)
            {
                cerr << MethodNames[(*i)->mMethod] << " method" << endl;
            }
            else
            {
                cerr << (*i)->mResponseCode << " status code" << endl;
            }
            */
            ++i;
        }
    }
#endif

    signal(SIGALRM, processTimeouts);
}

Here is the call graph for this function:

int sendto ( int  s,
const void *  msg,
size_t  len,
int  flags,
const struct sockaddr *  to,
int  tolen 
)

Definition at line 117 of file testTransactionFSM.cxx.

References fakeTxFifo, len, and resip::TestSupport::makeMessage().

Referenced by main(), and resip::UdpTransport::processTxOne().

{
    fakeTxFifo.add(TestSupport::makeMessage(Data((const char *)msg, (int)len), true));
    return len;
}

Here is the call graph for this function:


Variable Documentation

Definition at line 104 of file testTransactionFSM.cxx.

int clientFd

Definition at line 107 of file testTransactionFSM.cxx.

Referenced by main().

Definition at line 105 of file testTransactionFSM.cxx.

Referenced by processTimeouts().

struct sockaddr_in clientSa

Definition at line 106 of file testTransactionFSM.cxx.

Referenced by main().

int errorCount = 0

Definition at line 109 of file testTransactionFSM.cxx.

Referenced by main(), and processTimeouts().

Definition at line 108 of file testTransactionFSM.cxx.

Referenced by processTimeouts(), and sendto().

char* ProgramName = 0

Definition at line 100 of file testTransactionFSM.cxx.

Referenced by exitusage(), and main().

char* TestSpecBuf = 0

Definition at line 101 of file testTransactionFSM.cxx.

Referenced by main().

Definition at line 103 of file testTransactionFSM.cxx.

Referenced by main(), processExpect(), and processTimeouts().