extern crate url;
use super::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
macro_rules! make_check_parse {
($attr_type:ty, $attr_kind:path) => {
|attr_str: &str| -> $attr_type {
match parse_attribute(attr_str) {
Ok(SdpType::Attribute($attr_kind(attr))) => attr,
Err(e) => panic!("{}", e),
_ => unreachable!(),
}
}
};
($attr_kind:path) => {
|attr_str: &str| -> SdpAttribute {
match parse_attribute(attr_str) {
Ok(SdpType::Attribute($attr_kind)) => $attr_kind,
Err(e) => panic!("{}", e),
_ => unreachable!(),
}
}
};
}
macro_rules! make_check_parse_and_serialize {
($check_parse_func:ident, $attr_kind:path) => {
|attr_str: &str| {
let parsed = $attr_kind($check_parse_func(attr_str));
assert_eq!(parsed.to_string(), attr_str.to_string());
}
};
($check_parse_func:ident) => {
|attr_str: &str| {
let parsed = $check_parse_func(attr_str);
assert_eq!(parsed.to_string(), attr_str.to_string());
}
};
}
#[test]
fn test_parse_attribute_candidate_and_serialize() {
let check_parse = make_check_parse!(SdpAttributeCandidate, SdpAttribute::Candidate);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Candidate);
check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host");
check_parse_and_serialize("candidate:foo 1 UDP 2122252543 172.16.156.106 49760 typ host");
check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host");
check_parse_and_serialize("candidate:0 1 TCP 2122252543 ::1 49760 typ host");
check_parse_and_serialize("candidate:0 1 TCP 2122252543 2001:db8:4860::4444 49760 typ host");
check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ srflx");
check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ prflx");
check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ relay");
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype active",
);
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype passive",
);
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype so",
);
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host ufrag foobar",
);
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost 50",
);
check_parse_and_serialize("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation 0");
check_parse_and_serialize(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665",
);
check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive");
check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1");
check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd");
check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1");
check_parse_and_serialize(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported foo",
);
check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported foo more_unsupported bar");
let candidate = check_parse("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1 unsupported foo");
assert_eq!(candidate.foundation, "1".to_string());
assert_eq!(candidate.component, 1);
assert_eq!(candidate.transport, SdpAttributeCandidateTransport::Tcp);
assert_eq!(candidate.priority, 1_685_987_071);
assert_eq!(
candidate.address,
Address::from_str("24.23.204.141").unwrap()
);
assert_eq!(candidate.port, 54609);
assert_eq!(candidate.c_type, SdpAttributeCandidateType::Srflx);
assert_eq!(
candidate.raddr,
Some(Address::from_str("192.168.1.4").unwrap())
);
assert_eq!(candidate.rport, Some(61665));
assert_eq!(
candidate.tcp_type,
Some(SdpAttributeCandidateTcpType::Passive)
);
assert_eq!(candidate.generation, Some(1));
assert_eq!(candidate.ufrag, Some("+DGd".to_string()));
assert_eq!(candidate.networkcost, Some(1));
assert_eq!(
candidate.unknown_extensions,
vec![("unsupported".to_string(), "foo".to_string())]
)
}
#[test]
fn test_anonymize_attribute_candidate() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
let candidate_1 = parse_attribute("candidate:0 1 TCP 2122252543 ::8 49760 typ host")?;
let candidate_2 =
parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 19361 typ srflx")?;
let candidate_3 = parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd")?;
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_1 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == Address::Ip(IpAddr::V6(Ipv6Addr::from(1))));
assert!(masked.port == 1);
} else {
unreachable!();
}
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_2 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == Address::Ip(IpAddr::V4(Ipv4Addr::from(1))));
assert!(masked.port == 2);
} else {
unreachable!();
}
if let SdpType::Attribute(SdpAttribute::Candidate(candidate)) = candidate_3 {
let masked = candidate.masked_clone(&mut anon);
assert!(masked.address == Address::Ip(IpAddr::V4(Ipv4Addr::from(2))));
assert!(masked.port == 3);
assert!(masked.raddr.unwrap() == Address::Ip(IpAddr::V4(Ipv4Addr::from(3))));
assert!(masked.rport.unwrap() == 4);
} else {
unreachable!();
}
Ok(())
}
#[test]
fn test_parse_attribute_candidate_errors() {
assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ").is_err());
assert!(
parse_attribute("candidate:0 foo UDP 2122252543 172.16.156.106 49760 typ host").is_err()
);
assert!(parse_attribute("candidate:0 1 FOO 2122252543 172.16.156.106 49760 typ host").is_err());
assert!(parse_attribute("candidate:0 1 UDP foo 172.16.156.106 49760 typ host").is_err());
assert!(parse_attribute("candidate:0 1 UDP 2122252543 372.16.356 49760 typ host").is_err());
assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 70000 typ host").is_err());
assert!(
parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 type host").is_err()
);
assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ fost").is_err());
assert!(parse_attribute(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported"
)
.is_err());
assert!(parse_attribute(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost"
)
.is_err());
assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation B").is_err());
assert!(parse_attribute(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost C"
)
.is_err());
assert!(parse_attribute(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 1%92.168.1 rport 61665"
)
.is_err());
assert!(parse_attribute(
"candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype foobar"
)
.is_err());
assert!(parse_attribute(
"candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 70000"
)
.is_err());
}
#[test]
fn test_parse_dtls_message() {
let check_parse = make_check_parse!(SdpAttributeDtlsMessage, SdpAttribute::DtlsMessage);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::DtlsMessage);
check_parse_and_serialize("dtls-message:client SGVsbG8gV29ybGQ=");
check_parse_and_serialize("dtls-message:server SGVsbG8gV29ybGQ=");
check_parse_and_serialize("dtls-message:client IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=");
check_parse_and_serialize("dtls-message:server IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=");
match check_parse("dtls-message:client SGVsbG8gV29ybGQ=") {
SdpAttributeDtlsMessage::Client(x) => {
assert_eq!(x, "SGVsbG8gV29ybGQ=");
}
_ => {
unreachable!();
}
}
match check_parse("dtls-message:server SGVsbG8gV29ybGQ=") {
SdpAttributeDtlsMessage::Server(x) => {
assert_eq!(x, "SGVsbG8gV29ybGQ=");
}
_ => {
unreachable!();
}
}
assert!(parse_attribute("dtls-message:client").is_err());
assert!(parse_attribute("dtls-message:server").is_err());
assert!(parse_attribute("dtls-message:unsupported SGVsbG8gV29ybGQ=").is_err());
}
#[test]
fn test_parse_attribute_end_of_candidates() {
let check_parse = make_check_parse!(SdpAttribute::EndOfCandidates);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("end-of-candidates");
assert!(parse_attribute("end-of-candidates foobar").is_err());
}
#[test]
fn test_parse_attribute_extmap() {
let check_parse = make_check_parse!(SdpAttributeExtmap, SdpAttribute::Extmap);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Extmap);
check_parse_and_serialize("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level");
check_parse_and_serialize("extmap:2/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level");
check_parse_and_serialize(
"extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
);
check_parse_and_serialize(
"extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ext_attributes",
);
assert!(parse_attribute("extmap:1/sendrecv").is_err());
assert!(
parse_attribute("extmap:a/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level").is_err()
);
assert!(
parse_attribute("extmap:4/unsupported urn:ietf:params:rtp-hdrext:ssrc-audio-level")
.is_err()
);
let mut bad_char =
String::from("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ");
bad_char.push(0x00 as char);
assert!(parse_attribute(&bad_char).is_err());
}
#[test]
fn test_parse_attribute_fingerprint() {
let check_parse = make_check_parse!(SdpAttributeFingerprint, SdpAttribute::Fingerprint);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Fingerprint);
check_parse_and_serialize(
"fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC",
);
check_parse_and_serialize(
"fingerprint:sha-224 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\
27:97:EB:0B:23:73:AC:BC",
);
check_parse_and_serialize(
"fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\
27:97:EB:0B:23:73:AC:BC:CD:34:D1:62",
);
check_parse_and_serialize(
"fingerprint:sha-384 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\
27:97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:\
27:97:EB:0B:23:73:AC:BC",
);
check_parse_and_serialize(
"fingerprint:sha-512 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\
97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:\
EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:\
BC:EB:0B:23",
);
assert!(parse_attribute("fingerprint:sha-1").is_err());
assert!(parse_attribute(
"fingerprint:unsupported CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CDA:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CX:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 0xCD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD:0x34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD::D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD:0000A:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
assert!(parse_attribute(
"fingerprint:sha-1 CD:B:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC"
)
.is_err());
}
#[test]
fn test_parse_attribute_fmtp() {
let check_parse = make_check_parse!(SdpAttributeFmtp, SdpAttribute::Fmtp);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Fmtp);
check_parse_and_serialize("fmtp:109 maxplaybackrate=46000;stereo=1;useinbandfec=1");
check_parse_and_serialize(
"fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1",
);
check_parse_and_serialize("fmtp:66 0-15");
check_parse_and_serialize("fmtp:109 0-15,66");
check_parse_and_serialize("fmtp:66 111/115");
assert!(parse_attribute("fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1").is_ok());
assert!(parse_attribute("fmtp:109 maxplaybackrate=48000; stereo=1; useinbandfec=1").is_ok());
assert!(parse_attribute("fmtp:109 maxplaybackrate=48000; stereo=1;useinbandfec=1").is_ok());
check_parse_and_serialize("fmtp:8 maxplaybackrate=46000");
check_parse_and_serialize("fmtp:8 maxaveragebitrate=46000");
check_parse_and_serialize("fmtp:8 maxaveragebitrate=46000;ptime=60;minptime=20;maxptime=120");
check_parse_and_serialize(
"fmtp:8 max-cpb=1234;max-dpb=32000;max-br=3;max-mbps=46000;usedtx=1;cbr=1",
);
assert!(parse_attribute("fmtp:77 ").is_err());
assert!(parse_attribute("fmtp:109 stereo=2;").is_err());
assert!(parse_attribute("fmtp:109 111/129;").is_err());
assert!(parse_attribute("fmtp:109 packetization-mode=3;").is_err());
assert!(parse_attribute("fmtp:109 maxplaybackrate=48000stereo=1;").is_err());
assert!(parse_attribute("fmtp:8 ;maxplaybackrate=48000").is_ok());
assert!(parse_attribute("fmtp:8 packetization-mode=2;;maxplaybackrate=48000").is_ok());
assert!(parse_attribute("fmtp:8 packetization-mode=2; maxplaybackrate=48000").is_ok());
assert!(parse_attribute("fmtp:8 maxplaybackrate=48000;").is_ok());
assert!(parse_attribute("fmtp:8 x-google-start-bitrate=800; maxplaybackrate=48000;").is_ok());
check_parse_and_serialize("fmtp:97 apt=96");
check_parse_and_serialize("fmtp:97 apt=96;rtx-time=3000");
check_parse_and_serialize(
"fmtp:102 packetization-mode=1;sprop-parameter-sets=Z0LAFYyNQKD5APCIRqA=,aM48gA==",
);
check_parse_and_serialize("fmtp:97 profile=2;level-idx=3;tier=1");
}
#[test]
fn test_parse_attribute_framerate() {
let check_parse = make_check_parse!(f64, SdpAttribute::FrameRate);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::FrameRate);
check_parse_and_serialize("framerate:1");
check_parse_and_serialize("framerate:60");
check_parse_and_serialize("framerate:23.5");
check_parse_and_serialize("framerate:4294967297");
assert!(parse_attribute("framerate:").is_err());
assert!(parse_attribute("framerate:abc").is_err());
assert!(parse_attribute("framerate:0").is_err());
}
#[test]
fn test_anonymize_attribute_fingerprint() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
if let SdpType::Attribute(SdpAttribute::Fingerprint(print)) = parse_attribute(
"fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC",
)? {
assert!(print.masked_clone(&mut anon).to_string() == "sha-1 00:00:00:00:00:00:00:01");
} else {
unreachable!();
}
Ok(())
}
#[test]
fn test_parse_attribute_group() {
let check_parse = make_check_parse!(SdpAttributeGroup, SdpAttribute::Group);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Group);
check_parse_and_serialize("group:LS");
check_parse_and_serialize("group:LS 1 2");
check_parse_and_serialize("group:FID 1 2");
check_parse_and_serialize("group:SRF 1 2");
check_parse_and_serialize("group:FEC S1 R1");
check_parse_and_serialize("group:ANAT S1 R1");
check_parse_and_serialize("group:DDP L1 L2 L3");
check_parse_and_serialize("group:BUNDLE sdparta_0 sdparta_1 sdparta_2");
assert!(parse_attribute("group:").is_err());
assert!(matches!(
parse_attribute("group:NEVER_SUPPORTED_SEMANTICS"),
Err(SdpParserInternalError::Unsupported(_))
));
}
#[test]
fn test_parse_attribute_bundle_only() {
let check_parse = make_check_parse!(SdpAttribute::BundleOnly);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("bundle-only");
assert!(parse_attribute("bundle-only foobar").is_err());
}
#[test]
fn test_parse_attribute_ice_lite() {
let check_parse = make_check_parse!(SdpAttribute::IceLite);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("ice-lite");
assert!(parse_attribute("ice-lite foobar").is_err());
}
#[test]
fn test_parse_attribute_extmap_allow_mixed() {
let check_parse = make_check_parse!(SdpAttribute::ExtmapAllowMixed);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("extmap-allow-mixed");
assert!(parse_attribute("extmap-allow-mixed 100").is_err());
}
#[test]
fn test_parse_attribute_ice_mismatch() {
let check_parse = make_check_parse!(SdpAttribute::IceMismatch);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("ice-mismatch");
assert!(parse_attribute("ice-mismatch foobar").is_err());
}
#[test]
fn test_parse_attribute_ice_options() {
let check_parse = make_check_parse!(Vec<String>, SdpAttribute::IceOptions);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::IceOptions);
check_parse_and_serialize("ice-options:trickle");
assert!(parse_attribute("ice-options:").is_err());
}
#[test]
fn test_parse_attribute_ice_pacing() {
let check_parse = make_check_parse!(u64, SdpAttribute::IcePacing);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::IcePacing);
check_parse_and_serialize("ice-pacing:50");
assert!(parse_attribute("ice-pacing:").is_err());
assert!(parse_attribute("ice-pacing:10000000000").is_err());
assert!(parse_attribute("ice-pacing:50 100").is_err());
assert!(parse_attribute("ice-pacing:foobar").is_err());
}
#[test]
fn test_parse_attribute_ice_pwd() {
let check_parse = make_check_parse!(String, SdpAttribute::IcePwd);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::IcePwd);
check_parse_and_serialize("ice-pwd:e3baa26dd2fa5030d881d385f1e36cce");
assert!(parse_attribute("ice-pwd:").is_err());
}
#[test]
fn test_parse_attribute_ice_ufrag() {
let check_parse = make_check_parse!(String, SdpAttribute::IceUfrag);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::IceUfrag);
check_parse_and_serialize("ice-ufrag:58b99ead");
assert!(parse_attribute("ice-ufrag:").is_err());
}
#[test]
fn test_parse_attribute_identity() {
let check_parse = make_check_parse!(String, SdpAttribute::Identity);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Identity);
check_parse_and_serialize("identity:eyJpZHAiOnsiZG9tYWluIjoiZXhhbXBsZS5vcmciLCJwcm90b2NvbCI6ImJvZ3VzIn0sImFzc2VydGlvbiI6IntcImlkZW50aXR5XCI6XCJib2JAZXhhbXBsZS5vcmdcIixcImNvbnRlbnRzXCI6XCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3l6XCIsXCJzaWduYXR1cmVcIjpcIjAxMDIwMzA0MDUwNlwifSJ9");
assert!(parse_attribute("identity:").is_err());
}
#[test]
fn test_parse_attribute_imageattr() {
let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::ImageAttr);
check_parse_and_serialize("imageattr:120 send * recv *");
check_parse_and_serialize("imageattr:99 send [x=320,y=240] recv [x=320,y=240]");
check_parse_and_serialize(
"imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]",
);
check_parse_and_serialize("imageattr:97 send [x=[480:16:800],y=[320:16:640],par=[1.2-1.3],q=0.6] [x=[176:8:208],y=[144:8:176],par=[1.2-1.3]] recv *");
assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1] send [x=330,y=250]").is_ok());
check_parse_and_serialize("imageattr:99 send [x=320,y=240]");
assert!(parse_attribute("imageattr:100 recv [x=320,y=240]").is_ok());
assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1,foo=[123,456],q=0.5] send [x=330,y=250,bar=foo,sar=[20-40]]").is_ok());
assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1,foo=abc xyz,q=0.5] send [x=330,y=250,bar=foo,sar=[20-40]]").is_ok());
assert!(parse_attribute("imageattr:").is_err());
assert!(parse_attribute("imageattr:100").is_err());
assert!(parse_attribute("imageattr:120 send * recv * send *").is_err());
assert!(parse_attribute("imageattr:99 send [x=320]").is_err());
assert!(parse_attribute("imageattr:99 recv [y=240]").is_err());
assert!(parse_attribute("imageattr:99 send [x=320,y=240").is_err());
assert!(parse_attribute("imageattr:99 send x=320,y=240]").is_err());
assert!(parse_attribute("imageattr:97 send [x=800,y=640,sar=1.1] send [x=330,y=250]").is_err());
}
#[test]
fn test_parse_attribute_imageattr_recv_and_verify() {
let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr);
let imageattr = check_parse(
"imageattr:* recv [x=800,y=[50,80,30],sar=1.1] send [x=330,y=250,sar=[1.1,1.3,1.9],q=0.1]",
);
assert_eq!(imageattr.pt, SdpAttributePayloadType::Wildcard);
match imageattr.recv {
SdpAttributeImageAttrSetList::Sets(sets) => {
assert_eq!(sets.len(), 1);
let set = &sets[0];
assert_eq!(
set.x,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![800])
);
assert_eq!(
set.y,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![50, 80, 30])
);
assert_eq!(set.par, None);
assert_eq!(
set.sar,
Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![1.1]))
);
assert_eq!(set.q, None);
}
_ => {
unreachable!();
}
}
match imageattr.send {
SdpAttributeImageAttrSetList::Sets(sets) => {
assert_eq!(sets.len(), 1);
let set = &sets[0];
assert_eq!(
set.x,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![330])
);
assert_eq!(
set.y,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![250])
);
assert_eq!(set.par, None);
assert_eq!(
set.sar,
Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![
1.1, 1.3, 1.9,
]))
);
assert_eq!(set.q, Some(0.1));
}
_ => {
unreachable!();
}
}
}
#[test]
fn test_parse_attribute_imageattr_send_and_verify() {
let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr);
let imageattr = check_parse(
"imageattr:97 send [x=[480:16:800],y=[100,200,300],par=[1.2-1.3],q=0.6] [x=1080,y=[144:176],sar=[0.5-0.7]] recv *"
);
assert_eq!(imageattr.pt, SdpAttributePayloadType::PayloadType(97));
match imageattr.send {
SdpAttributeImageAttrSetList::Sets(sets) => {
assert_eq!(sets.len(), 2);
let first_set = &sets[0];
assert_eq!(
first_set.x,
SdpAttributeImageAttrXyRange::Range(480, 800, Some(16))
);
assert_eq!(
first_set.y,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![100, 200, 300])
);
assert_eq!(
first_set.par,
Some(SdpAttributeImageAttrPRange { min: 1.2, max: 1.3 })
);
assert_eq!(first_set.sar, None);
assert_eq!(first_set.q, Some(0.6));
let second_set = &sets[1];
assert_eq!(
second_set.x,
SdpAttributeImageAttrXyRange::DiscreteValues(vec![1080])
);
assert_eq!(
second_set.y,
SdpAttributeImageAttrXyRange::Range(144, 176, None)
);
assert_eq!(second_set.par, None);
assert_eq!(
second_set.sar,
Some(SdpAttributeImageAttrSRange::Range(0.5, 0.7))
);
assert_eq!(second_set.q, None);
}
_ => {
unreachable!();
}
}
assert_eq!(imageattr.recv, SdpAttributeImageAttrSetList::Wildcard);
}
#[test]
fn test_parse_attribute_inactive() {
let check_parse = make_check_parse!(SdpAttribute::Inactive);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("inactive");
assert!(parse_attribute("inactive foobar").is_err());
}
#[test]
fn test_parse_attribute_label() {
let check_parse = make_check_parse!(String, SdpAttribute::Label);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Label);
check_parse_and_serialize("label:1");
check_parse_and_serialize("label:foobar");
check_parse_and_serialize("label:foobar barfoo");
assert!(parse_attribute("label:").is_err());
}
#[test]
fn test_parse_attribute_maxptime() {
let check_parse = make_check_parse!(u64, SdpAttribute::MaxPtime);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::MaxPtime);
check_parse_and_serialize("maxptime:60");
assert!(parse_attribute("maxptime:").is_err());
assert!(parse_attribute("maxptime:60 100").is_err());
assert!(parse_attribute("maxptime:foobar").is_err());
}
#[test]
fn test_parse_attribute_mid() {
let check_parse = make_check_parse!(String, SdpAttribute::Mid);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Mid);
check_parse_and_serialize("mid:sdparta_0");
check_parse_and_serialize("mid:sdparta_0 sdparta_1 sdparta_2");
assert!(parse_attribute("mid:").is_err());
}
#[test]
fn test_parse_attribute_msid() {
let check_parse = make_check_parse!(SdpAttributeMsid, SdpAttribute::Msid);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Msid);
check_parse_and_serialize("msid:{5a990edd-0568-ac40-8d97-310fc33f3411}");
check_parse_and_serialize(
"msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}",
);
assert!(parse_attribute("msid:").is_err());
}
#[test]
fn test_parse_attribute_msid_semantics() {
let check_parse = make_check_parse!(SdpAttributeMsidSemantic, SdpAttribute::MsidSemantic);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::MsidSemantic);
check_parse_and_serialize("msid-semantic:WMS *");
check_parse_and_serialize("msid-semantic:WMS foo");
assert!(parse_attribute("msid-semantic:").is_err());
}
#[test]
fn test_parse_attribute_ptime() {
let check_parse = make_check_parse!(u64, SdpAttribute::Ptime);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Ptime);
check_parse_and_serialize("ptime:30");
assert!(parse_attribute("ptime:").is_err());
}
#[test]
fn test_anonymize_remote_candidate() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
if let SdpType::Attribute(SdpAttribute::RemoteCandidate(remote)) =
parse_attribute("remote-candidates:0 10.0.0.1 5555")?
{
let masked = remote.masked_clone(&mut anon);
assert_eq!(masked.address, Address::Ip(IpAddr::V4(Ipv4Addr::from(1))));
assert_eq!(masked.port, 1);
} else {
unreachable!();
}
Ok(())
}
#[test]
fn test_parse_attribute_rid() {
let check_parse = make_check_parse!(SdpAttributeRid, SdpAttribute::Rid);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Rid);
check_parse_and_serialize("rid:foo send pt=10");
check_parse_and_serialize("rid:110 send pt=9,10");
check_parse_and_serialize("rid:110 send pt=9,10;max-fs=10");
check_parse_and_serialize("rid:110 send pt=9,10;max-width=10;depends=1,2,3");
assert!(
parse_attribute("rid:110 send pt=9, 10;max-fs=10;UNKNOWN=100; depends=1, 2, 3").is_ok()
);
assert!(parse_attribute("rid:110 send max-fs=10").is_ok());
assert!(parse_attribute("rid:110 recv max-width=1920;max-height=1080").is_ok());
check_parse_and_serialize("rid:110 recv max-mbps=420;max-cpb=3;max-dpb=3");
check_parse_and_serialize("rid:110 recv scale-down-by=1.35;depends=1,2,3");
check_parse_and_serialize("rid:110 recv max-width=10;depends=1,2,3");
check_parse_and_serialize("rid:110 recv max-fs=10;UNKNOWN=100;depends=1,2,3");
assert!(parse_attribute("rid:").is_err());
assert!(parse_attribute("rid:120 send pt=").is_err());
assert!(parse_attribute("rid:120 send pt=;max-width=10").is_err());
assert!(parse_attribute("rid:120 send pt=9;max-width=").is_err());
assert!(parse_attribute("rid:120 send pt=9;max-width=;max-width=10").is_err());
}
#[test]
fn test_parse_attribute_rid_and_verify() {
let check_parse = make_check_parse!(SdpAttributeRid, SdpAttribute::Rid);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Rid);
check_parse_and_serialize("rid:foo send");
let mut rid = check_parse("rid:foo send");
assert_eq!(rid.id, "foo");
assert_eq!(rid.direction, SdpSingleDirection::Send);
check_parse_and_serialize("rid:110 send pt=9");
rid = check_parse("rid:110 send pt=9");
assert_eq!(rid.id, "110");
assert_eq!(rid.direction, SdpSingleDirection::Send);
assert_eq!(rid.formats, vec![9]);
check_parse_and_serialize("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3");
rid = check_parse("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3");
assert_eq!(rid.id, "110");
assert_eq!(rid.direction, SdpSingleDirection::Send);
assert_eq!(rid.formats, vec![9, 10]);
assert_eq!(rid.params.max_fs, 10);
assert_eq!(rid.params.unknown, vec!["UNKNOWN=100"]);
assert_eq!(rid.depends, vec!["1", "2", "3"]);
check_parse_and_serialize("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000");
rid = check_parse("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000");
assert_eq!(rid.id, "110");
assert_eq!(rid.direction, SdpSingleDirection::Recv);
assert_eq!(rid.params.max_fps, 42);
assert_eq!(rid.params.max_fs, 10);
assert_eq!(rid.params.max_br, 3);
assert_eq!(rid.params.max_pps, 1000);
}
#[test]
fn test_parse_attribute_recvonly() {
let check_parse = make_check_parse!(SdpAttribute::Recvonly);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("recvonly");
assert!(parse_attribute("recvonly foobar").is_err());
}
#[test]
fn test_parse_attribute_remote_candidate() {
let check_parse = make_check_parse!(SdpAttributeRemoteCandidate, SdpAttribute::RemoteCandidate);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::RemoteCandidate);
check_parse_and_serialize("remote-candidates:0 10.0.0.1 5555");
check_parse_and_serialize("remote-candidates:12345 ::1 5555");
assert!(parse_attribute("remote-candidates:abc 10.0.0.1 5555").is_err());
assert!(parse_attribute("remote-candidates:0 10.0.0.1 70000").is_err());
assert!(parse_attribute("remote-candidates:0 10.0.0.1").is_err());
assert!(parse_attribute("remote-candidates:0").is_err());
assert!(parse_attribute("remote-candidates:").is_err());
}
#[test]
fn test_parse_attribute_sendonly() {
let check_parse = make_check_parse!(SdpAttribute::Sendonly);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("sendonly");
assert!(parse_attribute("sendonly foobar").is_err());
}
#[test]
fn test_parse_attribute_sendrecv() {
let check_parse = make_check_parse!(SdpAttribute::Sendrecv);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("sendrecv");
assert!(parse_attribute("sendrecv foobar").is_err());
}
#[test]
fn test_parse_attribute_setup() {
let check_parse = make_check_parse!(SdpAttributeSetup, SdpAttribute::Setup);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Setup);
check_parse_and_serialize("setup:active");
check_parse_and_serialize("setup:passive");
check_parse_and_serialize("setup:actpass");
check_parse_and_serialize("setup:holdconn");
assert!(parse_attribute("setup:").is_err());
assert!(parse_attribute("setup:foobar").is_err());
}
#[test]
fn test_parse_attribute_rtcp() {
let check_parse = make_check_parse!(SdpAttributeRtcp, SdpAttribute::Rtcp);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtcp);
check_parse_and_serialize("rtcp:5000");
check_parse_and_serialize("rtcp:9 IN IP4 0.0.0.0");
check_parse_and_serialize("rtcp:9 IN IP6 2001:db8::1");
assert!(parse_attribute("rtcp:").is_err());
assert!(parse_attribute("rtcp:70000").is_err());
assert!(parse_attribute("rtcp:9 IN").is_err());
assert!(parse_attribute("rtcp:9 IN IP4").is_err());
assert!(parse_attribute("rtcp:9 IN IP4 ::1").is_err());
}
#[test]
fn test_parse_attribute_rtcp_fb() {
let check_parse = make_check_parse!(SdpAttributeRtcpFb, SdpAttribute::Rtcpfb);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtcpfb);
check_parse_and_serialize("rtcp-fb:101 ack rpsi");
check_parse_and_serialize("rtcp-fb:101 ack app");
check_parse_and_serialize("rtcp-fb:101 ccm");
check_parse_and_serialize("rtcp-fb:101 ccm fir");
check_parse_and_serialize("rtcp-fb:101 ccm tmmbr");
check_parse_and_serialize("rtcp-fb:101 ccm tstr");
check_parse_and_serialize("rtcp-fb:101 ccm vbcm");
check_parse_and_serialize("rtcp-fb:101 nack");
check_parse_and_serialize("rtcp-fb:101 nack sli");
check_parse_and_serialize("rtcp-fb:101 nack pli");
check_parse_and_serialize("rtcp-fb:101 nack rpsi");
check_parse_and_serialize("rtcp-fb:101 nack app");
check_parse_and_serialize("rtcp-fb:101 trr-int 1");
check_parse_and_serialize("rtcp-fb:101 goog-remb");
check_parse_and_serialize("rtcp-fb:101 transport-cc");
assert!(parse_attribute("rtcp-fb:101 unknown").is_err());
assert!(parse_attribute("rtcp-fb:101 ack").is_err());
assert!(parse_attribute("rtcp-fb:101 ccm unknwon").is_err());
assert!(parse_attribute("rtcp-fb:101 nack unknown").is_err());
assert!(parse_attribute("rtcp-fb:101 trr-int").is_err());
assert!(parse_attribute("rtcp-fb:101 trr-int a").is_err());
assert!(parse_attribute("rtcp-fb:101 goog-remb unknown").is_err());
assert!(parse_attribute("rtcp-fb:101 transport-cc unknown").is_err());
}
#[test]
fn test_parse_attribute_rtcp_mux() {
let check_parse = make_check_parse!(SdpAttribute::RtcpMux);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("rtcp-mux");
assert!(parse_attribute("rtcp-mux foobar").is_err());
}
#[test]
fn test_parse_attribute_rtcp_mux_only() {
let check_parse = make_check_parse!(SdpAttribute::RtcpMuxOnly);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("rtcp-mux-only");
assert!(parse_attribute("rtcp-mux-only bar").is_err());
}
#[test]
fn test_parse_attribute_rtcp_rsize() {
let check_parse = make_check_parse!(SdpAttribute::RtcpRsize);
let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse);
check_parse_and_serialize("rtcp-rsize");
assert!(parse_attribute("rtcp-rsize foobar").is_err());
}
#[test]
fn test_parse_attribute_rtpmap() {
let check_parse = make_check_parse!(SdpAttributeRtpmap, SdpAttribute::Rtpmap);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtpmap);
check_parse_and_serialize("rtpmap:109 opus/48000");
check_parse_and_serialize("rtpmap:109 opus/48000/2");
assert!(parse_attribute("rtpmap: ").is_err());
assert!(parse_attribute("rtpmap:109 ").is_err());
assert!(parse_attribute("rtpmap:109 opus").is_err());
assert!(parse_attribute("rtpmap:128 opus/48000").is_err());
}
#[test]
fn test_parse_attribute_sctpmap() {
let check_parse = make_check_parse!(SdpAttributeSctpmap, SdpAttribute::Sctpmap);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Sctpmap);
check_parse_and_serialize("sctpmap:5000 webrtc-datachannel 256");
assert!(parse_attribute("sctpmap:70000 webrtc-datachannel").is_err());
assert!(parse_attribute("sctpmap:70000 webrtc-datachannel 256").is_err());
assert!(parse_attribute("sctpmap:5000 unsupported 256").is_err());
assert!(parse_attribute("sctpmap:5000 webrtc-datachannel 2a").is_err());
}
#[test]
fn test_parse_attribute_sctp_port() {
let check_parse = make_check_parse!(u64, SdpAttribute::SctpPort);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::SctpPort);
check_parse_and_serialize("sctp-port:5000");
assert!(parse_attribute("sctp-port:").is_err());
assert!(parse_attribute("sctp-port:70000").is_err());
}
#[test]
fn test_parse_attribute_max_message_size() {
let check_parse = make_check_parse!(u64, SdpAttribute::MaxMessageSize);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::MaxMessageSize);
check_parse_and_serialize("max-message-size:1");
check_parse_and_serialize("max-message-size:100000");
check_parse_and_serialize("max-message-size:4294967297");
check_parse_and_serialize("max-message-size:0");
assert!(parse_attribute("max-message-size:").is_err());
assert!(parse_attribute("max-message-size:abc").is_err());
}
#[test]
fn test_parse_attribute_simulcast() {
let check_parse = make_check_parse!(SdpAttributeSimulcast, SdpAttribute::Simulcast);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Simulcast);
check_parse_and_serialize("simulcast:send 1");
check_parse_and_serialize("simulcast:recv test");
check_parse_and_serialize("simulcast:recv ~test");
check_parse_and_serialize("simulcast:recv test;foo");
check_parse_and_serialize("simulcast:recv foo,bar");
check_parse_and_serialize("simulcast:recv foo,bar;test");
check_parse_and_serialize("simulcast:send 1;4,5 recv 6;7");
check_parse_and_serialize("simulcast:send 1,2,3;~4,~5 recv 6;~7,~8");
assert!(parse_attribute("simulcast: send rid=foo;bar").is_ok());
assert!(parse_attribute("simulcast:").is_err());
assert!(parse_attribute("simulcast:send").is_err());
assert!(parse_attribute("simulcast:foobar 1").is_err());
assert!(parse_attribute("simulcast:send 1 foobar 2").is_err());
assert!(parse_attribute("simulcast: send foo=8;10").is_err());
}
#[test]
fn test_parse_attribute_ssrc() {
let check_parse = make_check_parse!(SdpAttributeSsrc, SdpAttribute::Ssrc);
let check_parse_and_serialize =
make_check_parse_and_serialize!(check_parse, SdpAttribute::Ssrc);
check_parse_and_serialize("ssrc:2655508255");
check_parse_and_serialize("ssrc:2655508255 foo");
check_parse_and_serialize("ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}");
check_parse_and_serialize("ssrc:2082260239 msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 315b086a-5cb6-4221-89de-caf0b038c79d");
assert!(parse_attribute("ssrc:").is_err());
assert!(parse_attribute("ssrc:foo").is_err());
}
#[test]
fn test_anonymize_attribute_ssrc() -> Result<(), SdpParserInternalError> {
let mut anon = StatefulSdpAnonymizer::new();
let parsed = parse_attribute("ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}")?;
let (ssrc1, masked) = if let SdpType::Attribute(a) = parsed {
let masked = a.masked_clone(&mut anon);
match (a, masked) {
(SdpAttribute::Ssrc(ssrc), SdpAttribute::Ssrc(masked)) => (ssrc, masked),
(_, _) => unreachable!(),
}
} else {
unreachable!()
};
assert_eq!(ssrc1.id, masked.id);
assert_eq!(ssrc1.attribute, masked.attribute);
assert_eq!("cname-00000001", masked.value.unwrap());
let ssrc2 = parse_attribute("ssrc:2082260239 msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 315b086a-5cb6-4221-89de-caf0b038c79d")?;
if let SdpType::Attribute(SdpAttribute::Ssrc(ssrc2)) = ssrc2 {
let masked = ssrc2.masked_clone(&mut anon);
assert_eq!(ssrc2.id, masked.id);
assert_eq!(ssrc2.attribute, masked.attribute);
assert_eq!(ssrc2.value, masked.value);
} else {
unreachable!()
}
Ok(())
}
#[test]
fn test_parse_attribute_ssrc_group() {
let parsed = parse_attribute("ssrc-group:FID 3156517279 2673335628");
match parsed {
Ok(SdpType::Attribute(attr)) => {
assert_eq!(attr.to_string(), "ssrc-group:FID 3156517279 2673335628");
let (semantic, ssrcs) = match attr {
SdpAttribute::SsrcGroup(semantic, ssrcs) => {
let stringified_ssrcs: Vec<String> =
ssrcs.iter().map(|ssrc| ssrc.to_string()).collect();
(semantic.to_string(), stringified_ssrcs)
}
_ => unreachable!(),
};
assert_eq!(semantic, "FID");
assert_eq!(ssrcs.len(), 2);
assert_eq!(ssrcs[0], "3156517279");
assert_eq!(ssrcs[1], "2673335628");
}
Err(e) => panic!("{}", e),
_ => unreachable!(),
}
assert!(parse_attribute("ssrc-group:").is_err());
assert!(parse_attribute("ssrc-group:BLAH").is_err());
assert!(parse_attribute("ssrc-group:FID").is_err());
}
#[test]
fn test_parse_unknown_attribute() {
assert!(parse_attribute("unknown").is_err())
}