uts46.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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 std::char;
  9. use test::TestFn;
  10. pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
  11. // http://www.unicode.org/Public/idna/latest/IdnaTest.txt
  12. for (i, line) in include_str!("IdnaTest.txt").lines().enumerate() {
  13. if line == "" || line.starts_with("#") {
  14. continue;
  15. }
  16. // Remove comments
  17. let mut line = match line.find("#") {
  18. Some(index) => &line[0..index],
  19. None => line,
  20. };
  21. let mut expected_failure = false;
  22. if line.starts_with("XFAIL") {
  23. expected_failure = true;
  24. line = &line[5..line.len()];
  25. };
  26. let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();
  27. let test_type = pieces.remove(0);
  28. let original = pieces.remove(0);
  29. let source = unescape(original);
  30. let to_unicode = pieces.remove(0);
  31. let to_ascii = pieces.remove(0);
  32. let nv8 = if pieces.len() > 0 {
  33. pieces.remove(0)
  34. } else {
  35. ""
  36. };
  37. if expected_failure {
  38. continue;
  39. }
  40. let test_name = format!("UTS #46 line {}", i + 1);
  41. add_test(
  42. test_name,
  43. TestFn::dyn_test_fn(move || {
  44. let result = idna::Config::default()
  45. .use_std3_ascii_rules(true)
  46. .verify_dns_length(true)
  47. .check_hyphens(true)
  48. .transitional_processing(test_type == "T")
  49. .to_ascii(&source);
  50. if to_ascii.starts_with("[") {
  51. if to_ascii.starts_with("[C") {
  52. // http://unicode.org/reports/tr46/#Deviations
  53. // applications that perform IDNA2008 lookup are not required to check
  54. // for these contexts
  55. return;
  56. }
  57. if to_ascii == "[V2]" {
  58. // Everybody ignores V2
  59. // https://github.com/servo/rust-url/pull/240
  60. // https://github.com/whatwg/url/issues/53#issuecomment-181528158
  61. // http://www.unicode.org/review/pri317/
  62. return;
  63. }
  64. let res = result.ok();
  65. assert!(
  66. res == None,
  67. "Expected error. result: {} | original: {} | source: {}",
  68. res.unwrap(),
  69. original,
  70. source
  71. );
  72. return;
  73. }
  74. let to_ascii = if to_ascii.len() > 0 {
  75. to_ascii.to_string()
  76. } else {
  77. if to_unicode.len() > 0 {
  78. to_unicode.to_string()
  79. } else {
  80. source.clone()
  81. }
  82. };
  83. if nv8 == "NV8" {
  84. // This result isn't valid under IDNA2008. Skip it
  85. return;
  86. }
  87. assert!(
  88. result.is_ok(),
  89. "Couldn't parse {} | original: {} | error: {:?}",
  90. source,
  91. original,
  92. result.err()
  93. );
  94. let output = result.ok().unwrap();
  95. assert!(
  96. output == to_ascii,
  97. "result: {} | expected: {} | original: {} | source: {}",
  98. output,
  99. to_ascii,
  100. original,
  101. source
  102. );
  103. }),
  104. )
  105. }
  106. }
  107. fn unescape(input: &str) -> String {
  108. let mut output = String::new();
  109. let mut chars = input.chars();
  110. loop {
  111. match chars.next() {
  112. None => return output,
  113. Some(c) => {
  114. if c == '\\' {
  115. match chars.next().unwrap() {
  116. '\\' => output.push('\\'),
  117. 'u' => {
  118. let c1 = chars.next().unwrap().to_digit(16).unwrap();
  119. let c2 = chars.next().unwrap().to_digit(16).unwrap();
  120. let c3 = chars.next().unwrap().to_digit(16).unwrap();
  121. let c4 = chars.next().unwrap().to_digit(16).unwrap();
  122. match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
  123. Some(c) => output.push(c),
  124. None => {
  125. output
  126. .push_str(&format!("\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4));
  127. }
  128. };
  129. }
  130. _ => panic!("Invalid test data input"),
  131. }
  132. } else {
  133. output.push(c);
  134. }
  135. }
  136. }
  137. }
  138. }