use std::io;
use oxixml_xml::events::Event::*;
use oxixml_xml::events::{
BytesCData, BytesDecl, BytesEnd, BytesPI, BytesRef, BytesStart, BytesText,
};
use oxixml_xml::writer::Writer;
type TestResult = io::Result<()>;
fn write_one<'a, E: Into<oxixml_xml::events::Event<'a>>>(event: E) -> String {
let mut writer = Writer::new(Vec::new());
writer.write_event(event).expect("write event");
String::from_utf8(writer.into_inner()).expect("utf-8 output")
}
mod declaration {
use super::*;
#[test]
fn full() {
assert_eq!(
write_one(Decl(BytesDecl::new("1.2", Some("utf-X"), Some("yo")))),
r#"<?xml version="1.2" encoding="utf-X" standalone="yo"?>"#,
);
}
#[test]
fn standalone() {
assert_eq!(
write_one(Decl(BytesDecl::new("1.2", None, Some("yo")))),
r#"<?xml version="1.2" standalone="yo"?>"#,
);
}
#[test]
fn encoding() {
assert_eq!(
write_one(Decl(BytesDecl::new("1.2", Some("utf-X"), None))),
r#"<?xml version="1.2" encoding="utf-X"?>"#,
);
}
#[test]
fn version() {
assert_eq!(
write_one(Decl(BytesDecl::new("1.2", None, None))),
r#"<?xml version="1.2"?>"#,
);
}
#[test]
fn empty() {
assert_eq!(
write_one(Decl(BytesDecl::new("", Some(""), Some("")))),
r#"<?xml version="" encoding="" standalone=""?>"#,
);
}
}
#[test]
fn pi() {
assert_eq!(
write_one(PI(BytesPI::new("xml-stylesheet href='theme.xls' "))),
"<?xml-stylesheet href='theme.xls' ?>",
);
}
#[test]
fn empty() {
assert_eq!(
write_one(Empty(
BytesStart::new("game").with_attributes([("publisher", "Blizzard")])
)),
r#"<game publisher="Blizzard"/>"#,
);
}
#[test]
fn start() {
assert_eq!(
write_one(Start(
BytesStart::new("info").with_attributes([("genre", "RTS")])
)),
r#"<info genre="RTS">"#,
);
}
#[test]
fn end() {
assert_eq!(write_one(End(BytesEnd::new("info"))), "</info>");
}
#[test]
fn text() {
assert_eq!(
write_one(Text(BytesText::new(
"Kerrigan & Raynor: The Z[erg] programming language",
))),
"Kerrigan & Raynor: The Z[erg] programming language",
);
}
#[test]
fn cdata() {
assert_eq!(
write_one(CData(BytesCData::new(
"Kerrigan & Raynor: The Z[erg] programming language",
))),
"<![CDATA[Kerrigan & Raynor: The Z[erg] programming language]]>",
);
}
#[test]
fn comment() {
assert_eq!(
write_one(Comment(BytesText::from_escaped(
"Kerrigan & Raynor: The Z[erg] programming language",
))),
"<!--Kerrigan & Raynor: The Z[erg] programming language-->",
);
}
#[test]
fn doctype() {
assert_eq!(
write_one(DocType(BytesText::new("some DTD here..."))),
"<!DOCTYPE some DTD here...>",
);
}
#[test]
fn general_ref() {
assert_eq!(write_one(GeneralRef(BytesRef::new("entity"))), "&entity;");
}
#[test]
fn eof() {
assert_eq!(write_one(Eof), "");
}
#[test]
fn write_borrowed_event() {
let event = Start(BytesStart::new("info").with_attributes([("genre", "RTS")]));
let mut writer = Writer::new(Vec::new());
writer.write_event(event.borrow()).expect("write borrowed");
writer.write_event(event).expect("write owned");
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
r#"<info genre="RTS"><info genre="RTS">"#,
);
}
#[test]
fn write_bom_prepends_marker() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer.write_bom()?;
writer.create_element("a").write_empty()?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
"\u{FEFF}<a/>",
);
Ok(())
}
#[test]
fn empty_element_slash_variants() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("tag")
.with_attribute(("a", "1"))
.write_empty()?;
assert_eq!(writer.into_inner(), br#"<tag a="1"/>"#.to_vec());
let mut writer = Writer::new(Vec::new());
writer.config_mut().add_space_before_slash_in_empty_elements = true;
writer
.create_element("tag")
.with_attribute(("a", "1"))
.write_empty()?;
assert_eq!(writer.into_inner(), br#"<tag a="1" />"#.to_vec());
Ok(())
}
mod element_writer {
use super::*;
#[test]
fn write_empty_without_attributes() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer.create_element("tag").write_empty()?;
assert_eq!(writer.into_inner(), b"<tag/>".to_vec());
Ok(())
}
#[test]
fn with_attribute_and_with_attributes() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("tag")
.with_attribute(("a", "1"))
.with_attributes([("b", "2"), ("c", "3")])
.write_empty()?;
assert_eq!(writer.into_inner(), br#"<tag a="1" b="2" c="3"/>"#.to_vec());
Ok(())
}
#[test]
fn with_attributes_empty_iterator() -> TestResult {
let mut writer = Writer::new(Vec::new());
let none: [(&str, &str); 0] = [];
writer
.create_element("tag")
.with_attributes(none)
.write_empty()?;
assert_eq!(writer.into_inner(), b"<tag/>".to_vec());
Ok(())
}
#[test]
fn attribute_values_are_escaped() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("tag")
.with_attribute(("a", r#"<&">"#))
.write_empty()?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
r#"<tag a="<&">"/>"#,
);
Ok(())
}
#[test]
fn write_text_content() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("t")
.write_text_content(BytesText::new("a < b"))?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
"<t>a < b</t>",
);
Ok(())
}
#[test]
fn write_cdata_content() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("c")
.write_cdata_content(BytesCData::new(" raw <b> & "))?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
"<c><![CDATA[ raw <b> & ]]></c>",
);
Ok(())
}
#[test]
fn write_pi_content() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("p")
.write_pi_content(BytesPI::new("php echo 1"))?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
"<p><?php echo 1?></p>",
);
Ok(())
}
#[test]
fn write_inner_content() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("list")
.write_inner_content(|writer| {
for (quantity, item) in ["apple", "orange"].iter().enumerate() {
writer
.create_element("fruit")
.with_attribute(("quantity", quantity.to_string().as_str()))
.write_text_content(BytesText::new(item))?;
}
Ok(())
})?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
r#"<list><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></list>"#,
);
Ok(())
}
#[test]
fn write_inner_content_propagates_errors() {
let mut writer = Writer::new(Vec::new());
let result = writer
.create_element("list")
.write_inner_content(|_| Err(io::Error::other("boom")));
let error = result.err().expect("closure error is propagated");
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!(writer.into_inner(), b"<list>".to_vec());
}
#[test]
fn builder_returns_the_writer() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer
.create_element("a")
.write_empty()?
.create_element("b")
.write_text_content(BytesText::new("x"))?
.create_element("c")
.write_empty()?;
assert_eq!(
String::from_utf8(writer.into_inner()).expect("utf-8 output"),
"<a/><b>x</b><c/>",
);
Ok(())
}
}
#[test]
fn get_ref_get_mut_into_inner() -> TestResult {
let mut writer = Writer::new(Vec::new());
writer.write_event(Empty(BytesStart::new("a")))?;
assert_eq!(writer.get_ref(), b"<a/>");
writer.get_mut().extend_from_slice(b"!");
assert_eq!(writer.into_inner(), b"<a/>!".to_vec());
Ok(())
}
#[test]
fn writes_through_a_borrowed_sink() -> TestResult {
let mut buffer = Vec::new();
{
let mut writer = Writer::new(&mut buffer);
writer.write_event(Empty(BytesStart::new("a")))?;
}
assert_eq!(buffer, b"<a/>".to_vec());
Ok(())
}
#[test]
fn io_errors_are_propagated() {
struct Failing;
impl io::Write for Failing {
fn write(&mut self, _: &[u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::BrokenPipe, "nope"))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
let mut writer = Writer::new(Failing);
let error = writer
.write_event(Empty(BytesStart::new("a")))
.expect_err("sink error is propagated");
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
}