wpt.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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. //! Data-driven tests imported from web-platform-tests
  9. use serde_json::Value;
  10. use std::collections::HashMap;
  11. use std::fmt::Write;
  12. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  13. use std::sync::Mutex;
  14. use url::Url;
  15. // https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/usage.html
  16. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  17. use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
  18. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  19. wasm_bindgen_test_configure!(run_in_browser);
  20. // wpt has its own test driver, but we shoe-horn this into wasm_bindgen_test
  21. // which will discard stdout and stderr. So, we make println! go to
  22. // console.log(), so we see failures that do not result in panics.
  23. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  24. static PRINT_BUF: Mutex<Option<String>> = Mutex::new(None);
  25. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  26. macro_rules! print {
  27. ($($arg:tt)*) => {
  28. let v = format!($($arg)*);
  29. {
  30. let mut buf = PRINT_BUF.lock().unwrap();
  31. if let Some(buf) = buf.as_mut() {
  32. buf.push_str(&v);
  33. } else {
  34. *buf = Some(v);
  35. }
  36. }
  37. };
  38. }
  39. #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
  40. macro_rules! println {
  41. () => {
  42. let buf = PRINT_BUF.lock().unwrap().take();
  43. match buf {
  44. Some(buf) => console_log!("{buf}"),
  45. None => console_log!(""),
  46. }
  47. };
  48. ($($arg:tt)*) => {
  49. let buf = PRINT_BUF.lock().unwrap().take();
  50. match buf {
  51. Some(buf) => {
  52. let v = format!($($arg)*);
  53. console_log!("{buf}{v}");
  54. },
  55. None => console_log!($($arg)*),
  56. }
  57. }
  58. }
  59. #[derive(Debug, serde_derive::Deserialize)]
  60. struct UrlTest {
  61. input: String,
  62. base: Option<String>,
  63. #[serde(flatten)]
  64. result: UrlTestResult,
  65. }
  66. #[derive(Debug, serde_derive::Deserialize)]
  67. #[serde(untagged)]
  68. #[allow(clippy::large_enum_variant)]
  69. enum UrlTestResult {
  70. Ok(UrlTestOk),
  71. Fail(UrlTestFail),
  72. }
  73. #[derive(Debug, serde_derive::Deserialize)]
  74. struct UrlTestOk {
  75. href: String,
  76. protocol: String,
  77. username: String,
  78. password: String,
  79. host: String,
  80. hostname: String,
  81. port: String,
  82. pathname: String,
  83. search: String,
  84. hash: String,
  85. }
  86. #[derive(Debug, serde_derive::Deserialize)]
  87. struct UrlTestFail {
  88. failure: bool,
  89. }
  90. #[derive(Debug, serde_derive::Deserialize)]
  91. struct SetterTest {
  92. href: String,
  93. new_value: String,
  94. expected: SetterTestExpected,
  95. }
  96. #[derive(Debug, serde_derive::Deserialize)]
  97. struct SetterTestExpected {
  98. href: Option<String>,
  99. protocol: Option<String>,
  100. username: Option<String>,
  101. password: Option<String>,
  102. host: Option<String>,
  103. hostname: Option<String>,
  104. port: Option<String>,
  105. pathname: Option<String>,
  106. search: Option<String>,
  107. hash: Option<String>,
  108. }
  109. #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
  110. fn main() {
  111. let mut filter = None;
  112. let mut args = std::env::args().skip(1);
  113. while filter.is_none() {
  114. if let Some(arg) = args.next() {
  115. if arg == "--test-threads" {
  116. args.next();
  117. continue;
  118. }
  119. filter = Some(arg);
  120. } else {
  121. break;
  122. }
  123. }
  124. let mut expected_failures = include_str!("expected_failures.txt")
  125. .lines()
  126. .collect::<Vec<_>>();
  127. let mut errors = vec![];
  128. // Copied from https://github.com/web-platform-tests/wpt/blob/master/url/
  129. let url_json: Vec<Value> = serde_json::from_str(include_str!("urltestdata.json"))
  130. .expect("JSON parse error in urltestdata.json");
  131. let url_tests = url_json
  132. .into_iter()
  133. .filter(|val| val.is_object())
  134. .map(|val| serde_json::from_value::<UrlTest>(val).expect("parsing failed"))
  135. .collect::<Vec<_>>();
  136. let setter_json: HashMap<String, Value> =
  137. serde_json::from_str(include_str!("setters_tests.json"))
  138. .expect("JSON parse error in setters_tests.json");
  139. let setter_tests = setter_json
  140. .into_iter()
  141. .filter(|(k, _)| k != "comment")
  142. .map(|(k, v)| {
  143. let test = serde_json::from_value::<Vec<SetterTest>>(v).expect("parsing failed");
  144. (k, test)
  145. })
  146. .collect::<HashMap<_, _>>();
  147. for url_test in url_tests {
  148. let mut name = format!("<{}>", url_test.input.escape_default());
  149. if let Some(base) = &url_test.base {
  150. write!(&mut name, " against <{}>", base.escape_default()).unwrap();
  151. }
  152. if should_skip(&name, filter.as_deref()) {
  153. continue;
  154. }
  155. print!("{name} ... ");
  156. let res = run_url_test(url_test);
  157. report(name, res, &mut errors, &mut expected_failures);
  158. }
  159. for (kind, tests) in setter_tests {
  160. for test in tests {
  161. let name = format!(
  162. "<{}> set {} to <{}>",
  163. test.href.escape_default(),
  164. kind,
  165. test.new_value.escape_default()
  166. );
  167. if should_skip(&name, filter.as_deref()) {
  168. continue;
  169. }
  170. print!("{name} ... ");
  171. let res = run_setter_test(&kind, test);
  172. report(name, res, &mut errors, &mut expected_failures);
  173. }
  174. }
  175. println!();
  176. println!("====================");
  177. println!();
  178. if !errors.is_empty() {
  179. println!("errors:");
  180. println!();
  181. for (name, err) in errors {
  182. println!(" name: {name}");
  183. println!(" err: {err}");
  184. println!();
  185. }
  186. std::process::exit(1);
  187. } else {
  188. println!("all tests passed");
  189. }
  190. if !expected_failures.is_empty() && filter.is_none() {
  191. println!();
  192. println!("====================");
  193. println!();
  194. println!("tests were expected to fail but did not run:");
  195. println!();
  196. for name in expected_failures {
  197. println!(" {name}");
  198. }
  199. println!();
  200. println!("if these tests were removed, update expected_failures.txt");
  201. println!();
  202. std::process::exit(1);
  203. }
  204. }
  205. fn should_skip(name: &str, filter: Option<&str>) -> bool {
  206. match filter {
  207. Some(filter) => !name.contains(filter),
  208. None => false,
  209. }
  210. }
  211. fn report(
  212. name: String,
  213. res: Result<(), String>,
  214. errors: &mut Vec<(String, String)>,
  215. expected_failures: &mut Vec<&str>,
  216. ) {
  217. let expected_failure = expected_failures.contains(&&*name);
  218. expected_failures.retain(|&s| s != &*name);
  219. match res {
  220. Ok(()) => {
  221. if expected_failure {
  222. println!("🟠 (unexpected success)");
  223. errors.push((name, "unexpected success".to_string()));
  224. } else {
  225. println!("✅");
  226. }
  227. }
  228. Err(err) => {
  229. if expected_failure {
  230. println!("✅ (expected fail)");
  231. } else {
  232. println!("❌");
  233. errors.push((name, err));
  234. }
  235. }
  236. }
  237. }
  238. fn run_url_test(
  239. UrlTest {
  240. base,
  241. input,
  242. result,
  243. }: UrlTest,
  244. ) -> Result<(), String> {
  245. let base = match base {
  246. Some(base) => {
  247. let base = Url::parse(&base).map_err(|e| format!("errored while parsing base: {e}"))?;
  248. Some(base)
  249. }
  250. None => None,
  251. };
  252. let res = Url::options()
  253. .base_url(base.as_ref())
  254. .parse(&input)
  255. .map_err(|e| format!("errored while parsing input: {e}"));
  256. match result {
  257. UrlTestResult::Ok(ok) => check_url_ok(res, ok),
  258. UrlTestResult::Fail(fail) => {
  259. assert!(fail.failure);
  260. if res.is_ok() {
  261. return Err("expected failure, but parsed successfully".to_string());
  262. }
  263. Ok(())
  264. }
  265. }
  266. }
  267. fn check_url_ok(res: Result<Url, String>, ok: UrlTestOk) -> Result<(), String> {
  268. let url = match res {
  269. Ok(url) => url,
  270. Err(err) => {
  271. return Err(format!("expected success, but errored: {err:?}"));
  272. }
  273. };
  274. let href = url::quirks::href(&url);
  275. if href != ok.href {
  276. return Err(format!("expected href {:?}, but got {:?}", ok.href, href));
  277. }
  278. let protocol = url::quirks::protocol(&url);
  279. if protocol != ok.protocol {
  280. return Err(format!(
  281. "expected protocol {:?}, but got {:?}",
  282. ok.protocol, protocol
  283. ));
  284. }
  285. let username = url::quirks::username(&url);
  286. if username != ok.username {
  287. return Err(format!(
  288. "expected username {:?}, but got {:?}",
  289. ok.username, username
  290. ));
  291. }
  292. let password = url::quirks::password(&url);
  293. if password != ok.password {
  294. return Err(format!(
  295. "expected password {:?}, but got {:?}",
  296. ok.password, password
  297. ));
  298. }
  299. let host = url::quirks::host(&url);
  300. if host != ok.host {
  301. return Err(format!("expected host {:?}, but got {:?}", ok.host, host));
  302. }
  303. let hostname = url::quirks::hostname(&url);
  304. if hostname != ok.hostname {
  305. return Err(format!(
  306. "expected hostname {:?}, but got {:?}",
  307. ok.hostname, hostname
  308. ));
  309. }
  310. let port = url::quirks::port(&url);
  311. if port != ok.port {
  312. return Err(format!("expected port {:?}, but got {:?}", ok.port, port));
  313. }
  314. let pathname = url::quirks::pathname(&url);
  315. if pathname != ok.pathname {
  316. return Err(format!(
  317. "expected pathname {:?}, but got {:?}",
  318. ok.pathname, pathname
  319. ));
  320. }
  321. let search = url::quirks::search(&url);
  322. if search != ok.search {
  323. return Err(format!(
  324. "expected search {:?}, but got {:?}",
  325. ok.search, search
  326. ));
  327. }
  328. let hash = url::quirks::hash(&url);
  329. if hash != ok.hash {
  330. return Err(format!("expected hash {:?}, but got {:?}", ok.hash, hash));
  331. }
  332. Ok(())
  333. }
  334. fn run_setter_test(
  335. kind: &str,
  336. SetterTest {
  337. href,
  338. new_value,
  339. expected,
  340. }: SetterTest,
  341. ) -> Result<(), String> {
  342. let mut url = Url::parse(&href).map_err(|e| format!("errored while parsing href: {e}"))?;
  343. match kind {
  344. "protocol" => {
  345. url::quirks::set_protocol(&mut url, &new_value).ok();
  346. }
  347. "username" => {
  348. url::quirks::set_username(&mut url, &new_value).ok();
  349. }
  350. "password" => {
  351. url::quirks::set_password(&mut url, &new_value).ok();
  352. }
  353. "host" => {
  354. url::quirks::set_host(&mut url, &new_value).ok();
  355. }
  356. "hostname" => {
  357. url::quirks::set_hostname(&mut url, &new_value).ok();
  358. }
  359. "port" => {
  360. url::quirks::set_port(&mut url, &new_value).ok();
  361. }
  362. "pathname" => url::quirks::set_pathname(&mut url, &new_value),
  363. "search" => url::quirks::set_search(&mut url, &new_value),
  364. "hash" => url::quirks::set_hash(&mut url, &new_value),
  365. _ => {
  366. return Err(format!("unknown setter kind: {kind:?}"));
  367. }
  368. }
  369. if let Some(expected_href) = expected.href {
  370. let href = url::quirks::href(&url);
  371. if href != expected_href {
  372. return Err(format!("expected href {expected_href:?}, but got {href:?}"));
  373. }
  374. }
  375. if let Some(expected_protocol) = expected.protocol {
  376. let protocol = url::quirks::protocol(&url);
  377. if protocol != expected_protocol {
  378. return Err(format!(
  379. "expected protocol {expected_protocol:?}, but got {protocol:?}"
  380. ));
  381. }
  382. }
  383. if let Some(expected_username) = expected.username {
  384. let username = url::quirks::username(&url);
  385. if username != expected_username {
  386. return Err(format!(
  387. "expected username {expected_username:?}, but got {username:?}"
  388. ));
  389. }
  390. }
  391. if let Some(expected_password) = expected.password {
  392. let password = url::quirks::password(&url);
  393. if password != expected_password {
  394. return Err(format!(
  395. "expected password {expected_password:?}, but got {password:?}"
  396. ));
  397. }
  398. }
  399. if let Some(expected_host) = expected.host {
  400. let host = url::quirks::host(&url);
  401. if host != expected_host {
  402. return Err(format!("expected host {expected_host:?}, but got {host:?}"));
  403. }
  404. }
  405. if let Some(expected_hostname) = expected.hostname {
  406. let hostname = url::quirks::hostname(&url);
  407. if hostname != expected_hostname {
  408. return Err(format!(
  409. "expected hostname {expected_hostname:?}, but got {hostname:?}"
  410. ));
  411. }
  412. }
  413. if let Some(expected_port) = expected.port {
  414. let port = url::quirks::port(&url);
  415. if port != expected_port {
  416. return Err(format!("expected port {expected_port:?}, but got {port:?}"));
  417. }
  418. }
  419. if let Some(expected_pathname) = expected.pathname {
  420. let pathname = url::quirks::pathname(&url);
  421. if pathname != expected_pathname {
  422. return Err(format!(
  423. "expected pathname {expected_pathname:?}, but got {pathname:?}"
  424. ));
  425. }
  426. }
  427. if let Some(expected_search) = expected.search {
  428. let search = url::quirks::search(&url);
  429. if search != expected_search {
  430. return Err(format!(
  431. "expected search {expected_search:?}, but got {search:?}"
  432. ));
  433. }
  434. }
  435. if let Some(expected_hash) = expected.hash {
  436. let hash = url::quirks::hash(&url);
  437. if hash != expected_hash {
  438. return Err(format!("expected hash {expected_hash:?}, but got {hash:?}"));
  439. }
  440. }
  441. Ok(())
  442. }