mirror of
https://git.proxmox.com/git/rustc
synced 2025-08-15 01:10:36 +00:00
302 lines
9.3 KiB
Rust
302 lines
9.3 KiB
Rust
// Copyright 2014-2017 The html5ever Project Developers. See the
|
|
// COPYRIGHT file at the top-level directory of this distribution.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
extern crate rustc_test as test;
|
|
#[macro_use]
|
|
extern crate html5ever;
|
|
|
|
mod foreach_html5lib_test;
|
|
use foreach_html5lib_test::foreach_html5lib_test;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::default::Default;
|
|
use std::ffi::OsStr;
|
|
use std::io::BufRead;
|
|
use std::iter::repeat;
|
|
use std::mem::replace;
|
|
use std::path::Path;
|
|
use std::{env, fs, io};
|
|
use test::{DynTestName, TestDesc, TestDescAndFn, TestFn};
|
|
|
|
use html5ever::rcdom::{Handle, NodeData, RcDom};
|
|
use html5ever::tendril::{StrTendril, TendrilSink};
|
|
use html5ever::{parse_document, parse_fragment, ParseOpts};
|
|
use html5ever::{LocalName, QualName};
|
|
|
|
fn parse_tests<It: Iterator<Item = String>>(mut lines: It) -> Vec<HashMap<String, String>> {
|
|
let mut tests = vec![];
|
|
let mut test = HashMap::new();
|
|
let mut key: Option<String> = None;
|
|
let mut val = String::new();
|
|
|
|
macro_rules! finish_val ( () => (
|
|
match key.take() {
|
|
None => (),
|
|
Some(key) => {
|
|
assert!(test.insert(key, replace(&mut val, String::new())).is_none());
|
|
}
|
|
}
|
|
));
|
|
|
|
macro_rules! finish_test ( () => (
|
|
if !test.is_empty() {
|
|
tests.push(replace(&mut test, HashMap::new()));
|
|
}
|
|
));
|
|
|
|
loop {
|
|
match lines.next() {
|
|
None => break,
|
|
Some(line) => {
|
|
if line.starts_with("#") {
|
|
finish_val!();
|
|
if line == "#data" {
|
|
finish_test!();
|
|
}
|
|
key = Some(line[1..].to_string());
|
|
} else {
|
|
val.push_str(&line);
|
|
val.push('\n');
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
finish_val!();
|
|
finish_test!();
|
|
tests
|
|
}
|
|
|
|
fn serialize(buf: &mut String, indent: usize, handle: Handle) {
|
|
buf.push_str("|");
|
|
buf.push_str(&repeat(" ").take(indent).collect::<String>());
|
|
|
|
let node = handle;
|
|
match node.data {
|
|
NodeData::Document => panic!("should not reach Document"),
|
|
|
|
NodeData::Doctype {
|
|
ref name,
|
|
ref public_id,
|
|
ref system_id,
|
|
} => {
|
|
buf.push_str("<!DOCTYPE ");
|
|
buf.push_str(&name);
|
|
if !public_id.is_empty() || !system_id.is_empty() {
|
|
buf.push_str(&format!(" \"{}\" \"{}\"", public_id, system_id));
|
|
}
|
|
buf.push_str(">\n");
|
|
},
|
|
|
|
NodeData::Text { ref contents } => {
|
|
buf.push_str("\"");
|
|
buf.push_str(&contents.borrow());
|
|
buf.push_str("\"\n");
|
|
},
|
|
|
|
NodeData::Comment { ref contents } => {
|
|
buf.push_str("<!-- ");
|
|
buf.push_str(&contents);
|
|
buf.push_str(" -->\n");
|
|
},
|
|
|
|
NodeData::Element {
|
|
ref name,
|
|
ref attrs,
|
|
..
|
|
} => {
|
|
buf.push_str("<");
|
|
match name.ns {
|
|
ns!(svg) => buf.push_str("svg "),
|
|
ns!(mathml) => buf.push_str("math "),
|
|
_ => (),
|
|
}
|
|
buf.push_str(&*name.local);
|
|
buf.push_str(">\n");
|
|
|
|
let mut attrs = attrs.borrow().clone();
|
|
attrs.sort_by(|x, y| x.name.local.cmp(&y.name.local));
|
|
// FIXME: sort by UTF-16 code unit
|
|
|
|
for attr in attrs.into_iter() {
|
|
buf.push_str("|");
|
|
buf.push_str(&repeat(" ").take(indent + 2).collect::<String>());
|
|
match attr.name.ns {
|
|
ns!(xlink) => buf.push_str("xlink "),
|
|
ns!(xml) => buf.push_str("xml "),
|
|
ns!(xmlns) => buf.push_str("xmlns "),
|
|
_ => (),
|
|
}
|
|
buf.push_str(&format!("{}=\"{}\"\n", attr.name.local, attr.value));
|
|
}
|
|
},
|
|
|
|
NodeData::ProcessingInstruction { .. } => unreachable!(),
|
|
}
|
|
|
|
for child in node.children.borrow().iter() {
|
|
serialize(buf, indent + 2, child.clone());
|
|
}
|
|
|
|
if let NodeData::Element {
|
|
template_contents: Some(ref content),
|
|
..
|
|
} = node.data
|
|
{
|
|
buf.push_str("|");
|
|
buf.push_str(&repeat(" ").take(indent + 2).collect::<String>());
|
|
buf.push_str("content\n");
|
|
for child in content.children.borrow().iter() {
|
|
serialize(buf, indent + 4, child.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
fn make_test(
|
|
tests: &mut Vec<TestDescAndFn>,
|
|
ignores: &HashSet<String>,
|
|
filename: &str,
|
|
idx: usize,
|
|
fields: HashMap<String, String>,
|
|
) {
|
|
let scripting_flags = &[false, true];
|
|
let scripting_flags = if fields.contains_key("script-off") {
|
|
&scripting_flags[0..1]
|
|
} else if fields.contains_key("script-on") {
|
|
&scripting_flags[1..2]
|
|
} else {
|
|
&scripting_flags[0..2]
|
|
};
|
|
let name = format!("tb: {}-{}", filename, idx);
|
|
for scripting_enabled in scripting_flags {
|
|
let test = make_test_desc_with_scripting_flag(ignores, &name, &fields, *scripting_enabled);
|
|
tests.push(test);
|
|
}
|
|
}
|
|
|
|
fn make_test_desc_with_scripting_flag(
|
|
ignores: &HashSet<String>,
|
|
name: &str,
|
|
fields: &HashMap<String, String>,
|
|
scripting_enabled: bool,
|
|
) -> TestDescAndFn {
|
|
let get_field = |key| {
|
|
let field = fields.get(key).expect("missing field");
|
|
field.trim_end_matches('\n').to_string()
|
|
};
|
|
|
|
let mut data = fields.get("data").expect("missing data").to_string();
|
|
data.pop();
|
|
let expected = get_field("document");
|
|
let context = fields
|
|
.get("document-fragment")
|
|
.map(|field| context_name(field.trim_end_matches('\n')));
|
|
let ignore = ignores.contains(name);
|
|
let mut name = name.to_owned();
|
|
if scripting_enabled {
|
|
name.push_str(" (scripting enabled)");
|
|
} else {
|
|
name.push_str(" (scripting disabled)");
|
|
};
|
|
let mut opts: ParseOpts = Default::default();
|
|
opts.tree_builder.scripting_enabled = scripting_enabled;
|
|
|
|
TestDescAndFn {
|
|
desc: TestDesc {
|
|
ignore: ignore,
|
|
..TestDesc::new(DynTestName(name))
|
|
},
|
|
testfn: TestFn::dyn_test_fn(move || {
|
|
// Do this here because Tendril isn't Send.
|
|
let data = StrTendril::from_slice(&data);
|
|
let mut result = String::new();
|
|
match context {
|
|
None => {
|
|
let dom = parse_document(RcDom::default(), opts).one(data.clone());
|
|
for child in dom.document.children.borrow().iter() {
|
|
serialize(&mut result, 1, child.clone());
|
|
}
|
|
},
|
|
Some(ref context) => {
|
|
let dom = parse_fragment(RcDom::default(), opts, context.clone(), vec![])
|
|
.one(data.clone());
|
|
// fragment case: serialize children of the html element
|
|
// rather than children of the document
|
|
let doc = &dom.document;
|
|
let root = &doc.children.borrow()[0];
|
|
for child in root.children.borrow().iter() {
|
|
serialize(&mut result, 1, child.clone());
|
|
}
|
|
},
|
|
};
|
|
let len = result.len();
|
|
result.truncate(len - 1); // drop the trailing newline
|
|
|
|
if result != expected {
|
|
panic!(
|
|
"\ninput: {}\ngot:\n{}\nexpected:\n{}\n",
|
|
data, result, expected
|
|
);
|
|
}
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn context_name(context: &str) -> QualName {
|
|
if context.starts_with("svg ") {
|
|
QualName::new(None, ns!(svg), LocalName::from(&context[4..]))
|
|
} else if context.starts_with("math ") {
|
|
QualName::new(None, ns!(mathml), LocalName::from(&context[5..]))
|
|
} else {
|
|
QualName::new(None, ns!(html), LocalName::from(context))
|
|
}
|
|
}
|
|
|
|
fn tests(src_dir: &Path, ignores: &HashSet<String>) -> Vec<TestDescAndFn> {
|
|
let mut tests = vec![];
|
|
|
|
foreach_html5lib_test(
|
|
src_dir,
|
|
"tree-construction",
|
|
OsStr::new("dat"),
|
|
|path, file| {
|
|
let buf = io::BufReader::new(file);
|
|
let lines = buf.lines().map(|res| res.ok().expect("couldn't read"));
|
|
let data = parse_tests(lines);
|
|
|
|
for (i, test) in data.into_iter().enumerate() {
|
|
make_test(
|
|
&mut tests,
|
|
ignores,
|
|
path.file_name().unwrap().to_str().unwrap(),
|
|
i,
|
|
test,
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
tests
|
|
}
|
|
|
|
fn main() {
|
|
let args: Vec<_> = env::args().collect();
|
|
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
let mut ignores = HashSet::new();
|
|
{
|
|
let f = fs::File::open(&src_dir.join("data/test/ignore")).unwrap();
|
|
let r = io::BufReader::new(f);
|
|
for ln in r.lines() {
|
|
ignores.insert(ln.unwrap().trim_end().to_string());
|
|
}
|
|
}
|
|
|
|
test::test_main(&args, tests(src_dir, &ignores));
|
|
}
|