/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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");
    // old draft 03 notation used by Firefox 55
    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());
    // old draft 03 notation used by Firefox 55
    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())
}
