SDP: Update ast_get_topology_from_sdp() to keep RTP map.
[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 #include "asterisk/rtp_engine.h"
33
34 static int validate_o_line(struct ast_test *test, const struct ast_sdp_o_line *o_line,
35         const char *sdpowner, const char *address_type, const char *address)
36 {
37         if (!o_line) {
38                 return -1;
39         }
40
41         if (strcmp(o_line->username, sdpowner)) {
42                 ast_test_status_update(test, "Expected o-line SDP owner %s but got %s\n",
43                         sdpowner, o_line->username);
44                 return -1;
45         }
46
47         if (strcmp(o_line->address_type, address_type)) {
48                 ast_test_status_update(test, "Expected o-line SDP address type %s but got %s\n",
49                         address_type, o_line->address_type);
50                 return -1;
51         }
52
53         if (strcmp(o_line->address, address)) {
54                 ast_test_status_update(test, "Expected o-line SDP address %s but got %s\n",
55                         address, o_line->address);
56                 return -1;
57         }
58
59         ast_test_status_update(test, "SDP o-line is as expected!\n");
60         return 0;
61 }
62
63 static int validate_c_line(struct ast_test *test, const struct ast_sdp_c_line *c_line,
64         const char *address_type, const char *address)
65 {
66         if (strcmp(c_line->address_type, address_type)) {
67                 ast_test_status_update(test, "Expected c-line SDP address type %s but got %s\n",
68                         address_type, c_line->address_type);
69                 return -1;
70         }
71
72         if (strcmp(c_line->address, address)) {
73                 ast_test_status_update(test, "Expected c-line SDP address %s but got %s\n",
74                         address, c_line->address);
75                 return -1;
76         }
77
78         ast_test_status_update(test, "SDP c-line is as expected!\n");
79         return 0;
80 }
81
82 static int validate_m_line(struct ast_test *test, const struct ast_sdp_m_line *m_line,
83         const char *media_type, int num_payloads)
84 {
85         if (strcmp(m_line->type, media_type)) {
86                 ast_test_status_update(test, "Expected m-line media type %s but got %s\n",
87                         media_type, m_line->type);
88                 return -1;
89         }
90
91         if (ast_sdp_m_get_payload_count(m_line) != num_payloads) {
92                 ast_test_status_update(test, "Expected m-line payload count %d but got %d\n",
93                         num_payloads, ast_sdp_m_get_payload_count(m_line));
94                 return -1;
95         }
96
97         ast_test_status_update(test, "SDP m-line is as expected\n");
98         return 0;
99 }
100
101 static int validate_rtpmap(struct ast_test *test, const struct ast_sdp_m_line *m_line,
102         const char *media_name)
103 {
104         struct ast_sdp_a_line *a_line;
105         int i;
106
107         for (i = 0; i < ast_sdp_m_get_a_count(m_line); ++i) {
108                 struct ast_sdp_rtpmap *rtpmap;
109                 int match;
110
111                 a_line = ast_sdp_m_get_a(m_line, i);
112                 if (strcmp(a_line->name, "rtpmap")) {
113                         continue;
114                 }
115
116                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
117                 if (!rtpmap) {
118                         return -1;
119                 }
120
121                 match = !strcmp(rtpmap->encoding_name, media_name);
122
123                 ast_sdp_rtpmap_free(rtpmap);
124                 if (match) {
125                         return 0;
126                 }
127         }
128
129         ast_test_status_update(test, "Could not find rtpmap with encoding name %s\n", media_name);
130
131         return -1;
132 }
133
134 static enum ast_test_result_state validate_t38(struct ast_test *test, const struct ast_sdp_m_line *m_line)
135 {
136         struct ast_sdp_a_line *a_line;
137
138         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxVersion", -1);
139         ast_test_validate(test, a_line && !strcmp(a_line->value, "0"));
140
141         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxMaxBitRate", -1);
142         ast_test_validate(test, a_line && !strcmp(a_line->value, "14400"));
143
144         a_line = ast_sdp_m_find_attribute(m_line, "T38FaxRateManagement", -1);
145         ast_test_validate(test, a_line && !strcmp(a_line->value, "transferredTCF"));
146
147         return AST_TEST_PASS;
148 }
149
150 AST_TEST_DEFINE(invalid_rtpmap)
151 {
152         /* a=rtpmap: is already assumed. This is the part after that */
153         static const char *invalids[] = {
154                 "J PCMU/8000",
155                 "0 PCMU:8000",
156                 "0 PCMU/EIGHT-THOUSAND",
157                 "0 PCMU/8000million/2",
158                 "0 PCMU//2",
159                 "0 /8000/2",
160                 "0 PCMU/8000/",
161                 "0 PCMU/8000million",
162         };
163         int i;
164         enum ast_test_result_state res = AST_TEST_PASS;
165
166         switch(cmd) {
167         case TEST_INIT:
168                 info->name = "invalid_rtpmap";
169                 info->category = "/main/sdp/";
170                 info->summary = "Ensure invalid rtpmaps are rejected";
171                 info->description =
172                         "Try to convert several invalid rtpmap attributes. If\n"
173                         "any succeeds, the test fails.";
174                 return AST_TEST_NOT_RUN;
175         case TEST_EXECUTE:
176                 break;
177         }
178
179         for (i = 0; i < ARRAY_LEN(invalids); ++i) {
180                 struct ast_sdp_a_line *a_line;
181                 struct ast_sdp_rtpmap *rtpmap;
182
183                 a_line = ast_sdp_a_alloc("rtpmap", invalids[i]);
184                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
185                 if (rtpmap) {
186                         ast_test_status_update(test, "Invalid rtpmap '%s' was accepted as valid\n",
187                                 invalids[i]);
188                         res = AST_TEST_FAIL;
189                 }
190                 ast_sdp_a_free(a_line);
191                 ast_sdp_rtpmap_free(rtpmap);
192         }
193
194         return res;
195 }
196
197 AST_TEST_DEFINE(rtpmap)
198 {
199         static const char *valids[] = {
200                 "0 PCMU/8000",
201                 "107 opus/48000/2",
202         };
203         static int payloads[] = {
204                 0,
205                 107,
206         };
207         static const char *encoding_names[] = {
208                 "PCMU",
209                 "opus",
210         };
211         static int clock_rates[] = {
212                 8000,
213                 48000,
214         };
215         static const char *encoding_parameters[] = {
216                 "",
217                 "2",
218         };
219         int i;
220         enum ast_test_result_state res = AST_TEST_PASS;
221
222         switch(cmd) {
223         case TEST_INIT:
224                 info->name = "rtpmap";
225                 info->category = "/main/sdp/";
226                 info->summary = "Ensure rtpmap attribute values are parsed correctly";
227                 info->description =
228                         "Parse several valid rtpmap attributes. Ensure that the parsed values\n"
229                         "are what we expect";
230                 return AST_TEST_NOT_RUN;
231         case TEST_EXECUTE:
232                 break;
233         }
234
235         for (i = 0; i < ARRAY_LEN(valids); ++i) {
236                 struct ast_sdp_a_line *a_line;
237                 struct ast_sdp_rtpmap *rtpmap;
238
239                 a_line = ast_sdp_a_alloc("rtpmap", valids[i]);
240                 rtpmap = ast_sdp_a_get_rtpmap(a_line);
241                 if (!rtpmap) {
242                         ast_test_status_update(test, "Valid rtpmap '%s' was rejected as invalid\n",
243                                 valids[i]);
244                         res = AST_TEST_FAIL;
245                         continue;
246                 }
247                 if (rtpmap->payload != payloads[i]) {
248                         ast_test_status_update(test, "RTPmap payload '%d' does not match expected '%d'\n",
249                                 rtpmap->payload, payloads[i]);
250                         res = AST_TEST_FAIL;
251                 }
252                 if (strcmp(rtpmap->encoding_name, encoding_names[i])) {
253                         ast_test_status_update(test, "RTPmap encoding_name '%s' does not match expected '%s'\n",
254                                 rtpmap->encoding_name, encoding_names[i]);
255                         res = AST_TEST_FAIL;
256                 }
257                 if (rtpmap->clock_rate != clock_rates[i]) {
258                         ast_test_status_update(test, "RTPmap clock rate '%d' does not match expected '%d'\n",
259                                 rtpmap->clock_rate, clock_rates[i]);
260                         res = AST_TEST_FAIL;
261                 }
262                 if (strcmp(rtpmap->encoding_parameters, encoding_parameters[i])) {
263                         ast_test_status_update(test, "RTPmap encoding_parameter '%s' does not match expected '%s'\n",
264                                 rtpmap->encoding_parameters, encoding_parameters[i]);
265                         res = AST_TEST_FAIL;
266                 }
267                 ast_sdp_a_free(a_line);
268                 ast_sdp_rtpmap_free(rtpmap);
269         }
270
271         return res;
272 }
273
274 AST_TEST_DEFINE(find_attr)
275 {
276         enum ast_test_result_state res = AST_TEST_PASS;
277         struct ast_sdp_m_line *m_line;
278         struct ast_sdp_a_line *a_line;
279
280         switch(cmd) {
281         case TEST_INIT:
282                 info->name = "find_attr";
283                 info->category = "/main/sdp/";
284                 info->summary = "Ensure that finding attributes works as expected";
285                 info->description =
286                         "An SDP m-line is created, and two attributes are added.\n"
287                         "We then attempt a series of attribute-finding calls that are expected to work\n"
288                         "followed by a series of attribute-finding calls that are expected fo fail.";
289                 return AST_TEST_NOT_RUN;
290         case TEST_EXECUTE:
291                 break;
292         }
293
294         m_line = ast_sdp_m_alloc("audio", 666, 1, "RTP/AVP", NULL);
295         if (!m_line) {
296                 res = AST_TEST_FAIL;
297                 goto end;
298         }
299         a_line = ast_sdp_a_alloc("foo", "0 bar");
300         if (!a_line) {
301                 res = AST_TEST_FAIL;
302                 goto end;
303         }
304         ast_sdp_m_add_a(m_line, a_line);
305
306         a_line = ast_sdp_a_alloc("baz", "howdy");
307         if (!a_line) {
308                 res = AST_TEST_FAIL;
309                 goto end;
310         }
311         ast_sdp_m_add_a(m_line, a_line);
312
313         /* These should work */
314         a_line = ast_sdp_m_find_attribute(m_line, "foo", 0);
315         if (!a_line) {
316                 ast_test_status_update(test, "Failed to find attribute 'foo' with payload '0'\n");
317                 res = AST_TEST_FAIL;
318         }
319         a_line = ast_sdp_m_find_attribute(m_line, "foo", -1);
320         if (!a_line) {
321                 ast_test_status_update(test, "Failed to find attribute 'foo' with unspecified payload\n");
322                 res = AST_TEST_FAIL;
323         }
324         a_line = ast_sdp_m_find_attribute(m_line, "baz", -1);
325         if (!a_line) {
326                 ast_test_status_update(test, "Failed to find attribute 'baz' with unspecified payload\n");
327                 res = AST_TEST_FAIL;
328         }
329
330         /* These should fail */
331         a_line = ast_sdp_m_find_attribute(m_line, "foo", 1);
332         if (a_line) {
333                 ast_test_status_update(test, "Found non-existent attribute 'foo' with payload '1'\n");
334                 res = AST_TEST_FAIL;
335         }
336         a_line = ast_sdp_m_find_attribute(m_line, "baz", 0);
337         if (a_line) {
338                 ast_test_status_update(test, "Found non-existent attribute 'baz' with payload '0'\n");
339                 res = AST_TEST_FAIL;
340         }
341         a_line = ast_sdp_m_find_attribute(m_line, "wibble", 0);
342         if (a_line) {
343                 ast_test_status_update(test, "Found non-existent attribute 'wibble' with payload '0'\n");
344                 res = AST_TEST_FAIL;
345         }
346         a_line = ast_sdp_m_find_attribute(m_line, "wibble", -1);
347         if (a_line) {
348                 ast_test_status_update(test, "Found non-existent attribute 'foo' with unspecified payload\n");
349                 res = AST_TEST_FAIL;
350         }
351
352 end:
353         ast_sdp_m_free(m_line);
354         return res;
355 }
356
357 static struct ast_sdp_options *sdp_options_common(void)
358 {
359         struct ast_sdp_options *options;
360
361         options = ast_sdp_options_alloc();
362         if (!options) {
363                 return NULL;
364         }
365         ast_sdp_options_set_media_address(options, "127.0.0.1");
366         ast_sdp_options_set_sdpowner(options, "me");
367         ast_sdp_options_set_rtp_engine(options, "asterisk");
368         ast_sdp_options_set_impl(options, AST_SDP_IMPL_PJMEDIA);
369
370         return options;
371 }
372
373 struct sdp_format {
374         enum ast_media_type type;
375         const char *formats;
376 };
377
378 /*!
379  * \brief Common method to build an SDP state for a test.
380  *
381  * This uses the passed-in formats to create a stream topology, which is then used to create the SDP
382  * state.
383  *
384  * There is an optional test_options field you can use if your test has specific options you need to
385  * set. If your test does not require anything special, it can just pass NULL for this parameter. If
386  * you do pass in test_options, this function steals ownership of those options.
387  *
388  * \param num_streams The number of elements in the formats array.
389  * \param formats Array of media types and formats that will be in the state.
390  * \param test_options Optional SDP options.
391  */
392 static struct ast_sdp_state *build_sdp_state(int num_streams, const struct sdp_format *formats, struct ast_sdp_options *test_options)
393 {
394         struct ast_stream_topology *topology = NULL;
395         struct ast_sdp_state *state = NULL;
396         struct ast_sdp_options *options;
397         int i;
398
399         if (!test_options) {
400                 options = sdp_options_common();
401                 if (!options) {
402                         goto end;
403                 }
404         } else {
405                 options = test_options;
406         }
407
408         topology = ast_stream_topology_alloc();
409         if (!topology) {
410                 goto end;
411         }
412
413         for (i = 0; i < num_streams; ++i) {
414                 RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
415                 struct ast_stream *stream;
416
417                 caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
418                 if (!caps) {
419                         goto end;
420                 }
421                 if (ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {
422                         goto end;
423                 }
424                 stream = ast_stream_alloc("sure_thing", formats[i].type);
425                 if (!stream) {
426                         goto end;
427                 }
428                 ast_stream_set_formats(stream, caps);
429                 ast_stream_topology_append_stream(topology, stream);
430         }
431
432         state = ast_sdp_state_alloc(topology, options);
433         if (!state) {
434                 goto end;
435         }
436
437 end:
438         ast_stream_topology_free(topology);
439         if (!state) {
440                 ast_sdp_options_free(options);
441         }
442
443         return state;
444 }
445
446 AST_TEST_DEFINE(topology_to_sdp)
447 {
448         enum ast_test_result_state res = AST_TEST_FAIL;
449         struct ast_sdp_state *sdp_state = NULL;
450         const struct ast_sdp *sdp = NULL;
451         struct ast_sdp_m_line *m_line = NULL;
452         struct sdp_format formats[] = {
453                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
454                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
455                 { AST_MEDIA_TYPE_IMAGE, "t38" },
456         };
457
458         switch(cmd) {
459         case TEST_INIT:
460                 info->name = "topology_to_sdp";
461                 info->category = "/main/sdp/";
462                 info->summary = "Convert a topology into an SDP";
463                 info->description =
464                         "Ensure SDPs get converted to expected stream topology";
465                 return AST_TEST_NOT_RUN;
466         case TEST_EXECUTE:
467                 break;
468         }
469
470         sdp_state = build_sdp_state(ARRAY_LEN(formats), formats, NULL);
471         if (!sdp_state) {
472                 goto end;
473         }
474
475         sdp = ast_sdp_state_get_local_sdp(sdp_state);
476         if (!sdp) {
477                 goto end;
478         }
479
480         if (validate_o_line(test, sdp->o_line, "me", "IP4", "127.0.0.1")) {
481                 goto end;
482         }
483
484         if (validate_c_line(test, sdp->c_line, "IP4", "127.0.0.1")) {
485                 goto end;
486         }
487
488         if (ast_sdp_get_m_count(sdp) != 3) {
489                 ast_test_status_update(test, "Unexpected number of streams in generated SDP: %d\n",
490                         ast_sdp_get_m_count(sdp));
491                 goto end;
492         }
493
494         m_line = ast_sdp_get_m(sdp, 0);
495
496         if (validate_m_line(test, m_line, "audio", 4)) {
497                 goto end;
498         }
499
500         if (validate_rtpmap(test, m_line, "PCMU")) {
501                 goto end;
502         }
503
504         if (validate_rtpmap(test, m_line, "PCMA")) {
505                 goto end;
506         }
507
508         if (validate_rtpmap(test, m_line, "G722")) {
509                 goto end;
510         }
511
512         if (validate_rtpmap(test, m_line, "opus")) {
513                 goto end;
514         }
515
516         m_line = ast_sdp_get_m(sdp, 1);
517         if (validate_m_line(test, m_line, "video", 2)) {
518                 goto end;
519         }
520
521         if (validate_rtpmap(test, m_line, "VP8")) {
522                 goto end;
523         }
524
525         if (validate_rtpmap(test, m_line, "H264")) {
526                 goto end;
527         }
528
529         m_line = ast_sdp_get_m(sdp, 2);
530         if (validate_m_line(test, m_line, "image", 1)) {
531                 goto end;
532         }
533         if (validate_t38(test, m_line) != AST_TEST_PASS) {
534                 goto end;
535         }
536
537         res = AST_TEST_PASS;
538
539 end:
540         ast_sdp_state_free(sdp_state);
541         return res;
542 }
543
544 static int validate_formats(struct ast_test *test, struct ast_stream_topology *topology, int index,
545         enum ast_media_type type, int format_count, const char **expected_formats)
546 {
547         struct ast_stream *stream;
548         struct ast_format_cap *caps;
549         struct ast_format *format;
550         int i;
551
552         stream = ast_stream_topology_get_stream(topology, index);
553         if (ast_stream_get_type(stream) != type) {
554                 ast_test_status_update(test, "Unexpected stream type encountered\n");
555                 return -1;
556         }
557         caps = ast_stream_get_formats(stream);
558
559         if (ast_format_cap_count(caps) != format_count) {
560                 ast_test_status_update(test, "Unexpected format count '%d'. Expecting '%d'\n",
561                         (int) ast_format_cap_count(caps), format_count);
562                 return -1;
563         }
564
565         for (i = 0; i < ast_format_cap_count(caps); ++i) {
566                 format = ast_format_cap_get_format(caps, i);
567                 if (strcmp(ast_format_get_name(format), expected_formats[i])) {
568                         ast_test_status_update(test, "Unexpected format '%s'at index %d. Expected '%s'\n",
569                                 ast_format_get_name(format), i, expected_formats[i]);
570                         return -1;
571                 }
572         }
573
574         return 0;
575 }
576
577 AST_TEST_DEFINE(sdp_to_topology)
578 {
579         enum ast_test_result_state res = AST_TEST_PASS;
580         struct ast_sdp_state *sdp_state;
581         const struct ast_sdp *sdp;
582         struct ast_stream_topology *topology = NULL;
583         struct sdp_format sdp_formats[] = {
584                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
585                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
586                 { AST_MEDIA_TYPE_IMAGE, "t38" },
587         };
588         static const char *expected_audio_formats[] = {
589                 "ulaw",
590                 "alaw",
591                 "g722",
592                 "opus",
593         };
594         static const char *expected_video_formats[] = {
595                 "h264",
596                 "vp8",
597         };
598         static const char *expected_image_formats[] = {
599                 "t38",
600         };
601
602         switch(cmd) {
603         case TEST_INIT:
604                 info->name = "sdp_to_topology";
605                 info->category = "/main/sdp/";
606                 info->summary = "Convert an SDP into a topology";
607                 info->description =
608                         "Ensure SDPs get converted to expected stream topology";
609                 return AST_TEST_NOT_RUN;
610         case TEST_EXECUTE:
611                 break;
612         }
613
614         sdp_state = build_sdp_state(ARRAY_LEN(sdp_formats), sdp_formats, NULL);
615         if (!sdp_state) {
616                 res = AST_TEST_FAIL;
617                 goto end;
618         }
619
620         sdp = ast_sdp_state_get_local_sdp(sdp_state);
621         if (!sdp) {
622                 res = AST_TEST_FAIL;
623                 goto end;
624         }
625
626         topology = ast_get_topology_from_sdp(sdp, 0);
627         if (!topology) {
628                 res = AST_TEST_FAIL;
629                 goto end;
630         }
631
632         if (ast_stream_topology_get_count(topology) != 3) {
633                 ast_test_status_update(test, "Unexpected topology count '%d'. Expecting 2\n",
634                         ast_stream_topology_get_count(topology));
635                 res = AST_TEST_FAIL;
636                 goto end;
637         }
638
639         if (validate_formats(test, topology, 0, AST_MEDIA_TYPE_AUDIO,
640                         ARRAY_LEN(expected_audio_formats), expected_audio_formats)) {
641                 res = AST_TEST_FAIL;
642                 goto end;
643         }
644
645         if (validate_formats(test, topology, 1, AST_MEDIA_TYPE_VIDEO,
646                         ARRAY_LEN(expected_video_formats), expected_video_formats)) {
647                 res = AST_TEST_FAIL;
648                 goto end;
649         }
650
651         if (validate_formats(test, topology, 2, AST_MEDIA_TYPE_IMAGE,
652                         ARRAY_LEN(expected_image_formats), expected_image_formats)) {
653                 res = AST_TEST_FAIL;
654                 goto end;
655         }
656
657 end:
658         ast_sdp_state_free(sdp_state);
659         ast_stream_topology_free(topology);
660         return res;
661 }
662
663 static int validate_merged_sdp(struct ast_test *test, const struct ast_sdp *sdp)
664 {
665         struct ast_sdp_m_line *m_line;
666
667         if (!sdp) {
668                 return -1;
669         }
670
671         m_line = ast_sdp_get_m(sdp, 0);
672
673         if (validate_m_line(test, m_line, "audio", 1)) {
674                 return -1;
675         }
676
677         if (validate_rtpmap(test, m_line, "PCMU")) {
678                 return -1;
679         }
680
681         /* The other audio formats should *NOT* be present */
682         if (!validate_rtpmap(test, m_line, "PCMA")) {
683                 return -1;
684         }
685
686         if (!validate_rtpmap(test, m_line, "G722")) {
687                 return -1;
688         }
689
690         if (!validate_rtpmap(test, m_line, "opus")) {
691                 return -1;
692         }
693
694         m_line = ast_sdp_get_m(sdp, 1);
695
696         if (validate_m_line(test, m_line, "video", 1)) {
697                 return -1;
698         }
699
700         if (validate_rtpmap(test, m_line, "VP8")) {
701                 return -1;
702         }
703
704         if (!validate_rtpmap(test, m_line, "H264")) {
705                 return -1;
706         }
707
708         return 0;
709 }
710
711 AST_TEST_DEFINE(sdp_merge_symmetric)
712 {
713         enum ast_test_result_state res = AST_TEST_PASS;
714         struct ast_sdp_state *sdp_state_offerer = NULL;
715         struct ast_sdp_state *sdp_state_answerer = NULL;
716         const struct ast_sdp *offerer_sdp;
717         const struct ast_sdp *answerer_sdp;
718
719         static const struct sdp_format offerer_formats[] = {
720                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
721                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
722         };
723         static const struct sdp_format answerer_formats[] = {
724                 { AST_MEDIA_TYPE_AUDIO, "ulaw" },
725                 { AST_MEDIA_TYPE_VIDEO, "vp8" },
726         };
727
728         switch(cmd) {
729         case TEST_INIT:
730                 info->name = "sdp_merge_symmetric";
731                 info->category = "/main/sdp/";
732                 info->summary = "Merge two SDPs with symmetric stream types";
733                 info->description =
734                         "SDPs 1 and 2 each have one audio and one video stream (in that order).\n"
735                         "SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
736                         "the expected stream types and the expected formats";
737                 return AST_TEST_NOT_RUN;
738         case TEST_EXECUTE:
739                 break;
740         }
741
742         sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats, NULL);
743         if (!sdp_state_offerer) {
744                 res = AST_TEST_FAIL;
745                 goto end;
746         }
747
748         sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats, NULL);
749         if (!sdp_state_answerer) {
750                 res = AST_TEST_FAIL;
751                 goto end;
752         }
753
754         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
755         if (!offerer_sdp) {
756                 res = AST_TEST_FAIL;
757                 goto end;
758         }
759
760         ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
761         answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
762         if (!answerer_sdp) {
763                 res = AST_TEST_FAIL;
764                 goto end;
765         }
766
767         ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
768
769         /* Get the offerer SDP again because it's now going to be the joint SDP */
770         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
771         if (validate_merged_sdp(test, offerer_sdp)) {
772                 res = AST_TEST_FAIL;
773                 goto end;
774         }
775         if (validate_merged_sdp(test, answerer_sdp)) {
776                 res = AST_TEST_FAIL;
777                 goto end;
778         }
779
780 end:
781         ast_sdp_state_free(sdp_state_offerer);
782         ast_sdp_state_free(sdp_state_answerer);
783
784         return res;
785 }
786
787 AST_TEST_DEFINE(sdp_merge_crisscross)
788 {
789         enum ast_test_result_state res = AST_TEST_PASS;
790         struct ast_sdp_state *sdp_state_offerer = NULL;
791         struct ast_sdp_state *sdp_state_answerer = NULL;
792         const struct ast_sdp *offerer_sdp;
793         const struct ast_sdp *answerer_sdp;
794
795         static const struct sdp_format offerer_formats[] = {
796                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
797                 { AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
798         };
799         static const struct sdp_format answerer_formats[] = {
800                 { AST_MEDIA_TYPE_VIDEO, "vp8" },
801                 { AST_MEDIA_TYPE_AUDIO, "ulaw" },
802         };
803
804         switch(cmd) {
805         case TEST_INIT:
806                 info->name = "sdp_merge_crisscross";
807                 info->category = "/main/sdp/";
808                 info->summary = "Merge two SDPs with symmetric stream types";
809                 info->description =
810                         "SDPs 1 and 2 each have one audio and one video stream. However, SDP 1 and\n"
811                         "2 natively have the formats in a different order.\n"
812                         "SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
813                         "the expected stream types and the expected formats. Since SDP 1 was the\n"
814                         "offerer, the format order on SDP 1 should determine the order of formats in the SDPs";
815                 return AST_TEST_NOT_RUN;
816         case TEST_EXECUTE:
817                 break;
818         }
819
820         sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats, NULL);
821         if (!sdp_state_offerer) {
822                 res = AST_TEST_FAIL;
823                 goto end;
824         }
825
826         sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats, NULL);
827         if (!sdp_state_answerer) {
828                 res = AST_TEST_FAIL;
829                 goto end;
830         }
831
832         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
833         if (!offerer_sdp) {
834                 res = AST_TEST_FAIL;
835                 goto end;
836         }
837
838         ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
839         answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
840         if (!answerer_sdp) {
841                 res = AST_TEST_FAIL;
842                 goto end;
843         }
844
845         ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
846
847         /* Get the offerer SDP again because it's now going to be the joint SDP */
848         offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
849         if (validate_merged_sdp(test, offerer_sdp)) {
850                 res = AST_TEST_FAIL;
851                 goto end;
852         }
853         if (validate_merged_sdp(test, answerer_sdp)) {
854                 res = AST_TEST_FAIL;
855                 goto end;
856         }
857
858 end:
859         ast_sdp_state_free(sdp_state_offerer);
860         ast_sdp_state_free(sdp_state_answerer);
861
862         return res;
863 }
864
865 static int validate_ssrc(struct ast_test *test, struct ast_sdp_m_line *m_line,
866         struct ast_rtp_instance *rtp)
867 {
868         unsigned int ssrc;
869         const char *cname;
870         struct ast_sdp_a_line *a_line;
871         char attr_value[128];
872
873         ssrc = ast_rtp_instance_get_ssrc(rtp);
874         cname = ast_rtp_instance_get_cname(rtp);
875
876         snprintf(attr_value, sizeof(attr_value), "%u cname:%s", ssrc, cname);
877
878         a_line = ast_sdp_m_find_attribute(m_line, "ssrc", -1);
879         if (!a_line) {
880                 ast_test_status_update(test, "Could not find 'ssrc' attribute\n");
881                 return -1;
882         }
883
884         if (strcmp(a_line->value, attr_value)) {
885                 ast_test_status_update(test, "SDP attribute '%s' did not match expected attribute '%s'\n",
886                         a_line->value, attr_value);
887                 return -1;
888         }
889
890         return 0;
891 }
892
893 AST_TEST_DEFINE(sdp_ssrc_attributes)
894 {
895         enum ast_test_result_state res;
896         struct ast_sdp_state *test_state = NULL;
897         struct ast_sdp_options *options;
898         struct sdp_format formats[] = {
899                 { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
900         };
901         const struct ast_sdp *sdp;
902         struct ast_sdp_m_line *m_line;
903         struct ast_rtp_instance *rtp;
904
905         switch(cmd) {
906         case TEST_INIT:
907                 info->name = "sdp_ssrc_attributes";
908                 info->category = "/main/sdp/";
909                 info->summary = "Ensure SSRC-level attributes are added to local SDPs";
910                 info->description =
911                         "An SDP is created and is instructed to include SSRC-level attributes.\n"
912                         "This test ensures that the CNAME SSRC-level attribute is present and\n"
913                         "that the values match what the RTP instance reports";
914                 return AST_TEST_NOT_RUN;
915         case TEST_EXECUTE:
916                 break;
917         }
918
919         res = AST_TEST_FAIL;
920
921         options = sdp_options_common();
922         if (!options) {
923                 ast_test_status_update(test, "Failed to allocate SDP options\n");
924                 goto end;
925         }
926         ast_sdp_options_set_ssrc(options, 1);
927
928         test_state = build_sdp_state(ARRAY_LEN(formats), formats, options);
929         if (!test_state) {
930                 ast_test_status_update(test, "Failed to create SDP state\n");
931                 goto end;
932         }
933
934         sdp = ast_sdp_state_get_local_sdp(test_state);
935         if (!sdp) {
936                 ast_test_status_update(test, "Failed to get local SDP\n");
937                 goto end;
938         }
939
940         /* Need a couple of sanity checks */
941         if (ast_sdp_get_m_count(sdp) != ARRAY_LEN(formats)) {
942                 ast_test_status_update(test, "SDP m count is %d instead of %zu\n",
943                         ast_sdp_get_m_count(sdp), ARRAY_LEN(formats));
944                 goto end;
945         }
946
947         m_line = ast_sdp_get_m(sdp, 0);
948         if (!m_line) {
949                 ast_test_status_update(test, "Failed to get SDP m-line\n");
950                 goto end;
951         }
952
953         rtp = ast_sdp_state_get_rtp_instance(test_state, 0);
954         if (!rtp) {
955                 ast_test_status_update(test, "Failed to get the RTP instance\n");
956                 goto end;
957         }
958
959         if (validate_ssrc(test, m_line, rtp)) {
960                 goto end;
961         }
962
963         res = AST_TEST_PASS;
964
965 end:
966         ast_sdp_state_free(test_state);
967         return res;
968 }
969
970 static int unload_module(void)
971 {
972         AST_TEST_UNREGISTER(invalid_rtpmap);
973         AST_TEST_UNREGISTER(rtpmap);
974         AST_TEST_UNREGISTER(find_attr);
975         AST_TEST_UNREGISTER(topology_to_sdp);
976         AST_TEST_UNREGISTER(sdp_to_topology);
977         AST_TEST_UNREGISTER(sdp_merge_symmetric);
978         AST_TEST_UNREGISTER(sdp_merge_crisscross);
979         AST_TEST_UNREGISTER(sdp_ssrc_attributes);
980
981         return 0;
982 }
983
984 static int load_module(void)
985 {
986         AST_TEST_REGISTER(invalid_rtpmap);
987         AST_TEST_REGISTER(rtpmap);
988         AST_TEST_REGISTER(find_attr);
989         AST_TEST_REGISTER(topology_to_sdp);
990         AST_TEST_REGISTER(sdp_to_topology);
991         AST_TEST_REGISTER(sdp_merge_symmetric);
992         AST_TEST_REGISTER(sdp_merge_crisscross);
993         AST_TEST_REGISTER(sdp_ssrc_attributes);
994
995         return AST_MODULE_LOAD_SUCCESS;
996 }
997
998 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SDP tests");