use std::thread;
use super::error::description::*;
use super::*;
fn check_doc_xml(doc: &Document, expected: &str) {
let xml = doc.to_string();
assert_eq!(xml, expected);
assert_eq!(xml.len(), xml.capacity());
let xml2 = format!("{}", doc);
assert_eq!(xml2, expected);
}
#[test]
fn it_works() {
let doc = Document::new("html").unwrap();
let blink = doc
.insert_tag("p")
.unwrap()
.insert_tag("b")
.unwrap()
.insert_tag("blink")
.unwrap()
.insert_cdata("lala")
.unwrap();
assert!(!blink.is_null());
let p2 = doc
.root()
.first_child()
.append_cdata("foo&")
.unwrap()
.append_tag("p2")
.unwrap();
p2.prepend_cdata("bar").unwrap().prepend_tag("p3").unwrap();
check_doc_xml(
&doc,
"<html><p><b><blink>lala</blink></b></p>foo&<p3/>bar<p2/></html>",
);
}
#[test]
fn attributes() {
let doc = Document::new("doc").unwrap();
let a = doc.insert_tag("a").unwrap();
assert!(a.insert_attribute("i", "1").unwrap().is_tag());
assert_eq!(
a.insert_attribute("i", "1").unwrap_err(),
ParseError::BadXml(description::DUPLICATE_ATTRIBUTE)
);
assert!(a.insert_attribute("j", "2").unwrap().is_tag());
assert_eq!(
a.insert_attribute("i", "1").unwrap_err(),
ParseError::BadXml(description::DUPLICATE_ATTRIBUTE)
);
assert_eq!(
a.insert_attribute("j", "1").unwrap_err(),
ParseError::BadXml(description::DUPLICATE_ATTRIBUTE)
);
let _ = doc
.insert_tag("b")
.unwrap()
.set_attribute("i", Some("1"))
.unwrap()
.set_attribute("i", Some("2"))
.unwrap();
check_doc_xml(&doc, "<doc><a i=\"1\" j=\"2\"/><b i=\"2\"/></doc>");
let _ = doc.root().first_child().set_attribute("k", Some("3"));
check_doc_xml(&doc, "<doc><a i=\"1\" j=\"2\" k=\"3\"/><b i=\"2\"/></doc>");
let mut iter = doc.first_child().attributes();
assert_eq!(iter.next(), Some(("i", "1")));
assert_eq!(iter.next(), Some(("j", "2")));
assert_eq!(iter.next(), Some(("k", "3")));
assert_eq!(iter.next(), None);
assert_eq!(doc.find_tag("a").attribute("i"), Some("1"));
assert_eq!(doc.find_tag("a").attribute("j"), Some("2"));
assert_eq!(doc.find_tag("a").attribute("k"), Some("3"));
assert_eq!(doc.find_tag("b").attribute("i"), Some("2"));
let _ = doc.find_tag("a").set_attribute("i", None);
let _ = doc.find_tag("a").set_attribute("x", None);
check_doc_xml(&doc, "<doc><a j=\"2\" k=\"3\"/><b i=\"2\"/></doc>");
let _ = doc.find_tag("a").set_attribute("k", None);
check_doc_xml(&doc, "<doc><a j=\"2\"/><b i=\"2\"/></doc>");
let _ = doc.find_tag("a").set_attribute("j", None);
check_doc_xml(&doc, "<doc><a/><b i=\"2\"/></doc>");
let tag = doc.find_tag("b");
tag.set_attribute("j", Some("3")).unwrap();
tag.set_attribute("k", Some("4")).unwrap();
check_doc_xml(&doc, "<doc><a/><b i=\"2\" j=\"3\" k=\"4\"/></doc>");
tag.set_attribute("k", None).unwrap();
check_doc_xml(&doc, "<doc><a/><b i=\"2\" j=\"3\"/></doc>");
tag.set_attribute("k", Some("b")).unwrap();
check_doc_xml(&doc, "<doc><a/><b i=\"2\" j=\"3\" k=\"b\"/></doc>");
}
#[test]
fn attribute_finds() {
let doc = Document::from_str("<t><a/><b x='1'/><c y='1'/><d/><e x='2'/></t>").unwrap();
assert!(doc.find_tag_with_attribute("z").is_null());
assert!(doc.find_tag_with_attribute_value("x", "3").is_null());
assert_eq!(doc.find_tag_with_attribute("y").to_string(), "<c y=\"1\"/>");
assert_eq!(
doc.find_tag_with_attribute_value("x", "2").to_string(),
"<e x=\"2\"/>"
);
}
#[test]
fn navigation() {
let doc = Document::from_str("<a><b>123<c/>456</b>.,;<d/> <e x='1' y='2'> lala<f/></e>789</a>")
.unwrap();
assert_eq!(doc.root().first_tag().first_tag().to_string(), "<c/>");
assert_eq!(doc.root().first_child().next().to_string(), ".,;");
assert_eq!(doc.root().first_child().next().next().to_string(), "<d/>");
assert_eq!(doc.root().first_child().next_tag().to_string(), "<d/>");
assert_eq!(doc.root().first_tag().last_child().cdata(), "456");
assert_eq!(doc.root().last_child().to_string(), "789");
assert_eq!(
doc.root().last_child().previous().previous().to_string(),
" "
);
assert_eq!(
doc.root()
.last_child()
.previous_tag()
.previous_tag()
.to_string(),
"<d/>"
);
assert_eq!(
doc.root().last_child().previous().first_tag().to_string(),
"<f/>"
);
assert_eq!(
doc.first_child()
.first_tag()
.parent()
.next_tag()
.next_tag()
.find_tag("f")
.root()
.find_tag("e")
.first_child()
.to_string(),
" lala"
);
assert_eq!(doc.first_tag().first_tag().to_string(), "<c/>");
assert_eq!(
doc.find_tag("e").to_string(),
"<e x=\"1\" y=\"2\"> lala<f/></e>"
);
}
#[test]
fn properties() {
let doc = Document::from_str("<doc><a></a><b/><c>lala</c></doc>").unwrap();
let a = doc.find_tag("a");
assert_eq!(a.has_children(), false);
assert_eq!(a.name(), "a");
assert_eq!(a.is_tag(), true);
assert_eq!(a.is_null(), false);
let b = doc.find_tag("b");
assert_eq!(b.has_children(), false);
let c = doc.find_tag("c");
assert_eq!(c.has_children(), true);
let cc = c.first_child();
assert_eq!(cc.has_children(), false);
assert_eq!(cc.cdata(), "lala");
assert_eq!(cc.is_tag(), false);
assert_eq!(cc.is_null(), false);
}
#[test]
fn doc_parser() {
let doc = Document::from_str("<a><b>123<c/>456</b><d x='1' y='2'>lala</d></a>").unwrap();
check_doc_xml(&doc, "<a><b>123<c/>456</b><d x=\"1\" y=\"2\">lala</d></a>");
assert_eq!(doc.str_size(), doc.root().str_size());
}
#[test]
fn serialize_subset() {
let doc = Document::from_str("<a><b>lala</b><c>bibi</c><d><e>123</e></d></a>").unwrap();
assert_eq!(doc.first_child().to_string(), "<b>lala</b>");
assert_eq!(doc.find_tag("c").to_string(), "<c>bibi</c>");
assert_eq!(doc.find_tag("d").to_string(), "<d><e>123</e></d>");
assert_eq!(doc.find_tag("d").first_child().to_string(), "<e>123</e>");
}
#[test]
fn cursor_clone() {
let doc = Document::from_str("<a><b>lala</b><c>bibi</c><d><e>123</e></d></a>").unwrap();
let c4: Cursor;
{
let c1 = doc.root();
c4 = c1.clone();
let c2 = c1.clone().find_tag("d").first_child();
assert_eq!(c2.first_child().to_string(), "123");
let c3 = c1.find_tag("b").first_child();
assert_eq!(c3.to_string(), "lala");
}
assert_eq!(c4.find_tag("c").first_child().to_string(), "bibi");
}
#[test]
fn removals() {
let doc = Document::from_str("<a>123<b/>456<c/><d><e/></d>789<f/></a>").unwrap();
doc.find_tag("d").remove();
assert_eq!(doc.to_string(), "<a>123<b/>456<c/>789<f/></a>");
doc.find_tag("f").remove();
assert_eq!(doc.to_string(), "<a>123<b/>456<c/>789</a>");
doc.first_child().remove();
assert_eq!(doc.to_string(), "<a><b/>456<c/>789</a>");
doc.root().last_child().remove();
assert_eq!(doc.to_string(), "<a><b/>456<c/></a>");
for child in doc.root().children() {
child.remove();
}
assert_eq!(doc.to_string(), "<a/>");
}
#[test]
fn iterators() {
let doc = Document::from_str("<a>lala<b><c>bibi</c><d><e>123</e></d>456</b>foo</a>").unwrap();
let mut iter = doc.find_tag("b").descendant_or_self();
assert_eq!(iter.next().unwrap().name(), "b");
assert_eq!(iter.next().unwrap().name(), "c");
assert_eq!(iter.next().unwrap().cdata(), "bibi");
assert_eq!(iter.next().unwrap().name(), "d");
assert_eq!(iter.next().unwrap().name(), "e");
assert_eq!(iter.next().unwrap().cdata(), "123");
assert_eq!(iter.next().unwrap().cdata(), "456");
assert!(iter.next().is_none());
let mut iter = doc.find_tag("b").children();
assert_eq!(iter.next().unwrap().name(), "c");
assert_eq!(iter.next().unwrap().name(), "d");
assert_eq!(iter.next().unwrap().cdata(), "456");
assert!(iter.next().is_none());
let mut iter = doc.find_tag("b").find_tag("d").ancestor();
assert_eq!(iter.next().unwrap().name(), "b");
assert_eq!(iter.next().unwrap().name(), "a");
assert!(iter.next().is_none());
let doc = Document::from_str("<a>lala<b/>123<c>101</c>456<d/>abc<e><f/></e></a>").unwrap();
let mut iter = doc.find_tag("d").following_sibling();
assert_eq!(iter.next().unwrap().cdata(), "abc");
assert_eq!(iter.next().unwrap().name(), "e");
assert!(iter.next().is_none());
let doc = Document::from_str("<a>lala<b/>123<c>101</c>456<d/>abc<e><f/></e></a>").unwrap();
let mut iter = doc.find_tag("d").preceding_sibling();
assert_eq!(iter.next().unwrap().cdata(), "456");
assert_eq!(iter.next().unwrap().name(), "c");
assert_eq!(iter.next().unwrap().cdata(), "123");
assert_eq!(iter.next().unwrap().name(), "b");
assert_eq!(iter.next().unwrap().cdata(), "lala");
assert!(iter.next().is_none());
}
#[test]
fn cdata_merges() {
let doc = Document::new("m").unwrap();
let _ = doc.insert_cdata("hello").unwrap();
let bytes = doc.arena_stats().used_bytes;
let c = doc.insert_cdata(" world").unwrap();
let bytes2 = doc.arena_stats().used_bytes;
assert_eq!(doc.to_string(), "<m>hello world</m>");
assert_eq!(bytes2, bytes + 6);
let _ = c.append_cdata("!");
assert_eq!(doc.to_string(), "<m>hello world!</m>");
let bytes3 = doc.arena_stats().used_bytes;
assert_eq!(bytes3, bytes2 + 1);
}
#[test]
fn null_checks() {
let doc = Document::new("a").unwrap();
assert_eq!(doc.root().next().is_null(), true);
assert_eq!(doc.root().next().is_tag(), false);
assert_eq!(doc.root().next().has_children(), false);
assert_eq!(doc.root().next().name(), "");
assert_eq!(doc.root().next().attribute("lala"), None);
assert_eq!(doc.root().next().cdata(), "");
assert_eq!(doc.root().next().str_size(), 0);
assert_eq!(doc.root().next().clone().is_null(), true);
assert!(doc.root().next().next().is_null());
assert!(doc.root().next().next_tag().is_null());
assert!(doc.root().next().previous().is_null());
assert!(doc.root().next().previous_tag().is_null());
assert!(doc.root().next().first_child().is_null());
assert!(doc.root().next().last_child().is_null());
assert!(doc.root().next().first_tag().is_null());
assert!(doc.root().next().parent().is_null());
assert!(doc.root().next().root().is_null());
assert!(doc.root().next().find_tag("lala").is_null());
assert!(doc.root().next().children().next().is_none());
assert!(doc.root().next().descendant_or_self().next().is_none());
assert!(doc.root().next().following_sibling().next().is_none());
assert!(doc.root().next().attributes().next().is_none());
assert!(doc.root().next().insert_tag("k").is_err());
assert!(doc.root().next().append_tag("k").is_err());
assert!(doc.root().next().prepend_tag("k").is_err());
assert!(doc.root().next().insert_attribute("k", "v").is_err());
assert!(doc.root().next().set_attribute("k", None).is_err());
assert!(doc.root().next().insert_cdata("k").is_err());
assert!(doc.root().next().append_cdata("k").is_err());
assert!(doc.root().next().prepend_cdata("k").is_err());
assert!(doc.root().next().to_document().is_err());
assert!(doc.root().next().insert_document(doc.root()).is_err());
assert!(doc.root().insert_document(doc.root().next()).is_err());
doc.root().next().remove();
}
#[test]
fn cursor_tofrom_doc() {
let doc = Document::from_str("<a>456<b><e></e><f/>&<d/>abc<d2 t=\"1\"></d2></b><c/></a>")
.unwrap();
let doc2 = doc.find_tag("b").to_document().unwrap();
assert_eq!(doc2.to_string(), "<b><e/><f/>&<d/>abc<d2 t=\"1\"/></b>");
let doc3 = Document::from_str("<a>xxx<h/></a>").unwrap();
doc3.find_tag("h").insert_document(doc2.root()).unwrap();
assert_eq!(
doc3.to_string(),
"<a>xxx<h><b><e/><f/>&<d/>abc<d2 t=\"1\"/></b></h></a>"
)
}
#[test]
fn bad_doc_parser() {
assert_eq!(
Document::from_str("<a>lala</b>").err(),
Some(ParseError::BadXml(TAG_MISMATCH))
);
assert_eq!(
Document::from_str("<a><b><c/></d></a>").err(),
Some(ParseError::BadXml(TAG_MISMATCH))
);
assert_eq!(
Document::from_str("<a><b><c/></b><d></d><e></e2></a>").err(),
Some(ParseError::BadXml(TAG_MISMATCH))
);
assert_eq!(
Document::from_str("<a><b x=\"1\" y=\"2\" x=\"abc\"/></a>").err(),
Some(ParseError::BadXml(DUPLICATE_ATTRIBUTE))
);
}
#[test]
fn sync_cursor_works() {
let document = Document::from_str("<a>lala<b>bibi</b></a>").unwrap();
let cursor = SyncCursor::new(document);
let c2 = cursor.clone();
cursor
.insert_tag("e")
.unwrap()
.insert_tag("f")
.unwrap()
.next();
assert_eq!(c2.to_string(), "<a>lala<b>bibi</b><e><f/></e></a>");
}
#[test]
fn sync_cursor_multi() {
let document = Document::with_size_hint("a", 10000).unwrap();
let cursor = SyncCursor::new(document);
let mut handles = vec![];
for _ in 0..10 {
let cursor = cursor.clone();
handles.push(thread::spawn(move || {
cursor.insert_tag("e").unwrap().insert_tag("f").unwrap();
}));
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(
cursor.to_string(),
"<a><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e><e><f/></e></a>"
);
}
#[test]
fn prints() {
let doc = Document::new("lala").unwrap();
let s1 = format!("{:?}", doc);
assert!(s1.len() > 0);
let s2 = format!("{}", doc);
assert!(s2.len() > 0);
let c = doc.insert_tag("x").unwrap();
let s3 = format!("{:?}", c);
assert!(s3.len() > 0);
let s4 = format!("{}", c);
assert!(s4.len() > 0);
}