tests.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Copyright 2013-2014 Simon Sapin.
  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. extern crate url;
  9. use std::char;
  10. use std::net::{Ipv4Addr, Ipv6Addr};
  11. use url::{UrlParser, Url, SchemeData, RelativeSchemeData, Host};
  12. #[test]
  13. fn url_parsing() {
  14. for test in parse_test_data(include_str!("urltestdata.txt")) {
  15. let Test {
  16. input,
  17. base,
  18. scheme: expected_scheme,
  19. username: expected_username,
  20. password: expected_password,
  21. host: expected_host,
  22. port: expected_port,
  23. path: expected_path,
  24. query: expected_query,
  25. fragment: expected_fragment,
  26. expected_failure,
  27. } = test;
  28. let base = match Url::parse(&base) {
  29. Ok(base) => base,
  30. Err(message) => panic!("Error parsing base {}: {}", base, message)
  31. };
  32. let url = UrlParser::new().base_url(&base).parse(&input);
  33. if expected_scheme.is_none() {
  34. if url.is_ok() && !expected_failure {
  35. panic!("Expected a parse error for URL {}", input);
  36. }
  37. continue
  38. }
  39. let Url { scheme, scheme_data, query, fragment, .. } = match url {
  40. Ok(url) => url,
  41. Err(message) => {
  42. if expected_failure {
  43. continue
  44. } else {
  45. panic!("Error parsing URL {}: {}", input, message)
  46. }
  47. }
  48. };
  49. macro_rules! assert_eq {
  50. ($a: expr, $b: expr) => {
  51. {
  52. let a = $a;
  53. let b = $b;
  54. if a != b {
  55. if expected_failure {
  56. continue
  57. } else {
  58. panic!("{:?} != {:?}", a, b)
  59. }
  60. }
  61. }
  62. }
  63. }
  64. assert_eq!(Some(scheme), expected_scheme);
  65. match scheme_data {
  66. SchemeData::Relative(RelativeSchemeData {
  67. username, password, host, port, default_port: _, path,
  68. }) => {
  69. assert_eq!(username, expected_username);
  70. assert_eq!(password, expected_password);
  71. let host = host.serialize();
  72. assert_eq!(host, expected_host);
  73. assert_eq!(port, expected_port);
  74. assert_eq!(Some(format!("/{}", str_join(&path, "/"))), expected_path);
  75. },
  76. SchemeData::NonRelative(scheme_data) => {
  77. assert_eq!(Some(scheme_data), expected_path);
  78. assert_eq!(String::new(), expected_username);
  79. assert_eq!(None, expected_password);
  80. assert_eq!(String::new(), expected_host);
  81. assert_eq!(None, expected_port);
  82. },
  83. }
  84. fn opt_prepend(prefix: &str, opt_s: Option<String>) -> Option<String> {
  85. opt_s.map(|s| format!("{}{}", prefix, s))
  86. }
  87. assert_eq!(opt_prepend("?", query), expected_query);
  88. assert_eq!(opt_prepend("#", fragment), expected_fragment);
  89. assert!(!expected_failure, "Unexpected success for {}", input);
  90. }
  91. }
  92. // FIMXE: Remove this when &[&str]::join (the new name) lands in the stable channel.
  93. #[allow(deprecated)]
  94. fn str_join<T: ::std::borrow::Borrow<str>>(pieces: &[T], separator: &str) -> String {
  95. pieces.connect(separator)
  96. }
  97. struct Test {
  98. input: String,
  99. base: String,
  100. scheme: Option<String>,
  101. username: String,
  102. password: Option<String>,
  103. host: String,
  104. port: Option<u16>,
  105. path: Option<String>,
  106. query: Option<String>,
  107. fragment: Option<String>,
  108. expected_failure: bool,
  109. }
  110. fn parse_test_data(input: &str) -> Vec<Test> {
  111. let mut tests: Vec<Test> = Vec::new();
  112. for line in input.lines() {
  113. if line == "" || line.starts_with("#") {
  114. continue
  115. }
  116. let mut pieces = line.split(' ').collect::<Vec<&str>>();
  117. let expected_failure = pieces[0] == "XFAIL";
  118. if expected_failure {
  119. pieces.remove(0);
  120. }
  121. let input = unescape(pieces.remove(0));
  122. let mut test = Test {
  123. input: input,
  124. base: if pieces.is_empty() || pieces[0] == "" {
  125. tests.last().unwrap().base.clone()
  126. } else {
  127. unescape(pieces.remove(0))
  128. },
  129. scheme: None,
  130. username: String::new(),
  131. password: None,
  132. host: String::new(),
  133. port: None,
  134. path: None,
  135. query: None,
  136. fragment: None,
  137. expected_failure: expected_failure,
  138. };
  139. for piece in pieces {
  140. if piece == "" || piece.starts_with("#") {
  141. continue
  142. }
  143. let colon = piece.find(':').unwrap();
  144. let value = unescape(&piece[colon + 1..]);
  145. match &piece[..colon] {
  146. "s" => test.scheme = Some(value),
  147. "u" => test.username = value,
  148. "pass" => test.password = Some(value),
  149. "h" => test.host = value,
  150. "port" => test.port = Some(value.parse().unwrap()),
  151. "p" => test.path = Some(value),
  152. "q" => test.query = Some(value),
  153. "f" => test.fragment = Some(value),
  154. _ => panic!("Invalid token")
  155. }
  156. }
  157. tests.push(test)
  158. }
  159. tests
  160. }
  161. fn unescape(input: &str) -> String {
  162. let mut output = String::new();
  163. let mut chars = input.chars();
  164. loop {
  165. match chars.next() {
  166. None => return output,
  167. Some(c) => output.push(
  168. if c == '\\' {
  169. match chars.next().unwrap() {
  170. '\\' => '\\',
  171. 'n' => '\n',
  172. 'r' => '\r',
  173. 's' => ' ',
  174. 't' => '\t',
  175. 'f' => '\x0C',
  176. 'u' => {
  177. char::from_u32((((
  178. chars.next().unwrap().to_digit(16).unwrap()) * 16 +
  179. chars.next().unwrap().to_digit(16).unwrap()) * 16 +
  180. chars.next().unwrap().to_digit(16).unwrap()) * 16 +
  181. chars.next().unwrap().to_digit(16).unwrap()).unwrap()
  182. }
  183. _ => panic!("Invalid test data input"),
  184. }
  185. } else {
  186. c
  187. }
  188. )
  189. }
  190. }
  191. }
  192. #[test]
  193. fn new_file_paths() {
  194. use std::path::{Path, PathBuf};
  195. if cfg!(unix) {
  196. assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
  197. assert_eq!(Url::from_file_path(Path::new("../relative")), Err(()));
  198. } else {
  199. assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
  200. assert_eq!(Url::from_file_path(Path::new(r"..\relative")), Err(()));
  201. assert_eq!(Url::from_file_path(Path::new(r"\drive-relative")), Err(()));
  202. assert_eq!(Url::from_file_path(Path::new(r"\\ucn\")), Err(()));
  203. }
  204. if cfg!(unix) {
  205. let mut url = Url::from_file_path(Path::new("/foo/bar")).unwrap();
  206. assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
  207. assert_eq!(url.path(), Some(&["foo".to_string(), "bar".to_string()][..]));
  208. assert!(url.to_file_path() == Ok(PathBuf::from("/foo/bar")));
  209. url.path_mut().unwrap()[1] = "ba\0r".to_string();
  210. url.to_file_path().is_ok();
  211. url.path_mut().unwrap()[1] = "ba%00r".to_string();
  212. url.to_file_path().is_ok();
  213. }
  214. }
  215. #[test]
  216. #[cfg(unix)]
  217. fn new_path_bad_utf8() {
  218. use std::ffi::OsStr;
  219. use std::os::unix::prelude::*;
  220. use std::path::{Path, PathBuf};
  221. let url = Url::from_file_path(Path::new("/foo/ba%80r")).unwrap();
  222. let os_str = OsStr::from_bytes(b"/foo/ba\x80r");
  223. assert_eq!(url.to_file_path(), Ok(PathBuf::from(os_str)));
  224. }
  225. #[test]
  226. fn new_path_windows_fun() {
  227. if cfg!(windows) {
  228. use std::path::{Path, PathBuf};
  229. let mut url = Url::from_file_path(Path::new(r"C:\foo\bar")).unwrap();
  230. assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
  231. assert_eq!(url.path(), Some(&["C:".to_string(), "foo".to_string(), "bar".to_string()][..]));
  232. assert_eq!(url.to_file_path(),
  233. Ok(PathBuf::from(r"C:\foo\bar")));
  234. url.path_mut().unwrap()[2] = "ba\0r".to_string();
  235. assert!(url.to_file_path().is_ok());
  236. url.path_mut().unwrap()[2] = "ba%00r".to_string();
  237. assert!(url.to_file_path().is_ok());
  238. // Invalid UTF-8
  239. url.path_mut().unwrap()[2] = "ba%80r".to_string();
  240. assert!(url.to_file_path().is_err());
  241. }
  242. }
  243. #[test]
  244. fn new_directory_paths() {
  245. use std::path::Path;
  246. if cfg!(unix) {
  247. assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
  248. assert_eq!(Url::from_directory_path(Path::new("../relative")), Err(()));
  249. let url = Url::from_directory_path(Path::new("/foo/bar")).unwrap();
  250. assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
  251. assert_eq!(url.path(), Some(&["foo".to_string(), "bar".to_string(),
  252. "".to_string()][..]));
  253. } else {
  254. assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
  255. assert_eq!(Url::from_directory_path(Path::new(r"..\relative")), Err(()));
  256. assert_eq!(Url::from_directory_path(Path::new(r"\drive-relative")), Err(()));
  257. assert_eq!(Url::from_directory_path(Path::new(r"\\ucn\")), Err(()));
  258. let url = Url::from_directory_path(Path::new(r"C:\foo\bar")).unwrap();
  259. assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
  260. assert_eq!(url.path(), Some(&["C:".to_string(), "foo".to_string(),
  261. "bar".to_string(), "".to_string()][..]));
  262. }
  263. }
  264. #[test]
  265. fn from_str() {
  266. assert!("http://testing.com/this".parse::<Url>().is_ok());
  267. }
  268. #[test]
  269. fn issue_124() {
  270. let url: Url = "file:a".parse().unwrap();
  271. assert_eq!(url.path().unwrap(), ["a"]);
  272. let url: Url = "file:...".parse().unwrap();
  273. assert_eq!(url.path().unwrap(), ["..."]);
  274. let url: Url = "file:..".parse().unwrap();
  275. assert_eq!(url.path().unwrap(), [""]);
  276. }
  277. #[test]
  278. fn relative_scheme_data_equality() {
  279. use std::hash::{Hash, Hasher, SipHasher};
  280. fn check_eq(a: &Url, b: &Url) {
  281. assert_eq!(a, b);
  282. let mut h1 = SipHasher::new();
  283. a.hash(&mut h1);
  284. let mut h2 = SipHasher::new();
  285. b.hash(&mut h2);
  286. assert_eq!(h1.finish(), h2.finish());
  287. }
  288. fn url(s: &str) -> Url {
  289. let rv = s.parse().unwrap();
  290. check_eq(&rv, &rv);
  291. rv
  292. }
  293. // Doesn't care if default port is given.
  294. let a: Url = url("https://example.com/");
  295. let b: Url = url("https://example.com:443/");
  296. check_eq(&a, &b);
  297. // Different ports
  298. let a: Url = url("http://example.com/");
  299. let b: Url = url("http://example.com:8080/");
  300. assert!(a != b);
  301. // Different scheme
  302. let a: Url = url("http://example.com/");
  303. let b: Url = url("https://example.com/");
  304. assert!(a != b);
  305. // Different host
  306. let a: Url = url("http://foo.com/");
  307. let b: Url = url("http://bar.com/");
  308. assert!(a != b);
  309. // Missing path, automatically substituted. Semantically the same.
  310. let a: Url = url("http://foo.com");
  311. let b: Url = url("http://foo.com/");
  312. check_eq(&a, &b);
  313. }
  314. #[test]
  315. fn host() {
  316. let a = Host::parse("www.mozilla.org").unwrap();
  317. let b = Host::parse("1.35.33.49").unwrap();
  318. let c = Host::parse("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]").unwrap();
  319. assert_eq!(a, Host::Domain("www.mozilla.org".to_owned()));
  320. assert_eq!(b, Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
  321. assert_eq!(c, Host::Ipv6(Ipv6Addr::new(0x2001, 0x0db8, 0x85a3, 0x08d3,
  322. 0x1319, 0x8a2e, 0x0370, 0x7344)));
  323. assert_eq!(Host::parse("[::]").unwrap(), Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
  324. assert_eq!(Host::parse("[::1]").unwrap(), Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
  325. assert_eq!(Host::parse("0x1.0X23.0x21.061").unwrap(), Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
  326. assert_eq!(Host::parse("0x1232131").unwrap(), Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
  327. assert!(Host::parse("42.0x1232131").is_err());
  328. assert_eq!(Host::parse("111").unwrap(), Host::Ipv4(Ipv4Addr::new(0, 0, 0, 111)));
  329. assert_eq!(Host::parse("2..2.3").unwrap(), Host::Domain("2..2.3".to_owned()));
  330. assert!(Host::parse("192.168.0.257").is_err());
  331. }