1 use super::common::*;
2 use super::media::*;
3 use super::session::*;
4 use crate::error::{Error, Result};
5 
6 use std::io::Cursor;
7 use url::Url;
8 
9 const CANONICAL_MARSHAL_SDP: &str = "v=0\r\n\
10      o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
11      s=SDP Seminar\r\n\
12      i=A Seminar on the session description protocol\r\n\
13      u=http://www.example.com/seminars/sdp.pdf\r\n\
14      [email protected] (Jane Doe)\r\n\
15      p=+1 617 555-6011\r\n\
16      c=IN IP4 224.2.17.12/127\r\n\
17      b=X-YZ:128\r\n\
18      b=AS:12345\r\n\
19      t=2873397496 2873404696\r\n\
20      t=3034423619 3042462419\r\n\
21      r=604800 3600 0 90000\r\n\
22      z=2882844526 -3600 2898848070 0\r\n\
23      k=prompt\r\n\
24      a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
25      a=recvonly\r\n\
26      m=audio 49170 RTP/AVP 0\r\n\
27      i=Vivamus a posuere nisl\r\n\
28      c=IN IP4 203.0.113.1\r\n\
29      b=X-YZ:128\r\n\
30      k=prompt\r\n\
31      a=sendrecv\r\n\
32      m=video 51372 RTP/AVP 99\r\n\
33      a=rtpmap:99 h263-1998/90000\r\n";
34 
35 #[test]
test_unmarshal_marshal() -> Result<()>36 fn test_unmarshal_marshal() -> Result<()> {
37     let input = CANONICAL_MARSHAL_SDP;
38     let mut reader = Cursor::new(input.as_bytes());
39     let sdp = SessionDescription::unmarshal(&mut reader)?;
40     let output = sdp.marshal();
41     assert_eq!(output, input);
42 
43     Ok(())
44 }
45 
46 #[test]
test_marshal() -> Result<()>47 fn test_marshal() -> Result<()> {
48     let sd = SessionDescription {
49         version: 0,
50         origin: Origin {
51             username: "jdoe".to_string(),
52             session_id: 2890844526,
53             session_version: 2890842807,
54             network_type: "IN".to_string(),
55             address_type: "IP4".to_string(),
56             unicast_address: "10.47.16.5".to_string(),
57         },
58         session_name: "SDP Seminar".to_string(),
59         session_information: Some("A Seminar on the session description protocol".to_string()),
60         uri: Some(Url::parse("http://www.example.com/seminars/sdp.pdf")?),
61         email_address: Some("[email protected] (Jane Doe)".to_string()),
62         phone_number: Some("+1 617 555-6011".to_string()),
63         connection_information: Some(ConnectionInformation {
64             network_type: "IN".to_string(),
65             address_type: "IP4".to_string(),
66             address: Some(Address {
67                 address: "224.2.17.12".to_string(),
68                 ttl: Some(127),
69                 range: None,
70             }),
71         }),
72         bandwidth: vec![
73             Bandwidth {
74                 experimental: true,
75                 bandwidth_type: "YZ".to_string(),
76                 bandwidth: 128,
77             },
78             Bandwidth {
79                 experimental: false,
80                 bandwidth_type: "AS".to_string(),
81                 bandwidth: 12345,
82             },
83         ],
84         time_descriptions: vec![
85             TimeDescription {
86                 timing: Timing {
87                     start_time: 2873397496,
88                     stop_time: 2873404696,
89                 },
90                 repeat_times: vec![],
91             },
92             TimeDescription {
93                 timing: Timing {
94                     start_time: 3034423619,
95                     stop_time: 3042462419,
96                 },
97                 repeat_times: vec![RepeatTime {
98                     interval: 604800,
99                     duration: 3600,
100                     offsets: vec![0, 90000],
101                 }],
102             },
103         ],
104         time_zones: vec![
105             TimeZone {
106                 adjustment_time: 2882844526,
107                 offset: -3600,
108             },
109             TimeZone {
110                 adjustment_time: 2898848070,
111                 offset: 0,
112             },
113         ],
114         encryption_key: Some("prompt".to_string()),
115         attributes: vec![
116             Attribute::new(
117                 "candidate".to_string(),
118                 Some("0 1 UDP 2113667327 203.0.113.1 54400 typ host".to_string()),
119             ),
120             Attribute::new("recvonly".to_string(), None),
121         ],
122         media_descriptions: vec![
123             MediaDescription {
124                 media_name: MediaName {
125                     media: "audio".to_string(),
126                     port: RangedPort {
127                         value: 49170,
128                         range: None,
129                     },
130                     protos: vec!["RTP".to_string(), "AVP".to_string()],
131                     formats: vec!["0".to_string()],
132                 },
133                 media_title: Some("Vivamus a posuere nisl".to_string()),
134                 connection_information: Some(ConnectionInformation {
135                     network_type: "IN".to_string(),
136                     address_type: "IP4".to_string(),
137                     address: Some(Address {
138                         address: "203.0.113.1".to_string(),
139                         ttl: None,
140                         range: None,
141                     }),
142                 }),
143                 bandwidth: vec![Bandwidth {
144                     experimental: true,
145                     bandwidth_type: "YZ".to_string(),
146                     bandwidth: 128,
147                 }],
148                 encryption_key: Some("prompt".to_string()),
149                 attributes: vec![Attribute::new("sendrecv".to_string(), None)],
150             },
151             MediaDescription {
152                 media_name: MediaName {
153                     media: "video".to_string(),
154                     port: RangedPort {
155                         value: 51372,
156                         range: None,
157                     },
158                     protos: vec!["RTP".to_string(), "AVP".to_string()],
159                     formats: vec!["99".to_string()],
160                 },
161                 media_title: None,
162                 connection_information: None,
163                 bandwidth: vec![],
164                 encryption_key: None,
165                 attributes: vec![Attribute::new(
166                     "rtpmap".to_string(),
167                     Some("99 h263-1998/90000".to_string()),
168                 )],
169             },
170         ],
171     };
172 
173     let actual = sd.marshal();
174     assert!(
175         actual == CANONICAL_MARSHAL_SDP,
176         "error:\n\nEXPECTED:\n{CANONICAL_MARSHAL_SDP}\nACTUAL:\n{actual}!!!!\n"
177     );
178 
179     Ok(())
180 }
181 
182 const BASE_SDP: &str = "v=0\r\n\
183 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
184 s=SDP Seminar\r\n";
185 
186 const SESSION_INFORMATION_SDP: &str = "v=0\r\n\
187 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
188 s=SDP Seminar\r\n\
189 i=A Seminar on the session description protocol\r\n\
190 t=3034423619 3042462419\r\n";
191 
192 // https://tools.ietf.org/html/rfc4566#section-5
193 // Parsers SHOULD be tolerant and also accept records terminated
194 // with a single newline character.
195 const SESSION_INFORMATION_SDPLFONLY: &str = "v=0\n\
196 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n\
197 s=SDP Seminar\n\
198 i=A Seminar on the session description protocol\n\
199 t=3034423619 3042462419\n";
200 
201 // SessionInformationSDPCROnly = "v=0\r" +
202 // 	"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r" +
203 // 	"s=SDP Seminar\r"
204 // 	"i=A Seminar on the session description protocol\r" +
205 // 	"t=3034423619 3042462419\r"
206 
207 // Other SDP parsers (e.g. one in VLC media player) allow
208 // empty lines.
209 const SESSION_INFORMATION_SDPEXTRA_CRLF: &str = "v=0\r\n\
210 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
211 \r\n\
212 s=SDP Seminar\r\n\
213 \r\n\
214 i=A Seminar on the session description protocol\r\n\
215 \r\n\
216 t=3034423619 3042462419\r\n\
217 \r\n";
218 
219 const URI_SDP: &str = "v=0\r\n\
220 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
221 s=SDP Seminar\r\n\
222 u=http://www.example.com/seminars/sdp.pdf\r\n\
223 t=3034423619 3042462419\r\n";
224 
225 const EMAIL_ADDRESS_SDP: &str = "v=0\r\n\
226 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
227 s=SDP Seminar\r\n\
228 [email protected] (Jane Doe)\r\n\
229 t=3034423619 3042462419\r\n";
230 
231 const PHONE_NUMBER_SDP: &str = "v=0\r\n\
232 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
233 s=SDP Seminar\r\n\
234 p=+1 617 555-6011\r\n\
235 t=3034423619 3042462419\r\n";
236 
237 const SESSION_CONNECTION_INFORMATION_SDP: &str = "v=0\r\n\
238 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
239 s=SDP Seminar\r\n\
240 c=IN IP4 224.2.17.12/127\r\n\
241 t=3034423619 3042462419\r\n";
242 
243 const SESSION_BANDWIDTH_SDP: &str = "v=0\r\n\
244 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
245 s=SDP Seminar\r\n\
246 b=X-YZ:128\r\n\
247 b=AS:12345\r\n\
248 t=3034423619 3042462419\r\n";
249 
250 const TIMING_SDP: &str = "v=0\r\n\
251 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
252 s=SDP Seminar\r\n\
253 t=2873397496 2873404696\r\n";
254 
255 // Short hand time notation is converted into NTP timestamp format in
256 // seconds. Because of that unittest comparisons will fail as the same time
257 // will be expressed in different units.
258 const REPEAT_TIMES_SDP: &str = "v=0\r\n\
259 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
260 s=SDP Seminar\r\n\
261 t=2873397496 2873404696\r\n\
262 r=604800 3600 0 90000\r\n\
263 r=3d 2h 0 21h\r\n";
264 
265 const REPEAT_TIMES_SDPEXPECTED: &str = "v=0\r\n\
266 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
267 s=SDP Seminar\r\n\
268 t=2873397496 2873404696\r\n\
269 r=604800 3600 0 90000\r\n\
270 r=259200 7200 0 75600\r\n";
271 
272 const REPEAT_TIMES_OVERFLOW_SDP: &str = "v=0\r\n\
273 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
274 s=SDP Seminar\r\n\
275 t=2873397496 2873404696\r\n\
276 r=604800 3600 0 90000\r\n\
277 r=106751991167301d 2h 0 21h\r\n";
278 
279 const REPEAT_TIMES_SDPEXTRA_CRLF: &str = "v=0\r\n\
280 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
281 s=SDP Seminar\r\n\
282 t=2873397496 2873404696\r\n\
283 r=604800 3600 0 90000\r\n\
284 r=259200 7200 0 75600\r\n\
285 \r\n";
286 
287 // The expected value looks a bit different for the same reason as mentioned
288 // above regarding RepeatTimes.
289 const TIME_ZONES_SDP: &str = "v=0\r\n\
290 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
291 s=SDP Seminar\r\n\
292 t=2873397496 2873404696\r\n\
293 r=2882844526 -1h 2898848070 0\r\n";
294 
295 const TIME_ZONES_SDPEXPECTED: &str = "v=0\r\n\
296 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
297 s=SDP Seminar\r\n\
298 t=2873397496 2873404696\r\n\
299 r=2882844526 -3600 2898848070 0\r\n";
300 
301 const TIME_ZONES_SDP2: &str = "v=0\r\n\
302 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
303 s=SDP Seminar\r\n\
304 t=2873397496 2873404696\r\n\
305 z=2882844526 -3600 2898848070 0\r\n";
306 
307 const TIME_ZONES_SDP2EXTRA_CRLF: &str = "v=0\r\n\
308 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
309 s=SDP Seminar\r\n\
310 t=2873397496 2873404696\r\n\
311 z=2882844526 -3600 2898848070 0\r\n\
312 \r\n";
313 
314 const SESSION_ENCRYPTION_KEY_SDP: &str = "v=0\r\n\
315 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
316 s=SDP Seminar\r\n\
317 t=2873397496 2873404696\r\n\
318 k=prompt\r\n";
319 
320 const SESSION_ENCRYPTION_KEY_SDPEXTRA_CRLF: &str = "v=0\r\n\
321 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
322 s=SDP Seminar\r\n\
323 t=2873397496 2873404696\r\n\
324 k=prompt\r\n
325 \r\n";
326 
327 const SESSION_ATTRIBUTES_SDP: &str = "v=0\r\n\
328 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
329 s=SDP Seminar\r\n\
330 t=2873397496 2873404696\r\n\
331 a=rtpmap:96 opus/48000\r\n";
332 
333 const MEDIA_NAME_SDP: &str = "v=0\r\n\
334 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
335 s=SDP Seminar\r\n\
336 t=2873397496 2873404696\r\n\
337 m=video 51372 RTP/AVP 99\r\n\
338 m=audio 54400 RTP/SAVPF 0 96\r\n";
339 
340 const MEDIA_NAME_SDPEXTRA_CRLF: &str = "v=0\r\n\
341 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
342 s=SDP Seminar\r\n\
343 t=2873397496 2873404696\r\n\
344 m=video 51372 RTP/AVP 99\r\n\
345 m=audio 54400 RTP/SAVPF 0 96\r\n
346 \r\n";
347 
348 const MEDIA_TITLE_SDP: &str = "v=0\r\n\
349 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
350 s=SDP Seminar\r\n\
351 t=2873397496 2873404696\r\n\
352 m=video 51372 RTP/AVP 99\r\n\
353 m=audio 54400 RTP/SAVPF 0 96\r\n\
354 i=Vivamus a posuere nisl\r\n";
355 
356 const MEDIA_CONNECTION_INFORMATION_SDP: &str = "v=0\r\n\
357 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
358 s=SDP Seminar\r\n\
359 t=2873397496 2873404696\r\n\
360 m=video 51372 RTP/AVP 99\r\n\
361 m=audio 54400 RTP/SAVPF 0 96\r\n\
362 c=IN IP4 203.0.113.1\r\n";
363 
364 const MEDIA_CONNECTION_INFORMATION_SDPEXTRA_CRLF: &str = "v=0\r\n\
365 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
366 s=SDP Seminar\r\n\
367 t=2873397496 2873404696\r\n\
368 m=video 51372 RTP/AVP 99\r\n\
369 m=audio 54400 RTP/SAVPF 0 96\r\n\
370 c=IN IP4 203.0.113.1\r\n\
371 \r\n";
372 
373 const MEDIA_DESCRIPTION_OUT_OF_ORDER_SDP: &str = "v=0\r\n\
374 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
375 s=SDP Seminar\r\n\
376 t=2873397496 2873404696\r\n\
377 m=video 51372 RTP/AVP 99\r\n\
378 m=audio 54400 RTP/SAVPF 0 96\r\n\
379 a=rtpmap:99 h263-1998/90000\r\n\
380 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
381 c=IN IP4 203.0.113.1\r\n\
382 i=Vivamus a posuere nisl\r\n";
383 
384 const MEDIA_DESCRIPTION_OUT_OF_ORDER_SDPACTUAL: &str = "v=0\r\n\
385 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
386 s=SDP Seminar\r\n\
387 t=2873397496 2873404696\r\n\
388 m=video 51372 RTP/AVP 99\r\n\
389 m=audio 54400 RTP/SAVPF 0 96\r\n\
390 i=Vivamus a posuere nisl\r\n\
391 c=IN IP4 203.0.113.1\r\n\
392 a=rtpmap:99 h263-1998/90000\r\n\
393 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n";
394 
395 const MEDIA_BANDWIDTH_SDP: &str = "v=0\r\n\
396 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
397 s=SDP Seminar\r\n\
398 t=2873397496 2873404696\r\n\
399 m=video 51372 RTP/AVP 99\r\n\
400 m=audio 54400 RTP/SAVPF 0 96\r\n\
401 b=X-YZ:128\r\n\
402 b=AS:12345\r\n";
403 
404 const MEDIA_ENCRYPTION_KEY_SDP: &str = "v=0\r\n\
405 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
406 s=SDP Seminar\r\n\
407 t=2873397496 2873404696\r\n\
408 m=video 51372 RTP/AVP 99\r\n\
409 m=audio 54400 RTP/SAVPF 0 96\r\n\
410 k=prompt\r\n";
411 
412 const MEDIA_ENCRYPTION_KEY_SDPEXTRA_CRLF: &str = "v=0\r\n\
413 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
414 s=SDP Seminar\r\n\
415 t=2873397496 2873404696\r\n\
416 m=video 51372 RTP/AVP 99\r\n\
417 m=audio 54400 RTP/SAVPF 0 96\r\n\
418 k=prompt\r\n\
419 \r\n";
420 
421 const MEDIA_ATTRIBUTES_SDP: &str = "v=0\r\n\
422 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
423 s=SDP Seminar\r\n\
424 t=2873397496 2873404696\r\n\
425 m=video 51372 RTP/AVP 99\r\n\
426 m=audio 54400 RTP/SAVPF 0 96\r\n\
427 a=rtpmap:99 h263-1998/90000\r\n\
428 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
429 a=rtcp-fb:97 ccm fir\r\n\
430 a=rtcp-fb:97 nack\r\n\
431 a=rtcp-fb:97 nack pli\r\n";
432 
433 const CANONICAL_UNMARSHAL_SDP: &str = "v=0\r\n\
434 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
435 s=SDP Seminar\r\n\
436 i=A Seminar on the session description protocol\r\n\
437 u=http://www.example.com/seminars/sdp.pdf\r\n\
438 [email protected] (Jane Doe)\r\n\
439 p=+1 617 555-6011\r\n\
440 c=IN IP4 224.2.17.12/127\r\n\
441 b=X-YZ:128\r\n\
442 b=AS:12345\r\n\
443 t=2873397496 2873404696\r\n\
444 t=3034423619 3042462419\r\n\
445 r=604800 3600 0 90000\r\n\
446 z=2882844526 -3600 2898848070 0\r\n\
447 k=prompt\r\n\
448 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
449 a=recvonly\r\n\
450 m=audio 49170 RTP/AVP 0\r\n\
451 i=Vivamus a posuere nisl\r\n\
452 c=IN IP4 203.0.113.1\r\n\
453 b=X-YZ:128\r\n\
454 k=prompt\r\n\
455 a=sendrecv\r\n\
456 m=video 51372 RTP/AVP 99\r\n\
457 a=rtpmap:99 h263-1998/90000\r\n";
458 
459 #[test]
test_round_trip() -> Result<()>460 fn test_round_trip() -> Result<()> {
461     let tests = vec![
462         (
463             "SessionInformationSDPLFOnly",
464             SESSION_INFORMATION_SDPLFONLY,
465             Some(SESSION_INFORMATION_SDP),
466         ),
467         (
468             "SessionInformationSDPExtraCRLF",
469             SESSION_INFORMATION_SDPEXTRA_CRLF,
470             Some(SESSION_INFORMATION_SDP),
471         ),
472         ("SessionInformation", SESSION_INFORMATION_SDP, None),
473         ("URI", URI_SDP, None),
474         ("EmailAddress", EMAIL_ADDRESS_SDP, None),
475         ("PhoneNumber", PHONE_NUMBER_SDP, None),
476         (
477             "RepeatTimesSDPExtraCRLF",
478             REPEAT_TIMES_SDPEXTRA_CRLF,
479             Some(REPEAT_TIMES_SDPEXPECTED),
480         ),
481         (
482             "SessionConnectionInformation",
483             SESSION_CONNECTION_INFORMATION_SDP,
484             None,
485         ),
486         ("SessionBandwidth", SESSION_BANDWIDTH_SDP, None),
487         ("SessionEncryptionKey", SESSION_ENCRYPTION_KEY_SDP, None),
488         (
489             "SessionEncryptionKeyExtraCRLF",
490             SESSION_ENCRYPTION_KEY_SDPEXTRA_CRLF,
491             Some(SESSION_ENCRYPTION_KEY_SDP),
492         ),
493         ("SessionAttributes", SESSION_ATTRIBUTES_SDP, None),
494         (
495             "TimeZonesSDP2ExtraCRLF",
496             TIME_ZONES_SDP2EXTRA_CRLF,
497             Some(TIME_ZONES_SDP2),
498         ),
499         ("MediaName", MEDIA_NAME_SDP, None),
500         (
501             "MediaNameExtraCRLF",
502             MEDIA_NAME_SDPEXTRA_CRLF,
503             Some(MEDIA_NAME_SDP),
504         ),
505         ("MediaTitle", MEDIA_TITLE_SDP, None),
506         (
507             "MediaConnectionInformation",
508             MEDIA_CONNECTION_INFORMATION_SDP,
509             None,
510         ),
511         (
512             "MediaConnectionInformationExtraCRLF",
513             MEDIA_CONNECTION_INFORMATION_SDPEXTRA_CRLF,
514             Some(MEDIA_CONNECTION_INFORMATION_SDP),
515         ),
516         (
517             "MediaDescriptionOutOfOrder",
518             MEDIA_DESCRIPTION_OUT_OF_ORDER_SDP,
519             Some(MEDIA_DESCRIPTION_OUT_OF_ORDER_SDPACTUAL),
520         ),
521         ("MediaBandwidth", MEDIA_BANDWIDTH_SDP, None),
522         ("MediaEncryptionKey", MEDIA_ENCRYPTION_KEY_SDP, None),
523         (
524             "MediaEncryptionKeyExtraCRLF",
525             MEDIA_ENCRYPTION_KEY_SDPEXTRA_CRLF,
526             Some(MEDIA_ENCRYPTION_KEY_SDP),
527         ),
528         ("MediaAttributes", MEDIA_ATTRIBUTES_SDP, None),
529         ("CanonicalUnmarshal", CANONICAL_UNMARSHAL_SDP, None),
530     ];
531 
532     for (name, sdp_str, expected) in tests {
533         let mut reader = Cursor::new(sdp_str.as_bytes());
534         let sdp = SessionDescription::unmarshal(&mut reader);
535         if let Ok(sdp) = sdp {
536             let actual = sdp.marshal();
537             if let Some(expected) = expected {
538                 assert_eq!(actual.as_str(), expected, "{name}\n{sdp_str}");
539             } else {
540                 assert_eq!(actual.as_str(), sdp_str, "{name}\n{sdp_str}");
541             }
542         } else {
543             panic!("{name}\n{sdp_str}");
544         }
545     }
546 
547     Ok(())
548 }
549 
550 #[test]
test_unmarshal_repeat_times() -> Result<()>551 fn test_unmarshal_repeat_times() -> Result<()> {
552     let mut reader = Cursor::new(REPEAT_TIMES_SDP.as_bytes());
553     let sdp = SessionDescription::unmarshal(&mut reader)?;
554     let actual = sdp.marshal();
555     assert_eq!(actual.as_str(), REPEAT_TIMES_SDPEXPECTED);
556     Ok(())
557 }
558 
559 #[test]
test_unmarshal_repeat_times_overflow() -> Result<()>560 fn test_unmarshal_repeat_times_overflow() -> Result<()> {
561     let mut reader = Cursor::new(REPEAT_TIMES_OVERFLOW_SDP.as_bytes());
562     let result = SessionDescription::unmarshal(&mut reader);
563     assert!(result.is_err());
564     assert_eq!(
565         Error::SdpInvalidValue("106751991167301d".to_owned()),
566         result.unwrap_err()
567     );
568     Ok(())
569 }
570 
571 #[test]
test_unmarshal_time_zones() -> Result<()>572 fn test_unmarshal_time_zones() -> Result<()> {
573     let mut reader = Cursor::new(TIME_ZONES_SDP.as_bytes());
574     let sdp = SessionDescription::unmarshal(&mut reader)?;
575     let actual = sdp.marshal();
576     assert_eq!(actual.as_str(), TIME_ZONES_SDPEXPECTED);
577     Ok(())
578 }
579 
580 #[test]
test_unmarshal_non_nil_address() -> Result<()>581 fn test_unmarshal_non_nil_address() -> Result<()> {
582     let input = "v=0\r\no=0 0 0 IN IP4 0\r\ns=0\r\nc=IN IP4\r\nt=0 0\r\n";
583     let mut reader = Cursor::new(input);
584     let sdp = SessionDescription::unmarshal(&mut reader);
585     if let Ok(sdp) = sdp {
586         let output = sdp.marshal();
587         assert_eq!(output.as_str(), input);
588     } else {
589         panic!("{}", input);
590     }
591     Ok(())
592 }
593