diff options
author | Richard Walters <rwalters@digitalstirling.com> | 2020-10-14 23:56:26 -0700 |
---|---|---|
committer | Richard Walters <rwalters@digitalstirling.com> | 2020-10-14 23:56:26 -0700 |
commit | 4bb39345ab9cfcaecf9a5aae7dda1b08a6ab490e (patch) | |
tree | 5a6b4c287452782861632438174eafedb5891bb2 /src | |
parent | 03b82171f70815e43fbc64d120a20e7a1eebd0bc (diff) |
Distribute tests closer to the code they test
Diffstat (limited to 'src')
-rw-r--r-- | src/authority.rs | 146 | ||||
-rw-r--r-- | src/parse_host_port.rs | 91 | ||||
-rw-r--r-- | src/uri.rs | 330 | ||||
-rw-r--r-- | src/validate_ipv4_address.rs | 53 | ||||
-rw-r--r-- | src/validate_ipv6_address.rs | 65 |
5 files changed, 374 insertions, 311 deletions
diff --git a/src/authority.rs b/src/authority.rs index 955816c..18d86fa 100644 --- a/src/authority.rs +++ b/src/authority.rs @@ -151,3 +151,149 @@ impl std::fmt::Display for Authority { Ok(()) } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn userinfo() { + named_tuple!( + struct TestVector { + authority_string: &'static str, + userinfo: Option<&'static str>, + } + ); + let test_vectors: &[TestVector] = &[ + ("www.example.com", None).into(), + ("joe@www.example.com", Some("joe")).into(), + ("pepe:feelsbadman@www.example.com", Some("pepe:feelsbadman")).into(), + ]; + for test_vector in test_vectors { + let authority = Authority::parse(test_vector.authority_string()); + assert!(authority.is_ok()); + let authority = authority.unwrap(); + assert_eq!( + test_vector.userinfo().map(str::as_bytes), + authority.userinfo.as_ref().map(|v| &v[..]) + ); + } + } + + #[test] + fn userinfo_illegal_characters() { + let test_vectors = [ + "%X@www.example.com", + "{@www.example.com", + ]; + for test_vector in &test_vectors { + let authority = Authority::parse(test_vector); + assert!(authority.is_err()); + } + } + + #[test] + fn userinfo_barely_legal() { + named_tuple!( + struct TestVector { + uri_string: &'static str, + userinfo: &'static str + } + ); + let test_vectors: &[TestVector] = &[ + ("%41@www.example.com", "A").into(), + ("@www.example.com", "").into(), + ("!@www.example.com", "!").into(), + ("'@www.example.com", "'").into(), + ("(@www.example.com", "(").into(), + (";@www.example.com", ";").into(), + (":@www.example.com", ":").into(), + ]; + for test_vector in test_vectors { + let authority = Authority::parse(test_vector.uri_string()); + assert!(authority.is_ok()); + let authority = authority.unwrap(); + assert_eq!( + Some(test_vector.userinfo().as_bytes()), + authority.userinfo.as_ref().map(|v| &v[..]) + ); + } + } + + #[test] + fn host_illegal_characters() { + let test_vectors = [ + "%X@www.example.com", + "@www:example.com", + "[vX.:]", + ]; + for test_vector in &test_vectors { + let authority = Authority::parse(test_vector); + assert!(authority.is_err()); + } + } + + #[test] + fn host_barely_legal() { + named_tuple!( + struct TestVector { + authority_string: &'static str, + host: &'static str + } + ); + let test_vectors: &[TestVector] = &[ + ("%41", "a").into(), + ("", "").into(), + ("!", "!").into(), + ("'", "'").into(), + ("(", "(").into(), + (";", ";").into(), + ("1.2.3.4", "1.2.3.4").into(), + ("[v7.:]", "v7.:").into(), + ("[v7.aB]", "v7.aB").into(), + ]; + for test_vector in test_vectors { + let authority = Authority::parse(test_vector.authority_string()); + assert!(authority.is_ok()); + let authority = authority.unwrap(); + assert_eq!( + test_vector.host().as_bytes(), + authority.host() + ); + } + } + + #[test] + fn host_ends_in_dot() { + let authority = Authority::parse("example.com."); + assert!(authority.is_ok()); + let authority = authority.unwrap(); + assert_eq!( + b"example.com.", + authority.host() + ); + } + + #[test] + fn host_mixed_case() { + let test_vectors = [ + "www.example.com", + "www.EXAMPLE.com", + "www.exAMple.com", + "www.example.cOM", + "wWw.exampLe.Com", + ]; + let normalized_host = "www.example.com"; + for test_vector in &test_vectors { + let authority = Authority::parse(*test_vector); + assert!(authority.is_ok()); + let authority = authority.unwrap(); + assert_eq!( + normalized_host.as_bytes(), + authority.host() + ); + } + } + +} diff --git a/src/parse_host_port.rs b/src/parse_host_port.rs index ae4a278..abb34e1 100644 --- a/src/parse_host_port.rs +++ b/src/parse_host_port.rs @@ -203,3 +203,94 @@ pub fn parse_host_port<T>(host_port_string: T) -> Result<(Vec<u8>, Option<u16>), })? .finalize() } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn non_empty_port_number() { + let result = parse_host_port("www.example.com:8080"); + assert!(result.is_ok()); + let (host, port) = result.unwrap(); + assert_eq!(b"www.example.com", &host[..]); + assert_eq!(Some(8080), port); + } + + #[test] + fn empty_port_number() { + let result = parse_host_port("www.example.com:"); + assert!(result.is_ok()); + let (host, port) = result.unwrap(); + assert_eq!(b"www.example.com", &host[..]); + assert_eq!(None, port); + } + + #[test] + fn no_port_number() { + let result = parse_host_port("www.example.com"); + assert!(result.is_ok()); + let (host, port) = result.unwrap(); + assert_eq!(b"www.example.com", &host[..]); + assert_eq!(None, port); + } + + #[test] + fn bad_port_number_purly_alphabetic() { + let result = parse_host_port("www.example.com:spam"); + assert!(result.is_err()); + } + + #[test] + fn bad_port_number_starts_numeric_ends_alphabetic() { + let result = parse_host_port("www.example.com:8080spam"); + assert!(result.is_err()); + } + + #[test] + fn largest_valid_port_number() { + let result = parse_host_port("www.example.com:65535"); + assert!(result.is_ok()); + let (_, port) = result.unwrap(); + assert_eq!(Some(65535), port); + } + + #[test] + fn bad_port_number_too_big() { + let result = parse_host_port("www.example.com:65536"); + assert!(matches!(result, Err(Error::IllegalPortNumber(_)))); + } + + #[test] + fn bad_port_number_negative() { + let result = parse_host_port("www.example.com:-1234"); + assert!(result.is_err()); + } + + #[test] + fn truncated_host() { + let test_vectors = [ + "[::ffff:1.2.3.4/", + "[:]/", + "[v]/", + ]; + for test_vector in &test_vectors { + assert_eq!( + Err(Error::TruncatedHost), + parse_host_port(test_vector), + "{}", + test_vector + ); + } + } + + #[test] + fn ipv6_address_with_ipv4_part_missing_bracket() { + assert!(matches!( + parse_host_port("::ffff:1.2.3.4]"), + Err(Error::IllegalPortNumber(_)) + )); + } + +} @@ -748,7 +748,7 @@ mod tests { use super::*; #[test] - fn parse_from_string_no_scheme() { + fn no_scheme() { let uri = Uri::parse("foo/bar"); assert!(uri.is_ok()); let uri = uri.unwrap(); @@ -758,7 +758,7 @@ mod tests { } #[test] - fn parse_from_string_url() { + fn url() { let uri = Uri::parse("http://www.example.com/foo/bar"); assert!(uri.is_ok()); let uri = uri.unwrap(); @@ -769,7 +769,7 @@ mod tests { } #[test] - fn parse_from_string_urn_default_path_delimiter() { + fn urn_default_path_delimiter() { let uri = Uri::parse("urn:book:fantasy:Hobbit"); assert!(uri.is_ok()); let uri = uri.unwrap(); @@ -779,7 +779,7 @@ mod tests { } #[test] - fn parse_from_string_path_corner_cases() { + fn path_corner_cases() { named_tuple!( struct TestVector { path_in: &'static str, @@ -801,82 +801,13 @@ mod tests { } #[test] - fn parse_from_string_has_a_non_empty_port_number() { - let uri = Uri::parse("http://www.example.com:8080/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some("www.example.com"), uri.host_to_string().unwrap().as_deref()); - assert_eq!(Some(8080), uri.port()); - } - - #[test] - fn parse_from_string_has_an_empty_port_number() { - let uri = Uri::parse("http://www.example.com:/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some("www.example.com"), uri.host_to_string().unwrap().as_deref()); - assert_eq!(None, uri.port()); - } - - #[test] - fn parse_from_string_does_not_have_a_port_number() { - let uri = Uri::parse("http://www.example.com/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some("www.example.com"), uri.host_to_string().unwrap().as_deref()); - assert_eq!(None, uri.port()); - } - - #[test] - fn parse_from_string_twice_first_with_port_number_then_without() { - let uri = Uri::parse("http://www.example.com:8080/foo/bar"); - assert!(uri.is_ok()); - let uri = Uri::parse("http://www.example.com/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(None, uri.port()); - } - - #[test] - fn parse_from_string_bad_port_number_purly_alphabetic() { - let uri = Uri::parse("http://www.example.com:spam/foo/bar"); - assert!(uri.is_err()); - } - - #[test] - fn parse_from_string_bad_port_number_starts_numeric_ends_alphabetic() { - let uri = Uri::parse("http://www.example.com:8080spam/foo/bar"); - assert!(uri.is_err()); - } - - #[test] - fn parse_from_string_largest_valid_port_number() { - let uri = Uri::parse("http://www.example.com:65535/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some(65535), uri.port()); - } - - #[test] - fn parse_from_string_bad_port_number_too_big() { - let uri = Uri::parse("http://www.example.com:65536/foo/bar"); - assert!(matches!(uri, Err(Error::IllegalPortNumber(_)))); - } - - #[test] - fn parse_from_string_bad_port_number_negative() { - let uri = Uri::parse("http://www.example.com:-1234/foo/bar"); - assert!(uri.is_err()); - } - - #[test] - fn parse_from_string_ends_after_authority() { + fn uri_ends_after_authority() { let uri = Uri::parse("http://www.example.com"); assert!(uri.is_ok()); } #[test] - fn parse_from_string_relative_vs_non_relative_references() { + fn relative_vs_non_relative_references() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -901,7 +832,7 @@ mod tests { } #[test] - fn parse_from_string_relative_vs_non_relative_paths() { + fn relative_vs_non_relative_paths() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -932,7 +863,7 @@ mod tests { } #[test] - fn parse_from_string_query_and_fragment_elements() { + fn query_and_fragment_elements() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -968,45 +899,7 @@ mod tests { } #[test] - fn parse_from_string_user_info() { - named_tuple!( - struct TestVector { - uri_string: &'static str, - userinfo: Option<&'static str>, - } - ); - let test_vectors: &[TestVector] = &[ - ("http://www.example.com/", None).into(), - ("http://joe@www.example.com", Some("joe")).into(), - ("http://pepe:feelsbadman@www.example.com", Some("pepe:feelsbadman")).into(), - ("//www.example.com", None).into(), - ("//bob@www.example.com", Some("bob")).into(), - ("/", None).into(), - ("foo", None).into(), - ]; - for test_vector in test_vectors { - let uri = Uri::parse(test_vector.uri_string()); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!( - *test_vector.userinfo(), - uri.userinfo_to_string().unwrap().as_deref() - ); - } - } - - #[test] - fn parse_from_string_twice_first_user_info_then_without() { - let uri = Uri::parse("http://joe@www.example.com/foo/bar"); - assert!(uri.is_ok()); - let uri = Uri::parse("/foo/bar"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(None, uri.userinfo()); - } - - #[test] - fn parse_from_string_scheme_illegal_characters() { + fn scheme_illegal_characters() { let test_vectors = [ "://www.example.com/", "0://www.example.com/", @@ -1022,7 +915,7 @@ mod tests { } #[test] - fn parse_from_string_scheme_barely_legal() { + fn scheme_barely_legal() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -1046,7 +939,7 @@ mod tests { } #[test] - fn parse_from_string_scheme_mixed_case () { + fn scheme_mixed_case () { let test_vectors = [ "http://www.example.com/", "hTtp://www.example.com/", @@ -1063,116 +956,7 @@ mod tests { } #[test] - fn parse_from_string_user_info_illegal_characters() { - let test_vectors = [ - "//%X@www.example.com/", - "//{@www.example.com/", - ]; - for test_vector in &test_vectors { - let uri = Uri::parse(test_vector); - assert!(uri.is_err()); - } - } - - #[test] - fn parse_from_string_user_info_barely_legal() { - named_tuple!( - struct TestVector { - uri_string: &'static str, - userinfo: &'static str - } - ); - let test_vectors: &[TestVector] = &[ - ("//%41@www.example.com/", "A").into(), - ("//@www.example.com/", "").into(), - ("//!@www.example.com/", "!").into(), - ("//'@www.example.com/", "'").into(), - ("//(@www.example.com/", "(").into(), - ("//;@www.example.com/", ";").into(), - ("http://:@www.example.com/", ":").into(), - ]; - for test_vector in test_vectors { - let uri = Uri::parse(test_vector.uri_string()); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!( - Some(*test_vector.userinfo()), - uri.userinfo_to_string().unwrap().as_deref() - ); - } - } - - #[test] - fn parse_from_string_host_illegal_characters() { - let test_vectors = [ - "//%X@www.example.com/", - "//@www:example.com/", - "//[vX.:]/", - ]; - for test_vector in &test_vectors { - let uri = Uri::parse(test_vector); - assert!(uri.is_err()); - } - } - - #[test] - fn parse_from_string_host_barely_legal() { - named_tuple!( - struct TestVector { - uri_string: &'static str, - host: &'static str - } - ); - let test_vectors: &[TestVector] = &[ - ("//%41/", "a").into(), - ("///", "").into(), - ("//!/", "!").into(), - ("//'/", "'").into(), - ("//(/", "(").into(), - ("//;/", ";").into(), - ("//1.2.3.4/", "1.2.3.4").into(), - ("//[v7.:]/", "v7.:").into(), - ("//[v7.aB]/", "v7.aB").into(), - ]; - for test_vector in test_vectors { - let uri = Uri::parse(test_vector.uri_string()); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some(*test_vector.host()), uri.host_to_string().unwrap().as_deref()); - } - } - - #[test] - fn parse_from_string_host_mixed_case() { - let test_vectors = [ - "http://www.example.com/", - "http://www.EXAMPLE.com/", - "http://www.exAMple.com/", - "http://www.example.cOM/", - "http://wWw.exampLe.Com/", - ]; - let normalized_host = "www.example.com"; - for test_vector in &test_vectors { - let uri = Uri::parse(*test_vector); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!( - Some(normalized_host), - uri.host_to_string().unwrap().as_deref() - ); - } - } - - #[test] - fn parse_from_string_host_ends_in_dot() { - let uri = Uri::parse("http://example.com./foo"); - assert!(uri.is_ok()); - let uri = uri.unwrap(); - assert_eq!(Some("example.com."), uri.host_to_string().unwrap().as_deref()); - } - - #[test] - fn parse_from_string_dont_misinterpret_colon_in_other_places_as_scheme_delimiter() { + fn dont_misinterpret_colon_in_other_places_as_scheme_delimiter() { let test_vectors = [ "//foo:bar@www.example.com/", "//www.example.com/a:b", @@ -1190,7 +974,7 @@ mod tests { } #[test] - fn parse_from_string_path_illegal_characters() { + fn path_illegal_characters() { let test_vectors = [ "http://www.example.com/foo[bar", "http://www.example.com/]bar", @@ -1220,7 +1004,7 @@ mod tests { } #[test] - fn parse_from_string_path_barely_legal() { + fn path_barely_legal() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -1243,7 +1027,7 @@ mod tests { } #[test] - fn parse_from_string_query_illegal_characters() { + fn query_illegal_characters() { let test_vectors = [ "http://www.example.com/?foo[bar", "http://www.example.com/?]bar", @@ -1273,7 +1057,7 @@ mod tests { } #[test] - fn parse_from_string_query_barely_legal() { + fn query_barely_legal() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -1301,7 +1085,7 @@ mod tests { } #[test] - fn parse_from_string_fragment_illegal_characters() { + fn fragment_illegal_characters() { let test_vectors = [ "http://www.example.com/#foo[bar", "http://www.example.com/#]bar", @@ -1331,7 +1115,7 @@ mod tests { } #[test] - fn parse_from_string_fragment_barely_legal() { + fn fragment_barely_legal() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -1358,7 +1142,7 @@ mod tests { } #[test] - fn parse_from_string_paths_with_percent_encoded_characters() { + fn paths_with_percent_encoded_characters() { named_tuple!( struct TestVector { uri_string: &'static str, @@ -1540,82 +1324,6 @@ mod tests { } #[test] - fn ipv6_address_good() { - named_tuple!( - struct TestVector { - uri_string: &'static str, - expected_host: &'static str, - } - ); - let test_vectors: &[TestVector] = &[ - ("http://[::1]/", "::1").into(), - ("http://[::ffff:1.2.3.4]/", "::ffff:1.2.3.4").into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/", "2001:db8:85a3:8d3:1319:8a2e:370:7348").into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e:370::]/", "2001:db8:85a3:8d3:1319:8a2e:370::").into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e::1]/", "2001:db8:85a3:8d3:1319:8a2e::1").into(), - ("http://[fFfF::1]", "fFfF::1").into(), - ("http://[1234::1]", "1234::1").into(), - ("http://[fFfF:1:2:3:4:5:6:a]", "fFfF:1:2:3:4:5:6:a").into(), - ("http://[2001:db8:85a3::8a2e:0]/", "2001:db8:85a3::8a2e:0").into(), - ("http://[2001:db8:85a3:8a2e::]/", "2001:db8:85a3:8a2e::").into(), - ]; - for test_vector in test_vectors { - let uri = Uri::parse(test_vector.uri_string()); - assert!(uri.is_ok()); - assert_eq!( - Some(*test_vector.expected_host()), - uri.unwrap().host_to_string().unwrap().as_deref() - ); - } - } - - #[test] - fn ipv6_address_bad() { - named_tuple!( - struct TestVector { - uri_string: &'static str, - expected_error: Error, - } - ); - let test_vectors: &[TestVector] = &[ - ("http://[::fFfF::1]", Error::TooManyDoubleColons).into(), - ("http://[::ffff:1.2.x.4]/", Error::IllegalCharacter(Context::Ipv4Address)).into(), - ("http://[::ffff:1.2.3.4.8]/", Error::TooManyAddressParts).into(), - ("http://[::ffff:1.2.3]/", Error::TooFewAddressParts).into(), - ("http://[::ffff:1.2.3.]/", Error::TruncatedHost).into(), - ("http://[::ffff:1.2.3.256]/", Error::InvalidDecimalOctet).into(), - ("http://[::fxff:1.2.3.4]/", Error::IllegalCharacter(Context::Ipv6Address)).into(), - ("http://[::ffff:1.2.3.-4]/", Error::IllegalCharacter(Context::Ipv4Address)).into(), - ("http://[::ffff:1.2.3. 4]/", Error::IllegalCharacter(Context::Ipv4Address)).into(), - ("http://[::ffff:1.2.3.4 ]/", Error::IllegalCharacter(Context::Ipv4Address)).into(), - ("http://[::ffff:1.2.3.4/", Error::TruncatedHost).into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e:370:7348:0000]/", Error::TooManyAddressParts).into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e:370:7348::1]/", Error::TooManyAddressParts).into(), - ("http://[2001:db8:85a3:8d3:1319:8a2e:370::1]/", Error::TooManyAddressParts).into(), - ("http://[2001:db8:85a3::8a2e:0:]/", Error::TruncatedHost).into(), - ("http://[2001:db8:85a3::8a2e::]/", Error::TooManyDoubleColons).into(), - ("http://[20001:db8:85a3::1]/", Error::TooManyDigits).into(), - ("http://[]/", Error::TooFewAddressParts).into(), - ("http://[:]/", Error::TruncatedHost).into(), - ("http://[v]/", Error::TruncatedHost).into(), - ]; - for test_vector in test_vectors { - let uri = Uri::parse(test_vector.uri_string()); - assert_eq!( - *test_vector.expected_error(), - uri.unwrap_err(), - "{}", - test_vector.uri_string() - ); - } - - // This is a special case because std::num doesn't trust that we're - // good enough to make our own ParseIntError values. FeelsBadMan - let uri = Uri::parse("http://::ffff:1.2.3.4]/"); - assert!(matches!(uri, Err(Error::IllegalPortNumber(_)))); - } - - #[test] // NOTE: `clippy::too_many_arguments` lint has to be disabled at the // test level because it's triggered inside the `named_tuple!` macro // expansion. diff --git a/src/validate_ipv4_address.rs b/src/validate_ipv4_address.rs index 1b9f7b2..98254df 100644 --- a/src/validate_ipv4_address.rs +++ b/src/validate_ipv4_address.rs @@ -94,3 +94,56 @@ pub fn validate_ipv4_address<T>(address: T) -> Result<(), Error> })? .finalize() } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn good() { + let test_vectors = [ + "0.0.0.0", + "1.2.3.0", + "1.2.3.4", + "1.2.3.255", + "1.2.255.4", + "1.255.3.4", + "255.2.3.4", + "255.255.255.255", + ]; + for test_vector in &test_vectors { + assert!(validate_ipv4_address(*test_vector).is_ok()); + } + } + + #[test] + fn bad() { + named_tuple!( + struct TestVector { + address_string: &'static str, + expected_error: Error, + } + ); + let test_vectors: &[TestVector] = &[ + ("1.2.x.4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("1.2.3.4.8", Error::TooManyAddressParts).into(), + ("1.2.3", Error::TooFewAddressParts).into(), + ("1.2.3.", Error::TruncatedHost).into(), + ("1.2.3.256", Error::InvalidDecimalOctet).into(), + ("1.2.3.-4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("1.2.3. 4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("1.2.3.4 ", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ]; + for test_vector in test_vectors { + let result = validate_ipv4_address(test_vector.address_string()); + assert!(result.is_err(), "{}", test_vector.address_string()); + assert_eq!( + *test_vector.expected_error(), + result.unwrap_err(), + "{}", + test_vector.address_string() + ); + } + } +} diff --git a/src/validate_ipv6_address.rs b/src/validate_ipv6_address.rs index eb3900d..502da65 100644 --- a/src/validate_ipv6_address.rs +++ b/src/validate_ipv6_address.rs @@ -210,3 +210,68 @@ pub fn validate_ipv6_address<T>(address: T) -> Result<(), Error> })? .finalize() } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn good() { + let test_vectors = [ + "::1", + "::ffff:1.2.3.4", + "2001:db8:85a3:8d3:1319:8a2e:370:7348", + "2001:db8:85a3:8d3:1319:8a2e:370::", + "2001:db8:85a3:8d3:1319:8a2e::1", + "fFfF::1", + "1234::1", + "fFfF:1:2:3:4:5:6:a", + "2001:db8:85a3::8a2e:0", + "2001:db8:85a3:8a2e::", + ]; + for test_vector in &test_vectors { + assert!(validate_ipv6_address(*test_vector).is_ok()); + } + } + + #[test] + fn bad() { + named_tuple!( + struct TestVector { + address_string: &'static str, + expected_error: Error, + } + ); + let test_vectors: &[TestVector] = &[ + ("::fFfF::1", Error::TooManyDoubleColons).into(), + ("::ffff:1.2.x.4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("::ffff:1.2.3.4.8", Error::TooManyAddressParts).into(), + ("::ffff:1.2.3", Error::TooFewAddressParts).into(), + ("::ffff:1.2.3.", Error::TruncatedHost).into(), + ("::ffff:1.2.3.256", Error::InvalidDecimalOctet).into(), + ("::fxff:1.2.3.4", Error::IllegalCharacter(Context::Ipv6Address)).into(), + ("::ffff:1.2.3.-4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("::ffff:1.2.3. 4", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("::ffff:1.2.3.4 ", Error::IllegalCharacter(Context::Ipv4Address)).into(), + ("2001:db8:85a3:8d3:1319:8a2e:370:7348:0000", Error::TooManyAddressParts).into(), + ("2001:db8:85a3:8d3:1319:8a2e:370:7348::1", Error::TooManyAddressParts).into(), + ("2001:db8:85a3:8d3:1319:8a2e:370::1", Error::TooManyAddressParts).into(), + ("2001:db8:85a3::8a2e:0:", Error::TruncatedHost).into(), + ("2001:db8:85a3::8a2e::", Error::TooManyDoubleColons).into(), + ("20001:db8:85a3::1", Error::TooManyDigits).into(), + ("", Error::TooFewAddressParts).into(), + ]; + for test_vector in test_vectors { + let result = validate_ipv6_address(test_vector.address_string()); + assert!(result.is_err(), "{}", test_vector.address_string()); + assert_eq!( + *test_vector.expected_error(), + result.unwrap_err(), + "{}", + test_vector.address_string() + ); + } + } + +} |