• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013-2014 The rust-url developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 //! Unit tests
10 
11 use std::borrow::Cow;
12 use std::cell::{Cell, RefCell};
13 use std::net::{Ipv4Addr, Ipv6Addr};
14 use std::path::{Path, PathBuf};
15 use url::{form_urlencoded, Host, Origin, Url};
16 
17 #[test]
size()18 fn size() {
19     use std::mem::size_of;
20     assert_eq!(size_of::<Url>(), size_of::<Option<Url>>());
21 }
22 
23 #[test]
test_relative()24 fn test_relative() {
25     let base: Url = "sc://%C3%B1".parse().unwrap();
26     let url = base.join("/resources/testharness.js").unwrap();
27     assert_eq!(url.as_str(), "sc://%C3%B1/resources/testharness.js");
28 }
29 
30 #[test]
test_relative_empty()31 fn test_relative_empty() {
32     let base: Url = "sc://%C3%B1".parse().unwrap();
33     let url = base.join("").unwrap();
34     assert_eq!(url.as_str(), "sc://%C3%B1");
35 }
36 
37 #[test]
test_set_empty_host()38 fn test_set_empty_host() {
39     let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap();
40     base.set_username("").unwrap();
41     assert_eq!(base.as_str(), "moz://:bar@servo/baz");
42     base.set_host(None).unwrap();
43     assert_eq!(base.as_str(), "moz:/baz");
44     base.set_host(Some("servo")).unwrap();
45     assert_eq!(base.as_str(), "moz://servo/baz");
46 
47     let mut base: Url = "file://server/share/foo/bar".parse().unwrap();
48     base.set_host(None).unwrap();
49     assert_eq!(base.as_str(), "file:///share/foo/bar");
50 
51     let mut base: Url = "file://server/share/foo/bar".parse().unwrap();
52     base.set_host(Some("foo")).unwrap();
53     assert_eq!(base.as_str(), "file://foo/share/foo/bar");
54 }
55 
56 #[test]
test_set_empty_hostname()57 fn test_set_empty_hostname() {
58     use url::quirks;
59     let mut base: Url = "moz://foo@servo/baz".parse().unwrap();
60     assert!(
61         quirks::set_hostname(&mut base, "").is_err(),
62         "setting an empty hostname to a url with a username should fail"
63     );
64     base = "moz://:pass@servo/baz".parse().unwrap();
65     assert!(
66         quirks::set_hostname(&mut base, "").is_err(),
67         "setting an empty hostname to a url with a password should fail"
68     );
69     base = "moz://servo/baz".parse().unwrap();
70     quirks::set_hostname(&mut base, "").unwrap();
71     assert_eq!(base.as_str(), "moz:///baz");
72 }
73 
74 macro_rules! assert_from_file_path {
75     ($path: expr) => {
76         assert_from_file_path!($path, $path)
77     };
78     ($path: expr, $url_path: expr) => {{
79         let url = Url::from_file_path(Path::new($path)).unwrap();
80         assert_eq!(url.host(), None);
81         assert_eq!(url.path(), $url_path);
82         assert_eq!(url.to_file_path(), Ok(PathBuf::from($path)));
83     }};
84 }
85 
86 #[test]
new_file_paths()87 fn new_file_paths() {
88     if cfg!(unix) {
89         assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
90         assert_eq!(Url::from_file_path(Path::new("../relative")), Err(()));
91     }
92     if cfg!(windows) {
93         assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
94         assert_eq!(Url::from_file_path(Path::new(r"..\relative")), Err(()));
95         assert_eq!(Url::from_file_path(Path::new(r"\drive-relative")), Err(()));
96         assert_eq!(Url::from_file_path(Path::new(r"\\ucn\")), Err(()));
97     }
98 
99     if cfg!(unix) {
100         assert_from_file_path!("/foo/bar");
101         assert_from_file_path!("/foo/ba\0r", "/foo/ba%00r");
102         assert_from_file_path!("/foo/ba%00r", "/foo/ba%2500r");
103     }
104 }
105 
106 #[test]
107 #[cfg(unix)]
new_path_bad_utf8()108 fn new_path_bad_utf8() {
109     use std::ffi::OsStr;
110     use std::os::unix::prelude::*;
111 
112     let url = Url::from_file_path(Path::new(OsStr::from_bytes(b"/foo/ba\x80r"))).unwrap();
113     let os_str = OsStr::from_bytes(b"/foo/ba\x80r");
114     assert_eq!(url.to_file_path(), Ok(PathBuf::from(os_str)));
115 }
116 
117 #[test]
new_path_windows_fun()118 fn new_path_windows_fun() {
119     if cfg!(windows) {
120         assert_from_file_path!(r"C:\foo\bar", "/C:/foo/bar");
121         assert_from_file_path!("C:\\foo\\ba\0r", "/C:/foo/ba%00r");
122 
123         // Invalid UTF-8
124         assert!(Url::parse("file:///C:/foo/ba%80r")
125             .unwrap()
126             .to_file_path()
127             .is_err());
128 
129         // test windows canonicalized path
130         let path = PathBuf::from(r"\\?\C:\foo\bar");
131         assert!(Url::from_file_path(path).is_ok());
132 
133         // Percent-encoded drive letter
134         let url = Url::parse("file:///C%3A/foo/bar").unwrap();
135         assert_eq!(url.to_file_path(), Ok(PathBuf::from(r"C:\foo\bar")));
136     }
137 }
138 
139 #[test]
new_directory_paths()140 fn new_directory_paths() {
141     if cfg!(unix) {
142         assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
143         assert_eq!(Url::from_directory_path(Path::new("../relative")), Err(()));
144 
145         let url = Url::from_directory_path(Path::new("/foo/bar")).unwrap();
146         assert_eq!(url.host(), None);
147         assert_eq!(url.path(), "/foo/bar/");
148     }
149     if cfg!(windows) {
150         assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
151         assert_eq!(Url::from_directory_path(Path::new(r"..\relative")), Err(()));
152         assert_eq!(
153             Url::from_directory_path(Path::new(r"\drive-relative")),
154             Err(())
155         );
156         assert_eq!(Url::from_directory_path(Path::new(r"\\ucn\")), Err(()));
157 
158         let url = Url::from_directory_path(Path::new(r"C:\foo\bar")).unwrap();
159         assert_eq!(url.host(), None);
160         assert_eq!(url.path(), "/C:/foo/bar/");
161     }
162 }
163 
164 #[test]
path_backslash_fun()165 fn path_backslash_fun() {
166     let mut special_url = "http://foobar.com".parse::<Url>().unwrap();
167     special_url.path_segments_mut().unwrap().push("foo\\bar");
168     assert_eq!(special_url.as_str(), "http://foobar.com/foo%5Cbar");
169 
170     let mut nonspecial_url = "thing://foobar.com".parse::<Url>().unwrap();
171     nonspecial_url.path_segments_mut().unwrap().push("foo\\bar");
172     assert_eq!(nonspecial_url.as_str(), "thing://foobar.com/foo\\bar");
173 }
174 
175 #[test]
from_str()176 fn from_str() {
177     assert!("http://testing.com/this".parse::<Url>().is_ok());
178 }
179 
180 #[test]
parse_with_params()181 fn parse_with_params() {
182     let url = Url::parse_with_params(
183         "http://testing.com/this?dont=clobberme",
184         &[("lang", "rust")],
185     )
186     .unwrap();
187 
188     assert_eq!(
189         url.as_str(),
190         "http://testing.com/this?dont=clobberme&lang=rust"
191     );
192 }
193 
194 #[test]
issue_124()195 fn issue_124() {
196     let url: Url = "file:a".parse().unwrap();
197     assert_eq!(url.path(), "/a");
198     let url: Url = "file:...".parse().unwrap();
199     assert_eq!(url.path(), "/...");
200     let url: Url = "file:..".parse().unwrap();
201     assert_eq!(url.path(), "/");
202 }
203 
204 #[test]
test_equality()205 fn test_equality() {
206     use std::collections::hash_map::DefaultHasher;
207     use std::hash::{Hash, Hasher};
208 
209     fn check_eq(a: &Url, b: &Url) {
210         assert_eq!(a, b);
211 
212         let mut h1 = DefaultHasher::new();
213         a.hash(&mut h1);
214         let mut h2 = DefaultHasher::new();
215         b.hash(&mut h2);
216         assert_eq!(h1.finish(), h2.finish());
217     }
218 
219     fn url(s: &str) -> Url {
220         let rv = s.parse().unwrap();
221         check_eq(&rv, &rv);
222         rv
223     }
224 
225     // Doesn't care if default port is given.
226     let a: Url = url("https://example.com/");
227     let b: Url = url("https://example.com:443/");
228     check_eq(&a, &b);
229 
230     // Different ports
231     let a: Url = url("http://example.com/");
232     let b: Url = url("http://example.com:8080/");
233     assert!(a != b, "{:?} != {:?}", a, b);
234 
235     // Different scheme
236     let a: Url = url("http://example.com/");
237     let b: Url = url("https://example.com/");
238     assert_ne!(a, b);
239 
240     // Different host
241     let a: Url = url("http://foo.com/");
242     let b: Url = url("http://bar.com/");
243     assert_ne!(a, b);
244 
245     // Missing path, automatically substituted. Semantically the same.
246     let a: Url = url("http://foo.com");
247     let b: Url = url("http://foo.com/");
248     check_eq(&a, &b);
249 }
250 
251 #[test]
host()252 fn host() {
253     fn assert_host(input: &str, host: Host<&str>) {
254         assert_eq!(Url::parse(input).unwrap().host(), Some(host));
255     }
256     assert_host("http://www.mozilla.org", Host::Domain("www.mozilla.org"));
257     assert_host(
258         "http://1.35.33.49",
259         Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)),
260     );
261     assert_host(
262         "http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]",
263         Host::Ipv6(Ipv6Addr::new(
264             0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344,
265         )),
266     );
267     assert_host(
268         "http://[::]",
269         Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
270     );
271     assert_host(
272         "http://[::1]",
273         Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
274     );
275     assert_host(
276         "http://0x1.0X23.0x21.061",
277         Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)),
278     );
279     assert_host("http://0x1232131", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
280     assert_host("http://111", Host::Ipv4(Ipv4Addr::new(0, 0, 0, 111)));
281     assert!(Url::parse("http://1.35.+33.49").is_err());
282     assert!(Url::parse("http://2..2.3").is_err());
283     assert!(Url::parse("http://42.0x1232131").is_err());
284     assert!(Url::parse("http://192.168.0.257").is_err());
285 
286     assert_eq!(Host::Domain("foo"), Host::Domain("foo").to_owned());
287     assert_ne!(Host::Domain("foo"), Host::Domain("bar").to_owned());
288 }
289 
290 #[test]
host_serialization()291 fn host_serialization() {
292     // libstd’s `Display for Ipv6Addr` serializes 0:0:0:0:0:0:_:_ and 0:0:0:0:0:ffff:_:_
293     // using IPv4-like syntax, as suggested in https://tools.ietf.org/html/rfc5952#section-4
294     // but https://url.spec.whatwg.org/#concept-ipv6-serializer specifies not to.
295 
296     // Not [::0.0.0.2] / [::ffff:0.0.0.2]
297     assert_eq!(
298         Url::parse("http://[0::2]").unwrap().host_str(),
299         Some("[::2]")
300     );
301     assert_eq!(
302         Url::parse("http://[0::ffff:0:2]").unwrap().host_str(),
303         Some("[::ffff:0:2]")
304     );
305 }
306 
307 #[test]
test_idna()308 fn test_idna() {
309     assert!("http://goșu.ro".parse::<Url>().is_ok());
310     assert_eq!(
311         Url::parse("http://☃.net/").unwrap().host(),
312         Some(Host::Domain("xn--n3h.net"))
313     );
314     assert!("https://r2---sn-huoa-cvhl.googlevideo.com/crossdomain.xml"
315         .parse::<Url>()
316         .is_ok());
317 }
318 
319 #[test]
test_serialization()320 fn test_serialization() {
321     let data = [
322         ("http://example.com/", "http://example.com/"),
323         ("http://addslash.com", "http://addslash.com/"),
324         ("http://@emptyuser.com/", "http://emptyuser.com/"),
325         ("http://:@emptypass.com/", "http://emptypass.com/"),
326         ("http://user@user.com/", "http://user@user.com/"),
327         (
328             "http://user:pass@userpass.com/",
329             "http://user:pass@userpass.com/",
330         ),
331         (
332             "http://slashquery.com/path/?q=something",
333             "http://slashquery.com/path/?q=something",
334         ),
335         (
336             "http://noslashquery.com/path?q=something",
337             "http://noslashquery.com/path?q=something",
338         ),
339     ];
340     for &(input, result) in &data {
341         let url = Url::parse(input).unwrap();
342         assert_eq!(url.as_str(), result);
343     }
344 }
345 
346 #[test]
test_form_urlencoded()347 fn test_form_urlencoded() {
348     let pairs: &[(Cow<'_, str>, Cow<'_, str>)] = &[
349         ("foo".into(), "é&".into()),
350         ("bar".into(), "".into()),
351         ("foo".into(), "#".into()),
352     ];
353     let encoded = form_urlencoded::Serializer::new(String::new())
354         .extend_pairs(pairs)
355         .finish();
356     assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23");
357     assert_eq!(
358         form_urlencoded::parse(encoded.as_bytes()).collect::<Vec<_>>(),
359         pairs.to_vec()
360     );
361 }
362 
363 #[test]
test_form_serialize()364 fn test_form_serialize() {
365     let encoded = form_urlencoded::Serializer::new(String::new())
366         .append_pair("foo", "é&")
367         .append_pair("bar", "")
368         .append_pair("foo", "#")
369         .append_key_only("json")
370         .finish();
371     assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23&json");
372 }
373 
374 #[test]
form_urlencoded_encoding_override()375 fn form_urlencoded_encoding_override() {
376     let encoded = form_urlencoded::Serializer::new(String::new())
377         .encoding_override(Some(&|s| s.as_bytes().to_ascii_uppercase().into()))
378         .append_pair("foo", "bar")
379         .append_key_only("xml")
380         .finish();
381     assert_eq!(encoded, "FOO=BAR&XML");
382 }
383 
384 #[test]
385 /// https://github.com/servo/rust-url/issues/61
issue_61()386 fn issue_61() {
387     let mut url = Url::parse("http://mozilla.org").unwrap();
388     url.set_scheme("https").unwrap();
389     assert_eq!(url.port(), None);
390     assert_eq!(url.port_or_known_default(), Some(443));
391     url.check_invariants().unwrap();
392 }
393 
394 #[test]
395 #[cfg(not(windows))]
396 /// https://github.com/servo/rust-url/issues/197
issue_197()397 fn issue_197() {
398     let mut url = Url::from_file_path("/").expect("Failed to parse path");
399     url.check_invariants().unwrap();
400     assert_eq!(
401         url,
402         Url::parse("file:///").expect("Failed to parse path + protocol")
403     );
404     url.path_segments_mut()
405         .expect("path_segments_mut")
406         .pop_if_empty();
407 }
408 
409 #[test]
issue_241()410 fn issue_241() {
411     Url::parse("mailto:").unwrap().cannot_be_a_base();
412 }
413 
414 #[test]
415 /// https://github.com/servo/rust-url/issues/222
append_trailing_slash()416 fn append_trailing_slash() {
417     let mut url: Url = "http://localhost:6767/foo/bar?a=b".parse().unwrap();
418     url.check_invariants().unwrap();
419     url.path_segments_mut().unwrap().push("");
420     url.check_invariants().unwrap();
421     assert_eq!(url.to_string(), "http://localhost:6767/foo/bar/?a=b");
422 }
423 
424 #[test]
425 /// https://github.com/servo/rust-url/issues/227
extend_query_pairs_then_mutate()426 fn extend_query_pairs_then_mutate() {
427     let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
428     url.query_pairs_mut()
429         .extend_pairs(vec![("auth", "my-token")].into_iter());
430     url.check_invariants().unwrap();
431     assert_eq!(
432         url.to_string(),
433         "http://localhost:6767/foo/bar?auth=my-token"
434     );
435     url.path_segments_mut().unwrap().push("some_other_path");
436     url.check_invariants().unwrap();
437     assert_eq!(
438         url.to_string(),
439         "http://localhost:6767/foo/bar/some_other_path?auth=my-token"
440     );
441 }
442 
443 #[test]
444 /// https://github.com/servo/rust-url/issues/222
append_empty_segment_then_mutate()445 fn append_empty_segment_then_mutate() {
446     let mut url: Url = "http://localhost:6767/foo/bar?a=b".parse().unwrap();
447     url.check_invariants().unwrap();
448     url.path_segments_mut().unwrap().push("").pop();
449     url.check_invariants().unwrap();
450     assert_eq!(url.to_string(), "http://localhost:6767/foo/bar?a=b");
451 }
452 
453 #[test]
454 /// https://github.com/servo/rust-url/issues/243
test_set_host()455 fn test_set_host() {
456     let mut url = Url::parse("https://example.net/hello").unwrap();
457     url.set_host(Some("foo.com")).unwrap();
458     assert_eq!(url.as_str(), "https://foo.com/hello");
459     assert!(url.set_host(None).is_err());
460     assert_eq!(url.as_str(), "https://foo.com/hello");
461     assert!(url.set_host(Some("")).is_err());
462     assert_eq!(url.as_str(), "https://foo.com/hello");
463 
464     let mut url = Url::parse("foobar://example.net/hello").unwrap();
465     url.set_host(None).unwrap();
466     assert_eq!(url.as_str(), "foobar:/hello");
467 
468     let mut url = Url::parse("foo://ș").unwrap();
469     assert_eq!(url.as_str(), "foo://%C8%99");
470     url.set_host(Some("goșu.ro")).unwrap();
471     assert_eq!(url.as_str(), "foo://go%C8%99u.ro");
472 }
473 
474 #[test]
475 // https://github.com/servo/rust-url/issues/166
test_leading_dots()476 fn test_leading_dots() {
477     assert_eq!(
478         Host::parse(".org").unwrap(),
479         Host::Domain(".org".to_owned())
480     );
481     assert_eq!(Url::parse("file://./foo").unwrap().domain(), Some("."));
482 }
483 
484 #[test]
485 /// https://github.com/servo/rust-url/issues/302
test_origin_hash()486 fn test_origin_hash() {
487     use std::collections::hash_map::DefaultHasher;
488     use std::hash::{Hash, Hasher};
489 
490     fn hash<T: Hash>(value: &T) -> u64 {
491         let mut hasher = DefaultHasher::new();
492         value.hash(&mut hasher);
493         hasher.finish()
494     }
495 
496     let origin = &Url::parse("http://example.net/").unwrap().origin();
497 
498     let origins_to_compare = [
499         Url::parse("http://example.net:80/").unwrap().origin(),
500         Url::parse("http://example.net:81/").unwrap().origin(),
501         Url::parse("http://example.net").unwrap().origin(),
502         Url::parse("http://example.net/hello").unwrap().origin(),
503         Url::parse("https://example.net").unwrap().origin(),
504         Url::parse("ftp://example.net").unwrap().origin(),
505         Url::parse("file://example.net").unwrap().origin(),
506         Url::parse("http://user@example.net/").unwrap().origin(),
507         Url::parse("http://user:pass@example.net/")
508             .unwrap()
509             .origin(),
510     ];
511 
512     for origin_to_compare in &origins_to_compare {
513         if origin == origin_to_compare {
514             assert_eq!(hash(origin), hash(origin_to_compare));
515         } else {
516             assert_ne!(hash(origin), hash(origin_to_compare));
517         }
518     }
519 
520     let opaque_origin = Url::parse("file://example.net").unwrap().origin();
521     let same_opaque_origin = Url::parse("file://example.net").unwrap().origin();
522     let other_opaque_origin = Url::parse("file://other").unwrap().origin();
523 
524     assert_ne!(hash(&opaque_origin), hash(&same_opaque_origin));
525     assert_ne!(hash(&opaque_origin), hash(&other_opaque_origin));
526 }
527 
528 #[test]
test_origin_blob_equality()529 fn test_origin_blob_equality() {
530     let origin = &Url::parse("http://example.net/").unwrap().origin();
531     let blob_origin = &Url::parse("blob:http://example.net/").unwrap().origin();
532 
533     assert_eq!(origin, blob_origin);
534 }
535 
536 #[test]
test_origin_opaque()537 fn test_origin_opaque() {
538     assert!(!Origin::new_opaque().is_tuple());
539     assert!(!&Url::parse("blob:malformed//").unwrap().origin().is_tuple())
540 }
541 
542 #[test]
test_origin_unicode_serialization()543 fn test_origin_unicode_serialization() {
544     let data = [
545         ("http://��.com", "http://��.com"),
546         ("ftp://��:��@��.com", "ftp://��.com"),
547         ("https://user@��.com", "https://��.com"),
548         ("http://��.��:40", "http://��.��:40"),
549     ];
550     for &(unicode_url, expected_serialization) in &data {
551         let origin = Url::parse(unicode_url).unwrap().origin();
552         assert_eq!(origin.unicode_serialization(), *expected_serialization);
553     }
554 
555     let ascii_origins = [
556         Url::parse("http://example.net/").unwrap().origin(),
557         Url::parse("http://example.net:80/").unwrap().origin(),
558         Url::parse("http://example.net:81/").unwrap().origin(),
559         Url::parse("http://example.net").unwrap().origin(),
560         Url::parse("http://example.net/hello").unwrap().origin(),
561         Url::parse("https://example.net").unwrap().origin(),
562         Url::parse("ftp://example.net").unwrap().origin(),
563         Url::parse("file://example.net").unwrap().origin(),
564         Url::parse("http://user@example.net/").unwrap().origin(),
565         Url::parse("http://user:pass@example.net/")
566             .unwrap()
567             .origin(),
568         Url::parse("http://127.0.0.1").unwrap().origin(),
569     ];
570     for ascii_origin in &ascii_origins {
571         assert_eq!(
572             ascii_origin.ascii_serialization(),
573             ascii_origin.unicode_serialization()
574         );
575     }
576 }
577 
578 #[test]
test_socket_addrs()579 fn test_socket_addrs() {
580     use std::net::ToSocketAddrs;
581 
582     let data = [
583         ("https://127.0.0.1/", "127.0.0.1", 443),
584         ("https://127.0.0.1:9742/", "127.0.0.1", 9742),
585         ("custom-protocol://127.0.0.1:9742/", "127.0.0.1", 9742),
586         ("custom-protocol://127.0.0.1/", "127.0.0.1", 9743),
587         ("https://[::1]/", "::1", 443),
588         ("https://[::1]:9742/", "::1", 9742),
589         ("custom-protocol://[::1]:9742/", "::1", 9742),
590         ("custom-protocol://[::1]/", "::1", 9743),
591         ("https://localhost/", "localhost", 443),
592         ("https://localhost:9742/", "localhost", 9742),
593         ("custom-protocol://localhost:9742/", "localhost", 9742),
594         ("custom-protocol://localhost/", "localhost", 9743),
595     ];
596 
597     for (url_string, host, port) in &data {
598         let url = url::Url::parse(url_string).unwrap();
599         let addrs = url
600             .socket_addrs(|| match url.scheme() {
601                 "custom-protocol" => Some(9743),
602                 _ => None,
603             })
604             .unwrap();
605         assert_eq!(
606             Some(addrs[0]),
607             (*host, *port).to_socket_addrs().unwrap().next()
608         );
609     }
610 }
611 
612 #[test]
test_no_base_url()613 fn test_no_base_url() {
614     let mut no_base_url = Url::parse("mailto:test@example.net").unwrap();
615 
616     assert!(no_base_url.cannot_be_a_base());
617     assert!(no_base_url.path_segments().is_none());
618     assert!(no_base_url.path_segments_mut().is_err());
619     assert!(no_base_url.set_host(Some("foo")).is_err());
620     assert!(no_base_url
621         .set_ip_host("127.0.0.1".parse().unwrap())
622         .is_err());
623 
624     no_base_url.set_path("/foo");
625     assert_eq!(no_base_url.path(), "%2Ffoo");
626 }
627 
628 #[test]
test_domain()629 fn test_domain() {
630     let url = Url::parse("https://127.0.0.1/").unwrap();
631     assert_eq!(url.domain(), None);
632 
633     let url = Url::parse("mailto:test@example.net").unwrap();
634     assert_eq!(url.domain(), None);
635 
636     let url = Url::parse("https://example.com/").unwrap();
637     assert_eq!(url.domain(), Some("example.com"));
638 }
639 
640 #[test]
test_query()641 fn test_query() {
642     let url = Url::parse("https://example.com/products?page=2#fragment").unwrap();
643     assert_eq!(url.query(), Some("page=2"));
644     assert_eq!(
645         url.query_pairs().next(),
646         Some((Cow::Borrowed("page"), Cow::Borrowed("2")))
647     );
648 
649     let url = Url::parse("https://example.com/products").unwrap();
650     assert!(url.query().is_none());
651     assert_eq!(url.query_pairs().count(), 0);
652 
653     let url = Url::parse("https://example.com/?country=español").unwrap();
654     assert_eq!(url.query(), Some("country=espa%C3%B1ol"));
655     assert_eq!(
656         url.query_pairs().next(),
657         Some((Cow::Borrowed("country"), Cow::Borrowed("español")))
658     );
659 
660     let url = Url::parse("https://example.com/products?page=2&sort=desc").unwrap();
661     assert_eq!(url.query(), Some("page=2&sort=desc"));
662     let mut pairs = url.query_pairs();
663     assert_eq!(pairs.count(), 2);
664     assert_eq!(
665         pairs.next(),
666         Some((Cow::Borrowed("page"), Cow::Borrowed("2")))
667     );
668     assert_eq!(
669         pairs.next(),
670         Some((Cow::Borrowed("sort"), Cow::Borrowed("desc")))
671     );
672 }
673 
674 #[test]
test_fragment()675 fn test_fragment() {
676     let url = Url::parse("https://example.com/#fragment").unwrap();
677     assert_eq!(url.fragment(), Some("fragment"));
678 
679     let url = Url::parse("https://example.com/").unwrap();
680     assert_eq!(url.fragment(), None);
681 }
682 
683 #[test]
test_set_ip_host()684 fn test_set_ip_host() {
685     let mut url = Url::parse("http://example.com").unwrap();
686 
687     url.set_ip_host("127.0.0.1".parse().unwrap()).unwrap();
688     assert_eq!(url.host_str(), Some("127.0.0.1"));
689 
690     url.set_ip_host("::1".parse().unwrap()).unwrap();
691     assert_eq!(url.host_str(), Some("[::1]"));
692 }
693 
694 #[test]
test_set_href()695 fn test_set_href() {
696     use url::quirks::set_href;
697 
698     let mut url = Url::parse("https://existing.url").unwrap();
699 
700     assert!(set_href(&mut url, "mal//formed").is_err());
701 
702     assert!(set_href(
703         &mut url,
704         "https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment"
705     )
706     .is_ok());
707     assert_eq!(
708         url,
709         Url::parse("https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment")
710             .unwrap()
711     );
712 }
713 
714 #[test]
test_domain_encoding_quirks()715 fn test_domain_encoding_quirks() {
716     use url::quirks::{domain_to_ascii, domain_to_unicode};
717 
718     let data = [
719         ("http://example.com", "", ""),
720         ("��.��", "xn--j28h.xn--938h", "��.��"),
721         ("example.com", "example.com", "example.com"),
722         ("mailto:test@example.net", "", ""),
723     ];
724 
725     for url in &data {
726         assert_eq!(domain_to_ascii(url.0), url.1);
727         assert_eq!(domain_to_unicode(url.0), url.2);
728     }
729 }
730 
731 #[cfg(feature = "expose_internals")]
732 #[test]
test_expose_internals()733 fn test_expose_internals() {
734     use url::quirks::internal_components;
735     use url::quirks::InternalComponents;
736 
737     let url = Url::parse("https://example.com/path/file.ext?key=val&key2=val2#fragment").unwrap();
738     let InternalComponents {
739         scheme_end,
740         username_end,
741         host_start,
742         host_end,
743         port,
744         path_start,
745         query_start,
746         fragment_start,
747     } = internal_components(&url);
748 
749     assert_eq!(scheme_end, 5);
750     assert_eq!(username_end, 8);
751     assert_eq!(host_start, 8);
752     assert_eq!(host_end, 19);
753     assert_eq!(port, None);
754     assert_eq!(path_start, 19);
755     assert_eq!(query_start, Some(33));
756     assert_eq!(fragment_start, Some(51));
757 }
758 
759 #[test]
test_windows_unc_path()760 fn test_windows_unc_path() {
761     if !cfg!(windows) {
762         return;
763     }
764 
765     let url = Url::from_file_path(Path::new(r"\\host\share\path\file.txt")).unwrap();
766     assert_eq!(url.as_str(), "file://host/share/path/file.txt");
767 
768     let url = Url::from_file_path(Path::new(r"\\höst\share\path\file.txt")).unwrap();
769     assert_eq!(url.as_str(), "file://xn--hst-sna/share/path/file.txt");
770 
771     let url = Url::from_file_path(Path::new(r"\\192.168.0.1\share\path\file.txt")).unwrap();
772     assert_eq!(url.host(), Some(Host::Ipv4(Ipv4Addr::new(192, 168, 0, 1))));
773 
774     let path = url.to_file_path().unwrap();
775     assert_eq!(path.to_str(), Some(r"\\192.168.0.1\share\path\file.txt"));
776 
777     // Another way to write these:
778     let url = Url::from_file_path(Path::new(r"\\?\UNC\host\share\path\file.txt")).unwrap();
779     assert_eq!(url.as_str(), "file://host/share/path/file.txt");
780 
781     // Paths starting with "\\.\" (Local Device Paths) are intentionally not supported.
782     let url = Url::from_file_path(Path::new(r"\\.\some\path\file.txt"));
783     assert!(url.is_err());
784 }
785 
786 #[test]
test_syntax_violation_callback()787 fn test_syntax_violation_callback() {
788     use url::SyntaxViolation::*;
789     let violation = Cell::new(None);
790     let url = Url::options()
791         .syntax_violation_callback(Some(&|v| violation.set(Some(v))))
792         .parse("http:////mozilla.org:42")
793         .unwrap();
794     assert_eq!(url.port(), Some(42));
795 
796     let v = violation.take().unwrap();
797     assert_eq!(v, ExpectedDoubleSlash);
798     assert_eq!(v.description(), "expected //");
799     assert_eq!(v.to_string(), "expected //");
800 }
801 
802 #[test]
test_syntax_violation_callback_lifetimes()803 fn test_syntax_violation_callback_lifetimes() {
804     use url::SyntaxViolation::*;
805     let violation = Cell::new(None);
806     let vfn = |s| violation.set(Some(s));
807 
808     let url = Url::options()
809         .syntax_violation_callback(Some(&vfn))
810         .parse("http:////mozilla.org:42")
811         .unwrap();
812     assert_eq!(url.port(), Some(42));
813     assert_eq!(violation.take(), Some(ExpectedDoubleSlash));
814 
815     let url = Url::options()
816         .syntax_violation_callback(Some(&vfn))
817         .parse("http://mozilla.org\\path")
818         .unwrap();
819     assert_eq!(url.path(), "/path");
820     assert_eq!(violation.take(), Some(Backslash));
821 }
822 
823 #[test]
test_syntax_violation_callback_types()824 fn test_syntax_violation_callback_types() {
825     use url::SyntaxViolation::*;
826 
827     let data = [
828         ("http://mozilla.org/\\foo", Backslash, "backslash"),
829         (" http://mozilla.org", C0SpaceIgnored, "leading or trailing control or space character are ignored in URLs"),
830         ("http://user:pass@mozilla.org", EmbeddedCredentials, "embedding authentication information (username or password) in an URL is not recommended"),
831         ("http:///mozilla.org", ExpectedDoubleSlash, "expected //"),
832         ("file:/foo.txt", ExpectedFileDoubleSlash, "expected // after file:"),
833         ("file://mozilla.org/c:/file.txt", FileWithHostAndWindowsDrive, "file: with host and Windows drive letter"),
834         ("http://mozilla.org/^", NonUrlCodePoint, "non-URL code point"),
835         ("http://mozilla.org/#\x000", NullInFragment, "NULL characters are ignored in URL fragment identifiers"),
836         ("http://mozilla.org/%1", PercentDecode, "expected 2 hex digits after %"),
837         ("http://mozilla.org\t/foo", TabOrNewlineIgnored, "tabs or newlines are ignored in URLs"),
838         ("http://user@:pass@mozilla.org", UnencodedAtSign, "unencoded @ sign in username or password")
839     ];
840 
841     for test_case in &data {
842         let violation = Cell::new(None);
843         Url::options()
844             .syntax_violation_callback(Some(&|v| violation.set(Some(v))))
845             .parse(test_case.0)
846             .unwrap();
847 
848         let v = violation.take();
849         assert_eq!(v, Some(test_case.1));
850         assert_eq!(v.unwrap().description(), test_case.2);
851         assert_eq!(v.unwrap().to_string(), test_case.2);
852     }
853 }
854 
855 #[test]
test_options_reuse()856 fn test_options_reuse() {
857     use url::SyntaxViolation::*;
858     let violations = RefCell::new(Vec::new());
859     let vfn = |v| violations.borrow_mut().push(v);
860 
861     let options = Url::options().syntax_violation_callback(Some(&vfn));
862     let url = options.parse("http:////mozilla.org").unwrap();
863 
864     let options = options.base_url(Some(&url));
865     let url = options.parse("/sub\\path").unwrap();
866     assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
867     assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
868 }
869 
870 /// https://github.com/servo/rust-url/issues/505
871 #[cfg(windows)]
872 #[test]
test_url_from_file_path()873 fn test_url_from_file_path() {
874     use std::path::PathBuf;
875     use url::Url;
876 
877     let p = PathBuf::from("c:///");
878     let u = Url::from_file_path(p).unwrap();
879     let path = u.to_file_path().unwrap();
880     assert_eq!("C:\\", path.to_str().unwrap());
881 }
882 
883 /// https://github.com/servo/rust-url/issues/505
884 #[cfg(not(windows))]
885 #[test]
886 fn test_url_from_file_path() {
887     use std::path::PathBuf;
888     use url::Url;
889 
890     let p = PathBuf::from("/c:/");
891     let u = Url::from_file_path(p).unwrap();
892     let path = u.to_file_path().unwrap();
893     assert_eq!("/c:/", path.to_str().unwrap());
894 }
895 
896 #[test]
897 fn test_non_special_path() {
898     let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
899     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
900     db_url.set_path("diesel_foo");
901     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/diesel_foo");
902     assert_eq!(db_url.path(), "/diesel_foo");
903 }
904 
905 #[test]
906 fn test_non_special_path2() {
907     let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
908     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
909     db_url.set_path("");
910     assert_eq!(db_url.path(), "");
911     assert_eq!(db_url.as_str(), "postgres://postgres@localhost");
912     db_url.set_path("foo");
913     assert_eq!(db_url.path(), "/foo");
914     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
915     db_url.set_path("/bar");
916     assert_eq!(db_url.path(), "/bar");
917     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/bar");
918 }
919 
920 #[test]
921 fn test_non_special_path3() {
922     let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
923     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
924     db_url.set_path("/");
925     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
926     assert_eq!(db_url.path(), "/");
927     db_url.set_path("/foo");
928     assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
929     assert_eq!(db_url.path(), "/foo");
930 }
931 
932 #[test]
933 fn test_set_scheme_to_file_with_host() {
934     let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
935     let result = url.set_scheme("file");
936     assert_eq!(url.to_string(), "http://localhost:6767/foo/bar");
937     assert_eq!(result, Err(()));
938 }
939 
940 #[test]
941 fn no_panic() {
942     let mut url = Url::parse("arhttpsps:/.//eom/dae.com/\\\\t\\:").unwrap();
943     url::quirks::set_hostname(&mut url, "//eom/datcom/\\\\t\\://eom/data.cs").unwrap();
944 }
945 
946 #[test]
947 fn pop_if_empty_in_bounds() {
948     let mut url = Url::parse("m://").unwrap();
949     let mut segments = url.path_segments_mut().unwrap();
950     segments.pop_if_empty();
951     segments.pop();
952 }
953 
954 #[test]
955 fn test_slicing() {
956     use url::Position::*;
957 
958     #[derive(Default)]
959     struct ExpectedSlices<'a> {
960         full: &'a str,
961         scheme: &'a str,
962         username: &'a str,
963         password: &'a str,
964         host: &'a str,
965         port: &'a str,
966         path: &'a str,
967         query: &'a str,
968         fragment: &'a str,
969     }
970 
971     let data = [
972         ExpectedSlices {
973             full: "https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment",
974             scheme: "https",
975             username: "user",
976             password: "pass",
977             host: "domain.com",
978             port: "9742",
979             path: "/path/file.ext",
980             query: "key=val&key2=val2",
981             fragment: "fragment",
982         },
983         ExpectedSlices {
984             full: "https://domain.com:9742/path/file.ext#fragment",
985             scheme: "https",
986             host: "domain.com",
987             port: "9742",
988             path: "/path/file.ext",
989             fragment: "fragment",
990             ..Default::default()
991         },
992         ExpectedSlices {
993             full: "https://domain.com:9742/path/file.ext",
994             scheme: "https",
995             host: "domain.com",
996             port: "9742",
997             path: "/path/file.ext",
998             ..Default::default()
999         },
1000         ExpectedSlices {
1001             full: "blob:blob-info",
1002             scheme: "blob",
1003             path: "blob-info",
1004             ..Default::default()
1005         },
1006     ];
1007 
1008     for expected_slices in &data {
1009         let url = Url::parse(expected_slices.full).unwrap();
1010         assert_eq!(&url[..], expected_slices.full);
1011         assert_eq!(&url[BeforeScheme..AfterScheme], expected_slices.scheme);
1012         assert_eq!(
1013             &url[BeforeUsername..AfterUsername],
1014             expected_slices.username
1015         );
1016         assert_eq!(
1017             &url[BeforePassword..AfterPassword],
1018             expected_slices.password
1019         );
1020         assert_eq!(&url[BeforeHost..AfterHost], expected_slices.host);
1021         assert_eq!(&url[BeforePort..AfterPort], expected_slices.port);
1022         assert_eq!(&url[BeforePath..AfterPath], expected_slices.path);
1023         assert_eq!(&url[BeforeQuery..AfterQuery], expected_slices.query);
1024         assert_eq!(
1025             &url[BeforeFragment..AfterFragment],
1026             expected_slices.fragment
1027         );
1028         assert_eq!(&url[..AfterFragment], expected_slices.full);
1029     }
1030 }
1031 
1032 #[test]
1033 fn test_make_relative() {
1034     let tests = [
1035         (
1036             "http://127.0.0.1:8080/test",
1037             "http://127.0.0.1:8080/test",
1038             "",
1039         ),
1040         (
1041             "http://127.0.0.1:8080/test",
1042             "http://127.0.0.1:8080/test/",
1043             "test/",
1044         ),
1045         (
1046             "http://127.0.0.1:8080/test/",
1047             "http://127.0.0.1:8080/test",
1048             "../test",
1049         ),
1050         (
1051             "http://127.0.0.1:8080/",
1052             "http://127.0.0.1:8080/?foo=bar#123",
1053             "?foo=bar#123",
1054         ),
1055         (
1056             "http://127.0.0.1:8080/",
1057             "http://127.0.0.1:8080/test/video",
1058             "test/video",
1059         ),
1060         (
1061             "http://127.0.0.1:8080/test",
1062             "http://127.0.0.1:8080/test/video",
1063             "test/video",
1064         ),
1065         (
1066             "http://127.0.0.1:8080/test/",
1067             "http://127.0.0.1:8080/test/video",
1068             "video",
1069         ),
1070         (
1071             "http://127.0.0.1:8080/test",
1072             "http://127.0.0.1:8080/test2/video",
1073             "test2/video",
1074         ),
1075         (
1076             "http://127.0.0.1:8080/test/",
1077             "http://127.0.0.1:8080/test2/video",
1078             "../test2/video",
1079         ),
1080         (
1081             "http://127.0.0.1:8080/test/bla",
1082             "http://127.0.0.1:8080/test2/video",
1083             "../test2/video",
1084         ),
1085         (
1086             "http://127.0.0.1:8080/test/bla/",
1087             "http://127.0.0.1:8080/test2/video",
1088             "../../test2/video",
1089         ),
1090         (
1091             "http://127.0.0.1:8080/test/?foo=bar#123",
1092             "http://127.0.0.1:8080/test/video",
1093             "video",
1094         ),
1095         (
1096             "http://127.0.0.1:8080/test/",
1097             "http://127.0.0.1:8080/test/video?baz=meh#456",
1098             "video?baz=meh#456",
1099         ),
1100         (
1101             "http://127.0.0.1:8080/test",
1102             "http://127.0.0.1:8080/test?baz=meh#456",
1103             "?baz=meh#456",
1104         ),
1105         (
1106             "http://127.0.0.1:8080/test/",
1107             "http://127.0.0.1:8080/test?baz=meh#456",
1108             "../test?baz=meh#456",
1109         ),
1110         (
1111             "http://127.0.0.1:8080/test/",
1112             "http://127.0.0.1:8080/test/?baz=meh#456",
1113             "?baz=meh#456",
1114         ),
1115         (
1116             "http://127.0.0.1:8080/test/?foo=bar#123",
1117             "http://127.0.0.1:8080/test/video?baz=meh#456",
1118             "video?baz=meh#456",
1119         ),
1120         (
1121             "http://127.0.0.1:8080/file.txt",
1122             "http://127.0.0.1:8080/test/file.txt",
1123             "test/file.txt",
1124         ),
1125         (
1126             "http://127.0.0.1:8080/not_equal.txt",
1127             "http://127.0.0.1:8080/test/file.txt",
1128             "test/file.txt",
1129         ),
1130     ];
1131 
1132     for (base, uri, relative) in &tests {
1133         let base_uri = url::Url::parse(base).unwrap();
1134         let relative_uri = url::Url::parse(uri).unwrap();
1135         let make_relative = base_uri.make_relative(&relative_uri).unwrap();
1136         assert_eq!(
1137             make_relative, *relative,
1138             "base: {}, uri: {}, relative: {}",
1139             base, uri, relative
1140         );
1141         assert_eq!(
1142             base_uri.join(relative).unwrap().as_str(),
1143             *uri,
1144             "base: {}, uri: {}, relative: {}",
1145             base,
1146             uri,
1147             relative
1148         );
1149     }
1150 
1151     let error_tests = [
1152         ("http://127.0.0.1:8080/", "https://127.0.0.1:8080/test/"),
1153         ("http://127.0.0.1:8080/", "http://127.0.0.1:8081/test/"),
1154         ("http://127.0.0.1:8080/", "http://127.0.0.2:8080/test/"),
1155         ("mailto:a@example.com", "mailto:b@example.com"),
1156     ];
1157 
1158     for (base, uri) in &error_tests {
1159         let base_uri = url::Url::parse(base).unwrap();
1160         let relative_uri = url::Url::parse(uri).unwrap();
1161         let make_relative = base_uri.make_relative(&relative_uri);
1162         assert_eq!(make_relative, None, "base: {}, uri: {}", base, uri);
1163     }
1164 }
1165