/[resiprocate]/main/resip/stack/test/testTransactionFSM.cxx
ViewVC logotype

Contents of /main/resip/stack/test/testTransactionFSM.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9080 - (show annotations) (download)
Thu Mar 17 04:58:27 2011 UTC (8 years, 7 months ago) by kwhite
File MIME type: text/plain
File size: 16752 byte(s)
update sipstack tests

* Adapt for SipStack internal changes
* For testStack, make 'event' the default thread model; this better simulates
  what applications do, and makes it a better unit test. The old behavior
  is available with --thread-type=common option.
* For testStack, make default interface for all platforms 127.0.0.1. Can get
  old behavior with --bind=''. Previously 127.0.0.1 was forced for
  Windows; but even Linux platforms don't always have working DNS.
* For testStack, add docs to better explain some options.
* Add new testStackStd.sh and invoke from runtests.sh to test
  more combinations as part of standard unit tests
1 // How this works:
2 //
3 // The first argument to this program needs to be a file that has a test in it.
4 //
5 // Clauses in this file can be: inject_wire, expect_wire, expect_tu,
6 // inject_tu, and delay. A keyword needs to be by itself on a line. They
7 // need to be in this form
8 //
9 // inject_wire (or inject_tu)
10 // {
11 // ...put a SIP message here that has a CRLF after each line...
12 // }
13 //
14 // expect_wire (or expect_tu)
15 // {
16 // status=100 (or method=REGISTER)
17 // timeout=32
18 // }
19 //
20 // delay
21 // {
22 // timeout=48
23 // }
24 //
25 // The inject_* clauses inject the SIP message described at the "wire"
26 // or the "tu" level. The expect_* clauses indicate that the test
27 // specification expects to read a certain response (distinguished only
28 // by its response code) or request (distinguished only by its method
29 // name). The expect_* clauses don't block but will queue the
30 // expectation for the duration of the timeout specified. When you
31 // really want to sit and wait for a message, insert a delay clause.
32 // This is used as a "barrier" at which you want all the above timeouts
33 // to happen. You'll want one at the end of your test, for example.
34
35 #ifndef __MINGW32__
36
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <time.h>
49 #include <unistd.h>
50
51 #include <list>
52 #include <iostream>
53 #include <fstream>
54 #include <string>
55
56 #include "rutil/Data.hxx"
57 #include "rutil/DataStream.hxx"
58 #include "rutil/Logger.hxx"
59 #include "rutil/Socket.hxx"
60
61 #include "resip/stack/test/TestSupport.hxx"
62 #include "resip/stack/MethodTypes.hxx"
63 #include "resip/stack/SipStack.hxx"
64 #include "resip/stack/SipMessage.hxx"
65 #include "resip/stack/Symbols.hxx"
66 #include "resip/stack/Transport.hxx"
67 #include "rutil/ParseBuffer.hxx"
68
69 using namespace resip;
70 using namespace std;
71
72 #define RESIPROCATE_SUBSYSTEM Subsystem::TEST
73 #define PORT 5060
74
75
76 // --------------------------------------------------
77
78 namespace resip
79 {
80 class TestFSM // this class is a friend of the SipStack and can directly access private stuff
81 {
82 public:
83 static void addMessage(SipStack* stack,SipMessage* message)
84 {
85 stack->mTransactionController->mStateMacFifo.add(message);
86 }
87 };
88 }
89
90 typedef struct {
91 struct timeval mExpiry;
92 bool mIsRequest;
93 bool mIsTransport;
94 int mResponseCode;
95 MethodTypes mMethod;
96 } WaitNode;
97
98 // --------------------------------------------------
99
100 char* ProgramName = 0;
101 char* TestSpecBuf = 0;
102 ParseBuffer* TestSpecParseBuf = 0;
103 list<WaitNode*> WaitQueue;
104 SipStack* client = 0;
105 FdSet clientFdSet;
106 struct sockaddr_in clientSa;
107 int clientFd;
108 Fifo<SipMessage> fakeTxFifo;
109 int errorCount = 0;
110
111 // --------------------------------------------------
112
113 // This is pure evil. We interpose our own version of sendto
114 // so that this gets called by the UDP transport instead of the libc
115 // version.
116 int
117 sendto(int s, const void *msg, size_t len, int flags,
118 const struct sockaddr *to, int tolen)
119 {
120 fakeTxFifo.add(TestSupport::makeMessage(Data((const char *)msg, (int)len), true));
121 return len;
122 }
123
124 // --------------------------------------------------
125
126
127 void
128 exitusage()
129 {
130 cerr << "Usage: " << ProgramName << " ";
131 cerr << "<testfileofsipmessagesandstuff>" << endl;
132 exit(1);
133 }
134
135 extern "C" { void processTimeouts(int arg); }
136
137 void
138 processTimeouts(int arg)
139 {
140 client->buildFdSet(clientFdSet);
141 client->process(clientFdSet);
142
143 if (WaitQueue.empty())
144 {
145 return;
146 }
147 SipMessage* message = 0;
148
149
150 #if defined(UGLY) || 1
151 // This should:
152 // 1. Take all messages from the "wire" and "tu" fifos
153 // 2. For each message, look through the WaitQueue and see
154 // if the new message matches something we were waiting for.
155 // If yes, through away the queue entry, else raise a warning.
156 // 3. When all messages from the "wire" and "tu" have been
157 // examined, see if anything in the queue has expired.
158 // If yes, warn, else just continue.
159
160 // First go through the "wire" data
161 while (fakeTxFifo.messageAvailable())
162 {
163 message = fakeTxFifo.getNext();
164 for (list<WaitNode*>::iterator i = WaitQueue.begin();
165 i != WaitQueue.end();
166 /* don't increment */)
167 {
168 if ((*i)->mIsRequest && message->isRequest())
169 {
170 if ((*i)->mMethod == message->header(h_RequestLine).getMethod())
171 {
172 // We matched something we expected.
173 delete message;
174 message = 0;
175 delete *i;
176 WaitQueue.erase(i++);
177 break;
178 }
179 else
180 {
181 ++i;
182 }
183 }
184 else if (!(*i)->mIsRequest && message->isResponse())
185 {
186 if ((*i)->mResponseCode ==
187 message->header(h_StatusLine).responseCode())
188 {
189 // We matched something we expected.
190 delete message;
191 message = 0;
192 delete *i;
193 WaitQueue.erase(i++);
194 break;
195 }
196 else
197 {
198 ++i;
199 }
200 }
201 else
202 {
203 ++i;
204 }
205 }
206 if (message)
207 {
208 DebugLog( << "Warning: unexpected message seen at the transport: "
209 << message);
210 }
211 else
212 {
213 DebugLog( << "Success: expected message seen at the transport");
214 }
215 delete message;
216 }
217
218 // Now go through the data at the TU.
219 while (0 != (message = client->receive()))
220 {
221 for (list<WaitNode*>::iterator i = WaitQueue.begin();
222 i != WaitQueue.end();
223 /* don't increment */)
224 {
225 if ((*i)->mIsRequest && message->isRequest())
226 {
227 if ((*i)->mMethod ==
228 message->header(h_RequestLine).getMethod())
229 {
230 // We matched something we expected.
231 delete message;
232 message = 0;
233 delete *i;
234 WaitQueue.erase(i++);
235 break;
236 }
237 else
238 {
239 ++i;
240 }
241 }
242 else if (!(*i)->mIsRequest && message->isResponse())
243 {
244 if ((*i)->mResponseCode ==
245 message->header(h_StatusLine).responseCode())
246 {
247 // We matched something we expected.
248 delete message;
249 message = 0;
250 delete *i;
251 WaitQueue.erase(i++);
252 break;
253 }
254 else
255 {
256 ++i;
257 }
258 }
259 else
260 {
261 ++i;
262 }
263 }
264 if (message)
265 {
266 DebugLog( << "Warning: unexpected message seen at the TU: "
267 << *message);
268 delete message;
269 }
270 else
271 {
272 DebugLog( << "Success: expected message seen at TU");
273 }
274 }
275
276 // Print the list of expected events that have failed to happen withing
277 // the specified timeout.
278 for (list<WaitNode*>::iterator i = WaitQueue.begin();
279 i != WaitQueue.end();
280 /* don't increment */)
281 {
282 struct timeval tv;
283 gettimeofday(&tv, NULL);
284 if ((*i)->mExpiry.tv_sec < tv.tv_sec ||
285 ((*i)->mExpiry.tv_sec == tv.tv_sec &&
286 (*i)->mExpiry.tv_usec < tv.tv_usec))
287 {
288 if ((*i)->mIsRequest)
289 {
290 DebugLog(<< "Error: timeout waiting for "
291 << getMethodName((*i)->mMethod) << " method");
292 ++errorCount;
293 }
294 else
295 {
296 DebugLog(<< "Error: timeout waiting for "
297 << (*i)->mResponseCode << " status code");
298 ++errorCount;
299 }
300 delete *i;
301 WaitQueue.erase(i++);
302 }
303 else
304 {
305 /*
306 cerr << "Still waiting for: ";
307 if ((*i)->mIsRequest)
308 {
309 cerr << MethodNames[(*i)->mMethod] << " method" << endl;
310 }
311 else
312 {
313 cerr << (*i)->mResponseCode << " status code" << endl;
314 }
315 */
316 ++i;
317 }
318 }
319 #endif
320
321 signal(SIGALRM, processTimeouts);
322 }
323
324 void
325 processInject()
326 {
327 const char* start = TestSpecParseBuf->position();
328 const char* now;
329 bool isWireInject = false;
330
331 if (!strncasecmp(start, "inject_wire", strlen("inject_wire")))
332 {
333 isWireInject = true;
334 }
335 else if (!strncasecmp(start, "inject_tu", strlen("inject_tu")))
336 {
337 isWireInject = false;
338 }
339 else
340 {
341 DebugLog(<< "Warning: error parsing test specification.");
342 TestSpecParseBuf->skipToChar('}');
343 TestSpecParseBuf->skipChar();
344 return;
345 }
346
347 TestSpecParseBuf->skipToChar('{');
348 TestSpecParseBuf->skipChar();
349 TestSpecParseBuf->skipWhitespace();
350
351 start = TestSpecParseBuf->position();
352 now = TestSpecParseBuf->skipToChar('}');
353 *const_cast<char*>(now) = 0;
354 DebugLog(<< "Injecting (isWireInject=" << isWireInject << "): " << start);
355 TestSpecParseBuf->skipChar();
356 if (isWireInject)
357 {
358 // sendToWire() is a helper function for TestTransport stuff.
359 // sendToWire(start);
360 SipMessage* message = TestSupport::makeMessage(start, true);
361 assert(message);
362
363 TestFSM::addMessage(client,message); // does a client->mStateMacFifo.add(message);
364
365 }
366 else
367 {
368 SipMessage* message = TestSupport::makeMessage(start, false);
369 assert(message);
370 client->send(*message);
371 }
372 }
373
374 void
375 processExpect()
376 {
377 const char* start = TestSpecParseBuf->position();
378 const char* now;
379 unsigned int expireTime = 1;
380 WaitNode* thisWait = new WaitNode;
381 assert(thisWait);
382 thisWait->mResponseCode = 0;
383
384 if (!strncasecmp(start, "expect_wire", strlen("expect_wire")))
385 {
386 thisWait->mIsTransport = true;
387 }
388 else if (!strncasecmp(start, "expect_tu", strlen("expect_tu")))
389 {
390 thisWait->mIsTransport = false;
391 }
392 else
393 {
394 DebugLog(<< "Warning: error parsing test specification");
395 TestSpecParseBuf->skipToChar('}');
396 TestSpecParseBuf->skipChar();
397 delete thisWait;
398 return;
399 }
400
401 TestSpecParseBuf->skipToChar('{');
402 TestSpecParseBuf->skipChar();
403 TestSpecParseBuf->skipWhitespace();
404 start = TestSpecParseBuf->position();
405
406 // We will want to get two of these in an expect_ clause.
407 for (int i = 0; i < 2; i++)
408 {
409 TestSpecParseBuf->skipToChar('=');
410 TestSpecParseBuf->skipChar();
411 TestSpecParseBuf->skipWhitespace();
412 if (!strncasecmp(start, "method", strlen("method")))
413 {
414 start = TestSpecParseBuf->position();
415 now = TestSpecParseBuf->skipToOneOf(ParseBuffer::Whitespace);
416 thisWait->mIsRequest = true;
417 thisWait->mMethod = getMethodType(start, now-start);
418 }
419 else if (!strncasecmp(start, "status", strlen("status")))
420 {
421 TestSpecParseBuf->skipToOneOf("0123456789");
422 thisWait->mIsRequest = false;
423 thisWait->mResponseCode = TestSpecParseBuf->integer();
424 }
425 else if (!strncasecmp(start, "timeout", strlen("timeout")))
426 {
427 TestSpecParseBuf->skipToOneOf("0123456789");
428 expireTime = TestSpecParseBuf->integer();
429 }
430 else
431 {
432 DebugLog(<< "Warning: error parsing test specification");
433 TestSpecParseBuf->skipToChar('}');
434 TestSpecParseBuf->skipChar();
435 delete thisWait;
436 return;
437 }
438 TestSpecParseBuf->skipWhitespace();
439 start = TestSpecParseBuf->position();
440 }
441
442 assert(thisWait);
443
444 gettimeofday(&thisWait->mExpiry, NULL);
445 thisWait->mExpiry.tv_sec += expireTime / 1000;
446 thisWait->mExpiry.tv_usec += (expireTime % 1000) * 1000;
447 WaitQueue.push_front(thisWait);
448
449 TestSpecParseBuf->skipToChar('}');
450 TestSpecParseBuf->skipChar();
451
452 /*
453 cerr << "-> Expecting " << endl;
454 cerr << " mIsTransport = " << (thisWait->mIsTransport == true) << endl;
455 cerr << " mIsRequest = " << (thisWait->mIsRequest == true) << endl;
456 cerr << " mResponseCode = " << (thisWait->mResponseCode) << endl;
457 cerr << " mExpiry = " << (thisWait->mExpiry.tv_sec) << endl;
458 */
459 }
460
461 void
462 processDelays()
463 {
464 TestSpecParseBuf->skipToChar('{');
465 TestSpecParseBuf->skipChar();
466 TestSpecParseBuf->skipWhitespace();
467
468 TestSpecParseBuf->skipToOneOf("0123456789");
469 int sleepLength = TestSpecParseBuf->integer();
470
471 DebugLog( << "Pausing for " << sleepLength << " ms");
472
473 // We sleep this way to avoid conflict with SIGALRM from alarm().
474 struct timespec ts, remainder;
475 ts.tv_sec = sleepLength / 1000;
476 ts.tv_nsec = (sleepLength % 1000) * 1000000;
477 while (nanosleep(&ts, &remainder) < 0)
478 {
479 ts = remainder;
480 }
481
482 TestSpecParseBuf->skipToChar('}');
483 TestSpecParseBuf->skipChar();
484 }
485
486 bool
487 processClause()
488 {
489 TestSpecParseBuf->skipWhitespace();
490
491 // Look for 'i'/'e'/'d' for inject... or expect... or delay respectively.
492 const char* now = TestSpecParseBuf->skipToOneOf("ied#");
493 switch (*now)
494 {
495 case 'i':
496 processInject();
497 break;
498 case 'e':
499 processExpect();
500 break;
501 case 'd':
502 processDelays();
503 break;
504 case '#':
505 TestSpecParseBuf->skipToChar('\n');
506 TestSpecParseBuf->skipChar();
507 break;
508 default:
509 DebugLog(<< "Warning: error parsing test specification");
510 TestSpecParseBuf->skipToChar('}');
511 TestSpecParseBuf->skipChar();
512 }
513
514 TestSpecParseBuf->skipWhitespace();
515 return !TestSpecParseBuf->eof();
516 }
517
518 int
519 main(int argc, char *argv[])
520 {
521 ProgramName = argv[0];
522
523 if (NULL == argv[1]) {
524 exitusage();
525 }
526
527 struct stat buf;
528 if (stat(argv[1], &buf) < 0)
529 {
530 cerr << "Error: " << strerror(errno) << endl;
531 exitusage();
532 }
533
534 ifstream testSpec;
535 testSpec.open(argv[1], ifstream::in);
536 if (!testSpec.is_open())
537 {
538 cerr << "Error: could not open "<< argv[1] << endl;
539 exitusage();
540 }
541
542 TestSpecBuf = new char[buf.st_size+1];
543 assert(TestSpecBuf);
544 testSpec.read(TestSpecBuf, buf.st_size);
545 TestSpecParseBuf = new ParseBuffer(TestSpecBuf, buf.st_size);
546 assert(TestSpecParseBuf);
547
548 int clientFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
549 clientSa.sin_family = PF_INET;
550 clientSa.sin_addr.s_addr = inet_addr("127.0.0.1");
551 clientSa.sin_port = htons(PORT);
552 int clientFdFlags = fcntl(clientFd, F_GETFL, 0);
553 fcntl(clientFd, F_SETFL, clientFdFlags | O_NONBLOCK);
554
555 client = new SipStack();
556 assert(client);
557 client->addTransport(UDP, PORT);
558
559 signal(SIGALRM, processTimeouts);
560
561 // Cause a signal to be generated with setitimer for its resolution
562 struct itimerval timer;
563 timer.it_value.tv_sec = 0;
564 timer.it_value.tv_usec = 100000; // 100 ms resolution
565 timer.it_interval.tv_sec = 0;
566 timer.it_interval.tv_usec = 100000; // 100 ms resolution
567 setitimer(ITIMER_REAL, &timer, NULL);
568
569 while (processClause())
570 {
571 }
572
573 // Catch any remaining events.
574 processTimeouts(0);
575 if (!WaitQueue.empty())
576 {
577 DebugLog( << "Warning: ending with expect clauses outstanding");
578 ++errorCount;
579 }
580
581 if (errorCount > 0)
582 {
583 cerr << "FAIL" << endl;
584 }
585 else
586 {
587 cerr << "PASS" << endl;
588 }
589
590 return errorCount;
591 }
592 #else
593 #include <iostream>
594 int
595 main(int argc, char *argv[])
596 {
597 std::cout << argv[0] << ": Sorry. This test driver hasn't been ported "
598 "to Windows yet." << std::endl;
599 return -1;
600 }
601 #endif
602 /* ====================================================================
603 * The Vovida Software License, Version 1.0
604 *
605 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
606 *
607 * Redistribution and use in source and binary forms, with or without
608 * modification, are permitted provided that the following conditions
609 * are met:
610 *
611 * 1. Redistributions of source code must retain the above copyright
612 * notice, this list of conditions and the following disclaimer.
613 *
614 * 2. Redistributions in binary form must reproduce the above copyright
615 * notice, this list of conditions and the following disclaimer in
616 * the documentation and/or other materials provided with the
617 * distribution.
618 *
619 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
620 * and "Vovida Open Communication Application Library (VOCAL)" must
621 * not be used to endorse or promote products derived from this
622 * software without prior written permission. For written
623 * permission, please contact vocal@vovida.org.
624 *
625 * 4. Products derived from this software may not be called "VOCAL", nor
626 * may "VOCAL" appear in their name, without prior written
627 * permission of Vovida Networks, Inc.
628 *
629 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
630 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
631 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
632 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
633 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
634 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
635 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
636 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
637 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
638 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
639 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
640 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
641 * DAMAGE.
642 *
643 * ====================================================================
644 *
645 * This software consists of voluntary contributions made by Vovida
646 * Networks, Inc. and many individuals on behalf of Vovida Networks,
647 * Inc. For more information on Vovida Networks, Inc., please see
648 * <http://www.vovida.org/>.
649 *
650 */

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27