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