Merge "channel: Add ability to request an outgoing channel with stream topology."
[asterisk/asterisk.git] / tests / test_sdp.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2017, Digium Inc.
5  *
6  * Mark Michelson <mmmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*** MODULEINFO
20         <depend>TEST_FRAMEWORK</depend>
21         <support_level>core</support_level>
22  ***/
23
24 #include "asterisk.h"
25 #include "asterisk/test.h"
26 #include "asterisk/module.h"
27 #include "asterisk/sdp.h"
28 #include "asterisk/stream.h"
29 #include "asterisk/format.h"
30 #include "asterisk/format_cache.h"
31 #include "asterisk/format_cap.h"
32
33 static int validate_o_line(struct ast_test *test, const struct ast_sdp_o_line *o_line,
34         const char *sdpowner, const char *address_type, const char *address)
35 {
36         if (!o_line) {
37                 return -1;
38         }
39
40         if (strcmp(o_line->username, sdpowner)) {
41                 ast_test_status_update(test, "Expected o-line SDP owner %s but got %s\n",
42                         sdpowner, o_line->username);
43                 return -1;
44         }
45
46         if (strcmp(o_line->address_type, address_type)) {
47                 ast_test_status_update(test, "Expected o-line SDP address type %s but got %s\n",
48                         address_type, o_line->address_type);
49                 return -1;
50         }
51
52         if (strcmp(o_line->address, address)) {
53                 ast_test_status_update(test, "Expected o-line SDP address %s but got %s\n",
54                         address, o_line->address);
55                 return -1;
56         }
57
58         ast_test_status_update(test, "SDP o-line is as expected!\n");
59         return 0;
60 }
61
62 static int validate_c_line(struct ast_test *test, const struct ast_sdp_c_line *c_line,
63         const char *address_type, const char *address)
64 {
65         if (strcmp(c_line->address_type, address_type)) {
66                 ast_test_status_update(test, "Expected c-line SDP address type %s but got %s\n",
67                         address_type, c_line->address_type);
68                 return -1;
69         }
70
71         if (strcmp(c_line->address, address)) {
72                 ast_test_status_update(test, "Expected c-line SDP address %s but got %s\n",
73                         address, c_line->address);
74                 return -1;
75         }
76
77         ast_test_status_update(test, "SDP c-line is as expected!\n");
78         return 0;
79 }
80
81 static int validate_m_line(struct ast_test *test, const struct ast_sdp_m_line *m_line,
82         const char *media_type, int num_payloads)
83 {
84         if (strcmp(m_line->type, media_type)) {
85                 ast_test_status_update(test, "Expected m-line media type %s but got %s\n",
86                         media_type, m_line->type);
87                 return -1;
88         }
89
90         if (ast_sdp_m_get_payload_count(m_line) != num_payloads) {
91                 ast_test_status_update(test, "Expected m-line payload count %d but got %d\n",
92                         num_payloads, ast_sdp_m_get_payload_count(m_line));
93                 return -1;
94         }
95
96         ast_test_status_update(test, "SDP m-line is as expected\n");
97         return 0;
98 }
99
100 static int validate_rtpmap(struct ast_test *test, const struct ast_sdp_m_line *m_line,
101         const char *media_name)
102 {
103         struct ast_sdp_a_line *a_line;
104         int i;
105
106         for (i = 0; i < ast_sdp_m_get_a_count(m_line); ++i) {
107                 struct ast_sdp_rtpmap *rtpmap;
108                 int match;
109
110                 a_line = ast_sdp_m_get_a(m_line, i);
111                 if (strcmp(a_line->name, "rtpmap")) {
112                         continue;
113                 }
114
115                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
116                 if (!rtpmap) {
117                         return -1;
118                 }
119
120                 match = !strcmp(rtpmap->encoding_name, media_name);
121
122                 ast_sdp_rtpmap_free(rtpmap);
123                 if (match) {
124                         return 0;
125                 }
126         }
127
128         ast_test_status_update(test, "Could not find rtpmap with encoding name %s\n", media_name);
129
130         return -1;
131 }
132
133 static enum ast_test_result_state validate_t38(struct ast_test *test, const struct ast_sdp_m_line *m_line)
134 {
135         struct ast_sdp_a_line *a_line;
136
137         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxVersion", -1);
138         ast_test_validate(test, a_line && !strcmp(a_line->value, "0"));
139
140         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxMaxBitRate", -1);
141         ast_test_validate(test, a_line && !strcmp(a_line->value, "14400"));
142
143         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxRateManagement", -1);
144         ast_test_validate(test, a_line && !strcmp(a_line->value, "transferredTCF"));
145
146         return AST_TEST_PASS;
147 }
148
149 AST_TEST_DEFINE(invalid_rtpmap)
150 {
151         /* a=rtpmap: is already assumed. This is the part after that */
152         static const char *invalids[] = {
153                 "J PCMU/8000",
154                 "0 PCMU:8000",
155                 "0 PCMU/EIGHT-THOUSAND",
156                 "0 PCMU/8000million/2",
157                 "0 PCMU//2",
158                 "0 /8000/2",
159                 "0 PCMU/8000/",
160                 "0 PCMU/8000million",
161         };
162         int i;
163         enum ast_test_result_state res = AST_TEST_PASS;
164
165         switch(cmd) {
166         case TEST_INIT:
167                 info->name = "invalid_rtpmap";
168                 info->category = "/main/sdp/";
169                 info->summary = "Ensure invalid rtpmaps are rejected";
170                 info->description =
171                         "Try to convert several invalid rtpmap attributes. If\n"
172                         "any succeeds, the test fails.";
173                 return AST_TEST_NOT_RUN;
174         case TEST_EXECUTE:
175                 break;
176         }
177
178         for (i = 0; i < ARRAY_LEN(invalids); ++i) {
179                 struct ast_sdp_a_line *a_line;
180                 struct ast_sdp_rtpmap *rtpmap;
181
182                 a_line = ast_sdp_a_alloc("rtpmap", invalids[i]);
183                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
184                 if (rtpmap) {
185                         ast_test_status_update(test, "Invalid rtpmap '%s' was accepted as valid\n",
186                                 invalids[i]);
187                         res = AST_TEST_FAIL;
188                 }
189                 ast_sdp_a_free(a_line);
190                 ast_sdp_rtpmap_free(rtpmap);
191         }
192
193         return res;
194 }
195
196 AST_TEST_DEFINE(rtpmap)
197 {
198         static const char *valids[] = {
199                 "0 PCMU/8000",
200                 "107 opus/48000/2",
201         };
202         static int payloads[] = {
203                 0,
204                 107,
205         };
206         static const char *encoding_names[] = {
207                 "PCMU",
208                 "opus",
209         };
210         static int clock_rates[] = {
211                 8000,
212                 48000,
213         };
214         static const char *encoding_parameters[] = {
215                 "",
216                 "2",
217         };
218         int i;
219         enum ast_test_result_state res = AST_TEST_PASS;
220
221         switch(cmd) {
222         case TEST_INIT:
223                 info->name = "rtpmap";
224                 info->category = "/main/sdp/";
225                 info->summary = "Ensure rtpmap attribute values are parsed correctly";
226                 info->description =
227                         "Parse several valid rtpmap attributes. Ensure that the parsed values\n"
228                         "are what we expect";
229                 return AST_TEST_NOT_RUN;
230         case TEST_EXECUTE:
231                 break;
232         }
233
234         for (i = 0; i < ARRAY_LEN(valids); ++i) {
235                 struct ast_sdp_a_line *a_line;
236                 struct ast_sdp_rtpmap *rtpmap;
237
238                 a_line = ast_sdp_a_alloc("rtpmap", valids[i]);
239                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
240                 if (!rtpmap) {
241                         ast_test_status_update(test, "Valid rtpmap '%s' was rejected as invalid\n",
242                                 valids[i]);
243                         res = AST_TEST_FAIL;
244                         continue;
245                 }
246                 if (rtpmap->payload != payloads[i]) {
247                         ast_test_status_update(test, "RTPmap payload '%d' does not match expected '%d'\n",
248                                 rtpmap->payload, payloads[i]);
249                         res = AST_TEST_FAIL;
250                 }
251                 if (strcmp(rtpmap->encoding_name, encoding_names[i])) {
252                         ast_test_status_update(test, "RTPmap encoding_name '%s' does not match expected '%s'\n",
253                                 rtpmap->encoding_name, encoding_names[i]);
254                         res = AST_TEST_FAIL;
255                 }
256                 if (rtpmap->clock_rate != clock_rates[i]) {
257                         ast_test_status_update(test, "RTPmap clock rate '%d' does not match expected '%d'\n",
258                                 rtpmap->clock_rate, clock_rates[i]);
259                         res = AST_TEST_FAIL;
260                 }
261                 if (strcmp(rtpmap->encoding_parameters, encoding_parameters[i])) {
262                         ast_test_status_update(test, "RTPmap encoding_parameter '%s' does not match expected '%s'\n",
263                                 rtpmap->encoding_parameters, encoding_parameters[i]);
264                         res = AST_TEST_FAIL;
265                 }
266                 ast_sdp_a_free(a_line);
267                 ast_sdp_rtpmap_free(rtpmap);
268         }
269
270         return res;
271 }
272
273 AST_TEST_DEFINE(find_attr)
274 {
275         enum ast_test_result_state res = AST_TEST_PASS;
276         struct ast_sdp_m_line *m_line;
277         struct ast_sdp_a_line *a_line;
278
279         switch(cmd) {
280         case TEST_INIT:
281                 info->name = "find_attr";
282                 info->category = "/main/sdp/";
283                 info->summary = "Ensure that finding attributes works as expected";
284                 info->description =
285                         "An SDP m-line is created, and two attributes are added.\n"
286                         "We then attempt a series of attribute-finding calls that are expected to work\n"
287                         "followed by a series of attribute-finding calls that are expected fo fail.";
288                 return AST_TEST_NOT_RUN;
289         case TEST_EXECUTE:
290                 break;
291         }
292
293         m_line = ast_sdp_m_alloc("audio", 666, 1, "RTP/AVP", NULL);
294         if (!m_line) {
295                 res = AST_TEST_FAIL;
296                 goto end;
297         }
298         a_line = ast_sdp_a_alloc("foo", "0 bar");
299         if (!a_line) {
300                 res = AST_TEST_FAIL;
301                 goto end;
302         }
303         ast_sdp_m_add_a(m_line, a_line);
304
305         a_line = ast_sdp_a_alloc("baz", "howdy");
306         if (!a_line) {
307                 res = AST_TEST_FAIL;
308                 goto end;
309         }
310         ast_sdp_m_add_a(m_line, a_line);
311
312         /* These should work */
313         a_line = ast_sdp_m_find_attribute(m_line, "foo", 0);
314         if (!a_line) {
315                 ast_test_status_update(test, "Failed to find attribute 'foo' with payload '0'\n");
316                 res = AST_TEST_FAIL;
317         }
318         a_line = ast_sdp_m_find_attribute(m_line, "foo", -1);
319         if (!a_line) {
320                 ast_test_status_update(test, "Failed to find attribute 'foo' with unspecified payload\n");
321                 res = AST_TEST_FAIL;
322         }
323         a_line = ast_sdp_m_find_attribute(m_line, "baz", -1);
324         if (!a_line) {
325                 ast_test_status_update(test, "Failed to find attribute 'baz' with unspecified payload\n");
326                 res = AST_TEST_FAIL;
327         }
328
329         /* These should fail */
330         a_line = ast_sdp_m_find_attribute(m_line, "foo", 1);
331         if (a_line) {
332                 ast_test_status_update(test, "Found non-existent attribute 'foo' with payload '1'\n");
333                 res = AST_TEST_FAIL;
334         }
335         a_line = ast_sdp_m_find_attribute(m_line, "baz", 0);
336         if (a_line) {
337                 ast_test_status_update(test, "Found non-existent attribute 'baz' with payload '0'\n");
338                 res = AST_TEST_FAIL;
339         }
340         a_line = ast_sdp_m_find_attribute(m_line, "wibble", 0);
341         if (a_line) {
342                 ast_test_status_update(test, "Found non-existent attribute 'wibble' with payload '0'\n");
343                 res = AST_TEST_FAIL;
344         }
345         a_line = ast_sdp_m_find_attribute(m_line, "wibble", -1);
346         if (a_line) {
347                 ast_test_status_update(test, "Found non-existent attribute 'foo' with unspecified payload\n");
348                 res = AST_TEST_FAIL;
349         }
350
351 end:
352         ast_sdp_m_free(m_line);
353         return res;
354 }
355
356 struct sdp_format {
357         enum ast_media_type type;
358         const char *formats;
359 };
360
361 static struct ast_sdp_state *build_sdp_state(int num_streams, const struct sdp_format *formats)
362 {
363         struct ast_stream_topology *topology = NULL;
364         struct ast_sdp_state *state = NULL;
365         struct ast_sdp_options *options;
366         int i;
367
368         options = ast_sdp_options_alloc();
369         if (!options) {
370                 goto end;
371         }
372         ast_sdp_options_set_media_address(options, "127.0.0.1");
373         ast_sdp_options_set_sdpowner(options, "me");
374         ast_sdp_options_set_rtp_engine(options, "asterisk");
375         ast_sdp_options_set_impl(options, AST_SDP_IMPL_PJMEDIA);
376
377         topology = ast_stream_topology_alloc();
378         if (!topology) {
379                 goto end;
380         }
381
382         for (i = 0; i < num_streams; ++i) {
383                 RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
384                 struct ast_stream *stream;
385
386                 caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
387                 if (!caps) {
388                         goto end;
389                 }
390                 if (ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {
391                         goto end;
392                 }
393                 stream = ast_stream_alloc("sure_thing", formats[i].type);
394                 if (!stream) {
395                         goto end;
396                 }
397                 ast_stream_set_formats(stream, caps);
398                 ast_stream_topology_append_stream(topology, stream);
399         }
400
401         state = ast_sdp_state_alloc(topology, options);
402         if (!state) {
403                 goto end;
404         }
405
406 end:
407         ast_stream_topology_free(topology);
408         if (!state) {
409                 ast_sdp_options_free(options);
410         }
411
412         return state;
413 }
414
415 AST_TEST_DEFINE(topology_to_sdp)
416 {
417         enum ast_test_result_state res = AST_TEST_FAIL;
418         struct ast_sdp_state *sdp_state = NULL;
419         const struct ast_sdp *sdp = NULL;
420         struct ast_sdp_m_line *m_line = NULL;
421         struct sdp_format formats[] = {
422                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
423                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
424                 { AST_MEDIA_TYPE_IMAGE, "t38" },
425         };
426
427         switch(cmd) {
428         case TEST_INIT:
429                 info->name = "topology_to_sdp";
430                 info->category = "/main/sdp/";
431                 info->summary = "Convert a topology into an SDP";
432                 info->description =
433                         "Ensure SDPs get converted to expected stream topology";
434                 return AST_TEST_NOT_RUN;
435         case TEST_EXECUTE:
436                 break;
437         }
438
439         sdp_state = build_sdp_state(ARRAY_LEN(formats), formats);
440         if (!sdp_state) {
441                 goto end;
442         }
443
444         sdp = ast_sdp_state_get_local_sdp(sdp_state);
445         if (!sdp) {
446                 goto end;
447         }
448
449         if (validate_o_line(test, sdp->o_line, "me", "IP4", "127.0.0.1")) {
450                 goto end;
451         }
452
453         if (validate_c_line(test, sdp->c_line, "IP4", "127.0.0.1")) {
454                 goto end;
455         }
456
457         if (ast_sdp_get_m_count(sdp) != 3) {
458                 ast_test_status_update(test, "Unexpected number of streams in generated SDP: %d\n",
459                         ast_sdp_get_m_count(sdp));
460                 goto end;
461         }
462
463         m_line = ast_sdp_get_m(sdp, 0);
464
465         if (validate_m_line(test, m_line, "audio", 4)) {
466                 goto end;
467         }
468
469         if (validate_rtpmap(test, m_line, "PCMU")) {
470                 goto end;
471         }
472
473         if (validate_rtpmap(test, m_line, "PCMA")) {
474                 goto end;
475         }
476
477         if (validate_rtpmap(test, m_line, "G722")) {
478                 goto end;
479         }
480
481         if (validate_rtpmap(test, m_line, "opus")) {
482                 goto end;
483         }
484
485         m_line = ast_sdp_get_m(sdp, 1);
486         if (validate_m_line(test, m_line, "video", 2)) {
487                 goto end;
488         }
489
490         if (validate_rtpmap(test, m_line, "VP8")) {
491                 goto end;
492         }
493
494         if (validate_rtpmap(test, m_line, "H264")) {
495                 goto end;
496         }
497
498         m_line = ast_sdp_get_m(sdp, 2);
499         if (validate_m_line(test, m_line, "image", 1)) {
500                 goto end;
501         }
502         if (validate_t38(test, m_line) != AST_TEST_PASS) {
503                 goto end;
504         }
505
506         res = AST_TEST_PASS;
507
508 end:
509         ast_sdp_state_free(sdp_state);
510         return res;
511 }
512
513 static int validate_formats(struct ast_test *test, struct ast_stream_topology *topology, int index,
514         enum ast_media_type type, int format_count, const char **expected_formats)
515 {
516         struct ast_stream *stream;
517         struct ast_format_cap *caps;
518         struct ast_format *format;
519         int i;
520
521         stream = ast_stream_topology_get_stream(topology, index);
522         if (ast_stream_get_type(stream) != type) {
523                 ast_test_status_update(test, "Unexpected stream type encountered\n");
524                 return -1;
525         }
526         caps = ast_stream_get_formats(stream);
527
528         if (ast_format_cap_count(caps) != format_count) {
529                 ast_test_status_update(test, "Unexpected format count '%d'. Expecting '%d'\n",
530                         (int) ast_format_cap_count(caps), format_count);
531                 return -1;
532         }
533
534         for (i = 0; i < ast_format_cap_count(caps); ++i) {
535                 format = ast_format_cap_get_format(caps, i);
536                 if (strcmp(ast_format_get_name(format), expected_formats[i])) {
537                         ast_test_status_update(test, "Unexpected format '%s'at index %d. Expected '%s'\n",
538                                 ast_format_get_name(format), i, expected_formats[i]);
539                         return -1;
540                 }
541         }
542
543         return 0;
544 }
545
546 AST_TEST_DEFINE(sdp_to_topology)
547 {
548         enum ast_test_result_state res = AST_TEST_PASS;
549         struct ast_sdp_state *sdp_state;
550         const struct ast_sdp *sdp;
551         struct ast_stream_topology *topology = NULL;
552         struct sdp_format sdp_formats[] = {
553                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
554                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
555                 { AST_MEDIA_TYPE_IMAGE, "t38" },
556         };
557         static const char *expected_audio_formats[] = {
558                 "ulaw",
559                 "alaw",
560                 "g722",
561                 "opus",
562         };
563         static const char *expected_video_formats[] = {
564                 "h264",
565                 "vp8",
566         };
567         static const char *expected_image_formats[] = {
568                 "t38",
569         };
570
571         switch(cmd) {
572         case TEST_INIT:
573                 info->name = "sdp_to_topology";
574                 info->category = "/main/sdp/";
575                 info->summary = "Convert an SDP into a topology";
576                 info->description =
577                         "Ensure SDPs get converted to expected stream topology";
578                 return AST_TEST_NOT_RUN;
579         case TEST_EXECUTE:
580                 break;
581         }
582
583         sdp_state = build_sdp_state(ARRAY_LEN(sdp_formats), sdp_formats);
584         if (!sdp_state) {
585                 res = AST_TEST_FAIL;
586                 goto end;
587         }
588
589         sdp = ast_sdp_state_get_local_sdp(sdp_state);
590         if (!sdp) {
591                 res = AST_TEST_FAIL;
592                 goto end;
593         }
594
595         topology = ast_get_topology_from_sdp(sdp);
596
597         if (ast_stream_topology_get_count(topology) != 3) {
598                 ast_test_status_update(test, "Unexpected topology count '%d'. Expecting 2\n",
599                         ast_stream_topology_get_count(topology));
600                 res = AST_TEST_FAIL;
601                 goto end;
602         }
603
604         if (validate_formats(test, topology, 0, AST_MEDIA_TYPE_AUDIO,
605                         ARRAY_LEN(expected_audio_formats), expected_audio_formats)) {
606                 res = AST_TEST_FAIL;
607                 goto end;
608         }
609
610         if (validate_formats(test, topology, 1, AST_MEDIA_TYPE_VIDEO,
611                         ARRAY_LEN(expected_video_formats), expected_video_formats)) {
612                 res = AST_TEST_FAIL;
613                 goto end;
614         }
615
616         if (validate_formats(test, topology, 2, AST_MEDIA_TYPE_IMAGE,
617                         ARRAY_LEN(expected_image_formats), expected_image_formats)) {
618                 res = AST_TEST_FAIL;
619                 goto end;
620         }
621
622 end:
623         ast_sdp_state_free(sdp_state);
624         ast_stream_topology_free(topology);
625         return res;
626 }
627
628 static int validate_merged_sdp(struct ast_test *test, const struct ast_sdp *sdp)
629 {
630         struct ast_sdp_m_line *m_line;
631
632         if (!sdp) {
633                 return -1;
634         }
635
636         m_line = ast_sdp_get_m(sdp, 0);
637
638         if (validate_m_line(test, m_line, "audio", 1)) {
639                 return -1;
640         }
641
642         if (validate_rtpmap(test, m_line, "PCMU")) {
643                 return -1;
644         }
645
646         /* The other audio formats should *NOT* be present */
647         if (!validate_rtpmap(test, m_line, "PCMA")) {
648                 return -1;
649         }
650
651         if (!validate_rtpmap(test, m_line, "G722")) {
652                 return -1;
653         }
654
655         if (!validate_rtpmap(test, m_line, "opus")) {
656                 return -1;
657         }
658
659         m_line = ast_sdp_get_m(sdp, 1);
660
661         if (validate_m_line(test, m_line, "video", 1)) {
662                 return -1;
663         }
664
665         if (validate_rtpmap(test, m_line, "VP8")) {
666                 return -1;
667         }
668
669         if (!validate_rtpmap(test, m_line, "H264")) {
670                 return -1;
671         }
672
673         return 0;
674 }
675
676 AST_TEST_DEFINE(sdp_merge_symmetric)
677 {
678         enum ast_test_result_state res = AST_TEST_PASS;
679         struct ast_sdp_state *sdp_state_offerer = NULL;
680         struct ast_sdp_state *sdp_state_answerer = NULL;
681         const struct ast_sdp *offerer_sdp;
682         const struct ast_sdp *answerer_sdp;
683
684         static const struct sdp_format offerer_formats[] = {
685                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
686                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
687         };
688         static const struct sdp_format answerer_formats[] = {
689                 { AST_MEDIA_TYPE_AUDIO, "ulaw" },
690                 { AST_MEDIA_TYPE_VIDEO, "vp8" },
691         };
692
693         switch(cmd) {
694         case TEST_INIT:
695                 info->name = "sdp_merge_symmetric";
696                 info->category = "/main/sdp/";
697                 info->summary = "Merge two SDPs with symmetric stream types";
698                 info->description =
699                         "SDPs 1 and 2 each have one audio and one video stream (in that order).\n"
700                         "SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
701                         "the expected stream types and the expected formats";
702                 return AST_TEST_NOT_RUN;
703         case TEST_EXECUTE:
704                 break;
705         }
706
707         sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats);
708         if (!sdp_state_offerer) {
709                 res = AST_TEST_FAIL;
710                 goto end;
711         }
712
713         sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats);
714         if (!sdp_state_answerer) {
715                 res = AST_TEST_FAIL;
716                 goto end;
717         }
718
719         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
720         if (!offerer_sdp) {
721                 res = AST_TEST_FAIL;
722                 goto end;
723         }
724
725         ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
726         answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
727         if (!answerer_sdp) {
728                 res = AST_TEST_FAIL;
729                 goto end;
730         }
731
732         ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
733
734         /* Get the offerer SDP again because it's now going to be the joint SDP */
735         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
736         if (validate_merged_sdp(test, offerer_sdp)) {
737                 res = AST_TEST_FAIL;
738                 goto end;
739         }
740         if (validate_merged_sdp(test, answerer_sdp)) {
741                 res = AST_TEST_FAIL;
742                 goto end;
743         }
744
745 end:
746         ast_sdp_state_free(sdp_state_offerer);
747         ast_sdp_state_free(sdp_state_answerer);
748
749         return res;
750 }
751
752 AST_TEST_DEFINE(sdp_merge_crisscross)
753 {
754         enum ast_test_result_state res = AST_TEST_PASS;
755         struct ast_sdp_state *sdp_state_offerer = NULL;
756         struct ast_sdp_state *sdp_state_answerer = NULL;
757         const struct ast_sdp *offerer_sdp;
758         const struct ast_sdp *answerer_sdp;
759
760         static const struct sdp_format offerer_formats[] = {
761                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
762                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
763         };
764         static const struct sdp_format answerer_formats[] = {
765                 { AST_MEDIA_TYPE_VIDEO, "vp8" },
766                 { AST_MEDIA_TYPE_AUDIO, "ulaw" },
767         };
768
769         switch(cmd) {
770         case TEST_INIT:
771                 info->name = "sdp_merge_crisscross";
772                 info->category = "/main/sdp/";
773                 info->summary = "Merge two SDPs with symmetric stream types";
774                 info->description =
775                         "SDPs 1 and 2 each have one audio and one video stream. However, SDP 1 and\n"
776                         "2 natively have the formats in a different order.\n"
777                         "SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
778                         "the expected stream types and the expected formats. Since SDP 1 was the\n"
779                         "offerer, the format order on SDP 1 should determine the order of formats in the SDPs";
780                 return AST_TEST_NOT_RUN;
781         case TEST_EXECUTE:
782                 break;
783         }
784
785         sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats);
786         if (!sdp_state_offerer) {
787                 res = AST_TEST_FAIL;
788                 goto end;
789         }
790
791         sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats);
792         if (!sdp_state_answerer) {
793                 res = AST_TEST_FAIL;
794                 goto end;
795         }
796
797         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
798         if (!offerer_sdp) {
799                 res = AST_TEST_FAIL;
800                 goto end;
801         }
802
803         ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
804         answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
805         if (!answerer_sdp) {
806                 res = AST_TEST_FAIL;
807                 goto end;
808         }
809
810         ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
811
812         /* Get the offerer SDP again because it's now going to be the joint SDP */
813         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
814         if (validate_merged_sdp(test, offerer_sdp)) {
815                 res = AST_TEST_FAIL;
816                 goto end;
817         }
818         if (validate_merged_sdp(test, answerer_sdp)) {
819                 res = AST_TEST_FAIL;
820                 goto end;
821         }
822
823 end:
824         ast_sdp_state_free(sdp_state_offerer);
825         ast_sdp_state_free(sdp_state_answerer);
826
827         return res;
828 }
829
830 static int unload_module(void)
831 {
832         AST_TEST_UNREGISTER(invalid_rtpmap);
833         AST_TEST_UNREGISTER(rtpmap);
834         AST_TEST_UNREGISTER(find_attr);
835         AST_TEST_UNREGISTER(topology_to_sdp);
836         AST_TEST_UNREGISTER(sdp_to_topology);
837         AST_TEST_UNREGISTER(sdp_merge_symmetric);
838         AST_TEST_UNREGISTER(sdp_merge_crisscross);
839
840         return 0;
841 }
842
843 static int load_module(void)
844 {
845         AST_TEST_REGISTER(invalid_rtpmap);
846         AST_TEST_REGISTER(rtpmap);
847         AST_TEST_REGISTER(find_attr);
848         AST_TEST_REGISTER(topology_to_sdp);
849         AST_TEST_REGISTER(sdp_to_topology);
850         AST_TEST_REGISTER(sdp_merge_symmetric);
851         AST_TEST_REGISTER(sdp_merge_crisscross);
852
853         return AST_MODULE_LOAD_SUCCESS;
854 }
855
856 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SDP tests");