]>
Commit | Line | Data |
---|---|---|
83c7162d XL |
1 | // Copyright 2014-2017 The html5ever Project Developers. See the |
2 | // COPYRIGHT file at the top-level directory of this distribution. | |
3 | // | |
4 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
5 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
6 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
7 | // option. This file may not be copied, modified, or distributed | |
8 | // except according to those terms. | |
9 | ||
3dfed10e XL |
10 | use markup5ever::{namespace_url, ns}; |
11 | use markup5ever_rcdom::*; | |
12 | use rustc_test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn}; | |
dc9dc135 | 13 | use std::collections::{HashMap, HashSet}; |
83c7162d | 14 | use std::ffi::OsStr; |
dc9dc135 | 15 | use std::io::BufRead; |
83c7162d XL |
16 | use std::iter::repeat; |
17 | use std::mem::replace; | |
83c7162d | 18 | use std::path::Path; |
dc9dc135 | 19 | use std::{env, fs, io}; |
3dfed10e XL |
20 | use util::find_tests::foreach_xml5lib_test; |
21 | use xml5ever::driver::parse_document; | |
22 | use xml5ever::tendril::TendrilSink; | |
83c7162d | 23 | |
3dfed10e XL |
24 | mod util { |
25 | pub mod find_tests; | |
26 | } | |
83c7162d | 27 | |
dc9dc135 XL |
28 | fn parse_tests<It: Iterator<Item = String>>(mut lines: It) -> Vec<HashMap<String, String>> { |
29 | let mut tests = vec![]; | |
83c7162d XL |
30 | let mut test = HashMap::new(); |
31 | let mut key: Option<String> = None; | |
32 | let mut val = String::new(); | |
33 | ||
34 | macro_rules! finish_val ( () => ( | |
35 | match key.take() { | |
36 | None => (), | |
37 | Some(key) => { | |
38 | assert!(test.insert(key, replace(&mut val, String::new())).is_none()); | |
39 | } | |
40 | } | |
41 | )); | |
42 | ||
43 | macro_rules! finish_test ( () => ( | |
44 | if !test.is_empty() { | |
45 | tests.push(replace(&mut test, HashMap::new())); | |
46 | } | |
47 | )); | |
48 | ||
49 | loop { | |
50 | match lines.next() { | |
51 | None => break, | |
52 | Some(line) => { | |
53 | if line.starts_with("#") { | |
54 | finish_val!(); | |
55 | if line == "#data" { | |
56 | finish_test!(); | |
57 | } | |
58 | key = Some(line[1..].to_string()); | |
59 | } else { | |
60 | val.push_str(&line); | |
61 | val.push('\n'); | |
62 | } | |
dc9dc135 | 63 | }, |
83c7162d XL |
64 | } |
65 | } | |
66 | ||
67 | finish_val!(); | |
68 | finish_test!(); | |
69 | tests | |
70 | } | |
71 | ||
72 | fn serialize(buf: &mut String, indent: usize, handle: Handle) { | |
73 | buf.push_str("|"); | |
74 | buf.push_str(&repeat(" ").take(indent).collect::<String>()); | |
75 | ||
76 | let node = handle; | |
77 | match node.data { | |
78 | NodeData::Document => panic!("should not reach Document"), | |
79 | ||
dc9dc135 XL |
80 | NodeData::Doctype { |
81 | ref name, | |
82 | ref public_id, | |
83 | ref system_id, | |
84 | } => { | |
83c7162d XL |
85 | buf.push_str("<!DOCTYPE "); |
86 | buf.push_str(&name); | |
87 | if !public_id.is_empty() || !system_id.is_empty() { | |
88 | buf.push_str(&format!(" \"{}\" \"{}\"", public_id, system_id)); | |
89 | } | |
90 | buf.push_str(">\n"); | |
dc9dc135 | 91 | }, |
83c7162d XL |
92 | |
93 | NodeData::Text { ref contents } => { | |
94 | buf.push_str("\""); | |
95 | buf.push_str(&contents.borrow()); | |
96 | buf.push_str("\"\n"); | |
dc9dc135 | 97 | }, |
83c7162d | 98 | |
3dfed10e XL |
99 | NodeData::ProcessingInstruction { |
100 | ref target, | |
101 | ref contents, | |
102 | } => { | |
103 | buf.push_str("<?"); | |
104 | buf.push_str(&target); | |
105 | buf.push_str(" "); | |
106 | buf.push_str(&contents); | |
107 | buf.push_str("?>\n"); | |
108 | }, | |
109 | ||
83c7162d XL |
110 | NodeData::Comment { ref contents } => { |
111 | buf.push_str("<!-- "); | |
112 | buf.push_str(&contents); | |
113 | buf.push_str(" -->\n"); | |
dc9dc135 | 114 | }, |
83c7162d | 115 | |
dc9dc135 XL |
116 | NodeData::Element { |
117 | ref name, | |
118 | ref attrs, | |
119 | .. | |
120 | } => { | |
83c7162d | 121 | buf.push_str("<"); |
3dfed10e XL |
122 | |
123 | if name.ns != ns!() { | |
124 | buf.push_str("{"); | |
125 | buf.push_str(&*name.ns); | |
126 | buf.push_str("}"); | |
127 | }; | |
128 | ||
129 | if let Some(ref prefix) = name.prefix { | |
130 | buf.push_str(&*prefix); | |
131 | buf.push_str(":"); | |
83c7162d | 132 | } |
3dfed10e | 133 | |
83c7162d XL |
134 | buf.push_str(&*name.local); |
135 | buf.push_str(">\n"); | |
136 | ||
137 | let mut attrs = attrs.borrow().clone(); | |
138 | attrs.sort_by(|x, y| x.name.local.cmp(&y.name.local)); | |
139 | // FIXME: sort by UTF-16 code unit | |
140 | ||
141 | for attr in attrs.into_iter() { | |
142 | buf.push_str("|"); | |
dc9dc135 | 143 | buf.push_str(&repeat(" ").take(indent + 2).collect::<String>()); |
3dfed10e XL |
144 | |
145 | if &*attr.name.ns != "" { | |
146 | buf.push_str("{"); | |
147 | buf.push_str(&*attr.name.ns); | |
148 | buf.push_str("}"); | |
83c7162d | 149 | } |
3dfed10e XL |
150 | |
151 | if let Some(attr_prefix) = attr.name.prefix { | |
152 | buf.push_str(&*attr_prefix); | |
153 | buf.push_str(":"); | |
154 | } | |
155 | ||
dc9dc135 | 156 | buf.push_str(&format!("{}=\"{}\"\n", attr.name.local, attr.value)); |
83c7162d | 157 | } |
dc9dc135 | 158 | }, |
83c7162d XL |
159 | } |
160 | ||
161 | for child in node.children.borrow().iter() { | |
dc9dc135 | 162 | serialize(buf, indent + 2, child.clone()); |
83c7162d | 163 | } |
83c7162d XL |
164 | } |
165 | ||
3dfed10e XL |
166 | // Ignore tests containing these strings; we don't support these features yet. |
167 | static IGNORE_SUBSTRS: &'static [&'static str] = &["<template"]; | |
168 | ||
169 | fn make_xml_test( | |
dc9dc135 XL |
170 | tests: &mut Vec<TestDescAndFn>, |
171 | ignores: &HashSet<String>, | |
172 | filename: &str, | |
173 | idx: usize, | |
174 | fields: HashMap<String, String>, | |
175 | ) { | |
83c7162d XL |
176 | let get_field = |key| { |
177 | let field = fields.get(key).expect("missing field"); | |
e74abb32 | 178 | field.trim_end_matches('\n').to_string() |
83c7162d XL |
179 | }; |
180 | ||
3dfed10e | 181 | let data = get_field("data"); |
83c7162d | 182 | let expected = get_field("document"); |
3dfed10e XL |
183 | let name = format!("tb: {}-{}", filename, idx); |
184 | let ignore = ignores.contains(&name) || IGNORE_SUBSTRS.iter().any(|&ig| data.contains(ig)); | |
83c7162d | 185 | |
3dfed10e | 186 | tests.push(TestDescAndFn { |
83c7162d XL |
187 | desc: TestDesc { |
188 | ignore: ignore, | |
dc9dc135 | 189 | ..TestDesc::new(DynTestName(name)) |
83c7162d | 190 | }, |
3dfed10e | 191 | testfn: DynTestFn(Box::new(move || { |
83c7162d | 192 | let mut result = String::new(); |
3dfed10e XL |
193 | |
194 | let dom = parse_document(RcDom::default(), Default::default()).one(data.clone()); | |
195 | for child in dom.document.children.borrow().iter() { | |
196 | serialize(&mut result, 1, child.clone()); | |
197 | } | |
198 | ||
83c7162d | 199 | let len = result.len(); |
dc9dc135 | 200 | result.truncate(len - 1); // drop the trailing newline |
83c7162d XL |
201 | |
202 | if result != expected { | |
dc9dc135 XL |
203 | panic!( |
204 | "\ninput: {}\ngot:\n{}\nexpected:\n{}\n", | |
205 | data, result, expected | |
206 | ); | |
83c7162d | 207 | } |
3dfed10e XL |
208 | })), |
209 | }); | |
83c7162d XL |
210 | } |
211 | ||
212 | fn tests(src_dir: &Path, ignores: &HashSet<String>) -> Vec<TestDescAndFn> { | |
dc9dc135 XL |
213 | let mut tests = vec![]; |
214 | ||
3dfed10e | 215 | foreach_xml5lib_test( |
dc9dc135 XL |
216 | src_dir, |
217 | "tree-construction", | |
218 | OsStr::new("dat"), | |
219 | |path, file| { | |
220 | let buf = io::BufReader::new(file); | |
221 | let lines = buf.lines().map(|res| res.ok().expect("couldn't read")); | |
222 | let data = parse_tests(lines); | |
223 | ||
224 | for (i, test) in data.into_iter().enumerate() { | |
3dfed10e | 225 | make_xml_test( |
dc9dc135 XL |
226 | &mut tests, |
227 | ignores, | |
228 | path.file_name().unwrap().to_str().unwrap(), | |
229 | i, | |
230 | test, | |
231 | ); | |
232 | } | |
233 | }, | |
234 | ); | |
83c7162d XL |
235 | |
236 | tests | |
237 | } | |
238 | ||
239 | fn main() { | |
240 | let args: Vec<_> = env::args().collect(); | |
241 | let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")); | |
242 | let mut ignores = HashSet::new(); | |
3dfed10e | 243 | if let Ok(f) = fs::File::open(&src_dir.join("data/test/ignore")) { |
83c7162d XL |
244 | let r = io::BufReader::new(f); |
245 | for ln in r.lines() { | |
e74abb32 | 246 | ignores.insert(ln.unwrap().trim_end().to_string()); |
83c7162d XL |
247 | } |
248 | } | |
249 | ||
3dfed10e | 250 | rustc_test::test_main(&args, tests(src_dir, &ignores)); |
83c7162d | 251 | } |