uts46.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. use crate::test::TestFn;
  9. use std::char;
  10. use idna::Errors;
  11. pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
  12. // https://www.unicode.org/Public/idna/13.0.0/IdnaTestV2.txt
  13. for (i, line) in include_str!("IdnaTestV2.txt").lines().enumerate() {
  14. if line.is_empty() || line.starts_with('#') {
  15. continue;
  16. }
  17. // Remove comments
  18. let line = match line.find('#') {
  19. Some(index) => &line[0..index],
  20. None => line,
  21. };
  22. let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();
  23. let source = unescape(pieces.remove(0));
  24. // ToUnicode
  25. let mut to_unicode = unescape(pieces.remove(0));
  26. if to_unicode.is_empty() {
  27. to_unicode = source.clone();
  28. }
  29. let to_unicode_status = status(pieces.remove(0));
  30. // ToAsciiN
  31. let to_ascii_n = pieces.remove(0);
  32. let to_ascii_n = if to_ascii_n.is_empty() {
  33. to_unicode.clone()
  34. } else {
  35. to_ascii_n.to_owned()
  36. };
  37. let to_ascii_n_status = pieces.remove(0);
  38. let to_ascii_n_status = if to_ascii_n_status.is_empty() {
  39. to_unicode_status.clone()
  40. } else {
  41. status(to_ascii_n_status)
  42. };
  43. // ToAsciiT
  44. let to_ascii_t = pieces.remove(0);
  45. let to_ascii_t = if to_ascii_t.is_empty() {
  46. to_ascii_n.clone()
  47. } else {
  48. to_ascii_t.to_owned()
  49. };
  50. let to_ascii_t_status = pieces.remove(0);
  51. let to_ascii_t_status = if to_ascii_t_status.is_empty() {
  52. to_ascii_n_status.clone()
  53. } else {
  54. status(to_ascii_t_status)
  55. };
  56. let test_name = format!("UTS #46 line {}", i + 1);
  57. add_test(
  58. test_name,
  59. TestFn::dyn_test_fn(move || {
  60. let config = idna::Config::default()
  61. .use_std3_ascii_rules(true)
  62. .verify_dns_length(true)
  63. .check_hyphens(true);
  64. // http://unicode.org/reports/tr46/#Deviations
  65. // applications that perform IDNA2008 lookup are not required to check
  66. // for these contexts, so we skip all tests annotated with C*
  67. // Everybody ignores V2
  68. // https://github.com/servo/rust-url/pull/240
  69. // https://github.com/whatwg/url/issues/53#issuecomment-181528158
  70. // http://www.unicode.org/review/pri317/
  71. // "The special error codes X3 and X4_2 are now returned where a toASCII error code
  72. // was formerly being generated in toUnicode due to an empty label."
  73. // This is not implemented yet, so we skip toUnicode X4_2 tests for now, too.
  74. let (to_unicode_value, to_unicode_result) =
  75. config.transitional_processing(false).to_unicode(&source);
  76. let to_unicode_result = to_unicode_result.map(|()| to_unicode_value);
  77. check(
  78. &source,
  79. (&to_unicode, &to_unicode_status),
  80. to_unicode_result,
  81. |e| e.starts_with('C') || e == "V2" || e == "X4_2",
  82. );
  83. let to_ascii_n_result = config.transitional_processing(false).to_ascii(&source);
  84. check(
  85. &source,
  86. (&to_ascii_n, &to_ascii_n_status),
  87. to_ascii_n_result,
  88. |e| e.starts_with('C') || e == "V2",
  89. );
  90. let to_ascii_t_result = config.transitional_processing(true).to_ascii(&source);
  91. check(
  92. &source,
  93. (&to_ascii_t, &to_ascii_t_status),
  94. to_ascii_t_result,
  95. |e| e.starts_with('C') || e == "V2",
  96. );
  97. }),
  98. )
  99. }
  100. }
  101. #[allow(clippy::redundant_clone)]
  102. fn check<F>(source: &str, expected: (&str, &[&str]), actual: Result<String, Errors>, ignore: F)
  103. where
  104. F: Fn(&str) -> bool,
  105. {
  106. if !expected.1.is_empty() {
  107. if !expected.1.iter().copied().any(ignore) {
  108. let res = actual.ok();
  109. assert_eq!(
  110. res.clone(),
  111. None,
  112. "Expected error {:?}. result: {} | source: {}",
  113. expected.1,
  114. res.unwrap(),
  115. source,
  116. );
  117. }
  118. } else {
  119. assert!(
  120. actual.is_ok(),
  121. "Couldn't parse {} | error: {:?}",
  122. source,
  123. actual.err().unwrap(),
  124. );
  125. assert_eq!(actual.unwrap(), expected.0, "source: {}", source);
  126. }
  127. }
  128. fn unescape(input: &str) -> String {
  129. let mut output = String::new();
  130. let mut chars = input.chars();
  131. loop {
  132. match chars.next() {
  133. None => return output,
  134. Some(c) => {
  135. if c == '\\' {
  136. match chars.next().unwrap() {
  137. '\\' => output.push('\\'),
  138. 'u' => {
  139. let c1 = chars.next().unwrap().to_digit(16).unwrap();
  140. let c2 = chars.next().unwrap().to_digit(16).unwrap();
  141. let c3 = chars.next().unwrap().to_digit(16).unwrap();
  142. let c4 = chars.next().unwrap().to_digit(16).unwrap();
  143. match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
  144. Some(c) => output.push(c),
  145. None => {
  146. output
  147. .push_str(&format!("\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4));
  148. }
  149. };
  150. }
  151. _ => panic!("Invalid test data input"),
  152. }
  153. } else {
  154. output.push(c);
  155. }
  156. }
  157. }
  158. }
  159. }
  160. fn status(status: &str) -> Vec<&str> {
  161. if status.is_empty() || status == "[]" {
  162. return Vec::new();
  163. }
  164. let mut result = status.split(", ").collect::<Vec<_>>();
  165. assert!(result[0].starts_with('['));
  166. result[0] = &result[0][1..];
  167. let idx = result.len() - 1;
  168. let last = &mut result[idx];
  169. assert!(last.ends_with(']'));
  170. *last = &last[..last.len() - 1];
  171. result
  172. }