#[cfg(not(test))]
compile_error!("`tests` module should be enable only when `cfg(tests)`");
use core::fmt::Write;
#[cfg(feature = "alloc")]
use alloc::string::ToString;
use crate::spec::{IriSpec, Spec, UriSpec};
use crate::tests::{Components, WritableByteBuffer};
use crate::types::{
IriAbsoluteStr, IriReferenceStr, IriRelativeStr, IriStr, RiReferenceStr, UriAbsoluteStr,
UriReferenceStr, UriRelativeStr, UriStr,
};
macro_rules! components {
($(($serialized:expr, $scheme:expr, $authority:expr, $path:expr, $query:expr, $fragment:expr)),* $(,)?) => {
&[
$(
(
$serialized,
Components::new(
{$scheme}.into(),
{$authority}.into(),
{$path}.into(),
{$query}.into(),
{$fragment}.into(),
),
)
),*
]
}
}
fn compose_roundtrip<S: Spec>(components_list: &[(&str, Components)]) {
#[cfg(feature = "alloc")]
{
for (expected_str, components) in components_list {
let serialized = components.to_string();
assert_eq!(serialized, *expected_str);
let parsed =
RiReferenceStr::<S>::new(&serialized).expect("the test case should be valid");
let decomposed = Components::from(parsed);
assert_eq!(
components, decomposed,
"serialized={:?}, parsed={:?}",
serialized, parsed
);
}
}
{
let buf = &mut [0_u8; 256];
let mut buf = WritableByteBuffer::new(buf);
for (expected_str, components) in components_list {
buf.clear();
write!(&mut buf, "{}", components).expect("failed to serialize");
let serialized = buf.as_str();
assert_eq!(serialized, *expected_str);
let parsed =
RiReferenceStr::<S>::new(serialized).expect("the test case should be valid");
let decomposed = Components::from(parsed);
assert_eq!(
components, decomposed,
"serialized={:?}, parsed={:?}",
serialized, parsed
);
}
}
}
fn assert_convertible<T: ?Sized>(source: &str)
where
T: PartialEq<str> + core::fmt::Debug,
for<'a> &'a T: TryFrom<&'a str>,
for<'a> <&'a T as TryFrom<&'a str>>::Error: core::fmt::Debug,
{
match <&T>::try_from(source) {
Ok(parsed) => assert_eq!(parsed, source),
Err(e) => panic!("should be convertible: source={:?}: {:?}", source, e),
}
}
fn assert_non_convertible<T: ?Sized>(source: &str)
where
T: PartialEq<str> + core::fmt::Debug,
for<'a> &'a T: TryFrom<&'a str>,
for<'a> <&'a T as TryFrom<&'a str>>::Error: core::fmt::Debug,
{
if let Ok(parsed) = <&T>::try_from(source) {
panic!(
"should not be convertible: source={:?}, parsed={:?}",
source, parsed
);
}
}
mod decompose {
use super::*;
#[test]
fn test_combinations() {
let components = components!(
("", None, None, "", None, None),
("#fragment", None, None, "", None, "fragment"),
("?query", None, None, "", "query", None),
("?query#fragment", None, None, "", "query", "fragment"),
("path", None, None, "path", None, None),
("path#fragment", None, None, "path", None, "fragment"),
("path?query", None, None, "path", "query", None),
(
"path?query#fragment",
None,
None,
"path",
"query",
"fragment"
),
("/path", None, None, "/path", None, None),
("/path#fragment", None, None, "/path", None, "fragment"),
("/path?query", None, None, "/path", "query", None),
(
"/path?query#fragment",
None,
None,
"/path",
"query",
"fragment"
),
("//authority", None, "authority", "", None, None),
(
"//authority#fragment",
None,
"authority",
"",
None,
"fragment"
),
("//authority?query", None, "authority", "", "query", None),
(
"//authority?query#fragment",
None,
"authority",
"",
"query",
"fragment"
),
("//authority/path", None, "authority", "/path", None, None),
(
"//authority/path#fragment",
None,
"authority",
"/path",
None,
"fragment"
),
(
"//authority/path?query",
None,
"authority",
"/path",
"query",
None
),
(
"//authority/path?query#fragment",
None,
"authority",
"/path",
"query",
"fragment"
),
("scheme:", "scheme", None, "", None, None),
("scheme:#fragment", "scheme", None, "", None, "fragment"),
("scheme:?query", "scheme", None, "", "query", None),
(
"scheme:?query#fragment",
"scheme",
None,
"",
"query",
"fragment"
),
("scheme:path", "scheme", None, "path", None, None),
(
"scheme:path#fragment",
"scheme",
None,
"path",
None,
"fragment"
),
("scheme:path?query", "scheme", None, "path", "query", None),
(
"scheme:path?query#fragment",
"scheme",
None,
"path",
"query",
"fragment"
),
("scheme:/path", "scheme", None, "/path", None, None),
(
"scheme:/path#fragment",
"scheme",
None,
"/path",
None,
"fragment"
),
("scheme:/path?query", "scheme", None, "/path", "query", None),
(
"scheme:/path?query#fragment",
"scheme",
None,
"/path",
"query",
"fragment"
),
("scheme://authority", "scheme", "authority", "", None, None),
(
"scheme://authority#fragment",
"scheme",
"authority",
"",
None,
"fragment"
),
(
"scheme://authority?query",
"scheme",
"authority",
"",
"query",
None
),
(
"scheme://authority?query#fragment",
"scheme",
"authority",
"",
"query",
"fragment"
),
(
"scheme://authority/path",
"scheme",
"authority",
"/path",
None,
None
),
(
"scheme://authority/path#fragment",
"scheme",
"authority",
"/path",
None,
"fragment"
),
(
"scheme://authority/path?query",
"scheme",
"authority",
"/path",
"query",
None
),
(
"scheme://authority/path?query#fragment",
"scheme",
"authority",
"/path",
"query",
"fragment"
),
);
compose_roundtrip::<UriSpec>(components);
compose_roundtrip::<IriSpec>(components);
}
#[test]
fn test_absolute_paths_slash_only() {
let components = components!(
("scheme:", "scheme", None, "", None, None),
("scheme:/", "scheme", None, "/", None, None),
("scheme://", "scheme", "", "", None, None),
("scheme:///", "scheme", "", "/", None, None),
("scheme:////", "scheme", "", "//", None, None),
("scheme://///", "scheme", "", "///", None, None),
);
compose_roundtrip::<UriSpec>(components);
compose_roundtrip::<IriSpec>(components);
}
#[test]
fn test_relative_paths_slash_only() {
let components = components!(
("", None, None, "", None, None),
("/", None, None, "/", None, None),
("//", None, "", "", None, None),
("///", None, "", "/", None, None),
("////", None, "", "//", None, None),
("/////", None, "", "///", None, None),
);
compose_roundtrip::<UriSpec>(components);
compose_roundtrip::<IriSpec>(components);
}
#[test]
fn test_hosts() {
let components = components!(
("//192.0.2.0", None, "192.0.2.0", "", None, None),
("//192.0.2.0:80", None, "192.0.2.0:80", "", None, None),
("//198.51.100.99", None, "198.51.100.99", "", None, None),
(
"//198.51.100.99:80",
None,
"198.51.100.99:80",
"",
None,
None
),
("//203.0.113.255", None, "203.0.113.255", "", None, None),
(
"//203.0.113.255:80",
None,
"203.0.113.255:80",
"",
None,
None
),
("//[2001:db8::]", None, "[2001:db8::]", "", None, None),
("//[2001:db8::]:80", None, "[2001:db8::]:80", "", None, None),
("//[2001:0db8::]", None, "[2001:0db8::]", "", None, None),
(
"//[2001:0db8::]:80",
None,
"[2001:0db8::]:80",
"",
None,
None
),
(
"//[2001:0db8:0:0:0:0:0:ffff]",
None,
"[2001:0db8:0:0:0:0:0:ffff]",
"",
None,
None
),
(
"//[2001:0db8:0:0:0:0:0:ffff]:80",
None,
"[2001:0db8:0:0:0:0:0:ffff]:80",
"",
None,
None
),
(
"//[2001:0DB8:0:0:0:A:BCDE:FFFF]",
None,
"[2001:0DB8:0:0:0:A:BCDE:FFFF]",
"",
None,
None
),
(
"//[2001:0DB8:0:0:0:A:BCDE:FFFF]:80",
None,
"[2001:0DB8:0:0:0:A:BCDE:FFFF]:80",
"",
None,
None
),
(
"//[2001:0db8::89ab:cdef:89AB:CDEF]:80",
None,
"[2001:0db8::89ab:cdef:89AB:CDEF]:80",
"",
None,
None
),
(
"//[2001:0db8:0:0:0:0::1]",
None,
"[2001:0db8:0:0:0:0::1]",
"",
None,
None
),
(
"//[2001:0db8:0:0:0:0::1]:80",
None,
"[2001:0db8:0:0:0:0::1]:80",
"",
None,
None
),
(
"//[2001:0db8:0:0:0::1]",
None,
"[2001:0db8:0:0:0::1]",
"",
None,
None
),
(
"//[2001:0db8:0:0:0::1]:80",
None,
"[2001:0db8:0:0:0::1]:80",
"",
None,
None
),
(
"//[2001:0db8:0:0::1]",
None,
"[2001:0db8:0:0::1]",
"",
None,
None
),
(
"//[2001:0db8:0:0::1]:80",
None,
"[2001:0db8:0:0::1]:80",
"",
None,
None
),
(
"//[2001:0db8:0::1]",
None,
"[2001:0db8:0::1]",
"",
None,
None
),
(
"//[2001:0db8:0::1]:80",
None,
"[2001:0db8:0::1]:80",
"",
None,
None
),
("//[2001:0db8::1]", None, "[2001:0db8::1]", "", None, None),
(
"//[2001:0db8::1]:80",
None,
"[2001:0db8::1]:80",
"",
None,
None
),
(
"//[v9999.this-is-future-version-of-ip-address:::::::::]",
None,
"[v9999.this-is-future-version-of-ip-address:::::::::]",
"",
None,
None
),
(
"//[v9999.this-is-future-version-of-ip-address:::::::::]:80",
None,
"[v9999.this-is-future-version-of-ip-address:::::::::]:80",
"",
None,
None
),
);
compose_roundtrip::<UriSpec>(components);
compose_roundtrip::<IriSpec>(components);
}
#[test]
fn test_abnormal_port() {
let components = components!(
("//localhost", None, "localhost", "", None, None),
("//localhost:", None, "localhost:", "", None, None),
(
"//localhost:999999999",
None,
"localhost:999999999",
"",
None,
None
),
);
compose_roundtrip::<UriSpec>(components);
compose_roundtrip::<IriSpec>(components);
}
}
mod validate {
use super::*;
#[test]
fn rfc3986_uris_absolute_without_fragment() {
const URIS: &[&str] = &[
"https://tools.ietf.org/html/rfc3986",
"https://datatracker.ietf.org/doc/html/rfc3986",
"ftp://ftp.is.co.za/rfc/rfc1808.txt",
"http://www.ietf.org/rfc/rfc2396.txt",
"ldap://[2001:db8::7]/c=GB?objectClass?one",
"mailto:John.Doe@example.com",
"news:comp.infosystems.www.servers.unix",
"tel:+1-816-555-1212",
"telnet://192.0.2.16:80/",
"urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
"urn:example:animal:ferret:nose",
"mailto:fred@example.com",
"foo://info.example.com?fred",
"http://a/b/c/d;p?q",
"g:h",
"http://a/b/c/g",
"http://a/b/c/g/",
"http://a/g",
"http://g",
"http://a/b/c/d;p?y",
"http://a/b/c/g?y",
"http://a/b/c/;x",
"http://a/b/c/g;x",
"http://a/b/c/d;p?q",
"http://a/b/c/",
"http://a/b/",
"http://a/b/g",
"http://a/",
"http://a/b/c/g.",
"http://a/b/c/.g",
"http://a/b/c/g..",
"http://a/b/c/..g",
"http://a/b/c/g/h",
"http://a/b/c/h",
"http://a/b/c/g;x=1/y",
"http://a/b/c/y",
"http://a/b/c/g?y/./x",
"http://a/b/c/g?y/../x",
"example://a/b/c/%7Bfoo%7D",
"eXAMPLE://a/./b/../b/%63/%7bfoo%7d",
"HTTP://www.EXAMPLE.com/",
"http://www.example.com/",
"http://example.com",
"http://example.com/",
"http://example.com:/",
"http://example.com:80/",
"http://example.com/?",
"mailto:Joe@Example.COM",
"mailto:Joe@example.com",
"http://example.com/data",
"http://example.com/data/",
"ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm",
"http://www.w3.org/Addressing/",
"ftp://foo.example.com/rfc/",
"https://tools.ietf.org/html/rfc3987",
"https://datatracker.ietf.org/doc/html/rfc3987",
"http://xn--rsum-bpad.example.org",
"http://r%C3%A9sum%C3%A9.example.org",
"http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82",
"http://www.example.org/r%C3%A9sum%C3%A9.html",
"http://www.example.org/r%E9sum%E9.html",
"http://www.example.org/D%C3%BCrst",
"http://www.example.org/D%FCrst",
"http://xn--99zt52a.example.org/%e2%80%ae",
"http://xn--99zt52a.example.org/%E2%80%AE",
"http://ab.CDEFGH.ij/kl/mn/op.html",
"http://ab.CDE.FGH/ij/kl/mn/op.html",
"http://AB.CD.ef/gh/IJ/KL.html",
"http://ab.cd.EF/GH/ij/kl.html",
"http://ab.CD.EF/GH/IJ/kl.html",
"http://ab.CDE123FGH.ij/kl/mn/op.html",
"http://ab.cd.ef/GH1/2IJ/KL.html",
"http://ab.cd.ef/GH%31/%32IJ/KL.html",
"http://ab.CDEFGH.123/kl/mn/op.html",
"eXAMPLE://a/./b/../b/%63/%7bfoo%7d/ros%C3%A9",
"HTTP://www.EXAMPLE.com/",
"http://www.example.com/",
"http://example.org/~user",
"http://example.org/%7euser",
"http://example.org/%7Euser",
"http://example.com",
"http://example.com/",
"http://example.com:/",
"http://example.com:80/",
"http://example.com/data",
"http://example.com/data/",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_convertible::<UriStr>(uri);
assert_convertible::<IriAbsoluteStr>(uri);
assert_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3986_uris_absolute_with_fragment() {
const URIS: &[&str] = &[
"foo://example.com:8042/over/there?name=ferret#nose",
"http://a/b/c/d;p?q#s",
"http://a/b/c/g#s",
"http://a/b/c/g?y#s",
"http://a/b/c/g;x?y#s",
"http://a/b/c/g#s/./x",
"http://a/b/c/g#s/../x",
"http://www.ics.uci.edu/pub/ietf/uri/#Related",
"http://www.ics.uci.edu/pub/ietf/uri/historical.html#WARNING",
"http://www.example.org/red%09ros%C3%A9#red",
"http://AB.CD.EF/GH/IJ/KL?MN=OP;QR=ST#UV",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3986_uris_relative() {
const URIS: &[&str] = &[
"g",
"./g",
"g/",
"/g",
"//g",
"?y",
"g?y",
"#s",
"g#s",
"g?y#s",
";x",
"g;x",
"g;x?y#s",
"",
".",
"./",
"..",
"../",
"../g",
"../..",
"../../",
"../../g",
"/./g",
"/../g",
"g.",
".g",
"g..",
"..g",
"./../g",
"./g/.",
"g/./h",
"g/../h",
"g;x=1/./y",
"g;x=1/../y",
"g?y/./x",
"g?y/../x",
"g#s/./x",
"g#s/../x",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_convertible::<IriRelativeStr>(uri);
assert_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3987_iris_absolute_without_fragment() {
const URIS: &[&str] = &[
"http://r\u{E9}sum\u{E9}.example.org",
"http://example.com/\u{10300}\u{10301}\u{10302}",
"http://www.example.org/D\u{FC}rst",
"http://\u{7D0D}\u{8C46}.example.org/%E2%80%AE",
"http://example.org/ros\u{E9}",
"example://a/b/c/%7Bfoo%7D/ros\u{E9}",
"http://www.example.org/r\u{E9}sum\u{E9}.html",
"http://www.example.org/re\u{301}sume\u{301}.html",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn rfc3987_iris_absolute_with_fragment() {
const URIS: &[&str] = &[
"http://www.example.org/r%E9sum%E9.xml#r\u{E9}sum\u{E9}",
];
for uri in URIS {
assert_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn test_invalid_char() {
const URIS: &[&str] = &[
"##", "<", ">", ];
for uri in URIS {
assert_non_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
#[test]
fn invalid_percent_encoding() {
const URIS: &[&str] = &["%", "%0", "%0g", "%f", "%fg", "%g", "%g0", "%gf", "%gg"];
for uri in URIS {
assert_non_convertible::<IriReferenceStr>(uri);
assert_non_convertible::<UriReferenceStr>(uri);
assert_non_convertible::<IriStr>(uri);
assert_non_convertible::<UriStr>(uri);
assert_non_convertible::<IriAbsoluteStr>(uri);
assert_non_convertible::<UriAbsoluteStr>(uri);
assert_non_convertible::<IriRelativeStr>(uri);
assert_non_convertible::<UriRelativeStr>(uri);
}
}
}