]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
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 | //! Data-driven tests | |
10 | ||
11 | use std::ops::Deref; | |
12 | use std::str::FromStr; | |
13 | ||
14 | use serde_json::Value; | |
15 | use url::{quirks, Url}; | |
16 | ||
17 | #[test] | |
18 | fn urltestdata() { | |
19 | // Copied form https://github.com/w3c/web-platform-tests/blob/master/url/ | |
20 | let mut json = Value::from_str(include_str!("urltestdata.json")) | |
21 | .expect("JSON parse error in urltestdata.json"); | |
22 | ||
23 | let mut passed = true; | |
24 | for entry in json.as_array_mut().unwrap() { | |
25 | if entry.is_string() { | |
26 | continue; // ignore comments | |
27 | } | |
28 | ||
29 | let base = entry.take_string("base"); | |
30 | let input = entry.take_string("input"); | |
31 | let failure = entry.take_key("failure").is_some(); | |
32 | ||
33 | let base = match Url::parse(&base) { | |
34 | Ok(base) => base, | |
35 | Err(_) if failure => continue, | |
36 | Err(message) => { | |
37 | eprint_failure( | |
38 | format!(" failed: error parsing base {:?}: {}", base, message), | |
39 | &format!("parse base for {:?}", input), | |
40 | None, | |
41 | ); | |
42 | passed = false; | |
43 | continue; | |
44 | } | |
45 | }; | |
46 | ||
47 | let url = match (base.join(&input), failure) { | |
48 | (Ok(url), false) => url, | |
49 | (Err(_), true) => continue, | |
50 | (Err(message), false) => { | |
51 | eprint_failure( | |
52 | format!(" failed: {}", message), | |
53 | &format!("parse URL for {:?}", input), | |
54 | None, | |
55 | ); | |
56 | passed = false; | |
57 | continue; | |
58 | } | |
59 | (Ok(_), true) => { | |
60 | eprint_failure( | |
61 | format!(" failed: expected parse error for URL {:?}", input), | |
62 | &format!("parse URL for {:?}", input), | |
63 | None, | |
64 | ); | |
65 | passed = false; | |
66 | continue; | |
67 | } | |
68 | }; | |
69 | ||
70 | passed &= check_invariants(&url, &format!("invariants for {:?}", input), None); | |
71 | ||
72 | for &attr in ATTRIBS { | |
73 | passed &= test_eq_eprint( | |
74 | entry.take_string(attr), | |
75 | get(&url, attr), | |
76 | &format!("{:?} - {}", input, attr), | |
77 | None, | |
78 | ); | |
79 | } | |
80 | ||
81 | if let Some(expected_origin) = entry.take_key("origin").map(|s| s.string()) { | |
82 | passed &= test_eq_eprint( | |
83 | expected_origin, | |
84 | &quirks::origin(&url), | |
85 | &format!("origin for {:?}", input), | |
86 | None, | |
87 | ); | |
88 | } | |
89 | } | |
90 | ||
91 | assert!(passed) | |
92 | } | |
93 | ||
94 | #[allow(clippy::option_as_ref_deref)] // introduced in 1.40, MSRV is 1.36 | |
95 | #[test] | |
96 | fn setters_tests() { | |
97 | let mut json = Value::from_str(include_str!("setters_tests.json")) | |
98 | .expect("JSON parse error in setters_tests.json"); | |
99 | ||
100 | let mut passed = true; | |
101 | for &attr in ATTRIBS { | |
102 | if attr == "href" { | |
103 | continue; | |
104 | } | |
105 | ||
106 | let mut tests = json.take_key(attr).unwrap(); | |
107 | for mut test in tests.as_array_mut().unwrap().drain(..) { | |
108 | let comment = test.take_key("comment").map(|s| s.string()); | |
109 | let href = test.take_string("href"); | |
110 | let new_value = test.take_string("new_value"); | |
111 | let name = format!("{:?}.{} = {:?}", href, attr, new_value); | |
112 | let mut expected = test.take_key("expected").unwrap(); | |
113 | ||
114 | let mut url = Url::parse(&href).unwrap(); | |
115 | let comment_ref = comment.as_ref().map(|s| s.deref()); | |
116 | passed &= check_invariants(&url, &name, comment_ref); | |
117 | let _ = set(&mut url, attr, &new_value); | |
118 | ||
119 | for attr in ATTRIBS { | |
120 | if let Some(value) = expected.take_key(attr) { | |
121 | passed &= test_eq_eprint(value.string(), get(&url, attr), &name, comment_ref); | |
122 | }; | |
123 | } | |
124 | ||
125 | passed &= check_invariants(&url, &name, comment_ref); | |
126 | } | |
127 | } | |
128 | ||
129 | assert!(passed); | |
130 | } | |
131 | ||
132 | fn check_invariants(url: &Url, name: &str, comment: Option<&str>) -> bool { | |
133 | let mut passed = true; | |
134 | if let Err(e) = url.check_invariants() { | |
135 | passed = false; | |
136 | eprint_failure( | |
137 | format!(" failed: invariants checked -> {:?}", e), | |
138 | name, | |
139 | comment, | |
140 | ); | |
141 | } | |
142 | ||
143 | #[cfg(feature = "serde")] | |
144 | { | |
145 | let bytes = serde_json::to_vec(url).unwrap(); | |
146 | let new_url: Url = serde_json::from_slice(&bytes).unwrap(); | |
147 | passed &= test_eq_eprint(url.to_string(), &new_url.to_string(), name, comment); | |
148 | } | |
149 | ||
150 | passed | |
151 | } | |
152 | ||
153 | trait JsonExt { | |
154 | fn take_key(&mut self, key: &str) -> Option<Value>; | |
155 | fn string(self) -> String; | |
156 | fn take_string(&mut self, key: &str) -> String; | |
157 | } | |
158 | ||
159 | impl JsonExt for Value { | |
160 | fn take_key(&mut self, key: &str) -> Option<Value> { | |
161 | self.as_object_mut().unwrap().remove(key) | |
162 | } | |
163 | ||
164 | fn string(self) -> String { | |
165 | if let Value::String(s) = self { | |
166 | s | |
167 | } else { | |
168 | panic!("Not a Value::String") | |
169 | } | |
170 | } | |
171 | ||
172 | fn take_string(&mut self, key: &str) -> String { | |
173 | self.take_key(key).unwrap().string() | |
174 | } | |
175 | } | |
176 | ||
177 | fn get<'a>(url: &'a Url, attr: &str) -> &'a str { | |
178 | match attr { | |
179 | "href" => quirks::href(url), | |
180 | "protocol" => quirks::protocol(url), | |
181 | "username" => quirks::username(url), | |
182 | "password" => quirks::password(url), | |
183 | "hostname" => quirks::hostname(url), | |
184 | "host" => quirks::host(url), | |
185 | "port" => quirks::port(url), | |
186 | "pathname" => quirks::pathname(url), | |
187 | "search" => quirks::search(url), | |
188 | "hash" => quirks::hash(url), | |
189 | _ => unreachable!(), | |
190 | } | |
191 | } | |
192 | ||
193 | #[allow(clippy::unit_arg)] | |
194 | fn set<'a>(url: &'a mut Url, attr: &str, new: &str) { | |
195 | let _ = match attr { | |
196 | "protocol" => quirks::set_protocol(url, new), | |
197 | "username" => quirks::set_username(url, new), | |
198 | "password" => quirks::set_password(url, new), | |
199 | "hostname" => quirks::set_hostname(url, new), | |
200 | "host" => quirks::set_host(url, new), | |
201 | "port" => quirks::set_port(url, new), | |
202 | "pathname" => Ok(quirks::set_pathname(url, new)), | |
203 | "search" => Ok(quirks::set_search(url, new)), | |
204 | "hash" => Ok(quirks::set_hash(url, new)), | |
205 | _ => unreachable!(), | |
206 | }; | |
207 | } | |
208 | ||
209 | fn test_eq_eprint(expected: String, actual: &str, name: &str, comment: Option<&str>) -> bool { | |
210 | if expected == actual { | |
211 | return true; | |
212 | } | |
213 | eprint_failure( | |
214 | format!("expected: {}\n actual: {}", expected, actual), | |
215 | name, | |
216 | comment, | |
217 | ); | |
218 | false | |
219 | } | |
220 | ||
221 | fn eprint_failure(err: String, name: &str, comment: Option<&str>) { | |
222 | eprintln!(" test: {}\n{}", name, err); | |
223 | if let Some(comment) = comment { | |
224 | eprintln!("{}\n", comment); | |
225 | } else { | |
226 | eprintln!(); | |
227 | } | |
228 | } | |
229 | ||
230 | const ATTRIBS: &[&str] = &[ | |
231 | "href", "protocol", "username", "password", "host", "hostname", "port", "pathname", "search", | |
232 | "hash", | |
233 | ]; |