reSIProcate/stack  9694
testTransactionFSM.cxx
Go to the documentation of this file.
00001 // How this works:
00002 //
00003 // The first argument to this program needs to be a file that has a test in it.
00004 //
00005 // Clauses in this file can be: inject_wire, expect_wire, expect_tu,
00006 // inject_tu, and delay.  A keyword needs to be by itself on a line.  They
00007 // need to be in this form
00008 //
00009 // inject_wire (or inject_tu)
00010 // {
00011 // ...put a SIP message here that has a CRLF after each line...
00012 // }
00013 //
00014 // expect_wire (or expect_tu)
00015 // {
00016 // status=100 (or method=REGISTER)
00017 // timeout=32
00018 // }
00019 //
00020 // delay
00021 // {
00022 // timeout=48
00023 // }
00024 //
00025 // The inject_* clauses inject the SIP message described at the "wire"
00026 // or the "tu" level.  The expect_* clauses indicate that the test
00027 // specification expects to read a certain response (distinguished only
00028 // by its response code) or request (distinguished only by its method
00029 // name).  The expect_* clauses don't block but will queue the
00030 // expectation for the duration of the timeout specified.  When you
00031 // really want to sit and wait for a message, insert a delay clause.
00032 // This is used as a "barrier" at which you want all the above timeouts
00033 // to happen.  You'll want one at the end of your test, for example.
00034 
00035 #ifndef __MINGW32__
00036 
00037 #include <sys/time.h>
00038 #include <sys/types.h>
00039 #include <sys/socket.h>
00040 #include <sys/stat.h>
00041 #include <netinet/in.h>
00042 #include <arpa/inet.h>
00043 #include <errno.h>
00044 #include <fcntl.h>
00045 #include <signal.h>
00046 #include <stdlib.h>
00047 #include <string.h>
00048 #include <time.h>
00049 #include <unistd.h>
00050 
00051 #include <list>
00052 #include <iostream>
00053 #include <fstream>
00054 #include <string>
00055 
00056 #include "rutil/Data.hxx"
00057 #include "rutil/DataStream.hxx"
00058 #include "rutil/Logger.hxx"
00059 #include "rutil/Socket.hxx"
00060 
00061 #include "resip/stack/test/TestSupport.hxx"
00062 #include "resip/stack/MethodTypes.hxx"
00063 #include "resip/stack/SipStack.hxx"
00064 #include "resip/stack/SipMessage.hxx"
00065 #include "resip/stack/Symbols.hxx"
00066 #include "resip/stack/Transport.hxx"
00067 #include "rutil/ParseBuffer.hxx"
00068 
00069 using namespace resip;
00070 using namespace std;
00071 
00072 #define RESIPROCATE_SUBSYSTEM Subsystem::TEST
00073 #define PORT 5060
00074 
00075 
00076 // --------------------------------------------------
00077 
00078 namespace resip 
00079 {
00080 class TestFSM  // this class is a friend of the SipStack and can directly access private stuff
00081 {
00082    public:
00083       static void addMessage(SipStack* stack,SipMessage* message)
00084       {
00085          stack->mTransactionController->mStateMacFifo.add(message);
00086       }
00087 };
00088 }
00089 
00090 typedef struct {
00091       struct timeval mExpiry;
00092       bool mIsRequest;
00093       bool mIsTransport;
00094       int mResponseCode;
00095       MethodTypes mMethod;
00096 } WaitNode;
00097 
00098 // --------------------------------------------------
00099 
00100 char* ProgramName = 0;
00101 char* TestSpecBuf = 0;
00102 ParseBuffer* TestSpecParseBuf = 0;
00103 list<WaitNode*> WaitQueue;
00104 SipStack* client = 0;
00105 FdSet clientFdSet;
00106 struct sockaddr_in clientSa;
00107 int clientFd;
00108 Fifo<SipMessage> fakeTxFifo;
00109 int errorCount = 0;
00110 
00111 // --------------------------------------------------
00112 
00113 // This is pure evil.  We interpose our own version of sendto
00114 // so that this gets called by the UDP transport instead of the libc
00115 // version.
00116 int
00117 sendto(int s, const void *msg, size_t len, int flags,
00118        const struct sockaddr *to, int tolen)
00119 {
00120     fakeTxFifo.add(TestSupport::makeMessage(Data((const char *)msg, (int)len), true));
00121     return len;
00122 }
00123 
00124 // --------------------------------------------------
00125 
00126 
00127 void
00128 exitusage()
00129 {
00130         cerr << "Usage: " << ProgramName << " ";
00131         cerr << "<testfileofsipmessagesandstuff>" << endl;
00132         exit(1);
00133 }
00134 
00135 extern "C" { void processTimeouts(int arg); }
00136 
00137 void
00138 processTimeouts(int arg)
00139 {
00140     client->buildFdSet(clientFdSet);
00141     client->process(clientFdSet);
00142 
00143     if (WaitQueue.empty())
00144     {
00145         return;
00146     }
00147     SipMessage* message = 0;
00148 
00149 
00150 #if defined(UGLY) || 1
00151     // This should:
00152     // 1. Take all messages from the "wire" and "tu" fifos
00153     // 2. For each message, look through the WaitQueue and see
00154     //    if the new message matches something we were waiting for.
00155     //    If yes, through away the queue entry, else raise a warning.
00156     // 3. When all messages from the "wire" and "tu" have been
00157     //    examined, see if anything in the queue has expired.
00158     //    If yes, warn, else just continue.
00159 
00160     // First go through the "wire" data
00161     while (fakeTxFifo.messageAvailable())
00162     {
00163         message = fakeTxFifo.getNext();
00164         for (list<WaitNode*>::iterator i = WaitQueue.begin();
00165              i != WaitQueue.end();
00166              /* don't increment */)
00167         {
00168             if ((*i)->mIsRequest && message->isRequest())
00169             {
00170                 if ((*i)->mMethod == message->header(h_RequestLine).getMethod())
00171                 {
00172                     // We matched something we expected.
00173                     delete message;
00174                     message = 0;
00175                     delete *i;
00176                     WaitQueue.erase(i++);
00177                     break;
00178                 }
00179                 else
00180                 {
00181                     ++i;
00182                 }
00183             }
00184             else if (!(*i)->mIsRequest && message->isResponse())
00185             {
00186                 if ((*i)->mResponseCode ==
00187                     message->header(h_StatusLine).responseCode())
00188                 {
00189                     // We matched something we expected.
00190                     delete message;
00191                     message = 0;
00192                     delete *i;
00193                     WaitQueue.erase(i++);
00194                     break;
00195                 }
00196                 else
00197                 {
00198                     ++i;
00199                 }
00200             }
00201             else
00202             {
00203                 ++i;
00204             }
00205         }
00206         if (message)
00207         {
00208             DebugLog( << "Warning: unexpected message seen at the transport: " 
00209                       << message);
00210         }
00211         else
00212         {
00213             DebugLog( << "Success: expected message seen at the transport");
00214         }
00215         delete message;
00216     }
00217 
00218     // Now go through the data at the TU.
00219     while (0 != (message = client->receive()))
00220     {
00221         for (list<WaitNode*>::iterator i = WaitQueue.begin();
00222              i != WaitQueue.end();
00223              /* don't increment */)
00224         {
00225             if ((*i)->mIsRequest && message->isRequest())
00226             {
00227                 if ((*i)->mMethod ==
00228                     message->header(h_RequestLine).getMethod())
00229                 {
00230                     // We matched something we expected.
00231                     delete message;
00232                     message = 0;
00233                     delete *i;
00234                     WaitQueue.erase(i++);
00235                     break;
00236                 }
00237                 else
00238                 {
00239                     ++i;
00240                 }
00241             }
00242             else if (!(*i)->mIsRequest && message->isResponse())
00243             {
00244                 if ((*i)->mResponseCode ==
00245                     message->header(h_StatusLine).responseCode())
00246                 {
00247                     // We matched something we expected.
00248                     delete message;
00249                     message = 0;
00250                     delete *i;
00251                     WaitQueue.erase(i++);
00252                     break;
00253                 }
00254                 else
00255                 {
00256                     ++i;
00257                 }
00258             }
00259             else
00260             {
00261                 ++i;
00262             }
00263         }
00264         if (message)
00265         {
00266             DebugLog( << "Warning: unexpected message seen at the TU: "
00267                       << *message);
00268             delete message;
00269         }
00270         else
00271         {
00272             DebugLog( << "Success: expected message seen at TU");
00273         }
00274     }
00275 
00276     // Print the list of expected events that have failed to happen withing
00277     // the specified timeout.
00278     for (list<WaitNode*>::iterator i = WaitQueue.begin();
00279          i != WaitQueue.end();
00280          /* don't increment */)
00281     {
00282         struct timeval tv;
00283         gettimeofday(&tv, NULL);
00284         if ((*i)->mExpiry.tv_sec < tv.tv_sec ||
00285            ((*i)->mExpiry.tv_sec == tv.tv_sec &&
00286             (*i)->mExpiry.tv_usec < tv.tv_usec))
00287         {
00288             if ((*i)->mIsRequest)
00289             {
00290                 DebugLog(<< "Error: timeout waiting for "
00291                          << getMethodName((*i)->mMethod) << " method");
00292                 ++errorCount;
00293             }
00294             else
00295             {
00296                 DebugLog(<< "Error: timeout waiting for "
00297                          << (*i)->mResponseCode << " status code");
00298                 ++errorCount;
00299             }
00300             delete *i;
00301             WaitQueue.erase(i++);
00302         }
00303         else
00304         {
00305             /*
00306             cerr << "Still waiting for: ";
00307             if ((*i)->mIsRequest)
00308             {
00309                 cerr << MethodNames[(*i)->mMethod] << " method" << endl;
00310             }
00311             else
00312             {
00313                 cerr << (*i)->mResponseCode << " status code" << endl;
00314             }
00315             */
00316             ++i;
00317         }
00318     }
00319 #endif
00320 
00321     signal(SIGALRM, processTimeouts);
00322 }
00323 
00324 void
00325 processInject()
00326 {
00327     const char* start = TestSpecParseBuf->position();
00328     const char* now;
00329     bool isWireInject = false;
00330 
00331     if (!strncasecmp(start, "inject_wire", strlen("inject_wire")))
00332     {
00333         isWireInject = true;
00334     }
00335     else if (!strncasecmp(start, "inject_tu", strlen("inject_tu")))
00336     {
00337         isWireInject = false;
00338     }
00339     else
00340     {
00341         DebugLog(<< "Warning: error parsing test specification.");
00342         TestSpecParseBuf->skipToChar('}');
00343         TestSpecParseBuf->skipChar();
00344         return;
00345     }
00346 
00347     TestSpecParseBuf->skipToChar('{');
00348     TestSpecParseBuf->skipChar();
00349     TestSpecParseBuf->skipWhitespace();
00350 
00351     start = TestSpecParseBuf->position();
00352     now = TestSpecParseBuf->skipToChar('}');
00353     *const_cast<char*>(now) = 0;
00354     DebugLog(<< "Injecting (isWireInject=" << isWireInject << "): " << start);
00355     TestSpecParseBuf->skipChar();
00356     if (isWireInject)
00357     {
00358         // sendToWire() is a helper function for TestTransport stuff.
00359         // sendToWire(start);
00360         SipMessage* message = TestSupport::makeMessage(start, true);
00361         assert(message);
00362 
00363         TestFSM::addMessage(client,message); //  does a client->mStateMacFifo.add(message);
00364         
00365     }
00366     else
00367     {
00368         SipMessage* message = TestSupport::makeMessage(start, false);
00369         assert(message);
00370         client->send(*message);
00371     }
00372 }
00373 
00374 void
00375 processExpect()
00376 {
00377     const char* start = TestSpecParseBuf->position();
00378     const char* now;
00379     unsigned int expireTime = 1;
00380     WaitNode* thisWait = new WaitNode;
00381     assert(thisWait);
00382     thisWait->mResponseCode = 0;
00383 
00384     if (!strncasecmp(start, "expect_wire", strlen("expect_wire")))
00385     {
00386         thisWait->mIsTransport = true;
00387     }
00388     else if (!strncasecmp(start, "expect_tu", strlen("expect_tu")))
00389     {
00390         thisWait->mIsTransport = false;
00391     }
00392     else
00393     {
00394         DebugLog(<< "Warning: error parsing test specification"); 
00395         TestSpecParseBuf->skipToChar('}');
00396         TestSpecParseBuf->skipChar();
00397         delete thisWait;
00398         return;
00399     }
00400 
00401     TestSpecParseBuf->skipToChar('{');
00402     TestSpecParseBuf->skipChar();
00403     TestSpecParseBuf->skipWhitespace();
00404     start = TestSpecParseBuf->position();
00405 
00406     // We will want to get two of these in an expect_ clause.
00407     for (int i = 0; i < 2; i++)
00408     {
00409         TestSpecParseBuf->skipToChar('=');
00410         TestSpecParseBuf->skipChar();
00411         TestSpecParseBuf->skipWhitespace();
00412         if (!strncasecmp(start, "method", strlen("method")))
00413         {
00414             start = TestSpecParseBuf->position();
00415             now = TestSpecParseBuf->skipToOneOf(ParseBuffer::Whitespace);
00416             thisWait->mIsRequest = true;
00417             thisWait->mMethod = getMethodType(start, now-start);
00418         }
00419         else if (!strncasecmp(start, "status", strlen("status")))
00420         {
00421             TestSpecParseBuf->skipToOneOf("0123456789");
00422             thisWait->mIsRequest = false;
00423             thisWait->mResponseCode = TestSpecParseBuf->integer();
00424         }
00425         else if (!strncasecmp(start, "timeout", strlen("timeout")))
00426         {
00427             TestSpecParseBuf->skipToOneOf("0123456789");
00428             expireTime = TestSpecParseBuf->integer();
00429         }
00430         else
00431         {
00432             DebugLog(<< "Warning: error parsing test specification"); 
00433             TestSpecParseBuf->skipToChar('}');
00434             TestSpecParseBuf->skipChar();
00435             delete thisWait;
00436             return;
00437         }
00438         TestSpecParseBuf->skipWhitespace();
00439         start = TestSpecParseBuf->position();
00440     }
00441 
00442     assert(thisWait);
00443 
00444     gettimeofday(&thisWait->mExpiry, NULL);
00445     thisWait->mExpiry.tv_sec += expireTime / 1000;
00446     thisWait->mExpiry.tv_usec += (expireTime % 1000) * 1000;
00447     WaitQueue.push_front(thisWait);
00448 
00449     TestSpecParseBuf->skipToChar('}');
00450     TestSpecParseBuf->skipChar();
00451 
00452     /*
00453     cerr << "-> Expecting " << endl;
00454     cerr << "   mIsTransport = " << (thisWait->mIsTransport == true) << endl;
00455     cerr << "   mIsRequest = " << (thisWait->mIsRequest == true) << endl;
00456     cerr << "   mResponseCode = " << (thisWait->mResponseCode) << endl;
00457     cerr << "   mExpiry = " << (thisWait->mExpiry.tv_sec) << endl;
00458     */
00459 }
00460 
00461 void
00462 processDelays()
00463 {
00464     TestSpecParseBuf->skipToChar('{');
00465     TestSpecParseBuf->skipChar();
00466     TestSpecParseBuf->skipWhitespace();
00467 
00468     TestSpecParseBuf->skipToOneOf("0123456789");
00469     int sleepLength = TestSpecParseBuf->integer();
00470 
00471     DebugLog( << "Pausing for " << sleepLength << " ms");
00472 
00473     // We sleep this way to avoid conflict with SIGALRM from alarm().
00474     struct timespec ts, remainder;
00475     ts.tv_sec = sleepLength / 1000;
00476     ts.tv_nsec = (sleepLength % 1000) * 1000000;
00477     while (nanosleep(&ts, &remainder) < 0)
00478     {
00479         ts = remainder;
00480     }
00481 
00482     TestSpecParseBuf->skipToChar('}');
00483     TestSpecParseBuf->skipChar();
00484 }
00485 
00486 bool
00487 processClause()
00488 {
00489     TestSpecParseBuf->skipWhitespace();
00490 
00491     // Look for 'i'/'e'/'d' for inject... or expect... or delay respectively.
00492     const char* now = TestSpecParseBuf->skipToOneOf("ied#");
00493     switch (*now)
00494     {
00495     case 'i':
00496         processInject();
00497         break;
00498     case 'e':
00499         processExpect();
00500         break;
00501     case 'd':
00502         processDelays();
00503         break;
00504     case '#':
00505         TestSpecParseBuf->skipToChar('\n');
00506         TestSpecParseBuf->skipChar();
00507         break;
00508     default:
00509         DebugLog(<< "Warning: error parsing test specification");
00510         TestSpecParseBuf->skipToChar('}');
00511         TestSpecParseBuf->skipChar();
00512     }
00513 
00514     TestSpecParseBuf->skipWhitespace();
00515     return !TestSpecParseBuf->eof();
00516 }
00517 
00518 int
00519 main(int argc, char *argv[])
00520 {
00521     ProgramName = argv[0];
00522 
00523     if (NULL == argv[1]) {
00524         exitusage();
00525     }
00526 
00527     struct stat buf;
00528     if (stat(argv[1], &buf) < 0)
00529     {
00530         cerr << "Error: " << strerror(errno) << endl;
00531         exitusage();
00532     }
00533 
00534     ifstream testSpec;
00535     testSpec.open(argv[1], ifstream::in);
00536     if (!testSpec.is_open())
00537     {
00538         cerr << "Error: could not open "<< argv[1] << endl;
00539         exitusage();
00540     }
00541 
00542     TestSpecBuf = new char[buf.st_size+1];
00543     assert(TestSpecBuf);
00544     testSpec.read(TestSpecBuf, buf.st_size);
00545     TestSpecParseBuf = new ParseBuffer(TestSpecBuf, buf.st_size);
00546     assert(TestSpecParseBuf);
00547 
00548     int clientFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
00549     clientSa.sin_family = PF_INET;
00550     clientSa.sin_addr.s_addr = inet_addr("127.0.0.1");
00551     clientSa.sin_port = htons(PORT);
00552     int clientFdFlags = fcntl(clientFd, F_GETFL, 0);
00553     fcntl(clientFd, F_SETFL, clientFdFlags | O_NONBLOCK);
00554 
00555     client = new SipStack();
00556     assert(client);
00557     client->addTransport(UDP, PORT);
00558 
00559     signal(SIGALRM, processTimeouts);
00560 
00561     // Cause a signal to be generated with setitimer for its resolution
00562     struct itimerval timer;
00563     timer.it_value.tv_sec = 0;
00564     timer.it_value.tv_usec = 100000; // 100 ms resolution
00565     timer.it_interval.tv_sec = 0;
00566     timer.it_interval.tv_usec = 100000; // 100 ms resolution
00567     setitimer(ITIMER_REAL, &timer, NULL);
00568 
00569     while (processClause())
00570     {
00571     }
00572 
00573     // Catch any remaining events.
00574     processTimeouts(0);
00575     if (!WaitQueue.empty())
00576     {
00577         DebugLog( << "Warning: ending with expect clauses outstanding"); 
00578         ++errorCount;
00579     }
00580 
00581     if (errorCount > 0)
00582     {
00583         cerr << "FAIL" << endl;
00584     }
00585     else
00586     {
00587         cerr << "PASS" << endl;
00588     }
00589 
00590     return errorCount;
00591 }
00592 #else
00593 #include <iostream>
00594 int
00595 main(int argc, char *argv[])
00596 {
00597   std::cout << argv[0] << ": Sorry. This test driver hasn't been ported "
00598                           "to Windows yet." << std::endl;
00599   return -1;
00600 }
00601 #endif
00602 /* ====================================================================
00603  * The Vovida Software License, Version 1.0 
00604  * 
00605  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00606  * 
00607  * Redistribution and use in source and binary forms, with or without
00608  * modification, are permitted provided that the following conditions
00609  * are met:
00610  * 
00611  * 1. Redistributions of source code must retain the above copyright
00612  *    notice, this list of conditions and the following disclaimer.
00613  * 
00614  * 2. Redistributions in binary form must reproduce the above copyright
00615  *    notice, this list of conditions and the following disclaimer in
00616  *    the documentation and/or other materials provided with the
00617  *    distribution.
00618  * 
00619  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00620  *    and "Vovida Open Communication Application Library (VOCAL)" must
00621  *    not be used to endorse or promote products derived from this
00622  *    software without prior written permission. For written
00623  *    permission, please contact vocal@vovida.org.
00624  *
00625  * 4. Products derived from this software may not be called "VOCAL", nor
00626  *    may "VOCAL" appear in their name, without prior written
00627  *    permission of Vovida Networks, Inc.
00628  * 
00629  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00630  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00631  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00632  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00633  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00634  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00635  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00636  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00637  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00638  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00639  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00640  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00641  * DAMAGE.
00642  * 
00643  * ====================================================================
00644  * 
00645  * This software consists of voluntary contributions made by Vovida
00646  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00647  * Inc.  For more information on Vovida Networks, Inc., please see
00648  * <http://www.vovida.org/>.
00649  *
00650  */