blob: ff474a9a26e7ed98ac9790c857e46747c0b950d7 [file] [log] [blame]
Benny Prijono4ea0bf12006-02-02 19:16:07 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms oa the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 oa the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty oa
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy oa the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia/sdp.h>
20#include <pjmedia/sdp_neg.h>
21#include "test.h"
22
23
24#define THIS_FILE "sdp_neg_test.c"
25#define START_TEST 0
26
27enum session_type
28{
29 REMOTE_OFFER,
30 LOCAL_OFFER,
31};
32
33struct offer_answer
34{
35 enum session_type type; /* LOCAL_OFFER: REMOTE_OFFER: */
36 char *sdp1; /* local offer remote offer */
37 char *sdp2; /* remote answer initial local */
38 char *sdp3; /* local active media local answer */
39};
40
41struct test
42{
43 const char *title;
44 unsigned offer_answer_count;
45 struct offer_answer offer_answer[4];
46} test[] =
47{
48 /* test 0: */
49 {
50 /*********************************************************************
51 * RFC 3264 examples, section 10.1 (Alice's view)
52 *
53 * Difference from the example:
54 * - Bob's port number of the third media stream in the first answer
55 * is changed (make it different than Alice's)
56 * - in the second offer/answer exchange, Alice can't accept the
57 * additional line since she didn't specify the capability
58 * in the initial negotiator creation.
59 */
60
61 "RFC 3264 example 10.1 (Alice's view)",
62 2,
63 {
64 {
65 LOCAL_OFFER,
66 /* Alice sends offer: */
67 "v=0\r\n"
68 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
69 "s= \r\n"
70 "c=IN IP4 host.anywhere.com\r\n"
71 "t=0 0\r\n"
72 "m=audio 49170 RTP/AVP 0\r\n"
73 "a=rtpmap:0 PCMU/8000\r\n"
74 "m=video 51372 RTP/AVP 31\r\n"
75 "a=rtpmap:31 H261/90000\r\n"
76 "m=video 53000 RTP/AVP 32\r\n"
77 "a=rtpmap:32 MPV/90000\r\n",
78 /* Received Bob's answer: */
79 "v=0\r\n"
80 "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
81 "s= \r\n"
82 "c=IN IP4 host.example.com\r\n"
83 "t=0 0\r\n"
84 "m=audio 49920 RTP/AVP 0\r\n"
85 "a=rtpmap:0 PCMU/8000\r\n"
86 "m=video 0 RTP/AVP 31\r\n"
87 "m=video 53002 RTP/AVP 32\r\n"
88 "a=rtpmap:32 MPV/90000\r\n",
89 /* Alice's SDP now: */
90 "v=0\r\n"
91 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
92 "s= \r\n"
93 "c=IN IP4 host.anywhere.com\r\n"
94 "t=0 0\r\n"
95 "m=audio 49170 RTP/AVP 0\r\n"
96 "a=rtpmap:0 PCMU/8000\r\n"
97 "m=video 0 RTP/AVP 31\r\n"
98 "a=rtpmap:31 H261/90000\r\n"
99 "m=video 53000 RTP/AVP 32\r\n"
100 "a=rtpmap:32 MPV/90000\r\n"
101 },
102 {
103 REMOTE_OFFER,
104 /* Bob wants to change his local SDP
105 * (change local port for the first stream and add new stream)
106 * Received SDP from Bob:
107 */
108 "v=0\r\n"
109 "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
110 "s=-\r\n"
111 "c=IN IP4 host.example.com\r\n"
112 "t=0 0\r\n"
113 "m=audio 65422 RTP/AVP 0\r\n"
114 "a=rtpmap:0 PCMU/8000\r\n"
115 "m=video 0 RTP/AVP 31\r\n"
116 "m=video 53002 RTP/AVP 32\r\n"
117 "a=rtpmap:32 MPV/90000\r\n"
118 "m=audio 51434 RTP/AVP 110\r\n"
119 "a=rtpmap:110 telephone-events/8000\r\n"
120 "a=recvonly\r\n",
121 NULL,
122 /* Alice's SDP now */
123 "v=0\r\n"
124 "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
125 "s= \r\n"
126 "c=IN IP4 host.anywhere.com\r\n"
127 "t=0 0\r\n"
128 "m=audio 49170 RTP/AVP 0\r\n"
129 "a=rtpmap:0 PCMU/8000\r\n"
130 "m=video 0 RTP/AVP 31\r\n"
131 "a=rtpmap:31 H261/90000\r\n" /* <-- this is not necessary */
132 "m=video 53000 RTP/AVP 32\r\n"
133 "a=rtpmap:32 MPV/90000\r\n"
134 "m=audio 0 RTP/AVP 110\r\n"
135 "a=rtpmap:110 telephone-events/8000\r\n"
136 "a=sendonly\r\n"
137 }
138 }
139 },
140
141 /* test 1: */
142 {
143 /*********************************************************************
144 * RFC 3264 examples, section 10.1. (Bob's view)
145 *
146 * Difference:
147 * - the SDP version in Bob's capability is changed to ver-1.
148 */
149
150 "RFC 3264 example 10.1 (Bob's view)",
151 2,
152 {
153 {
154 REMOTE_OFFER,
155 /* Remote offer from Alice: */
156 "v=0\r\n"
157 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
158 "s= \r\n"
159 "c=IN IP4 host.anywhere.com\r\n"
160 "t=0 0\r\n"
161 "m=audio 49170 RTP/AVP 0\r\n"
162 "a=rtpmap:0 PCMU/8000\r\n"
163 "m=video 51372 RTP/AVP 31\r\n"
164 "a=rtpmap:31 H261/90000\r\n"
165 "m=video 53000 RTP/AVP 32\r\n"
166 "a=rtpmap:32 MPV/90000\r\n",
167 /* Bob's capability: */
168 "v=0\r\n"
169 "o=bob 2890844730 2890844729 IN IP4 host.example.com\r\n"
170 "s= \r\n"
171 "c=IN IP4 host.example.com\r\n"
172 "t=0 0\r\n"
173 "m=audio 49920 RTP/AVP 0\r\n"
174 "a=rtpmap:0 PCMU/8000\r\n"
175 "m=video 0 RTP/AVP 31\r\n"
176 "m=video 53000 RTP/AVP 32\r\n"
177 "a=rtpmap:32 MPV/90000\r\n",
178 /* This's how Bob's answer should look like: */
179 "v=0\r\n"
180 "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
181 "s= \r\n"
182 "c=IN IP4 host.example.com\r\n"
183 "t=0 0\r\n"
184 "m=audio 49920 RTP/AVP 0\r\n"
185 "a=rtpmap:0 PCMU/8000\r\n"
186 "m=video 0 RTP/AVP 31\r\n"
187 "m=video 53000 RTP/AVP 32\r\n"
188 "a=rtpmap:32 MPV/90000\r\n"
189 },
190 {
191 LOCAL_OFFER,
192 /* Bob wants to change his local SDP
193 * (change local port for the first stream and add new stream)
194 */
195 "v=0\r\n"
196 "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
197 "s=-\r\n"
198 "c=IN IP4 host.example.com\r\n"
199 "t=0 0\r\n"
200 "m=audio 65422 RTP/AVP 0\r\n"
201 "a=rtpmap:0 PCMU/8000\r\n"
202 "m=video 0 RTP/AVP 31\r\n"
203 "m=video 53000 RTP/AVP 32\r\n"
204 "a=rtpmap:32 MPV/90000\r\n"
205 "m=audio 51434 RTP/AVP 110\r\n"
206 "a=rtpmap:110 telephone-events/8000\r\n"
207 "a=recvonly\r\n",
208 /* Got answer from Alice */
209 "v=0\r\n"
210 "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
211 "s=-\r\n"
212 "c=IN IP4 host.anywhere.com\r\n"
213 "t=0 0\r\n"
214 "m=audio 49170 RTP/AVP 0\r\n"
215 "a=rtpmap:0 PCMU/8000\r\n"
216 "m=video 0 RTP/AVP 31\r\n"
217 "a=rtpmap:31 H261/90000\r\n"
218 "m=video 53000 RTP/AVP 32\r\n"
219 "a=rtpmap:32 MPV/90000\r\n"
220 "m=audio 53122 RTP/AVP 110\r\n"
221 "a=rtpmap:110 telephone-events/8000\r\n"
222 "a=sendonly\r\n",
223 /* This is how Bob's SDP should look like after negotiation */
224 "v=0\r\n"
225 "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
226 "s=-\r\n"
227 "c=IN IP4 host.example.com\r\n"
228 "t=0 0\r\n"
229 "m=audio 65422 RTP/AVP 0\r\n"
230 "a=rtpmap:0 PCMU/8000\r\n"
231 "m=video 0 RTP/AVP 31\r\n"
232 "m=video 53000 RTP/AVP 32\r\n"
233 "a=rtpmap:32 MPV/90000\r\n"
234 "m=audio 51434 RTP/AVP 110\r\n"
235 "a=rtpmap:110 telephone-events/8000\r\n"
236 "a=recvonly\r\n"
237 }
238 }
239 },
240
241 /* test 2: */
242 {
243 /*********************************************************************
244 * RFC 3264 examples, section 10.2.
245 * This is from Alice's point of view.
246 */
247
248 "RFC 3264 example 10.2 (Alice's view)",
249 2,
250 {
251 {
252 LOCAL_OFFER,
253 /* The initial offer from Alice to Bob indicates a single audio
254 * stream with the three audio codecs that are available in the
255 * DSP. The stream is marked as inactive,
256 */
257 "v=0\r\n"
258 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
259 "s=-\r\n"
260 "c=IN IP4 host.anywhere.com\r\n"
261 "t=0 0\r\n"
262 "m=audio 62986 RTP/AVP 0 4 18\r\n"
263 "a=rtpmap:0 PCMU/8000\r\n"
264 "a=rtpmap:4 G723/8000\r\n"
265 "a=rtpmap:18 G729/8000\r\n"
266 "a=inactive\r\n",
267 /* Bob can support dynamic switching between PCMU and G.723. So,
268 * he sends the following answer:
269 */
270 "v=0\r\n"
271 "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
272 "s=-\r\n"
273 "c=IN IP4 host.example.com\r\n"
274 "t=0 0\r\n"
275 "m=audio 54344 RTP/AVP 0 4\r\n"
276 "a=rtpmap:0 PCMU/8000\r\n"
277 "a=rtpmap:4 G723/8000\r\n"
278 "a=inactive\r\n",
279 /* This is how Alice's media should look like after negotiation */
280 "v=0\r\n"
281 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
282 "s=-\r\n"
283 "c=IN IP4 host.anywhere.com\r\n"
284 "t=0 0\r\n"
285 "m=audio 62986 RTP/AVP 0 4\r\n"
286 "a=rtpmap:0 PCMU/8000\r\n"
287 "a=rtpmap:4 G723/8000\r\n"
288 "a=inactive\r\n",
289 },
290 {
291 LOCAL_OFFER,
292 /* Alice sends an updated offer with a sendrecv stream: */
293 "v=0\r\n"
294 "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
295 "s=-\r\n"
296 "c=IN IP4 host.anywhere.com\r\n"
297 "t=0 0\r\n"
298 "m=audio 62986 RTP/AVP 4\r\n"
299 "a=rtpmap:4 G723/8000\r\n"
300 "a=sendrecv\r\n",
301 /* Bob accepts the single codec: */
302 "v=0\r\n"
303 "o=bob 2890844730 2890844732 IN IP4 host.example.com\r\n"
304 "s= \r\n"
305 "c=IN IP4 host.example.com\r\n"
306 "t=0 0\r\n"
307 "m=audio 54344 RTP/AVP 4\r\n"
308 "a=rtpmap:4 G723/8000\r\n"
309 "a=sendrecv\r\n",
310 /* This is how Alice's media should look like after negotiation */
311 "v=0\r\n"
312 "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
313 "s=-\r\n"
314 "c=IN IP4 host.anywhere.com\r\n"
315 "t=0 0\r\n"
316 "m=audio 62986 RTP/AVP 4\r\n"
317 "a=rtpmap:4 G723/8000\r\n"
318 "a=sendrecv\r\n"
319 }
320 }
321 },
322
323 /* test 3: */
324 {
325 /*********************************************************************
326 * RFC 3264 examples, section 10.2.
327 * This is from Bob's point of view.
328 *
329 * Difference:
330 * - The SDP version number in Bob's initial capability is ver-1
331 */
332
333 "RFC 3264 example 10.2 (Bob's view)",
334 2,
335 {
336 {
337 REMOTE_OFFER,
338 /* Bob received offer from Alice:
339 */
340 "v=0\r\n"
341 "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
342 "s=-\r\n"
343 "c=IN IP4 host.anywhere.com\r\n"
344 "t=0 0\r\n"
345 "m=audio 62986 RTP/AVP 0 4 18\r\n"
346 "a=rtpmap:0 PCMU/8000\r\n"
347 "a=rtpmap:4 G723/8000\r\n"
348 "a=rtpmap:18 G729/8000\r\n"
349 "a=inactive\r\n",
350 /* Bob's capability:
351 */
352 "v=0\r\n"
353 "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
354 "s=-\r\n"
355 "c=IN IP4 host.example.com\r\n"
356 "t=0 0\r\n"
357 "m=audio 54344 RTP/AVP 0 4\r\n"
358 "a=rtpmap:0 PCMU/8000\r\n"
359 "a=rtpmap:4 G723/8000\r\n"
360 "a=inactive\r\n",
361 /* This is how Bob's media should look like after negotiation */
362 "v=0\r\n"
363 "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
364 "s=-\r\n"
365 "c=IN IP4 host.example.com\r\n"
366 "t=0 0\r\n"
367 "m=audio 54344 RTP/AVP 0\r\n"
368 "a=rtpmap:0 PCMU/8000\r\n"
369 "a=inactive\r\n"
370 },
371 {
372 REMOTE_OFFER,
373 /* Received updated Alice's SDP: offer with a sendrecv stream: */
374 "v=0\r\n"
375 "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
376 "s=-\r\n"
377 "c=IN IP4 host.anywhere.com\r\n"
378 "t=0 0\r\n"
379 "m=audio 62986 RTP/AVP 4\r\n"
380 "a=rtpmap:4 G723/8000\r\n"
381 "a=sendrecv\r\n",
382 /* Bob accepts the single codec: */
383 NULL,
384 /* This is how Bob's media should look like after negotiation */
385 "v=0\r\n"
386 "o=bob 2890844730 2890844732 IN IP4 host.example.com\r\n"
387 "s=-\r\n"
388 "c=IN IP4 host.example.com\r\n"
389 "t=0 0\r\n"
390 "m=audio 54344 RTP/AVP 4\r\n"
391 "a=rtpmap:4 G723/8000\r\n"
392 "a=sendrecv\r\n",
393 }
394 }
395 },
396
397 /* test 4: */
398 {
399 /*********************************************************************
400 * RFC 4317 Sample 2.1: Audio and Video 1 (Alice's view)
401 *
402 * This common scenario shows a video and audio session in which
403 * multiple codecs are offered but only one is accepted. As a result of
404 * the exchange shown below, Alice and Bob may send only PCMU audio and
405 * MPV video. Note: Dynamic payload type 97 is used for iLBC codec
406 */
407 "RFC 4317 section 2.1: Audio and Video 1 (Alice's view)",
408 1,
409 {
410 {
411 LOCAL_OFFER,
412 /* Alice's local offer: */
413 "v=0\r\n"
414 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
415 "s=-\r\n"
416 "c=IN IP4 host.atlanta.example.com\r\n"
417 "t=0 0\r\n"
418 "m=audio 49170 RTP/AVP 0 8 97\r\n"
419 "a=rtpmap:0 PCMU/8000\r\n"
420 "a=rtpmap:8 PCMA/8000\r\n"
421 "a=rtpmap:97 iLBC/8000\r\n"
422 "m=video 51372 RTP/AVP 31 32\r\n"
423 "a=rtpmap:31 H261/90000\r\n"
424 "a=rtpmap:32 MPV/90000\r\n",
425 /* Received answer from Bob: */
426 "v=0\r\n"
427 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
428 "s=-\r\n"
429 "c=IN IP4 host.biloxi.example.com\r\n"
430 "t=0 0\r\n"
431 "m=audio 49174 RTP/AVP 0\r\n"
432 "a=rtpmap:0 PCMU/8000\r\n"
433 "m=video 49170 RTP/AVP 32\r\n"
434 "a=rtpmap:32 MPV/90000\r\n",
435 /* This is how Alice's media should look like now: */
436 "v=0\r\n"
437 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
438 "s=-\r\n"
439 "c=IN IP4 host.atlanta.example.com\r\n"
440 "t=0 0\r\n"
441 "m=audio 49170 RTP/AVP 0\r\n"
442 "a=rtpmap:0 PCMU/8000\r\n"
443 "m=video 51372 RTP/AVP 32\r\n"
444 "a=rtpmap:32 MPV/90000\r\n"
445 }
446 }
447 },
448
449 /* test 5: */
450 {
451 /*********************************************************************
452 * RFC 4317 Sample 2.1: Audio and Video 1 (Bob's view)
453 *
454 * This common scenario shows a video and audio session in which
455 * multiple codecs are offered but only one is accepted. As a result of
456 * the exchange shown below, Alice and Bob may send only PCMU audio and
457 * MPV video. Note: Dynamic payload type 97 is used for iLBC codec
458 *
459 * Difference:
460 * - Bob's initial capability version number
461 */
462 "RFC 4317 section 2.1: Audio and Video 1 (Bob's view)",
463 1,
464 {
465 {
466 REMOTE_OFFER,
467 /* Received Alice's local offer: */
468 "v=0\r\n"
469 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
470 "s=-\r\n"
471 "c=IN IP4 host.atlanta.example.com\r\n"
472 "t=0 0\r\n"
473 "m=audio 49170 RTP/AVP 0 8 97\r\n"
474 "a=rtpmap:0 PCMU/8000\r\n"
475 "a=rtpmap:8 PCMA/8000\r\n"
476 "a=rtpmap:97 iLBC/8000\r\n"
477 "m=video 51372 RTP/AVP 31 32\r\n"
478 "a=rtpmap:31 H261/90000\r\n"
479 "a=rtpmap:32 MPV/90000\r\n",
480 /* Bob's capability: */
481 "v=0\r\n"
482 "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
483 "s=-\r\n"
484 "c=IN IP4 host.biloxi.example.com\r\n"
485 "t=0 0\r\n"
486 "m=audio 49174 RTP/AVP 0\r\n"
487 "a=rtpmap:0 PCMU/8000\r\n"
488 "m=video 49170 RTP/AVP 32\r\n"
489 "a=rtpmap:32 MPV/90000\r\n",
490 /* This is how Bob's media should look like now: */
491 "v=0\r\n"
492 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
493 "s=-\r\n"
494 "c=IN IP4 host.biloxi.example.com\r\n"
495 "t=0 0\r\n"
496 "m=audio 49174 RTP/AVP 0\r\n"
497 "a=rtpmap:0 PCMU/8000\r\n"
498 "m=video 49170 RTP/AVP 32\r\n"
499 "a=rtpmap:32 MPV/90000\r\n"
500 }
501 }
502 },
503
504 /* test 6: */
505 {
506 /*********************************************************************
507 * RFC 4317 Sample 2.2: Audio and Video 2 (Alice's view)
508 *
509 * Difference:
510 * - Bob's initial capability version number
511 */
512 "RFC 4317 section 2.2: Audio and Video 2 (Alice's view)",
513 2,
514 {
515 {
516 LOCAL_OFFER,
517 /* Alice sends offer: */
518 "v=0\r\n"
519 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
520 "s=alice\r\n"
521 "c=IN IP4 host.atlanta.example.com\r\n"
522 "t=0 0\r\n"
523 "m=audio 49170 RTP/AVP 0 8 97\r\n"
524 "a=rtpmap:0 PCMU/8000\r\n"
525 "a=rtpmap:8 PCMA/8000\r\n"
526 "a=rtpmap:97 iLBC/8000\r\n"
527 "m=video 51372 RTP/AVP 31 32\r\n"
528 "a=rtpmap:31 H261/90000\r\n"
529 "a=rtpmap:32 MPV/90000\r\n",
530 /* Bob's answer: */
531 "v=0\r\n"
532 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
533 "s=bob\r\n"
534 "c=IN IP4 host.biloxi.example.com\r\n"
535 "t=0 0\r\n"
536 "m=audio 49172 RTP/AVP 0 8\r\n"
537 "a=rtpmap:0 PCMU/8000\r\n"
538 "a=rtpmap:8 PCMA/8000\r\n"
539 "m=video 0 RTP/AVP 31\r\n"
540 "a=rtpmap:31 H261/90000\r\n",
541 /* This is how Alice's media should look like now: */
542 "v=0\r\n"
543 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
544 "s=alice\r\n"
545 "c=IN IP4 host.atlanta.example.com\r\n"
546 "t=0 0\r\n"
547 "m=audio 49170 RTP/AVP 0 8\r\n"
548 "a=rtpmap:0 PCMU/8000\r\n"
549 "a=rtpmap:8 PCMA/8000\r\n"
550 "m=video 0 RTP/AVP 31\r\n"
551 "a=rtpmap:31 H261/90000\r\n"
552 },
553 {
554 LOCAL_OFFER,
555 /* Alice sends updated offer: */
556 "v=0\r\n"
557 "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
558 "s=alice\r\n"
559 "c=IN IP4 host.atlanta.example.com\r\n"
560 "t=0 0\r\n"
561 "m=audio 51372 RTP/AVP 0\r\n"
562 "a=rtpmap:0 PCMU/8000\r\n"
563 "m=video 0 RTP/AVP 31\r\n"
564 "a=rtpmap:31 H261/90000\r\n",
565 /* Bob's answer: */
566 "v=0\r\n"
567 "o=bob 2808844564 2808844565 IN IP4 host.biloxi.example.com\r\n"
568 "s=bob\r\n"
569 "c=IN IP4 host.biloxi.example.com\r\n"
570 "t=0 0\r\n"
571 "m=audio 49172 RTP/AVP 0\r\n"
572 "a=rtpmap:0 PCMU/8000\r\n"
573 "m=video 0 RTP/AVP 31\r\n"
574 "a=rtpmap:31 H261/90000\r\n",
575 /* This is how Alice's SDP should look like: */
576 "v=0\r\n"
577 "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
578 "s=alice\r\n"
579 "c=IN IP4 host.atlanta.example.com\r\n"
580 "t=0 0\r\n"
581 "m=audio 51372 RTP/AVP 0\r\n"
582 "a=rtpmap:0 PCMU/8000\r\n"
583 "m=video 0 RTP/AVP 31\r\n"
584 "a=rtpmap:31 H261/90000\r\n"
585 }
586 }
587 },
588
589 /* test 7: */
590 {
591 /*********************************************************************
592 * RFC 4317 Sample 2.2: Audio and Video 2 (Bob's view)
593 *
594 * Difference:
595 * - Bob's initial capability version number
596 */
597 "RFC 4317 section 2.2: Audio and Video 2 (Bob's view)",
598 2,
599 {
600 {
601 REMOTE_OFFER,
602 /* Received offer from alice: */
603 "v=0\r\n"
604 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
605 "s=alice\r\n"
606 "c=IN IP4 host.atlanta.example.com\r\n"
607 "t=0 0\r\n"
608 "m=audio 49170 RTP/AVP 0 8 97\r\n"
609 "a=rtpmap:0 PCMU/8000\r\n"
610 "a=rtpmap:8 PCMA/8000\r\n"
611 "a=rtpmap:97 iLBC/8000\r\n"
612 "m=video 51372 RTP/AVP 31 32\r\n"
613 "a=rtpmap:31 H261/90000\r\n"
614 "a=rtpmap:32 MPV/90000\r\n",
615 /* Bob's initial capability: */
616 "v=0\r\n"
617 "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
618 "s=bob\r\n"
619 "c=IN IP4 host.biloxi.example.com\r\n"
620 "t=0 0\r\n"
621 "m=audio 49172 RTP/AVP 0 8\r\n"
622 "a=rtpmap:0 PCMU/8000\r\n"
623 "a=rtpmap:8 PCMA/8000\r\n"
624 "m=video 0 RTP/AVP 31\r\n"
625 "a=rtpmap:31 H261/90000\r\n",
626 /* This is how Bob's answer should look like now: */
627 "v=0\r\n"
628 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
629 "s=bob\r\n"
630 "c=IN IP4 host.biloxi.example.com\r\n"
631 "t=0 0\r\n"
632 "m=audio 49172 RTP/AVP 0\r\n"
633 "a=rtpmap:0 PCMU/8000\r\n"
634 "m=video 0 RTP/AVP 31\r\n"
635 "a=rtpmap:31 H261/90000\r\n"
636 },
637 {
638 REMOTE_OFFER,
639 /* Received updated offer from Alice: */
640 "v=0\r\n"
641 "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
642 "s=alice\r\n"
643 "c=IN IP4 host.atlanta.example.com\r\n"
644 "t=0 0\r\n"
645 "m=audio 51372 RTP/AVP 0\r\n"
646 "a=rtpmap:0 PCMU/8000\r\n"
647 "m=video 0 RTP/AVP 31\r\n"
648 "a=rtpmap:31 H261/90000\r\n",
649 /* Bob's answer: */
650 NULL,
651 /* This is how Bob's answer should look like: */
652 "v=0\r\n"
653 "o=bob 2808844564 2808844565 IN IP4 host.biloxi.example.com\r\n"
654 "s=bob\r\n"
655 "c=IN IP4 host.biloxi.example.com\r\n"
656 "t=0 0\r\n"
657 "m=audio 49172 RTP/AVP 0\r\n"
658 "a=rtpmap:0 PCMU/8000\r\n"
659 "m=video 0 RTP/AVP 31\r\n"
660 "a=rtpmap:31 H261/90000\r\n"
661 }
662 }
663 },
664
665 /* test 8: */
666 {
667 /*********************************************************************
668 * RFC 4317 Sample 2.4: Audio and Telephone-Events (Alice's view)
669 *
670 */
671
672 "RFC 4317 section 2.4: Audio and Telephone-Events (Alice's view)",
673 1,
674 {
675 {
676 LOCAL_OFFER,
677 /* Alice sends offer: */
678 "v=0\r\n"
679 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
680 "s=alice\r\n"
681 "c=IN IP4 host.atlanta.example.com\r\n"
682 "t=0 0\r\n"
683 "m=audio 49170 RTP/AVP 0 97\r\n"
684 "a=rtpmap:0 PCMU/8000\r\n"
685 "a=rtpmap:97 iLBC/8000\r\n"
686 "m=audio 49172 RTP/AVP 98\r\n"
687 "a=rtpmap:98 telephone-event/8000\r\n"
688 "a=sendonly\r\n",
689 /* Received Bob's answer: */
690 "v=0\r\n"
691 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
692 "s=bob\r\n"
693 "c=IN IP4 host.biloxi.example.com\r\n"
694 "t=0 0\r\n"
695 "m=audio 49172 RTP/AVP 97\r\n"
696 "a=rtpmap:97 iLBC/8000\r\n"
697 "m=audio 49174 RTP/AVP 98\r\n"
698 "a=rtpmap:98 telephone-event/8000\r\n"
699 "a=recvonly\r\n",
700 /* Alice's SDP now: */
701 "v=0\r\n"
702 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
703 "s=alice\r\n"
704 "c=IN IP4 host.atlanta.example.com\r\n"
705 "t=0 0\r\n"
706 "m=audio 49170 RTP/AVP 97\r\n"
707 "a=rtpmap:97 iLBC/8000\r\n"
708 "m=audio 49172 RTP/AVP 98\r\n"
709 "a=rtpmap:98 telephone-event/8000\r\n"
710 "a=sendonly\r\n"
711 }
712 }
713 },
714
715
716 /* test 9: */
717 {
718 /*********************************************************************
719 * RFC 4317 Sample 2.4: Audio and Telephone-Events (Bob's view)
720 *
721 * Difference:
722 * - Bob's initial SDP version number
723 * - Bob's capability are added with more formats, and the
724 * stream order is interchanged to test the negotiator.
725 */
726
727 "RFC 4317 section 2.4: Audio and Telephone-Events (Bob's view)",
728 1,
729 {
730 {
731 REMOTE_OFFER,
732 /* Received Alice's offer: */
733 "v=0\r\n"
734 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
735 "s=alice\r\n"
736 "c=IN IP4 host.atlanta.example.com\r\n"
737 "t=0 0\r\n"
738 "m=audio 49170 RTP/AVP 0 97\r\n"
739 "a=rtpmap:0 PCMU/8000\r\n"
740 "a=rtpmap:97 iLBC/8000\r\n"
741 "m=audio 49172 RTP/AVP 98\r\n"
742 "a=rtpmap:98 telephone-event/8000\r\n"
743 "a=sendonly\r\n",
744 /* Bob's initial capability: */
745 "v=0\r\n"
746 "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
747 "s=bob\r\n"
748 "c=IN IP4 host.biloxi.example.com\r\n"
749 "t=0 0\r\n"
750 "m=audio 49174 RTP/AVP 4 98\r\n"
751 "a=rtpmap:98 telephone-event/8000\r\n"
752 "m=audio 49172 RTP/AVP 97 8 99\r\n"
753 "a=rtpmap:97 iLBC/8000\r\n"
754 "a=rtpmap:99 telephone-event/8000\r\n"
755 "a=recvonly\r\n",
756 /* Bob's answer should be: */
757 "v=0\r\n"
758 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
759 "s=bob\r\n"
760 "c=IN IP4 host.biloxi.example.com\r\n"
761 "t=0 0\r\n"
762 "m=audio 49172 RTP/AVP 97\r\n"
763 "a=rtpmap:97 iLBC/8000\r\n"
764 "m=audio 49174 RTP/AVP 98\r\n"
765 "a=rtpmap:98 telephone-event/8000\r\n"
766 "a=recvonly\r\n"
767 }
768 }
769 },
770
771 /* test 10: */
772 {
773 /*********************************************************************
774 * RFC 4317 Sample 2.6: Audio with Telephone-Events (Alice's view)
775 *
776 */
777
778 "RFC 4317 section 2.6: Audio with Telephone-Events (Alice's view)",
779 1,
780 {
781 {
782 LOCAL_OFFER,
783 /* Alice sends offer: */
784 "v=0\r\n"
785 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
786 "s=alice\r\n"
787 "c=IN IP4 host.atlanta.example.com\r\n"
788 "t=0 0\r\n"
789 "m=audio 49170 RTP/AVP 0\r\n"
790 "a=rtpmap:0 PCMU/8000\r\n"
791 "m=audio 51372 RTP/AVP 97 101\r\n"
792 "a=rtpmap:97 iLBC/8000\r\n"
793 "a=rtpmap:101 telephone-event/8000\r\n",
794 /* Received bob's answer: */
795 "v=0\r\n"
796 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
797 "s=bob\r\n"
798 "c=IN IP4 host.biloxi.example.com\r\n"
799 "t=0 0\r\n"
800 "m=audio 0 RTP/AVP 0\r\n"
801 "a=rtpmap:0 PCMU/8000\r\n"
802 "m=audio 49170 RTP/AVP 97 101\r\n"
803 "a=rtpmap:97 iLBC/8000\r\n"
804 "a=rtpmap:101 telephone-event/8000\r\n",
805 /* Alice's local SDP should be: */
806 "v=0\r\n"
807 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
808 "s=alice\r\n"
809 "c=IN IP4 host.atlanta.example.com\r\n"
810 "t=0 0\r\n"
811 "m=audio 0 RTP/AVP 0\r\n"
812 "a=rtpmap:0 PCMU/8000\r\n"
813 "m=audio 51372 RTP/AVP 97 101\r\n"
814 "a=rtpmap:97 iLBC/8000\r\n"
815 "a=rtpmap:101 telephone-event/8000\r\n"
816 }
817 }
818 },
819
820 /* test 11: */
821 {
822 /*********************************************************************
823 * RFC 4317 Sample 2.6: Audio with Telephone-Events (Bob's view)
824 *
825 * Difference:
826 * - Bob's SDP version number
827 * - Bob's initial capability are expanded with multiple m lines
828 * and more formats
829 */
830
831 "RFC 4317 section 2.6: Audio with Telephone-Events (Bob's view)",
832 1,
833 {
834 {
835 REMOTE_OFFER,
836 /* Received Alice's offer: */
837 "v=0\r\n"
838 "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
839 "s=alice\r\n"
840 "c=IN IP4 host.atlanta.example.com\r\n"
841 "t=0 0\r\n"
842 "m=audio 49170 RTP/AVP 0\r\n"
843 "a=rtpmap:0 PCMU/8000\r\n"
844 "m=audio 51372 RTP/AVP 97 101\r\n"
845 "a=rtpmap:97 iLBC/8000\r\n"
846 "a=rtpmap:101 telephone-event/8000\r\n",
847 /* Bob's initial capability also has video: */
848 "v=0\r\n"
849 "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
850 "s=bob\r\n"
851 "c=IN IP4 host.biloxi.example.com\r\n"
852 "t=0 0\r\n"
853 "m=audio 49170 RTP/AVP 4 97 101\r\n"
854 "a=rtpmap:4 G723/8000\r\n"
855 "a=rtpmap:97 iLBC/8000\r\n"
856 "a=rtpmap:101 telephone-event/8000\r\n"
857 "m=video 1000 RTP/AVP 31\r\n"
858 "a=rtpmap:31 H261/90000\r\n",
859 /* Bob's answer should be: */
860 "v=0\r\n"
861 "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
862 "s=bob\r\n"
863 "c=IN IP4 host.biloxi.example.com\r\n"
864 "t=0 0\r\n"
865 "m=audio 0 RTP/AVP 0\r\n"
866 "a=rtpmap:0 PCMU/8000\r\n"
867 "m=audio 49170 RTP/AVP 97 101\r\n"
868 "a=rtpmap:97 iLBC/8000\r\n"
869 "a=rtpmap:101 telephone-event/8000\r\n",
870 }
871 }
872 },
873
874};
875
876static const char *find_diff(const char *s1, const char *s2,
877 int *line)
878{
879 *line = 1;
880 while (*s2 && *s1) {
881 if (*s2 != *s1)
882 return s2;
883 if (*s2 == '\n')
884 ++*line;
885 ++s2, ++s1;
886 }
887
888 return s2;
889}
890
891static int compare_sdp_string(const char *cmp_title,
892 const char *title1,
893 const pjmedia_sdp_session *sdp1,
894 const char *title2,
895 const pjmedia_sdp_session *sdp2,
896 pj_status_t logical_cmp)
897{
898 char sdpbuf1[1024], sdpbuf2[1024];
899 pj_ssize_t len1, len2;
900
901 len1 = pjmedia_sdp_print(sdp1, sdpbuf1, sizeof(sdpbuf1));
902 if (len1 < 1) {
903 PJ_LOG(3,(THIS_FILE," error: printing sdp1"));
904 return -1;
905 }
906 sdpbuf1[len1] = '\0';
907
908 len2 = pjmedia_sdp_print(sdp2, sdpbuf2, sizeof(sdpbuf2));
909 if (len2 < 1) {
910 PJ_LOG(3,(THIS_FILE," error: printing sdp2"));
911 return -1;
912 }
913 sdpbuf2[len2] = '\0';
914
915 if (logical_cmp != PJ_SUCCESS) {
916 char errbuf[80];
917
918 pjmedia_strerror(logical_cmp, errbuf, sizeof(errbuf));
919
920 PJ_LOG(3,(THIS_FILE,"%s mismatch: %s\n"
921 "%s:\n"
922 "%s\n"
923 "%s:\n"
924 "%s\n",
925 cmp_title,
926 errbuf,
927 title1, sdpbuf1,
928 title2, sdpbuf2));
929
930 return -1;
931
932 } else if (strcmp(sdpbuf1, sdpbuf2) != 0) {
933 int line;
934 const char *diff;
935
936 PJ_LOG(3,(THIS_FILE,"%s mismatch:\n"
937 "%s:\n"
938 "%s\n"
939 "%s:\n"
940 "%s\n",
941 cmp_title,
942 title1, sdpbuf1,
943 title2, sdpbuf2));
944
945 diff = find_diff(sdpbuf1, sdpbuf2, &line);
946 PJ_LOG(3,(THIS_FILE,"Difference: line %d:\n"
947 "%s",
948 line, diff));
949 return -1;
950
951 } else {
952 return 0;
953 }
954
955}
956
957static int offer_answer_test(pj_pool_t *pool, pjmedia_sdp_neg **p_neg,
958 struct offer_answer *oa)
959{
960 pjmedia_sdp_session *sdp1;
961 pjmedia_sdp_neg *neg;
962 pj_status_t status;
963
964 status = pjmedia_sdp_parse(pool, oa->sdp1, pj_native_strlen(oa->sdp1),
965 &sdp1);
966 if (status != PJ_SUCCESS) {
967 app_perror(status, " error: unexpected parse status for sdp1");
968 return -10;
969 }
970
971 status = pjmedia_sdp_validate(sdp1);
972 if (status != PJ_SUCCESS) {
973 app_perror(status, " error: sdp1 validation failed");
974 return -15;
975 }
976
977 neg = *p_neg;
978
979 if (oa->type == LOCAL_OFFER) {
980
981 /*
982 * Local creates offer first.
983 */
984 pjmedia_sdp_session *sdp2, *sdp3;
985 const pjmedia_sdp_session *active;
986
987 if (neg == NULL) {
988 /* Create negotiator with local offer. */
989 status = pjmedia_sdp_neg_create_w_local_offer(pool, sdp1, &neg);
990 if (status != PJ_SUCCESS) {
991 app_perror(status, " error: pjmedia_sdp_neg_create_w_local_offer");
992 return -20;
993 }
994 *p_neg = neg;
995
996 } else {
997 /* Modify local offer */
998 status = pjmedia_sdp_neg_modify_local_offer(pool, neg, sdp1);
999 if (status != PJ_SUCCESS) {
1000 app_perror(status, " error: pjmedia_sdp_neg_modify_local_offer");
1001 return -30;
1002 }
1003 }
1004
1005 /* Parse and validate remote answer */
1006 status = pjmedia_sdp_parse(pool, oa->sdp2, pj_native_strlen(oa->sdp2),
1007 &sdp2);
1008 if (status != PJ_SUCCESS) {
1009 app_perror(status, " error: parsing sdp2");
1010 return -40;
1011 }
1012
1013 status = pjmedia_sdp_validate(sdp2);
1014 if (status != PJ_SUCCESS) {
1015 app_perror(status, " error: sdp2 validation failed");
1016 return -50;
1017 }
1018
1019 /* Give the answer to negotiator. */
1020 status = pjmedia_sdp_neg_rx_remote_answer(pool, neg, sdp2);
1021 if (status != PJ_SUCCESS) {
1022 app_perror(status, " error: pjmedia_sdp_neg_rx_remote_answer");
1023 return -60;
1024 }
1025
1026 /* Negotiate remote answer with local answer */
1027 status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
1028 if (status != PJ_SUCCESS) {
1029 app_perror(status, " error: pjmedia_sdp_neg_negotiate");
1030 return -70;
1031 }
1032
1033 /* Get the local active media. */
1034 status = pjmedia_sdp_neg_get_local(neg, &active);
1035 if (status != PJ_SUCCESS) {
1036 app_perror(status, " error: pjmedia_sdp_neg_get_local");
1037 return -80;
1038 }
1039
1040 /* Parse and validate the correct active media. */
1041 status = pjmedia_sdp_parse(pool, oa->sdp3, pj_native_strlen(oa->sdp3),
1042 &sdp3);
1043 if (status != PJ_SUCCESS) {
1044 app_perror(status, " error: parsing sdp3");
1045 return -90;
1046 }
1047
1048 status = pjmedia_sdp_validate(sdp3);
1049 if (status != PJ_SUCCESS) {
1050 app_perror(status, " error: sdp3 validation failed");
1051 return -100;
1052 }
1053
1054 /* Compare active with sdp3 */
1055 status = pjmedia_sdp_session_cmp(active, sdp3, 0);
1056 if (status != PJ_SUCCESS) {
1057 app_perror(status, " error: active local comparison mismatch");
1058 compare_sdp_string("Logical cmp after negotiatin remote answer",
1059 "Active local sdp from negotiator", active,
1060 "The correct active local sdp", sdp3,
1061 status);
1062 return -110;
1063 }
1064
1065 /* Compare the string representation oa both sdps */
1066 status = compare_sdp_string("String cmp after negotiatin remote answer",
1067 "Active local sdp from negotiator", active,
1068 "The correct active local sdp", sdp3,
1069 PJ_SUCCESS);
1070 if (status != 0)
1071 return -120;
1072
1073 } else {
1074 /*
1075 * Remote creates offer first.
1076 */
1077
1078 pjmedia_sdp_session *sdp2, *sdp3;
1079 const pjmedia_sdp_session *answer;
1080
1081 if (neg == NULL) {
1082 /* Parse and validate initial local capability */
1083 status = pjmedia_sdp_parse(pool, oa->sdp2, pj_native_strlen(oa->sdp2),
1084 &sdp2);
1085 if (status != PJ_SUCCESS) {
1086 app_perror(status, " error: parsing sdp2");
1087 return -200;
1088 }
1089
1090 status = pjmedia_sdp_validate(sdp2);
1091 if (status != PJ_SUCCESS) {
1092 app_perror(status, " error: sdp2 validation failed");
1093 return -210;
1094 }
1095
1096 /* Create negotiator with remote offer. */
1097 status = pjmedia_sdp_neg_create_w_remote_offer(pool, sdp2, sdp1, &neg);
1098 if (status != PJ_SUCCESS) {
1099 app_perror(status, " error: pjmedia_sdp_neg_create_w_remote_offer");
1100 return -220;
1101 }
1102 *p_neg = neg;
1103
1104 } else {
1105 /* Received subsequent offer from remote. */
1106 status = pjmedia_sdp_neg_rx_remote_offer(pool, neg, sdp1);
1107 if (status != PJ_SUCCESS) {
1108 app_perror(status, " error: pjmedia_sdp_neg_rx_remote_offer");
1109 return -230;
1110 }
1111 }
1112
1113 /* Negotiate. */
1114 status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
1115 if (status != PJ_SUCCESS) {
1116 app_perror(status, " error: pjmedia_sdp_neg_negotiate");
1117 return -240;
1118 }
1119
1120 /* Get our answer. */
1121 status = pjmedia_sdp_neg_get_local(neg, &answer);
1122 if (status != PJ_SUCCESS) {
1123 app_perror(status, " error: pjmedia_sdp_neg_get_local");
1124 return -250;
1125 }
1126
1127 /* Parse the correct answer. */
1128 status = pjmedia_sdp_parse(pool, oa->sdp3, pj_native_strlen(oa->sdp3),
1129 &sdp3);
1130 if (status != PJ_SUCCESS) {
1131 app_perror(status, " error: parsing sdp3");
1132 return -260;
1133 }
1134
1135 /* Validate the correct answer. */
1136 status = pjmedia_sdp_validate(sdp3);
1137 if (status != PJ_SUCCESS) {
1138 app_perror(status, " error: sdp3 validation failed");
1139 return -270;
1140 }
1141
1142 /* Compare answer from negotiator and the correct answer */
1143 status = pjmedia_sdp_session_cmp(sdp3, answer, 0);
1144 if (status != PJ_SUCCESS) {
1145 compare_sdp_string("Logical cmp after negotiating remote offer",
1146 "Local answer from negotiator", answer,
1147 "The correct local answer", sdp3,
1148 status);
1149
1150 return -280;
1151 }
1152
1153 /* Compare the string representation oa both answers */
1154 status = compare_sdp_string("String cmp after negotiating remote offer",
1155 "Local answer from negotiator", answer,
1156 "The correct local answer", sdp3,
1157 PJ_SUCCESS);
1158 if (status != 0)
1159 return -290;
1160
1161 }
1162
1163 return 0;
1164}
1165
1166static int perform_test(pj_pool_t *pool, int test_index)
1167{
1168 pjmedia_sdp_neg *neg = NULL;
1169 unsigned i;
1170 int rc;
1171
1172 for (i=0; i<test[test_index].offer_answer_count; ++i) {
1173
1174 rc = offer_answer_test(pool, &neg, &test[test_index].offer_answer[i]);
1175 if (rc != 0) {
1176 PJ_LOG(3,(THIS_FILE, " test %d offer answer %d failed with status %d",
1177 test_index, i, rc));
1178 return rc;
1179 }
1180 }
1181
1182 return 0;
1183}
1184
1185int sdp_neg_test()
1186{
1187 unsigned i;
1188 int status;
1189
1190 for (i=START_TEST; i<PJ_ARRAY_SIZE(test); ++i) {
1191 pj_pool_t *pool;
1192
1193 pool = pj_pool_create(mem, "sdp_neg_test", 4000, 4000, NULL);
1194 if (!pool)
1195 return PJ_ENOMEM;
1196
1197 PJ_LOG(3,(THIS_FILE," test %d: %s", i, test[i].title));
1198 status = perform_test(pool, i);
1199
1200 pj_pool_release(pool);
1201
1202 if (status != 0) {
1203 return status;
1204 }
1205 }
1206
1207 return 0;
1208}
1209