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]
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]
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{}\nACTUAL:\n{}!!!!\n",
177         CANONICAL_MARSHAL_SDP,
178         actual
179     );
180 
181     Ok(())
182 }
183 
184 const BASE_SDP: &str = "v=0\r\n\
185 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
186 s=SDP Seminar\r\n";
187 
188 const SESSION_INFORMATION_SDP: &str = "v=0\r\n\
189 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
190 s=SDP Seminar\r\n\
191 i=A Seminar on the session description protocol\r\n\
192 t=3034423619 3042462419\r\n";
193 
194 // https://tools.ietf.org/html/rfc4566#section-5
195 // Parsers SHOULD be tolerant and also accept records terminated
196 // with a single newline character.
197 const SESSION_INFORMATION_SDPLFONLY: &str = "v=0\n\
198 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\n\
199 s=SDP Seminar\n\
200 i=A Seminar on the session description protocol\n\
201 t=3034423619 3042462419\n";
202 
203 // SessionInformationSDPCROnly = "v=0\r" +
204 // 	"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r" +
205 // 	"s=SDP Seminar\r"
206 // 	"i=A Seminar on the session description protocol\r" +
207 // 	"t=3034423619 3042462419\r"
208 
209 // Other SDP parsers (e.g. one in VLC media player) allow
210 // empty lines.
211 const SESSION_INFORMATION_SDPEXTRA_CRLF: &str = "v=0\r\n\
212 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
213 \r\n\
214 s=SDP Seminar\r\n\
215 \r\n\
216 i=A Seminar on the session description protocol\r\n\
217 \r\n\
218 t=3034423619 3042462419\r\n\
219 \r\n";
220 
221 const URI_SDP: &str = "v=0\r\n\
222 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
223 s=SDP Seminar\r\n\
224 u=http://www.example.com/seminars/sdp.pdf\r\n\
225 t=3034423619 3042462419\r\n";
226 
227 const EMAIL_ADDRESS_SDP: &str = "v=0\r\n\
228 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
229 s=SDP Seminar\r\n\
230 [email protected] (Jane Doe)\r\n\
231 t=3034423619 3042462419\r\n";
232 
233 const PHONE_NUMBER_SDP: &str = "v=0\r\n\
234 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
235 s=SDP Seminar\r\n\
236 p=+1 617 555-6011\r\n\
237 t=3034423619 3042462419\r\n";
238 
239 const SESSION_CONNECTION_INFORMATION_SDP: &str = "v=0\r\n\
240 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
241 s=SDP Seminar\r\n\
242 c=IN IP4 224.2.17.12/127\r\n\
243 t=3034423619 3042462419\r\n";
244 
245 const SESSION_BANDWIDTH_SDP: &str = "v=0\r\n\
246 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
247 s=SDP Seminar\r\n\
248 b=X-YZ:128\r\n\
249 b=AS:12345\r\n\
250 t=3034423619 3042462419\r\n";
251 
252 const TIMING_SDP: &str = "v=0\r\n\
253 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
254 s=SDP Seminar\r\n\
255 t=2873397496 2873404696\r\n";
256 
257 // Short hand time notation is converted into NTP timestamp format in
258 // seconds. Because of that unittest comparisons will fail as the same time
259 // will be expressed in different units.
260 const REPEAT_TIMES_SDP: &str = "v=0\r\n\
261 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
262 s=SDP Seminar\r\n\
263 t=2873397496 2873404696\r\n\
264 r=604800 3600 0 90000\r\n\
265 r=3d 2h 0 21h\r\n";
266 
267 const REPEAT_TIMES_SDPEXPECTED: &str = "v=0\r\n\
268 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
269 s=SDP Seminar\r\n\
270 t=2873397496 2873404696\r\n\
271 r=604800 3600 0 90000\r\n\
272 r=259200 7200 0 75600\r\n";
273 
274 const REPEAT_TIMES_OVERFLOW_SDP: &str = "v=0\r\n\
275 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
276 s=SDP Seminar\r\n\
277 t=2873397496 2873404696\r\n\
278 r=604800 3600 0 90000\r\n\
279 r=106751991167301d 2h 0 21h\r\n";
280 
281 const REPEAT_TIMES_SDPEXTRA_CRLF: &str = "v=0\r\n\
282 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
283 s=SDP Seminar\r\n\
284 t=2873397496 2873404696\r\n\
285 r=604800 3600 0 90000\r\n\
286 r=259200 7200 0 75600\r\n\
287 \r\n";
288 
289 // The expected value looks a bit different for the same reason as mentioned
290 // above regarding RepeatTimes.
291 const TIME_ZONES_SDP: &str = "v=0\r\n\
292 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
293 s=SDP Seminar\r\n\
294 t=2873397496 2873404696\r\n\
295 r=2882844526 -1h 2898848070 0\r\n";
296 
297 const TIME_ZONES_SDPEXPECTED: &str = "v=0\r\n\
298 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
299 s=SDP Seminar\r\n\
300 t=2873397496 2873404696\r\n\
301 r=2882844526 -3600 2898848070 0\r\n";
302 
303 const TIME_ZONES_SDP2: &str = "v=0\r\n\
304 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
305 s=SDP Seminar\r\n\
306 t=2873397496 2873404696\r\n\
307 z=2882844526 -3600 2898848070 0\r\n";
308 
309 const TIME_ZONES_SDP2EXTRA_CRLF: &str = "v=0\r\n\
310 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
311 s=SDP Seminar\r\n\
312 t=2873397496 2873404696\r\n\
313 z=2882844526 -3600 2898848070 0\r\n\
314 \r\n";
315 
316 const SESSION_ENCRYPTION_KEY_SDP: &str = "v=0\r\n\
317 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
318 s=SDP Seminar\r\n\
319 t=2873397496 2873404696\r\n\
320 k=prompt\r\n";
321 
322 const SESSION_ENCRYPTION_KEY_SDPEXTRA_CRLF: &str = "v=0\r\n\
323 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
324 s=SDP Seminar\r\n\
325 t=2873397496 2873404696\r\n\
326 k=prompt\r\n
327 \r\n";
328 
329 const SESSION_ATTRIBUTES_SDP: &str = "v=0\r\n\
330 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
331 s=SDP Seminar\r\n\
332 t=2873397496 2873404696\r\n\
333 a=rtpmap:96 opus/48000\r\n";
334 
335 const MEDIA_NAME_SDP: &str = "v=0\r\n\
336 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
337 s=SDP Seminar\r\n\
338 t=2873397496 2873404696\r\n\
339 m=video 51372 RTP/AVP 99\r\n\
340 m=audio 54400 RTP/SAVPF 0 96\r\n";
341 
342 const MEDIA_NAME_SDPEXTRA_CRLF: &str = "v=0\r\n\
343 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
344 s=SDP Seminar\r\n\
345 t=2873397496 2873404696\r\n\
346 m=video 51372 RTP/AVP 99\r\n\
347 m=audio 54400 RTP/SAVPF 0 96\r\n
348 \r\n";
349 
350 const MEDIA_TITLE_SDP: &str = "v=0\r\n\
351 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
352 s=SDP Seminar\r\n\
353 t=2873397496 2873404696\r\n\
354 m=video 51372 RTP/AVP 99\r\n\
355 m=audio 54400 RTP/SAVPF 0 96\r\n\
356 i=Vivamus a posuere nisl\r\n";
357 
358 const MEDIA_CONNECTION_INFORMATION_SDP: &str = "v=0\r\n\
359 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
360 s=SDP Seminar\r\n\
361 t=2873397496 2873404696\r\n\
362 m=video 51372 RTP/AVP 99\r\n\
363 m=audio 54400 RTP/SAVPF 0 96\r\n\
364 c=IN IP4 203.0.113.1\r\n";
365 
366 const MEDIA_CONNECTION_INFORMATION_SDPEXTRA_CRLF: &str = "v=0\r\n\
367 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
368 s=SDP Seminar\r\n\
369 t=2873397496 2873404696\r\n\
370 m=video 51372 RTP/AVP 99\r\n\
371 m=audio 54400 RTP/SAVPF 0 96\r\n\
372 c=IN IP4 203.0.113.1\r\n\
373 \r\n";
374 
375 const MEDIA_DESCRIPTION_OUT_OF_ORDER_SDP: &str = "v=0\r\n\
376 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
377 s=SDP Seminar\r\n\
378 t=2873397496 2873404696\r\n\
379 m=video 51372 RTP/AVP 99\r\n\
380 m=audio 54400 RTP/SAVPF 0 96\r\n\
381 a=rtpmap:99 h263-1998/90000\r\n\
382 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
383 c=IN IP4 203.0.113.1\r\n\
384 i=Vivamus a posuere nisl\r\n";
385 
386 const MEDIA_DESCRIPTION_OUT_OF_ORDER_SDPACTUAL: &str = "v=0\r\n\
387 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
388 s=SDP Seminar\r\n\
389 t=2873397496 2873404696\r\n\
390 m=video 51372 RTP/AVP 99\r\n\
391 m=audio 54400 RTP/SAVPF 0 96\r\n\
392 i=Vivamus a posuere nisl\r\n\
393 c=IN IP4 203.0.113.1\r\n\
394 a=rtpmap:99 h263-1998/90000\r\n\
395 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n";
396 
397 const MEDIA_BANDWIDTH_SDP: &str = "v=0\r\n\
398 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
399 s=SDP Seminar\r\n\
400 t=2873397496 2873404696\r\n\
401 m=video 51372 RTP/AVP 99\r\n\
402 m=audio 54400 RTP/SAVPF 0 96\r\n\
403 b=X-YZ:128\r\n\
404 b=AS:12345\r\n";
405 
406 const MEDIA_ENCRYPTION_KEY_SDP: &str = "v=0\r\n\
407 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
408 s=SDP Seminar\r\n\
409 t=2873397496 2873404696\r\n\
410 m=video 51372 RTP/AVP 99\r\n\
411 m=audio 54400 RTP/SAVPF 0 96\r\n\
412 k=prompt\r\n";
413 
414 const MEDIA_ENCRYPTION_KEY_SDPEXTRA_CRLF: &str = "v=0\r\n\
415 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
416 s=SDP Seminar\r\n\
417 t=2873397496 2873404696\r\n\
418 m=video 51372 RTP/AVP 99\r\n\
419 m=audio 54400 RTP/SAVPF 0 96\r\n\
420 k=prompt\r\n\
421 \r\n";
422 
423 const MEDIA_ATTRIBUTES_SDP: &str = "v=0\r\n\
424 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
425 s=SDP Seminar\r\n\
426 t=2873397496 2873404696\r\n\
427 m=video 51372 RTP/AVP 99\r\n\
428 m=audio 54400 RTP/SAVPF 0 96\r\n\
429 a=rtpmap:99 h263-1998/90000\r\n\
430 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
431 a=rtcp-fb:97 ccm fir\r\n\
432 a=rtcp-fb:97 nack\r\n\
433 a=rtcp-fb:97 nack pli\r\n";
434 
435 const CANONICAL_UNMARSHAL_SDP: &str = "v=0\r\n\
436 o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
437 s=SDP Seminar\r\n\
438 i=A Seminar on the session description protocol\r\n\
439 u=http://www.example.com/seminars/sdp.pdf\r\n\
440 [email protected] (Jane Doe)\r\n\
441 p=+1 617 555-6011\r\n\
442 c=IN IP4 224.2.17.12/127\r\n\
443 b=X-YZ:128\r\n\
444 b=AS:12345\r\n\
445 t=2873397496 2873404696\r\n\
446 t=3034423619 3042462419\r\n\
447 r=604800 3600 0 90000\r\n\
448 z=2882844526 -3600 2898848070 0\r\n\
449 k=prompt\r\n\
450 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\
451 a=recvonly\r\n\
452 m=audio 49170 RTP/AVP 0\r\n\
453 i=Vivamus a posuere nisl\r\n\
454 c=IN IP4 203.0.113.1\r\n\
455 b=X-YZ:128\r\n\
456 k=prompt\r\n\
457 a=sendrecv\r\n\
458 m=video 51372 RTP/AVP 99\r\n\
459 a=rtpmap:99 h263-1998/90000\r\n";
460 
461 #[test]
462 fn test_round_trip() -> Result<()> {
463     let tests = vec![
464         (
465             "SessionInformationSDPLFOnly",
466             SESSION_INFORMATION_SDPLFONLY,
467             Some(SESSION_INFORMATION_SDP),
468         ),
469         (
470             "SessionInformationSDPExtraCRLF",
471             SESSION_INFORMATION_SDPEXTRA_CRLF,
472             Some(SESSION_INFORMATION_SDP),
473         ),
474         ("SessionInformation", SESSION_INFORMATION_SDP, None),
475         ("URI", URI_SDP, None),
476         ("EmailAddress", EMAIL_ADDRESS_SDP, None),
477         ("PhoneNumber", PHONE_NUMBER_SDP, None),
478         (
479             "RepeatTimesSDPExtraCRLF",
480             REPEAT_TIMES_SDPEXTRA_CRLF,
481             Some(REPEAT_TIMES_SDPEXPECTED),
482         ),
483         (
484             "SessionConnectionInformation",
485             SESSION_CONNECTION_INFORMATION_SDP,
486             None,
487         ),
488         ("SessionBandwidth", SESSION_BANDWIDTH_SDP, None),
489         ("SessionEncryptionKey", SESSION_ENCRYPTION_KEY_SDP, None),
490         (
491             "SessionEncryptionKeyExtraCRLF",
492             SESSION_ENCRYPTION_KEY_SDPEXTRA_CRLF,
493             Some(SESSION_ENCRYPTION_KEY_SDP),
494         ),
495         ("SessionAttributes", SESSION_ATTRIBUTES_SDP, None),
496         (
497             "TimeZonesSDP2ExtraCRLF",
498             TIME_ZONES_SDP2EXTRA_CRLF,
499             Some(TIME_ZONES_SDP2),
500         ),
501         ("MediaName", MEDIA_NAME_SDP, None),
502         (
503             "MediaNameExtraCRLF",
504             MEDIA_NAME_SDPEXTRA_CRLF,
505             Some(MEDIA_NAME_SDP),
506         ),
507         ("MediaTitle", MEDIA_TITLE_SDP, None),
508         (
509             "MediaConnectionInformation",
510             MEDIA_CONNECTION_INFORMATION_SDP,
511             None,
512         ),
513         (
514             "MediaConnectionInformationExtraCRLF",
515             MEDIA_CONNECTION_INFORMATION_SDPEXTRA_CRLF,
516             Some(MEDIA_CONNECTION_INFORMATION_SDP),
517         ),
518         (
519             "MediaDescriptionOutOfOrder",
520             MEDIA_DESCRIPTION_OUT_OF_ORDER_SDP,
521             Some(MEDIA_DESCRIPTION_OUT_OF_ORDER_SDPACTUAL),
522         ),
523         ("MediaBandwidth", MEDIA_BANDWIDTH_SDP, None),
524         ("MediaEncryptionKey", MEDIA_ENCRYPTION_KEY_SDP, None),
525         (
526             "MediaEncryptionKeyExtraCRLF",
527             MEDIA_ENCRYPTION_KEY_SDPEXTRA_CRLF,
528             Some(MEDIA_ENCRYPTION_KEY_SDP),
529         ),
530         ("MediaAttributes", MEDIA_ATTRIBUTES_SDP, None),
531         ("CanonicalUnmarshal", CANONICAL_UNMARSHAL_SDP, None),
532     ];
533 
534     for (name, sdp_str, expected) in tests {
535         let mut reader = Cursor::new(sdp_str.as_bytes());
536         let sdp = SessionDescription::unmarshal(&mut reader);
537         if let Ok(sdp) = sdp {
538             let actual = sdp.marshal();
539             if let Some(expected) = expected {
540                 assert_eq!(actual.as_str(), expected, "{}\n{}", name, sdp_str);
541             } else {
542                 assert_eq!(actual.as_str(), sdp_str, "{}\n{}", name, sdp_str);
543             }
544         } else {
545             panic!("{}\n{}", name, sdp_str);
546         }
547     }
548 
549     Ok(())
550 }
551 
552 #[test]
553 fn test_unmarshal_repeat_times() -> Result<()> {
554     let mut reader = Cursor::new(REPEAT_TIMES_SDP.as_bytes());
555     let sdp = SessionDescription::unmarshal(&mut reader)?;
556     let actual = sdp.marshal();
557     assert_eq!(actual.as_str(), REPEAT_TIMES_SDPEXPECTED);
558     Ok(())
559 }
560 
561 #[test]
562 fn test_unmarshal_repeat_times_overflow() -> Result<()> {
563     let mut reader = Cursor::new(REPEAT_TIMES_OVERFLOW_SDP.as_bytes());
564     let result = SessionDescription::unmarshal(&mut reader);
565     assert!(result.is_err());
566     assert_eq!(
567         Error::SdpInvalidValue("106751991167301d".to_owned()),
568         result.unwrap_err()
569     );
570     Ok(())
571 }
572 
573 #[test]
574 fn test_unmarshal_time_zones() -> Result<()> {
575     let mut reader = Cursor::new(TIME_ZONES_SDP.as_bytes());
576     let sdp = SessionDescription::unmarshal(&mut reader)?;
577     let actual = sdp.marshal();
578     assert_eq!(actual.as_str(), TIME_ZONES_SDPEXPECTED);
579     Ok(())
580 }
581 
582 #[test]
583 fn test_unmarshal_non_nil_address() -> Result<()> {
584     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";
585     let mut reader = Cursor::new(input);
586     let sdp = SessionDescription::unmarshal(&mut reader);
587     if let Ok(sdp) = sdp {
588         let output = sdp.marshal();
589         assert_eq!(output.as_str(), input);
590     } else {
591         panic!("{}", input);
592     }
593     Ok(())
594 }
595