From ee5a073c2759bf89cd3fc52b35449c5809a6cdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20Pr=C3=B6=C3=9Fdorf?= Date: Sun, 27 Sep 2020 14:46:31 +0300 Subject: [PATCH] Introduce an AsyncReader instead of overloading the Reader --- Cargo.toml | 4 +- examples/custom_entities.rs | 71 ++- examples/issue68.rs | 91 ++- examples/nested_readers.rs | 111 +++- examples/read_texts.rs | 69 +- src/errors.rs | 6 +- src/escapei.rs | 2 +- src/events/attributes.rs | 14 +- src/events/mod.rs | 78 ++- src/lib.rs | 13 +- src/reader/asynchronous.rs | 62 +- src/reader/mod.rs | 8 +- src/writer.rs | 2 +- tests/documents/html5.txt | 4 +- tests/serde-migrated.rs | 4 +- tests/test.rs | 228 +------ tests/test_async.rs | 1187 +++++++++++++++++++++++++++++++++++ tests/unit_tests.rs | 467 +++----------- tests/unit_tests_async.rs | 1026 ++++++++++++++++++++++++++++++ tests/xmlrs_reader_tests.rs | 115 +++- 20 files changed, 2789 insertions(+), 773 deletions(-) create mode 100644 tests/test_async.rs create mode 100644 tests/unit_tests_async.rs diff --git a/Cargo.toml b/Cargo.toml index a29e994e..9039e81a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ travis-ci = { repository = "tafia/quick-xml" } [dependencies] async-recursion = { version = "0.3.2", optional = true } encoding_rs = { version = "0.8.26", optional = true } -tokio = { version = "0.2.22", features = ["fs", "io-util"], optional = true } +tokio = { version = "1.4.0", features = ["fs", "io-util"], optional = true } serde = { version = "1.0", optional = true } memchr = "2.3.4" @@ -26,7 +26,7 @@ memchr = "2.3.4" serde = { version = "1.0", features = ["derive"] } serde-value = "0.7" regex = "1" -tokio = { version = "0.2.22", features = ["macros", "rt-threaded"] } +tokio = { version = "1.4.0", features = ["macros", "rt-multi-thread"] } [lib] bench = false diff --git a/examples/custom_entities.rs b/examples/custom_entities.rs index 42986126..4fde855b 100644 --- a/examples/custom_entities.rs +++ b/examples/custom_entities.rs @@ -11,6 +11,8 @@ extern crate quick_xml; extern crate regex; use quick_xml::events::Event; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; use quick_xml::Reader; use regex::bytes::Regex; use std::collections::HashMap; @@ -27,22 +29,15 @@ const DATA: &str = r#" "#; -fn main() -> Result<(), Box> { - let mut reader = Reader::from_str(DATA); +fn custom_entities(data: &str) -> Result<(), Box> { + let mut reader = Reader::from_str(data); reader.trim_text(true); let mut buf = Vec::new(); let mut custom_entities = HashMap::new(); let entity_re = Regex::new(r#""#)?; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - #[cfg(not(feature = "asynchronous"))] let event = reader.read_event(&mut buf); match event { @@ -80,3 +75,61 @@ fn main() -> Result<(), Box> { } Ok(()) } + +#[cfg(feature = "asynchronous")] +async fn custom_entities_async(data: &str) -> Result<(), Box> { + let mut reader = AsyncReader::from_str(data); + reader.trim_text(true); + + let mut buf = Vec::new(); + let mut custom_entities = HashMap::new(); + let entity_re = Regex::new(r#""#)?; + + loop { + match reader.read_event(&mut buf).await { + Ok(Event::DocType(ref e)) => { + for cap in entity_re.captures_iter(&e) { + custom_entities.insert(cap[1].to_vec(), cap[2].to_vec()); + } + } + Ok(Event::Start(ref e)) => match e.name() { + b"test" => println!( + "attributes values: {:?}", + e.attributes() + .map(|a| a + .unwrap() + .unescape_and_decode_value_with_custom_entities( + &reader, + &custom_entities + ) + .unwrap()) + .collect::>() + ), + _ => (), + }, + Ok(Event::Text(ref e)) => { + println!( + "text value: {}", + e.unescape_and_decode_with_custom_entities(&reader, &custom_entities) + .unwrap() + ); + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + _ => (), + } + } + Ok(()) +} + +fn main() -> Result<(), Box> { + custom_entities(DATA)?; + + #[cfg(feature = "asynchronous")] + let runtime = Runtime::new().expect("Runtime cannot be initialized"); + + #[cfg(feature = "asynchronous")] + runtime.block_on(async { custom_entities_async(DATA).await })?; + + Ok(()) +} diff --git a/examples/issue68.rs b/examples/issue68.rs index d738ed84..c90ed684 100644 --- a/examples/issue68.rs +++ b/examples/issue68.rs @@ -1,6 +1,8 @@ #![allow(unused)] use quick_xml::events::Event; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; use quick_xml::Reader; use std::io::Read; #[cfg(feature = "asynchronous")] @@ -55,25 +57,26 @@ impl Response { } } -fn parse_report(xml_data: &str) -> Vec { +#[derive(Clone, Copy)] +enum State { + Root, + MultiStatus, + Response, + Success, + Error, +} + +#[cfg(feature = "asynchronous")] +async fn parse_report_async(xml_data: &str) -> Vec { let result = Vec::::new(); - let mut reader = Reader::from_str(xml_data); + let mut reader = AsyncReader::from_str(xml_data); reader.trim_text(true); let mut count = 0; let mut buf = Vec::new(); let mut ns_buffer = Vec::new(); - #[derive(Clone, Copy)] - enum State { - Root, - MultiStatus, - Response, - Success, - Error, - }; - let mut responses = Vec::::new(); let mut current_response = Response::new(); let mut current_prop = Prop::new(); @@ -81,18 +84,60 @@ fn parse_report(xml_data: &str) -> Vec { let mut depth = 0; let mut state = State::MultiStatus; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(feature = "asynchronous")] - let event = runtime - .block_on(async { reader.read_namespaced_event(&mut buf, &mut ns_buffer).await }); + match reader.read_namespaced_event(&mut buf, &mut ns_buffer).await { + Ok((namespace_value, Event::Start(e))) => { + let namespace_value = namespace_value.unwrap_or_default(); + match (depth, state, namespace_value, e.local_name()) { + (0, State::Root, b"DAV:", b"multistatus") => state = State::MultiStatus, + (1, State::MultiStatus, b"DAV:", b"response") => { + state = State::Response; + current_response = Response::new(); + } + (2, State::Response, b"DAV:", b"href") => { + current_response.href = e.unescape_and_decode(&reader).unwrap(); + } + _ => {} + } + depth += 1; + } + Ok((namespace_value, Event::End(e))) => { + let namespace_value = namespace_value.unwrap_or_default(); + let local_name = e.local_name(); + match (depth, state, &*namespace_value, local_name) { + (1, State::MultiStatus, b"DAV:", b"multistatus") => state = State::Root, + (2, State::MultiStatus, b"DAV:", b"multistatus") => state = State::MultiStatus, + _ => {} + } + depth -= 1; + } + Ok((_, Event::Eof)) => break, + Err(e) => break, + _ => (), + } + } + result +} + +fn parse_report(xml_data: &str) -> Vec { + let result = Vec::::new(); - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer); + let mut reader = Reader::from_str(xml_data); + reader.trim_text(true); - match event { + let mut count = 0; + let mut buf = Vec::new(); + let mut ns_buffer = Vec::new(); + + let mut responses = Vec::::new(); + let mut current_response = Response::new(); + let mut current_prop = Prop::new(); + + let mut depth = 0; + let mut state = State::MultiStatus; + + loop { + match reader.read_namespaced_event(&mut buf, &mut ns_buffer) { Ok((namespace_value, Event::Start(e))) => { let namespace_value = namespace_value.unwrap_or_default(); match (depth, state, namespace_value, e.local_name()) { @@ -148,4 +193,10 @@ fn main() { "#; parse_report(test_data); + + #[cfg(feature = "asynchronous")] + let runtime = Runtime::new().expect("Runtime cannot be initialized"); + + #[cfg(feature = "asynchronous")] + runtime.block_on(async { parse_report_async(test_data).await }); } diff --git a/examples/nested_readers.rs b/examples/nested_readers.rs index 5dd1dbbc..6a415e1f 100644 --- a/examples/nested_readers.rs +++ b/examples/nested_readers.rs @@ -1,4 +1,6 @@ use quick_xml::events::Event; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; use quick_xml::Reader; #[cfg(feature = "asynchronous")] use tokio::runtime::Runtime; @@ -10,34 +12,18 @@ struct TableStat { index: u8, rows: Vec>, } -// demonstrate how to nest readers -// This is useful for when you need to traverse -// a few levels of a document to extract things. -fn main() -> Result<(), quick_xml::Error> { + +fn nest_readers() -> Result<(), quick_xml::Error> { let mut buf = Vec::new(); // buffer for nested reader let mut skip_buf = Vec::new(); let mut count = 0; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let mut reader = - runtime.block_on(async { Reader::from_file("tests/documents/document.xml").await })?; - - #[cfg(not(feature = "asynchronous"))] let mut reader = Reader::from_file("tests/documents/document.xml")?; let mut found_tables = Vec::new(); loop { - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await })?; - - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf)?; - - match event { + match reader.read_event(&mut buf)? { Event::Start(element) => match element.name() { b"w:tbl" => { count += 1; @@ -51,14 +37,74 @@ fn main() -> Result<(), quick_xml::Error> { loop { skip_buf.clear(); - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { reader.read_event(&mut skip_buf).await })?; + match reader.read_event(&mut skip_buf)? { + Event::Start(element) => match element.name() { + b"w:tr" => { + stats.rows.push(vec![]); + row_index = stats.rows.len() - 1; + } + b"w:tc" => { + stats.rows[row_index] + .push(String::from_utf8(element.name().to_vec()).unwrap()); + } + _ => {} + }, + Event::End(element) => { + if element.name() == b"w:tbl" { + found_tables.push(stats); + break; + } + } + _ => {} + } + } + } + _ => {} + }, + Event::Eof => break, + _ => {} + } + buf.clear(); + } + assert_eq!(found_tables.len(), 2); + // pretty print the table + println!("{:#?}", found_tables); + assert_eq!(found_tables[0].rows.len(), 2); + assert_eq!(found_tables[0].rows[0].len(), 4); + assert_eq!(found_tables[0].rows[1].len(), 4); - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut skip_buf)?; + assert_eq!(found_tables[1].rows.len(), 2); + assert_eq!(found_tables[1].rows[0].len(), 4); + assert_eq!(found_tables[1].rows[1].len(), 4); + Ok(()) +} + +#[cfg(feature = "asynchronous")] +async fn nest_readers_async() -> Result<(), quick_xml::Error> { + let mut buf = Vec::new(); + // buffer for nested reader + let mut skip_buf = Vec::new(); + let mut count = 0; + + let mut reader = AsyncReader::from_file("tests/documents/document.xml").await?; + + let mut found_tables = Vec::new(); + loop { + match reader.read_event(&mut buf).await? { + Event::Start(element) => match element.name() { + b"w:tbl" => { + count += 1; + let mut stats = TableStat { + index: count, + rows: vec![], + }; + // must define stateful variables + // outside the nested loop else they are overwritten + let mut row_index = 0; + loop { + skip_buf.clear(); - match event { + match reader.read_event(&mut skip_buf).await? { Event::Start(element) => match element.name() { b"w:tr" => { stats.rows.push(vec![]); @@ -99,3 +145,18 @@ fn main() -> Result<(), quick_xml::Error> { assert_eq!(found_tables[1].rows[1].len(), 4); Ok(()) } + +// demonstrate how to nest readers +// This is useful for when you need to traverse +// a few levels of a document to extract things. +fn main() -> Result<(), quick_xml::Error> { + #[cfg(feature = "asynchronous")] + let runtime = Runtime::new().expect("Runtime cannot be initialized"); + + #[cfg(feature = "asynchronous")] + runtime.block_on(async { nest_readers_async().await })?; + + nest_readers()?; + + Ok(()) +} diff --git a/examples/read_texts.rs b/examples/read_texts.rs index 227911cc..a8e2988c 100644 --- a/examples/read_texts.rs +++ b/examples/read_texts.rs @@ -1,40 +1,48 @@ +use quick_xml::events::Event; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; +use quick_xml::Reader; #[cfg(feature = "asynchronous")] use tokio::runtime::Runtime; -fn main() { - use quick_xml::events::Event; - use quick_xml::Reader; +#[cfg(feature = "asynchronous")] +async fn read_text_async(xml: &str) { + let mut reader = AsyncReader::from_str(xml); + reader.trim_text(true); - let xml = "text1text2\ - text3text4"; + let mut txt = Vec::new(); + let mut buf = Vec::new(); + loop { + match reader.read_event(&mut buf).await { + Ok(Event::Start(ref e)) if e.name() == b"tag2" => { + #[cfg(feature = "asynchronous")] + let text = reader + .read_text(b"tag2", &mut Vec::new()) + .await + .expect("Cannot decode text value"); + + txt.push(text); + println!("{:?}", txt); + } + Ok(Event::Eof) => break, // exits the loop when reaching end of file + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + _ => (), // There are several other `Event`s we do not consider here + } + buf.clear(); + } +} + +fn read_text(xml: &str) { let mut reader = Reader::from_str(xml); reader.trim_text(true); let mut txt = Vec::new(); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - match event { + match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) if e.name() == b"tag2" => { - #[cfg(feature = "asynchronous")] - let text = runtime.block_on(async { - reader - .read_text(b"tag2", &mut Vec::new()) - .await - .expect("Cannot decode text value") - }); - - #[cfg(not(feature = "asynchronous"))] let text = reader .read_text(b"tag2", &mut Vec::new()) .expect("Cannot decode text value"); @@ -49,3 +57,16 @@ fn main() { buf.clear(); } } + +fn main() { + let xml = "text1text2\ + text3text4"; + + read_text(xml); + + #[cfg(feature = "asynchronous")] + let runtime = Runtime::new().expect("Runtime cannot be initialized"); + + #[cfg(feature = "asynchronous")] + runtime.block_on(async { read_text_async(xml).await }); +} diff --git a/src/errors.rs b/src/errors.rs index 4b911208..6683c9fe 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -33,7 +33,7 @@ pub enum Error { /// Duplicate attribute DuplicatedAttribute(usize, usize), /// Escape error - Escape(crate::escape::EscapeError), + EscapeError(crate::escape::EscapeError), } impl From<::std::io::Error> for Error { @@ -101,7 +101,7 @@ impl std::fmt::Display for Error { Duplicate attribute at position {1} and {0}", pos1, pos2 ), - Error::Escape(e) => write!(f, "{}", e), + Error::EscapeError(e) => write!(f, "{}", e), } } } @@ -111,7 +111,7 @@ impl std::error::Error for Error { match self { Error::Io(e) => Some(e), Error::Utf8(e) => Some(e), - Error::Escape(e) => Some(e), + Error::EscapeError(e) => Some(e), _ => None, } } diff --git a/src/escapei.rs b/src/escapei.rs index 322c1f90..ccbb7476 100644 --- a/src/escapei.rs +++ b/src/escapei.rs @@ -119,7 +119,7 @@ pub fn unescape_with<'a>( } /// Unescape a `&[u8]` and replaces all xml escaped characters ('&...;') into their corresponding -/// value, using an optional dictionnary of custom entities. +/// value, using an optional dictionary of custom entities. /// /// # Pre-condition /// diff --git a/src/events/attributes.rs b/src/events/attributes.rs index 93636f4e..e2327a3f 100644 --- a/src/events/attributes.rs +++ b/src/events/attributes.rs @@ -122,7 +122,7 @@ impl<'a> Attribute<'a> { &self, custom_entities: Option<&HashMap, Vec>>, ) -> Result> { - do_unescape(&*self.value, custom_entities).map_err(Error::Escape) + do_unescape(&*self.value, custom_entities).map_err(Error::EscapeError) } /// Decode then unescapes the value @@ -169,8 +169,7 @@ impl<'a> Attribute<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self.value); - let unescaped = - do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -181,7 +180,7 @@ impl<'a> Attribute<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self.value)?; - let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::Escape)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -256,8 +255,7 @@ impl<'a> Attribute<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode_without_bom(&*self.value); - let unescaped = - do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -268,7 +266,7 @@ impl<'a> Attribute<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode_without_bom(&*self.value)?; - let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::Escape)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } } @@ -349,7 +347,7 @@ impl<'a> Iterator for Attributes<'a> { return Some(Ok(Attribute { key: &self.bytes[$key], value: Cow::Borrowed(&self.bytes[$val]), - })); + })) }; } diff --git a/src/events/mod.rs b/src/events/mod.rs index 9ee69036..b8b062c9 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -187,7 +187,7 @@ impl<'a> BytesStart<'a> { &'s self, custom_entities: Option<&HashMap, Vec>>, ) -> Result> { - do_unescape(&*self.buf, custom_entities).map_err(Error::Escape) + do_unescape(&*self.buf, custom_entities).map_err(Error::EscapeError) } /// Returns an iterator over the attributes of this tag. @@ -269,8 +269,7 @@ impl<'a> BytesStart<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self); - let unescaped = - do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -282,7 +281,7 @@ impl<'a> BytesStart<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self)?; - let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::Escape)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -598,7 +597,7 @@ impl<'a> BytesText<'a> { &'s self, custom_entities: Option<&HashMap, Vec>>, ) -> Result> { - do_unescape(self, custom_entities).map_err(Error::Escape) + do_unescape(self, custom_entities).map_err(Error::EscapeError) } /// helper method to unescape then decode self using the reader encoding @@ -672,8 +671,7 @@ impl<'a> BytesText<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode_without_bom(&*self); - let unescaped = - do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -684,7 +682,7 @@ impl<'a> BytesText<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode_without_bom(&*self)?; - let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::Escape)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -723,8 +721,7 @@ impl<'a> BytesText<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self); - let unescaped = - do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -735,7 +732,7 @@ impl<'a> BytesText<'a> { custom_entities: Option<&HashMap, Vec>>, ) -> Result { let decoded = reader.decode(&*self)?; - let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::Escape)?; + let unescaped = do_unescape(decoded.as_bytes(), custom_entities).map_err(Error::EscapeError)?; String::from_utf8(unescaped.into_owned()).map_err(|e| Error::Utf8(e.utf8_error())) } @@ -856,8 +853,6 @@ impl<'a> AsRef> for Event<'a> { #[cfg(test)] mod test { use super::*; - #[cfg(feature = "asynchronous")] - use tokio::runtime::Runtime; #[test] fn local_name() { @@ -872,18 +867,7 @@ mod test { let mut buf = Vec::new(); let mut parsed_local_names = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { - rdr.read_event(&mut buf) - .await - .expect("unable to read xml event") - }); - - #[cfg(not(feature = "asynchronous"))] let event = rdr.read_event(&mut buf).expect("unable to read xml event"); match event { @@ -912,6 +896,52 @@ mod test { assert_eq!(parsed_local_names[7], "bus:baz".to_string()); } + #[cfg(feature = "asynchronous")] + #[tokio::test] + async fn local_name_async() { + use std::str::from_utf8; + let xml = r#" + foobusbar + foobusbar + <:foo attr='bar'>foobusbar + foobusbar + "#; + let mut rdr = crate::AsyncReader::from_str(xml); + let mut buf = Vec::new(); + let mut parsed_local_names = Vec::new(); + + loop { + let event = rdr + .read_event(&mut buf) + .await + .expect("unable to read xml event"); + + match event { + Event::Start(ref e) => parsed_local_names.push( + from_utf8(e.local_name()) + .expect("unable to build str from local_name") + .to_string(), + ), + Event::End(ref e) => parsed_local_names.push( + from_utf8(e.local_name()) + .expect("unable to build str from local_name") + .to_string(), + ), + Event::Eof => break, + _ => {} + } + } + + assert_eq!(parsed_local_names[0], "bus".to_string()); + assert_eq!(parsed_local_names[1], "bus".to_string()); + assert_eq!(parsed_local_names[2], "".to_string()); + assert_eq!(parsed_local_names[3], "".to_string()); + assert_eq!(parsed_local_names[4], "foo".to_string()); + assert_eq!(parsed_local_names[5], "foo".to_string()); + assert_eq!(parsed_local_names[6], "bus:baz".to_string()); + assert_eq!(parsed_local_names[7], "bus:baz".to_string()); + } + #[test] fn bytestart_create() { let b = BytesStart::owned_name("test"); diff --git a/src/lib.rs b/src/lib.rs index 5fd73a15..c1faf89f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ //! //! ### Reader //! -//! ```ignore +//! ```rust //! use quick_xml::Reader; //! use quick_xml::events::Event; //! @@ -24,8 +24,8 @@ //! reader.trim_text(true); //! //! let mut count = 0; -//! let mut txt: Vec = Vec::new(); -//! let mut buf: Vec = Vec::new(); +//! let mut txt = Vec::new(); +//! let mut buf = Vec::new(); //! //! // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s) //! loop { @@ -57,7 +57,7 @@ //! //! ### Writer //! -//! ```ignore +//! ```rust //! use quick_xml::Writer; //! use quick_xml::events::{Event, BytesEnd, BytesStart}; //! use quick_xml::Reader; @@ -68,7 +68,7 @@ //! let mut reader = Reader::from_str(xml); //! reader.trim_text(true); //! let mut writer = Writer::new(Cursor::new(Vec::new())); -//! let mut buf: Vec = Vec::new(); +//! let mut buf = Vec::new(); //! loop { //! match reader.read_event(&mut buf) { //! Ok(Event::Start(ref e)) if e.name() == b"this_tag" => { @@ -132,7 +132,6 @@ pub mod reader; pub use errors::serialize::DeError; pub use errors::{Error, Result}; #[cfg(feature = "asynchronous")] -pub use reader::asynchronous::Reader; -#[cfg(not(feature = "asynchronous"))] +pub use reader::asynchronous::AsyncReader; pub use reader::sync::Reader; pub use writer::Writer; diff --git a/src/reader/asynchronous.rs b/src/reader/asynchronous.rs index bba8a31f..089ab093 100644 --- a/src/reader/asynchronous.rs +++ b/src/reader/asynchronous.rs @@ -1,4 +1,4 @@ -//! A module to handle the async `Reader` +//! A module to handle the `AsyncReader` use async_recursion::async_recursion; #[cfg(feature = "encoding")] @@ -18,7 +18,7 @@ use crate::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event}; use super::{is_whitespace, Decode, Decoder, NamespaceBufferIndex, TagState}; -impl Decode for Reader { +impl Decode for AsyncReader { #[cfg(feature = "encoding")] fn read_encoding(&self) -> &'static Encoding { self.encoding @@ -47,7 +47,7 @@ impl Decode for Reader { /// # Examples /// /// ``` -/// use quick_xml::Reader; +/// use quick_xml::AsyncReader; /// use quick_xml::events::Event; /// /// #[tokio::main] @@ -56,7 +56,7 @@ impl Decode for Reader { /// Test /// Test 2 /// "#; -/// let mut reader = Reader::from_str(xml); +/// let mut reader = AsyncReader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut txt = Vec::new(); @@ -81,7 +81,7 @@ impl Decode for Reader { /// } /// } /// ``` -pub struct Reader { +pub struct AsyncReader { /// reader reader: B, /// current buffer position, useful for debuging errors @@ -113,10 +113,10 @@ pub struct Reader { is_encoding_set: bool, } -impl Reader { +impl AsyncReader { /// Creates a `Reader` that reads from a reader implementing `BufRead`. - pub fn from_reader(reader: B) -> Reader { - Reader { + pub fn from_reader(reader: B) -> AsyncReader { + AsyncReader { reader, opened_buffer: Vec::new(), opened_starts: Vec::new(), @@ -146,7 +146,7 @@ impl Reader { /// [`Empty`]: events/enum.Event.html#variant.Empty /// [`Start`]: events/enum.Event.html#variant.Start /// [`End`]: events/enum.Event.html#variant.End - pub fn expand_empty_elements(&mut self, val: bool) -> &mut Reader { + pub fn expand_empty_elements(&mut self, val: bool) -> &mut AsyncReader { self.expand_empty_elements = val; self } @@ -159,7 +159,7 @@ impl Reader { /// (`false` by default) /// /// [`Text`]: events/enum.Event.html#variant.Text - pub fn trim_text(&mut self, val: bool) -> &mut Reader { + pub fn trim_text(&mut self, val: bool) -> &mut AsyncReader { self.trim_text = val; self } @@ -175,7 +175,7 @@ impl Reader { /// (`true` by default) /// /// [`End`]: events/enum.Event.html#variant.End - pub fn trim_markup_names_in_closing_tags(&mut self, val: bool) -> &mut Reader { + pub fn trim_markup_names_in_closing_tags(&mut self, val: bool) -> &mut AsyncReader { self.trim_markup_names_in_closing_tags = val; self } @@ -193,7 +193,7 @@ impl Reader { /// (`true` by default) /// /// [`End`]: events/enum.Event.html#variant.End - pub fn check_end_names(&mut self, val: bool) -> &mut Reader { + pub fn check_end_names(&mut self, val: bool) -> &mut AsyncReader { self.check_end_names = val; self } @@ -208,7 +208,7 @@ impl Reader { /// (`false` by default) /// /// [`Comment`]: events/enum.Event.html#variant.Comment - pub fn check_comments(&mut self, val: bool) -> &mut Reader { + pub fn check_comments(&mut self, val: bool) -> &mut AsyncReader { self.check_comments = val; self } @@ -392,7 +392,7 @@ impl Reader { &buf[buf_start + 8..buf.len() - 2], ))) } - b"DOCTYPE" => { + x if x.eq_ignore_ascii_case(b"DOCTYPE") => { let mut count = buf.iter().skip(buf_start).filter(|&&b| b == b'<').count(); while count > 0 { buf.push(b'>'); @@ -516,7 +516,7 @@ impl Reader { /// # Examples /// /// ``` - /// use quick_xml::Reader; + /// use quick_xml::AsyncReader; /// use quick_xml::events::Event; /// /// #[tokio::main] @@ -525,7 +525,7 @@ impl Reader { /// Test /// Test 2 /// "#; - /// let mut reader = Reader::from_str(xml); + /// let mut reader = AsyncReader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut buf = Vec::new(); @@ -599,7 +599,7 @@ impl Reader { /// /// ``` /// use std::str::from_utf8; - /// use quick_xml::Reader; + /// use quick_xml::AsyncReader; /// use quick_xml::events::Event; /// /// #[tokio::main] @@ -608,7 +608,7 @@ impl Reader { /// Test /// Test 2 /// "#; - /// let mut reader = Reader::from_str(xml); + /// let mut reader = AsyncReader::from_str(xml); /// reader.trim_text(true); /// let mut count = 0; /// let mut buf = Vec::new(); @@ -685,7 +685,7 @@ impl Reader { } } - /// Returns the `Reader`s encoding. + /// Returns the `AsyncReader`s encoding. /// /// The used encoding may change after parsing the XML declaration. /// @@ -748,12 +748,12 @@ impl Reader { /// # Examples /// /// ``` - /// use quick_xml::Reader; + /// use quick_xml::AsyncReader; /// use quick_xml::events::Event; /// /// #[tokio::main] /// async fn main() { - /// let mut xml = Reader::from_reader(b" + /// let mut xml = AsyncReader::from_reader(b" /// <b> /// /// " as &[u8]); @@ -785,7 +785,7 @@ impl Reader { s } - /// Consumes `Reader` returning the underlying reader + /// Consumes `AsyncReader` returning the underlying reader /// /// Can be used to compute line and column of a parsing error position /// @@ -793,10 +793,10 @@ impl Reader { /// /// ```ignore /// use std::{str, io::Cursor}; - /// use quick_xml::Reader; + /// use quick_xml::AsyncReader; /// use quick_xml::events::Event; /// - /// fn into_line_and_column(reader: Reader>) -> (usize, usize) { + /// fn into_line_and_column(reader: AsyncReader>) -> (usize, usize) { /// let end_pos = reader.buffer_position(); /// let mut cursor = reader.into_underlying_reader(); /// let s = String::from_utf8(cursor.into_inner()[0..end_pos].to_owned()) @@ -820,7 +820,7 @@ impl Reader { /// Test /// Test 2 /// "#; - /// let mut reader = Reader::from_reader(Cursor::new(xml.as_bytes())); + /// let mut reader = AsyncReader::from_reader(Cursor::new(xml.as_bytes())); /// let mut buf = Vec::new(); /// /// loop { @@ -845,19 +845,19 @@ impl Reader { } } -impl Reader> { +impl AsyncReader> { /// Creates an XML reader from a file path. - pub async fn from_file>(path: P) -> Result>> { + pub async fn from_file>(path: P) -> Result>> { let file = File::open(path).await.map_err(Error::Io)?; let reader = BufReader::new(file); - Ok(Reader::from_reader(reader)) + Ok(AsyncReader::from_reader(reader)) } } -impl<'a> Reader<&'a [u8]> { +impl<'a> AsyncReader<&'a [u8]> { /// Creates an XML reader from a string slice. - pub fn from_str(s: &'a str) -> Reader<&'a [u8]> { - Reader::from_reader(s.as_bytes()) + pub fn from_str(s: &'a str) -> AsyncReader<&'a [u8]> { + AsyncReader::from_reader(s.as_bytes()) } } diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 748feb6c..7619db57 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -1,9 +1,10 @@ //! A module to handle `Reader` -use crate::events::{attributes::Attribute, BytesStart}; - #[cfg(not(feature = "encoding"))] -use crate::errors::{Error, Result}; +use crate::errors::Error; +#[cfg(not(feature = "encoding"))] +use crate::errors::Result; +use crate::events::{attributes::Attribute, BytesStart}; #[cfg(not(feature = "encoding"))] use std::str::from_utf8; @@ -14,7 +15,6 @@ use std::borrow::Cow; #[cfg(feature = "asynchronous")] pub mod asynchronous; -#[cfg(not(feature = "asynchronous"))] pub mod sync; /// Trait for decoding, which is shared by the sync and async `Reader` diff --git a/src/writer.rs b/src/writer.rs index 0b6b5f0f..7daa6e86 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -11,7 +11,7 @@ use crate::events::Event; /// /// # Examples /// -/// ```ignore +/// ```rust /// extern crate quick_xml; /// fn main() { /// use quick_xml::{Reader, Writer}; diff --git a/tests/documents/html5.txt b/tests/documents/html5.txt index 5f0d6bc7..1ad39c09 100644 --- a/tests/documents/html5.txt +++ b/tests/documents/html5.txt @@ -4,5 +4,7 @@ Characters( StartElement(a, attr-error: error while parsing attribute at position 7: Attribute value must start with a quote.) Characters(Hey) EndElement(a) -InvalidUtf8([10, 38, 110, 98, 115, 112, 59, 10]; invalid utf-8 sequence of 1 bytes from index 1) +Characters( +  +) EndDocument diff --git a/tests/serde-migrated.rs b/tests/serde-migrated.rs index f6360c7c..62b1a384 100644 --- a/tests/serde-migrated.rs +++ b/tests/serde-migrated.rs @@ -953,12 +953,12 @@ fn futile2() { #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Object { field: Option, - }; + } #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Stuff { stuff_field: Option, - }; + } test_parse_ok(&[ ( diff --git a/tests/test.rs b/tests/test.rs index 301292a4..495b0ec3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -5,8 +5,6 @@ use quick_xml::Reader; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::io::Cursor; -#[cfg(feature = "asynchronous")] -use tokio::runtime::Runtime; #[test] fn test_sample() { @@ -15,17 +13,8 @@ fn test_sample() { let mut r = Reader::from_reader(src); let mut count = 0; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event.unwrap() { + match r.read_event(&mut buf).unwrap() { Start(_) => count += 1, Decl(e) => println!("{:?}", e.version()), Eof => break, @@ -42,17 +31,9 @@ fn test_sample_async() { let mut buf = Vec::new(); let mut r = Reader::from_reader(src); let mut count = 0; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event.unwrap() { + match r.read_event(&mut buf).unwrap() { Start(_) => count += 1, Decl(e) => println!("{:?}", e.version()), Eof => break, @@ -69,16 +50,8 @@ fn test_attributes_empty() { let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - match event { + match r.read_event(&mut buf) { Ok(Empty(e)) => { let mut atts = e.attributes(); match atts.next() { @@ -104,23 +77,14 @@ fn test_attributes_empty() { } } -#[cfg(not(feature = "asynchronous"))] #[test] fn test_attribute_equal() { let src = b""; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Empty(e)) => { let mut atts = e.attributes(); match atts.next() { @@ -139,24 +103,15 @@ fn test_attribute_equal() { } } -#[cfg(not(feature = "asynchronous"))] #[test] fn test_comment_starting_with_gt() { let src = b"-->"; let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Comment(ref e)) if &**e == b">" => break, Ok(Eof) => panic!("Expecting Comment"), _ => (), @@ -175,16 +130,8 @@ fn test_attributes_empty_ns() { r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - let e = match event { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Empty(e))) => e, e => panic!("Expecting Empty event, got {:?}", e), }; @@ -220,7 +167,6 @@ fn test_attributes_empty_ns() { /// Single empty element with qualified attributes. /// Empty element expansion: enabled /// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`. -#[cfg(not(feature = "asynchronous"))] #[test] fn test_attributes_empty_ns_expanded() { let src = b""; @@ -229,18 +175,9 @@ fn test_attributes_empty_ns_expanded() { r.trim_text(true).expand_empty_elements(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - let e = match event { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Start(e))) => e, e => panic!("Expecting Empty event, got {:?}", e), }; @@ -273,13 +210,7 @@ fn test_attributes_empty_ns_expanded() { } } - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, End(e))) => assert_eq!(b"a", e.name()), e => panic!("Expecting End event, got {:?}", e), } @@ -293,19 +224,10 @@ fn test_default_ns_shadowing_empty() { r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); // { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); @@ -316,14 +238,7 @@ fn test_default_ns_shadowing_empty() { // { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - let e = match event { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Empty(e))) => { assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i"); assert_eq!(e.name(), b"e"); @@ -354,14 +269,7 @@ fn test_default_ns_shadowing_empty() { } // - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); @@ -370,7 +278,6 @@ fn test_default_ns_shadowing_empty() { } } -#[cfg(not(feature = "asynchronous"))] #[test] fn test_default_ns_shadowing_expanded() { let src = b""; @@ -379,19 +286,10 @@ fn test_default_ns_shadowing_expanded() { r.trim_text(true).expand_empty_elements(true); let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); // { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); @@ -403,14 +301,7 @@ fn test_default_ns_shadowing_expanded() { // { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - let e = match event { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), Start(e))) => { assert_eq!(&ns[..], b"urn:example:i"); assert_eq!(e.name(), b"e"); @@ -440,13 +331,7 @@ fn test_default_ns_shadowing_expanded() { } // virtual - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:i"); assert_eq!(e.name(), b"e"); @@ -455,13 +340,7 @@ fn test_default_ns_shadowing_expanded() { } // - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(ns), End(e))) => { assert_eq!(&ns[..], b"urn:example:o"); assert_eq!(e.name(), b"e"); @@ -477,17 +356,9 @@ fn test_koi8_r_encoding() { let mut r = Reader::from_reader(src as &[u8]); r.trim_text(true).expand_empty_elements(false); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Text(e)) => { e.unescape_and_decode(&r).unwrap(); } @@ -505,24 +376,15 @@ fn fuzz_53() { let cursor = Cursor::new(data); let mut reader = Reader::from_reader(cursor); let mut buf = vec![]; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(quick_xml::events::Event::Eof) | Err(..) => break, _ => buf.clear(), } } } -#[cfg(not(feature = "asynchronous"))] #[test] fn test_issue94() { let data = br#" @@ -531,17 +393,9 @@ fn test_issue94() { let mut reader = Reader::from_reader(&data[..]); reader.trim_text(true); let mut buf = vec![]; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(quick_xml::events::Event::Eof) | Err(..) => break, _ => buf.clear(), } @@ -557,17 +411,9 @@ fn fuzz_101() { let cursor = Cursor::new(data); let mut reader = Reader::from_reader(cursor); let mut buf = vec![]; - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(Start(ref e)) | Ok(Empty(ref e)) => { if e.unescaped().is_err() { break; @@ -598,30 +444,15 @@ fn test_default_namespace() { // let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, Start(_))) = event { + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { panic!("expecting outer start element with no namespace"); } // { - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = - runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - let event = match event { + let event = match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(b"www1"), Start(event))) => event, Ok((Some(_), Start(_))) => panic!("expecting namespace to resolve to 'www1'"), _ => panic!("expecting namespace resolution"), @@ -639,13 +470,7 @@ fn test_default_namespace() { } // - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((Some(b"www1"), End(_))) => (), Ok((Some(_), End(_))) => panic!("expecting namespace to resolve to 'www1'"), _ => panic!("expecting namespace resolution"), @@ -653,14 +478,7 @@ fn test_default_namespace() { // very important: a should not be in any namespace. The default namespace only applies to // the sub-document it is defined on. - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, End(_))) = event { + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { panic!("expecting outer end element with no namespace"); } diff --git a/tests/test_async.rs b/tests/test_async.rs new file mode 100644 index 00000000..c1b6d975 --- /dev/null +++ b/tests/test_async.rs @@ -0,0 +1,1187 @@ +#[cfg(feature = "asynchronous")] +use quick_xml::events::attributes::Attribute; +#[cfg(feature = "asynchronous")] +use quick_xml::events::Event::*; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; +#[cfg(feature = "asynchronous")] +use std::borrow::Cow; +#[cfg(feature = "asynchronous")] +use std::io::Cursor; + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_sample() { + let src: &[u8] = include_bytes!("sample_rss.xml"); + let mut buf = Vec::new(); + let mut r = AsyncReader::from_reader(src); + let mut count = 0; + + loop { + match r.read_event(&mut buf).await.unwrap() { + Start(_) => count += 1, + Decl(e) => println!("{:?}", e.version()), + Eof => break, + _ => (), + } + buf.clear(); + } + println!("{}", count); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_sample_async() { + let src: &[u8] = include_bytes!("sample_rss.xml"); + let mut buf = Vec::new(); + let mut r = AsyncReader::from_reader(src); + let mut count = 0; + + loop { + match r.read_event(&mut buf).await.unwrap() { + Start(_) => count += 1, + Decl(e) => println!("{:?}", e.version()), + Eof => break, + _ => (), + } + buf.clear(); + } + println!("{}", count); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_attributes_empty() { + let src = b""; + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Empty(e)) => { + let mut atts = e.attributes(); + match atts.next() { + Some(Ok(Attribute { + key: b"att1", + value: Cow::Borrowed(b"a"), + })) => (), + e => panic!("Expecting att1='a' attribute, found {:?}", e), + } + match atts.next() { + Some(Ok(Attribute { + key: b"att2", + value: Cow::Borrowed(b"b"), + })) => (), + e => panic!("Expecting att2='b' attribute, found {:?}", e), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } + } + e => panic!("Expecting Empty event, got {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_attribute_equal() { + let src = b""; + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Empty(e)) => { + let mut atts = e.attributes(); + match atts.next() { + Some(Ok(Attribute { + key: b"att1", + value: Cow::Borrowed(b"a=b"), + })) => (), + e => panic!("Expecting att1=\"a=b\" attribute, found {:?}", e), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } + } + e => panic!("Expecting Empty event, got {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_comment_starting_with_gt() { + let src = b"-->"; + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + + loop { + match r.read_event(&mut buf).await { + Ok(Comment(ref e)) if &**e == b">" => break, + Ok(Eof) => panic!("Expecting Comment"), + _ => (), + } + } +} + +/// Single empty element with qualified attributes. +/// Empty element expansion: disabled +/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`. +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_attributes_empty_ns() { + let src = b""; + + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((None, Empty(e))) => e, + e => panic!("Expecting Empty event, got {:?}", e), + }; + + let mut atts = e + .attributes() + .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) + // we don't care about xmlns attributes for this test + .filter(|kv| !kv.key.starts_with(b"xmlns")) + .map(|Attribute { key: name, value }| { + let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); + (opt_ns, local_name, value) + }); + match atts.next() { + Some((None, b"att1", Cow::Borrowed(b"a"))) => (), + e => panic!("Expecting att1='a' attribute, found {:?}", e), + } + match atts.next() { + Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => { + assert_eq!(&ns[..], b"urn:example:r"); + } + e => panic!( + "Expecting {{urn:example:r}}att2='b' attribute, found {:?}", + e + ), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } +} + +/// Single empty element with qualified attributes. +/// Empty element expansion: enabled +/// The code path for namespace handling is slightly different for `Empty` vs. `Start+End`. +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_attributes_empty_ns_expanded() { + let src = b""; + + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(true); + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((None, Start(e))) => e, + e => panic!("Expecting Empty event, got {:?}", e), + }; + + let mut atts = e + .attributes() + .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) + // we don't care about xmlns attributes for this test + .filter(|kv| !kv.key.starts_with(b"xmlns")) + .map(|Attribute { key: name, value }| { + let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); + (opt_ns, local_name, value) + }); + match atts.next() { + Some((None, b"att1", Cow::Borrowed(b"a"))) => (), + e => panic!("Expecting att1='a' attribute, found {:?}", e), + } + match atts.next() { + Some((Some(ns), b"att2", Cow::Borrowed(b"b"))) => { + assert_eq!(&ns[..], b"urn:example:r"); + } + e => panic!( + "Expecting {{urn:example:r}}att2='b' attribute, found {:?}", + e + ), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } + } + + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((None, End(e))) => assert_eq!(b"a", e.name()), + e => panic!("Expecting End event, got {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_default_ns_shadowing_empty() { + let src = b""; + + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + // + { + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), Start(e))) => { + assert_eq!(&ns[..], b"urn:example:o"); + assert_eq!(e.name(), b"e"); + } + e => panic!("Expected Start event (), got {:?}", e), + } + } + + // + { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), Empty(e))) => { + assert_eq!(::std::str::from_utf8(ns).unwrap(), "urn:example:i"); + assert_eq!(e.name(), b"e"); + e + } + e => panic!("Expecting Empty event, got {:?}", e), + }; + + let mut atts = e + .attributes() + .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) + // we don't care about xmlns attributes for this test + .filter(|kv| !kv.key.starts_with(b"xmlns")) + .map(|Attribute { key: name, value }| { + let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); + (opt_ns, local_name, value) + }); + // the attribute should _not_ have a namespace name. The default namespace does not + // apply to attributes. + match atts.next() { + Some((None, b"att1", Cow::Borrowed(b"a"))) => (), + e => panic!("Expecting att1='a' attribute, found {:?}", e), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } + } + + // + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), End(e))) => { + assert_eq!(&ns[..], b"urn:example:o"); + assert_eq!(e.name(), b"e"); + } + e => panic!("Expected End event (), got {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_default_ns_shadowing_expanded() { + let src = b""; + + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(true); + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + // + { + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), Start(e))) => { + assert_eq!(&ns[..], b"urn:example:o"); + assert_eq!(e.name(), b"e"); + } + e => panic!("Expected Start event (), got {:?}", e), + } + } + buf.clear(); + + // + { + let e = match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), Start(e))) => { + assert_eq!(&ns[..], b"urn:example:i"); + assert_eq!(e.name(), b"e"); + e + } + e => panic!("Expecting Start event (), got {:?}", e), + }; + let mut atts = e + .attributes() + .map(|ar| ar.expect("Expecting attribute parsing to succeed.")) + // we don't care about xmlns attributes for this test + .filter(|kv| !kv.key.starts_with(b"xmlns")) + .map(|Attribute { key: name, value }| { + let (opt_ns, local_name) = r.attribute_namespace(name, &ns_buf); + (opt_ns, local_name, value) + }); + // the attribute should _not_ have a namespace name. The default namespace does not + // apply to attributes. + match atts.next() { + Some((None, b"att1", Cow::Borrowed(b"a"))) => (), + e => panic!("Expecting att1='a' attribute, found {:?}", e), + } + match atts.next() { + None => (), + e => panic!("Expecting None, found {:?}", e), + } + } + + // virtual + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), End(e))) => { + assert_eq!(&ns[..], b"urn:example:i"); + assert_eq!(e.name(), b"e"); + } + e => panic!("Expected End event (), got {:?}", e), + } + + // + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(ns), End(e))) => { + assert_eq!(&ns[..], b"urn:example:o"); + assert_eq!(e.name(), b"e"); + } + e => panic!("Expected End event (), got {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +#[cfg(feature = "encoding_rs")] +async fn test_koi8_r_encoding() { + let src: &[u8] = include_bytes!("documents/opennews_all.rss"); + let mut r = AsyncReader::from_reader(src as &[u8]); + r.trim_text(true).expand_empty_elements(false); + let mut buf = Vec::new(); + + loop { + match r.read_event(&mut buf).await { + Ok(Text(e)) => { + e.unescape_and_decode(&r).unwrap(); + } + Ok(Eof) => break, + _ => (), + } + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn fuzz_53() { + let data: &[u8] = b"\xe9\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\n(\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\ +\x00<>\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<<\x00\x00\x00"; + let cursor = Cursor::new(data); + let mut reader = AsyncReader::from_reader(cursor); + let mut buf = vec![]; + + loop { + match reader.read_event(&mut buf).await { + Ok(quick_xml::events::Event::Eof) | Err(..) => break, + _ => buf.clear(), + } + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_issue94() { + let data = br#" + +"#; + let mut reader = AsyncReader::from_reader(&data[..]); + reader.trim_text(true); + let mut buf = vec![]; + + loop { + match reader.read_event(&mut buf).await { + Ok(quick_xml::events::Event::Eof) | Err(..) => break, + _ => buf.clear(), + } + buf.clear(); + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn fuzz_101() { + let data: &[u8] = b"\x00\x00<\x00\x00\x0a>�?#\x0a413518\ + #\x0a\x0a\x0a;<:<)(<:\x0a\x0a\x0a\x0a;<:\x0a\x0a\ + <:\x0a\x0a\x0a\x0a\x0a<\x00*\x00\x00\x00\x00"; + let cursor = Cursor::new(data); + let mut reader = AsyncReader::from_reader(cursor); + let mut buf = vec![]; + + loop { + match reader.read_event(&mut buf).await { + Ok(Start(ref e)) | Ok(Empty(ref e)) => { + if e.unescaped().is_err() { + break; + } + for a in e.attributes() { + if a.ok().map_or(true, |a| a.unescaped_value().is_err()) { + break; + } + } + } + Ok(Text(ref e)) => { + if e.unescaped().is_err() { + break; + } + } + Ok(Eof) | Err(..) => break, + _ => (), + } + buf.clear(); + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_default_namespace() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + + // + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + panic!("expecting outer start element with no namespace"); + } + + // + { + let event = match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(b"www1"), Start(event))) => event, + Ok((Some(_), Start(_))) => panic!("expecting namespace to resolve to 'www1'"), + _ => panic!("expecting namespace resolution"), + }; + + //We check if the resolve_namespace method also work properly + match r.event_namespace(event.name(), &mut ns_buf) { + (Some(b"www1"), _) => (), + (Some(_), _) => panic!("expecting namespace to resolve to 'www1'"), + ns => panic!( + "expecting namespace resolution by the resolve_nemespace method {:?}", + ns + ), + } + } + + // + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((Some(b"www1"), End(_))) => (), + Ok((Some(_), End(_))) => panic!("expecting namespace to resolve to 'www1'"), + _ => panic!("expecting namespace resolution"), + } + + // very important: a should not be in any namespace. The default namespace only applies to + // the sub-document it is defined on. + + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + panic!("expecting outer end element with no namespace"); + } +} + +// #[cfg(feature = "asynchronous")] +// #[cfg(feature = "serialize")] +// #[tokio::test] +// async fn line_score() { +// #[derive(Debug, PartialEq, Deserialize)] +// struct LineScoreData { +// game_pk: u32, +// game_type: char, +// venue: String, +// venue_w_chan_loc: String, +// venue_id: u32, +// time: String, +// time_zone: String, +// ampm: String, +// home_team_id: u32, +// home_team_city: String, +// home_team_name: String, +// home_league_id: u32, +// away_team_id: u32, +// away_team_city: String, +// away_team_name: String, +// away_league_id: u32, +// #[serde(rename = "linescore", skip_serializing)] +// innings: Vec, +// } +// #[derive(Debug, PartialEq, Deserialize)] +// struct LineScore { +// #[serde(rename = "away_inning_runs")] +// away_runs: u32, +// #[serde(rename = "home_inning_runs")] +// //needs to be an Option, since home team doesn't always bat. +// home_runs: Option, +// // Keeping the inning as a string, since we'll need it to construct URLs later +// inning: String, +// } + +// let res: LineScoreData = quick_xml::de::from_str(include_str!("linescore.xml")).unwrap(); + +// let expected = LineScoreData { +// game_pk: 239575, +// game_type: 'R', +// venue: "Generic".to_owned(), +// venue_w_chan_loc: "USNY0996".to_owned(), +// venue_id: 401, +// time: "Gm 2".to_owned(), +// time_zone: "ET".to_owned(), +// ampm: "AM".to_owned(), +// home_team_id: 611, +// home_team_city: "DSL Dodgers".to_owned(), +// home_team_name: "DSL Dodgers".to_owned(), +// home_league_id: 130, +// away_team_id: 604, +// away_team_city: "DSL Blue Jays1".to_owned(), +// away_team_name: "DSL Blue Jays1".to_owned(), +// away_league_id: 130, +// innings: vec![ +// LineScore { +// away_runs: 1, +// home_runs: Some(0), +// inning: "1".to_owned(), +// }, +// LineScore { +// away_runs: 0, +// home_runs: Some(0), +// inning: "2".to_owned(), +// }, +// LineScore { +// away_runs: 1, +// home_runs: Some(1), +// inning: "3".to_owned(), +// }, +// LineScore { +// away_runs: 2, +// home_runs: Some(0), +// inning: "4".to_owned(), +// }, +// LineScore { +// away_runs: 0, +// home_runs: Some(0), +// inning: "5".to_owned(), +// }, +// LineScore { +// away_runs: 0, +// home_runs: Some(0), +// inning: "6".to_owned(), +// }, +// LineScore { +// away_runs: 0, +// home_runs: Some(0), +// inning: "7".to_owned(), +// }, +// ], +// }; +// assert_eq!(res, expected); +// } + +// #[cfg(feature = "serialize")] +// #[test] +// fn players() { +// #[derive(PartialEq, Deserialize, Serialize, Debug)] +// struct Game { +// #[serde(rename = "team")] +// teams: Vec, +// //umpires: Umpires +// } + +// #[derive(PartialEq, Deserialize, Serialize, Debug)] +// struct Team { +// #[serde(rename = "type")] +// home_away: HomeAway, +// id: String, +// name: String, +// #[serde(rename = "player")] +// players: Vec, +// #[serde(rename = "coach")] +// coaches: Vec, +// } + +// #[derive(PartialEq, Deserialize, Serialize, Debug)] +// enum HomeAway { +// #[serde(rename = "home")] +// Home, +// #[serde(rename = "away")] +// Away, +// } + +// #[derive(PartialEq, Deserialize, Serialize, Debug, Clone)] +// struct Player { +// id: u32, +// #[serde(rename = "first")] +// name_first: String, +// #[serde(rename = "last")] +// name_last: String, +// game_position: Option, +// bat_order: Option, +// position: String, +// } + +// #[derive(PartialEq, Deserialize, Serialize, Debug)] +// struct Coach { +// position: String, +// #[serde(rename = "first")] +// name_first: String, +// #[serde(rename = "last")] +// name_last: String, +// id: u32, +// } + +// let res: Game = quick_xml::de::from_str(include_str!("players.xml")).unwrap(); + +// let expected = Game { +// teams: vec![ +// Team { +// home_away: HomeAway::Away, +// id: "CIN".to_owned(), +// name: "Cincinnati Reds".to_owned(), +// players: vec![ +// Player { +// id: 115135, +// name_first: "Ken".to_owned(), +// name_last: "Griffey".to_owned(), +// game_position: Some("RF".to_owned()), +// bat_order: Some(3), +// position: "RF".to_owned(), +// }, +// Player { +// id: 115608, +// name_first: "Scott".to_owned(), +// name_last: "Hatteberg".to_owned(), +// game_position: None, +// bat_order: None, +// position: "1B".to_owned(), +// }, +// Player { +// id: 118967, +// name_first: "Kent".to_owned(), +// name_last: "Mercker".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 136460, +// name_first: "Alex".to_owned(), +// name_last: "Gonzalez".to_owned(), +// game_position: None, +// bat_order: None, +// position: "SS".to_owned(), +// }, +// Player { +// id: 150020, +// name_first: "Jerry".to_owned(), +// name_last: "Hairston".to_owned(), +// game_position: None, +// bat_order: None, +// position: "SS".to_owned(), +// }, +// Player { +// id: 150188, +// name_first: "Francisco".to_owned(), +// name_last: "Cordero".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 150221, +// name_first: "Mike".to_owned(), +// name_last: "Lincoln".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 150319, +// name_first: "Josh".to_owned(), +// name_last: "Fogg".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 150472, +// name_first: "Ryan".to_owned(), +// name_last: "Freel".to_owned(), +// game_position: Some("LF".to_owned()), +// bat_order: Some(2), +// position: "CF".to_owned(), +// }, +// Player { +// id: 276520, +// name_first: "Bronson".to_owned(), +// name_last: "Arroyo".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 279571, +// name_first: "Matt".to_owned(), +// name_last: "Belisle".to_owned(), +// game_position: Some("P".to_owned()), +// bat_order: Some(9), +// position: "P".to_owned(), +// }, +// Player { +// id: 279913, +// name_first: "Corey".to_owned(), +// name_last: "Patterson".to_owned(), +// game_position: Some("CF".to_owned()), +// bat_order: Some(1), +// position: "CF".to_owned(), +// }, +// Player { +// id: 346793, +// name_first: "Jeremy".to_owned(), +// name_last: "Affeldt".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 408252, +// name_first: "Brandon".to_owned(), +// name_last: "Phillips".to_owned(), +// game_position: Some("2B".to_owned()), +// bat_order: Some(4), +// position: "2B".to_owned(), +// }, +// Player { +// id: 421685, +// name_first: "Aaron".to_owned(), +// name_last: "Harang".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 424325, +// name_first: "David".to_owned(), +// name_last: "Ross".to_owned(), +// game_position: Some("C".to_owned()), +// bat_order: Some(8), +// position: "C".to_owned(), +// }, +// Player { +// id: 429665, +// name_first: "Edwin".to_owned(), +// name_last: "Encarnacion".to_owned(), +// game_position: Some("3B".to_owned()), +// bat_order: Some(6), +// position: "3B".to_owned(), +// }, +// Player { +// id: 433898, +// name_first: "Jeff".to_owned(), +// name_last: "Keppinger".to_owned(), +// game_position: Some("SS".to_owned()), +// bat_order: Some(7), +// position: "SS".to_owned(), +// }, +// Player { +// id: 435538, +// name_first: "Bill".to_owned(), +// name_last: "Bray".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 440361, +// name_first: "Norris".to_owned(), +// name_last: "Hopper".to_owned(), +// game_position: None, +// bat_order: None, +// position: "O".to_owned(), +// }, +// Player { +// id: 450172, +// name_first: "Edinson".to_owned(), +// name_last: "Volquez".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 454537, +// name_first: "Jared".to_owned(), +// name_last: "Burton".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 455751, +// name_first: "Bobby".to_owned(), +// name_last: "Livingston".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 456501, +// name_first: "Johnny".to_owned(), +// name_last: "Cueto".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 458015, +// name_first: "Joey".to_owned(), +// name_last: "Votto".to_owned(), +// game_position: Some("1B".to_owned()), +// bat_order: Some(5), +// position: "1B".to_owned(), +// }, +// ], +// coaches: vec![ +// Coach { +// position: "manager".to_owned(), +// name_first: "Dusty".to_owned(), +// name_last: "Baker".to_owned(), +// id: 110481, +// }, +// Coach { +// position: "batting_coach".to_owned(), +// name_first: "Brook".to_owned(), +// name_last: "Jacoby".to_owned(), +// id: 116461, +// }, +// Coach { +// position: "pitching_coach".to_owned(), +// name_first: "Dick".to_owned(), +// name_last: "Pole".to_owned(), +// id: 120649, +// }, +// Coach { +// position: "first_base_coach".to_owned(), +// name_first: "Billy".to_owned(), +// name_last: "Hatcher".to_owned(), +// id: 115602, +// }, +// Coach { +// position: "third_base_coach".to_owned(), +// name_first: "Mark".to_owned(), +// name_last: "Berry".to_owned(), +// id: 427028, +// }, +// Coach { +// position: "bench_coach".to_owned(), +// name_first: "Chris".to_owned(), +// name_last: "Speier".to_owned(), +// id: 122573, +// }, +// Coach { +// position: "bullpen_coach".to_owned(), +// name_first: "Juan".to_owned(), +// name_last: "Lopez".to_owned(), +// id: 427306, +// }, +// Coach { +// position: "bullpen_catcher".to_owned(), +// name_first: "Mike".to_owned(), +// name_last: "Stefanski".to_owned(), +// id: 150464, +// }, +// ], +// }, +// Team { +// home_away: HomeAway::Home, +// id: "NYM".to_owned(), +// name: "New York Mets".to_owned(), +// players: vec![ +// Player { +// id: 110189, +// name_first: "Moises".to_owned(), +// name_last: "Alou".to_owned(), +// game_position: Some("LF".to_owned()), +// bat_order: Some(6), +// position: "LF".to_owned(), +// }, +// Player { +// id: 112116, +// name_first: "Luis".to_owned(), +// name_last: "Castillo".to_owned(), +// game_position: Some("2B".to_owned()), +// bat_order: Some(2), +// position: "2B".to_owned(), +// }, +// Player { +// id: 113232, +// name_first: "Carlos".to_owned(), +// name_last: "Delgado".to_owned(), +// game_position: Some("1B".to_owned()), +// bat_order: Some(7), +// position: "1B".to_owned(), +// }, +// Player { +// id: 113702, +// name_first: "Damion".to_owned(), +// name_last: "Easley".to_owned(), +// game_position: None, +// bat_order: None, +// position: "2B".to_owned(), +// }, +// Player { +// id: 118377, +// name_first: "Pedro".to_owned(), +// name_last: "Martinez".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 123790, +// name_first: "Billy".to_owned(), +// name_last: "Wagner".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 133340, +// name_first: "Orlando".to_owned(), +// name_last: "Hernandez".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 135783, +// name_first: "Ramon".to_owned(), +// name_last: "Castro".to_owned(), +// game_position: None, +// bat_order: None, +// position: "C".to_owned(), +// }, +// Player { +// id: 136724, +// name_first: "Marlon".to_owned(), +// name_last: "Anderson".to_owned(), +// game_position: None, +// bat_order: None, +// position: "LF".to_owned(), +// }, +// Player { +// id: 136860, +// name_first: "Carlos".to_owned(), +// name_last: "Beltran".to_owned(), +// game_position: Some("CF".to_owned()), +// bat_order: Some(4), +// position: "CF".to_owned(), +// }, +// Player { +// id: 150411, +// name_first: "Brian".to_owned(), +// name_last: "Schneider".to_owned(), +// game_position: Some("C".to_owned()), +// bat_order: Some(8), +// position: "C".to_owned(), +// }, +// Player { +// id: 276371, +// name_first: "Johan".to_owned(), +// name_last: "Santana".to_owned(), +// game_position: Some("P".to_owned()), +// bat_order: Some(9), +// position: "P".to_owned(), +// }, +// Player { +// id: 277184, +// name_first: "Matt".to_owned(), +// name_last: "Wise".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 346795, +// name_first: "Endy".to_owned(), +// name_last: "Chavez".to_owned(), +// game_position: None, +// bat_order: None, +// position: "RF".to_owned(), +// }, +// Player { +// id: 407901, +// name_first: "Jorge".to_owned(), +// name_last: "Sosa".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 408230, +// name_first: "Pedro".to_owned(), +// name_last: "Feliciano".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 408310, +// name_first: "Aaron".to_owned(), +// name_last: "Heilman".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 408314, +// name_first: "Jose".to_owned(), +// name_last: "Reyes".to_owned(), +// game_position: Some("SS".to_owned()), +// bat_order: Some(1), +// position: "SS".to_owned(), +// }, +// Player { +// id: 425508, +// name_first: "Ryan".to_owned(), +// name_last: "Church".to_owned(), +// game_position: Some("RF".to_owned()), +// bat_order: Some(5), +// position: "RF".to_owned(), +// }, +// Player { +// id: 429720, +// name_first: "John".to_owned(), +// name_last: "Maine".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 431151, +// name_first: "David".to_owned(), +// name_last: "Wright".to_owned(), +// game_position: Some("3B".to_owned()), +// bat_order: Some(3), +// position: "3B".to_owned(), +// }, +// Player { +// id: 434586, +// name_first: "Ambiorix".to_owned(), +// name_last: "Burgos".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 434636, +// name_first: "Angel".to_owned(), +// name_last: "Pagan".to_owned(), +// game_position: None, +// bat_order: None, +// position: "LF".to_owned(), +// }, +// Player { +// id: 450306, +// name_first: "Jason".to_owned(), +// name_last: "Vargas".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// Player { +// id: 460059, +// name_first: "Mike".to_owned(), +// name_last: "Pelfrey".to_owned(), +// game_position: None, +// bat_order: None, +// position: "P".to_owned(), +// }, +// ], +// coaches: vec![ +// Coach { +// position: "manager".to_owned(), +// name_first: "Willie".to_owned(), +// name_last: "Randolph".to_owned(), +// id: 120927, +// }, +// Coach { +// position: "batting_coach".to_owned(), +// name_first: "Howard".to_owned(), +// name_last: "Johnson".to_owned(), +// id: 116593, +// }, +// Coach { +// position: "pitching_coach".to_owned(), +// name_first: "Rick".to_owned(), +// name_last: "Peterson".to_owned(), +// id: 427395, +// }, +// Coach { +// position: "first_base_coach".to_owned(), +// name_first: "Tom".to_owned(), +// name_last: "Nieto".to_owned(), +// id: 119796, +// }, +// Coach { +// position: "third_base_coach".to_owned(), +// name_first: "Sandy".to_owned(), +// name_last: "Alomar".to_owned(), +// id: 110185, +// }, +// Coach { +// position: "bench_coach".to_owned(), +// name_first: "Jerry".to_owned(), +// name_last: "Manuel".to_owned(), +// id: 118262, +// }, +// Coach { +// position: "bullpen_coach".to_owned(), +// name_first: "Guy".to_owned(), +// name_last: "Conti".to_owned(), +// id: 434699, +// }, +// Coach { +// position: "bullpen_catcher".to_owned(), +// name_first: "Dave".to_owned(), +// name_last: "Racaniello".to_owned(), +// id: 534948, +// }, +// Coach { +// position: "coach".to_owned(), +// name_first: "Sandy".to_owned(), +// name_last: "Alomar".to_owned(), +// id: 110184, +// }, +// Coach { +// position: "coach".to_owned(), +// name_first: "Juan".to_owned(), +// name_last: "Lopez".to_owned(), +// id: 495390, +// }, +// ], +// }, +// ], +// }; + +// assert_eq!(res, expected); +// } diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index f9a23110..9998b562 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -3,23 +3,12 @@ use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event}; use quick_xml::{Reader, Result, Writer}; use std::io::Cursor; use std::str::from_utf8; -#[cfg(feature = "asynchronous")] -use tokio::runtime::Runtime; macro_rules! next_eq_name { ($r:expr, $t:tt, $bytes:expr) => { let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = $r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { $r.read_event(&mut buf).await }); - - match event.unwrap() { + match $r.read_event(&mut buf).unwrap() { $t(ref e) if e.name() == $bytes => (), e => panic!( "expecting {}({:?}), found {:?}", @@ -36,16 +25,7 @@ macro_rules! next_eq_content { ($r:expr, $t:tt, $bytes:expr) => { let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = $r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { $r.read_event(&mut buf).await }); - - match event.unwrap() { + match $r.read_event(&mut buf).unwrap() { $t(ref e) if &**e == $bytes => (), e => panic!( "expecting {}({:?}), found {:?}", @@ -147,16 +127,7 @@ fn test_xml_decl() { r.trim_text(true); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event.unwrap() { + match r.read_event(&mut buf).unwrap() { Decl(ref e) => { match e.version() { Ok(v) => assert_eq!( @@ -230,85 +201,58 @@ fn test_nested() { } #[test] -fn test_writer() { +fn test_writer() -> Result<()> { let txt = include_str!("../tests/documents/test_writer.xml").trim(); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + match reader.read_event(&mut buf)? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(result, txt.as_bytes()); + Ok(()) } #[test] -fn test_writer_borrow() { +fn test_writer_borrow() -> Result<()> { let txt = include_str!("../tests/documents/test_writer.xml").trim(); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - + let event = reader.read_event(&mut buf)?; match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(&e).is_ok()), // either `e` or `&e` - Err(e) => panic!("{}", e), + Eof => break, + e => assert!(writer.write_event(&e).is_ok()), // either `e` or `&e` } } let result = writer.into_inner().into_inner(); assert_eq!(result, txt.as_bytes()); + Ok(()) } #[test] -fn test_writer_indent() { +fn test_writer_indent() -> Result<()> { let txt = include_str!("../tests/documents/test_writer_indent.xml"); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + match reader.read_event(&mut buf)? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } @@ -319,30 +263,24 @@ fn test_writer_indent() { #[cfg(not(windows))] assert_eq!(result, txt.as_bytes()); + + Ok(()) } #[test] -fn test_writer_indent_cdata() { +fn test_writer_indent_cdata() -> Result<()> { let txt = include_str!("../tests/documents/test_writer_indent_cdata.xml"); let mut reader = Reader::from_str(txt); reader.trim_text(true); let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); + let event = reader.read_event(&mut buf)?; match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } @@ -353,10 +291,12 @@ fn test_writer_indent_cdata() { #[cfg(not(windows))] assert_eq!(result, txt.as_bytes()); + + Ok(()) } #[test] -fn test_write_empty_element_attrs() { +fn test_write_empty_element_attrs() -> Result<()> { let str_from = r#""#; let expected = r#""#; let mut reader = Reader::from_str(str_from); @@ -364,29 +304,21 @@ fn test_write_empty_element_attrs() { let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + match reader.read_event(&mut buf)? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), expected); + + Ok(()) } #[test] -fn test_write_attrs() { +fn test_write_attrs() -> Result<()> { let str_from = r#""#; let expected = r#""#; let mut reader = Reader::from_str(str_from); @@ -394,19 +326,10 @@ fn test_write_attrs() { let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - loop { - #[cfg(not(feature = "asynchronous"))] - let ev = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let ev = runtime.block_on(async { reader.read_event(&mut buf).await }); - - let event = match ev { - Ok(Eof) => break, - Ok(Start(elem)) => { + let event = match reader.read_event(&mut buf)? { + Eof => break, + Start(elem) => { let mut attrs = elem.attributes().collect::>>().unwrap(); attrs.extend_from_slice(&[("a", "b").into(), ("c", "d").into()]); let mut elem = BytesStart::owned(b"copy".to_vec(), 4); @@ -414,15 +337,16 @@ fn test_write_attrs() { elem.push_attribute(("x", "y\"z")); Start(elem) } - Ok(End(_)) => End(BytesEnd::borrowed(b"copy")), - Ok(e) => e, - Err(e) => panic!("{}", e), + End(_) => End(BytesEnd::borrowed(b"copy")), + e => e, }; assert!(writer.write_event(event).is_ok()); } let result = writer.into_inner().into_inner(); assert_eq!(result, expected.as_bytes()); + + Ok(()) } #[test] @@ -510,16 +434,7 @@ fn test_buf_position_err_end_element() { let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Err(_) if r.buffer_position() == 2 => (), // error at char 2: no opening tag Err(e) => panic!( "expecting buf_pos = 2, found {}, err: {:?}", @@ -540,16 +455,7 @@ fn test_buf_position_err_comment() { let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Err(_) if r.buffer_position() == 4 => { // error at char 5: no closing --> tag found assert!(true); @@ -568,28 +474,15 @@ fn test_buf_position_err_comment_2_buf() { let mut r = Reader::from_str(" tag found assert!(true); @@ -613,16 +506,7 @@ fn test_buf_position_err_comment_trim_text() { let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Err(_) if r.buffer_position() == 7 => { // error at char 5: no closing --> tag found assert!(true); @@ -644,27 +528,12 @@ fn test_namespace() { let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, Start(_))) = event { + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting start element with no namespace"); } - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((Some(a), Start(_))) = event { + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { @@ -682,30 +551,15 @@ fn test_default_namespace() { let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); // - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, Start(_))) = event { + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting outer start element with no namespace"); } // - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((Some(a), Start(_))) = event { + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { @@ -716,14 +570,7 @@ fn test_default_namespace() { } // - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((Some(a), End(_))) = event { + if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { if &*a == b"www1" { assert!(true); } else { @@ -735,14 +582,7 @@ fn test_default_namespace() { // very important: a should not be in any namespace. The default namespace only applies to // the sub-document it is defined on. - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, End(_))) = event { + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting outer end element with no namespace"); } @@ -755,16 +595,8 @@ fn test_default_namespace_reset() { let mut buf = Vec::new(); let mut ns_buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - if let Ok((Some(a), Start(_))) = event { + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { assert_eq!( &a[..], b"www1", @@ -774,35 +606,17 @@ fn test_default_namespace_reset() { panic!("expecting outer start element with to resolve to 'www1'"); } - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - match event { + match r.read_namespaced_event(&mut buf, &mut ns_buf) { Ok((None, Start(_))) => (), e => panic!("expecting inner start element, got {:?}", e), } - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((None, End(_))) = event { + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { } else { assert!(false, "expecting inner end element"); } - #[cfg(not(feature = "asynchronous"))] - let event = r.read_namespaced_event(&mut buf, &mut ns_buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_namespaced_event(&mut buf, &mut ns_buf).await }); - - if let Ok((Some(a), End(_))) = event { + if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf) { assert_eq!( &a[..], b"www1", @@ -820,16 +634,7 @@ fn test_escaped_content() { next_eq!(r, Start, b"a"); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Text(e)) => { if &*e != b"<test>" { panic!( @@ -864,7 +669,7 @@ fn test_escaped_content() { } #[test] -fn test_read_write_roundtrip_results_in_identity() { +fn test_read_write_roundtrip_results_in_identity() -> Result<()> { let input = r#"
@@ -878,29 +683,22 @@ fn test_read_write_roundtrip_results_in_identity() { reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + match reader.read_event(&mut buf)? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(result, input.as_bytes()); + + Ok(()) } #[test] -fn test_read_write_roundtrip() { +fn test_read_write_roundtrip() -> Result<()> { let input = r#"
@@ -914,29 +712,22 @@ fn test_read_write_roundtrip() { reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + match reader.read_event(&mut buf)? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) } #[test] -fn test_read_write_roundtrip_escape() { +fn test_read_write_roundtrip_escape() -> Result<()> { let input = r#"
@@ -950,35 +741,28 @@ fn test_read_write_roundtrip_escape() { reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(Text(e)) => { + match reader.read_event(&mut buf)? { + Eof => break, + Text(e) => { let t = e.escaped(); assert!(writer .write_event(Event::Text(BytesText::from_escaped(t.to_vec()))) .is_ok()); } - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) } #[test] -fn test_read_write_roundtrip_escape_text() { +fn test_read_write_roundtrip_escape_text() -> Result<()> { let input = r#"
@@ -992,31 +776,24 @@ fn test_read_write_roundtrip_escape_text() { reader.trim_text(false).expand_empty_elements(false); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { - Ok(Eof) => break, - Ok(Text(e)) => { + match reader.read_event(&mut buf)? { + Eof => break, + Text(e) => { let t = e.unescape_and_decode(&reader).unwrap(); assert!(writer .write_event(Event::Text(BytesText::from_plain_str(&t))) .is_ok()); } - Ok(e) => assert!(writer.write_event(e).is_ok()), - Err(e) => panic!("{}", e), + e => assert!(writer.write_event(e).is_ok()), } } let result = writer.into_inner().into_inner(); assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) } #[test] @@ -1025,16 +802,7 @@ fn test_closing_bracket_in_single_quote_attr() { r.trim_text(true); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { @@ -1058,16 +826,7 @@ fn test_closing_bracket_in_double_quote_attr() { r.trim_text(true); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { @@ -1091,16 +850,7 @@ fn test_closing_bracket_in_double_quote_mixed() { r.trim_text(true); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { @@ -1124,16 +874,7 @@ fn test_closing_bracket_in_single_quote_mixed() { r.trim_text(true); let mut buf = Vec::new(); - #[cfg(not(feature = "asynchronous"))] - let event = r.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { r.read_event(&mut buf).await }); - - match event { + match r.read_event(&mut buf) { Ok(Start(e)) => { let mut attrs = e.attributes(); match attrs.next() { @@ -1161,17 +902,9 @@ fn test_unescape_and_decode_without_bom_removes_utf8_bom() { let mut txt = Vec::new(); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&reader).unwrap()), Ok(Event::Eof) => break, _ => (), @@ -1188,17 +921,9 @@ fn test_unescape_and_decode_without_bom_removes_utf16be_bom() { let mut txt = Vec::new(); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), Ok(Event::Eof) => break, _ => (), @@ -1215,17 +940,9 @@ fn test_unescape_and_decode_without_bom_removes_utf16le_bom() { let mut txt = Vec::new(); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), Ok(Event::Eof) => break, _ => (), @@ -1244,17 +961,9 @@ fn test_unescape_and_decode_without_bom_does_nothing_if_no_bom_exists() { let mut txt = Vec::new(); let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); loop { - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); - - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); - - match event { + match reader.read_event(&mut buf) { Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), Ok(Event::Eof) => break, _ => (), diff --git a/tests/unit_tests_async.rs b/tests/unit_tests_async.rs new file mode 100644 index 00000000..f875c0d1 --- /dev/null +++ b/tests/unit_tests_async.rs @@ -0,0 +1,1026 @@ +#[cfg(feature = "asynchronous")] +use quick_xml::events::Event::*; +#[cfg(feature = "asynchronous")] +use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event}; +#[cfg(feature = "asynchronous")] +use quick_xml::{AsyncReader, Result, Writer}; +#[cfg(feature = "asynchronous")] +use std::io::Cursor; +#[cfg(feature = "asynchronous")] +use std::str::from_utf8; + +#[cfg(feature = "asynchronous")] +macro_rules! next_eq_name { + ($r:expr, $t:tt, $bytes:expr) => { + let mut buf = Vec::new(); + + match $r.read_event(&mut buf).await.unwrap() { + $t(ref e) if e.name() == $bytes => (), + e => panic!( + "expecting {}({:?}), found {:?}", + stringify!($t), + from_utf8($bytes), + e + ), + } + buf.clear(); + }; +} + +#[cfg(feature = "asynchronous")] +macro_rules! next_eq_content { + ($r:expr, $t:tt, $bytes:expr) => { + let mut buf = Vec::new(); + + match $r.read_event(&mut buf).await.unwrap() { + $t(ref e) if &**e == $bytes => (), + e => panic!( + "expecting {}({:?}), found {:?}", + stringify!($t), + from_utf8($bytes), + e + ), + } + buf.clear(); + }; +} + +#[cfg(feature = "asynchronous")] +macro_rules! next_eq { + ($r:expr, Start, $bytes:expr) => (next_eq_name!($r, Start, $bytes);); + ($r:expr, End, $bytes:expr) => (next_eq_name!($r, End, $bytes);); + ($r:expr, Empty, $bytes:expr) => (next_eq_name!($r, Empty, $bytes);); + ($r:expr, Comment, $bytes:expr) => (next_eq_content!($r, Comment, $bytes);); + ($r:expr, Text, $bytes:expr) => (next_eq_content!($r, Text, $bytes);); + ($r:expr, CData, $bytes:expr) => (next_eq_content!($r, CData, $bytes);); + ($r:expr, $t0:tt, $b0:expr, $($t:tt, $bytes:expr),*) => { + next_eq!($r, $t0, $b0); + next_eq!($r, $($t, $bytes),*); + }; +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Start, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_end() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Start, b"a", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_end_with_ws() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Start, b"a", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_end_attr() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Start, b"a", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_empty() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true).expand_empty_elements(false); + next_eq!(r, Empty, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_empty_can_be_expanded() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true).expand_empty_elements(true); + next_eq!(r, Start, b"a", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_empty_attr() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true).expand_empty_elements(false); + next_eq!(r, Empty, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_end_comment() { + let mut r = AsyncReader::from_str(" "); + r.trim_text(true).expand_empty_elements(false); + next_eq!(r, Start, b"b", Empty, b"a", Empty, b"a", Comment, b"t", End, b"b"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_txt_end() { + let mut r = AsyncReader::from_str("test"); + r.trim_text(true); + next_eq!(r, Start, b"a", Text, b"test", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_comment() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Comment, b"test"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_xml_decl() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await.unwrap() { + Decl(ref e) => { + match e.version() { + Ok(v) => assert_eq!( + &*v, + b"1.0", + "expecting version '1.0', got '{:?}", + from_utf8(&*v) + ), + Err(e) => assert!(false, "{:?}", e), + } + match e.encoding() { + Some(Ok(v)) => assert_eq!( + &*v, + b"utf-8", + "expecting encoding 'utf-8', got '{:?}", + from_utf8(&*v) + ), + Some(Err(e)) => panic!("{:?}", e), + None => panic!("cannot find encoding"), + } + match e.standalone() { + None => (), + e => panic!("doesn't expect standalone, got {:?}", e), + } + } + _ => panic!("unable to parse XmlDecl"), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_trim_test() { + let txt = " "; + let mut r = AsyncReader::from_str(txt); + r.trim_text(true); + next_eq!(r, Start, b"a", Start, b"b", End, b"b", End, b"a"); + + let mut r = AsyncReader::from_str(txt); + r.trim_text(false); + next_eq!( + r, Text, b"", Start, b"a", Text, b"", Start, b"b", Text, b" ", End, b"b", Text, b"", End, + b"a" + ); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_cdata() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, CData, b"test"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_cdata_open_close() { + let mut r = AsyncReader::from_str(" test]]>"); + r.trim_text(true); + next_eq!(r, CData, b"test <> test"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_start_attr() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + next_eq!(r, Start, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_nested() { + let mut r = AsyncReader::from_str("test"); + r.trim_text(true).expand_empty_elements(false); + next_eq!(r, Start, b"a", Start, b"b", Text, b"test", End, b"b", Empty, b"c", End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_writer() -> Result<()> { + let txt = include_str!("../tests/documents/test_writer.xml").trim(); + let mut reader = AsyncReader::from_str(txt); + reader.trim_text(true); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(result, txt.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_writer_borrow() -> Result<()> { + let txt = include_str!("../tests/documents/test_writer.xml").trim(); + let mut reader = AsyncReader::from_str(txt); + reader.trim_text(true); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(&e).is_ok()), // either `e` or `&e` + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(result, txt.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_writer_indent() -> Result<()> { + let txt = include_str!("../tests/documents/test_writer_indent.xml"); + let mut reader = AsyncReader::from_str(txt); + reader.trim_text(true); + let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(result, txt.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_writer_indent_cdata() -> Result<()> { + let txt = include_str!("../tests/documents/test_writer_indent_cdata.xml"); + let mut reader = AsyncReader::from_str(txt); + reader.trim_text(true); + let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + + #[cfg(windows)] + assert!(result.into_iter().eq(txt.bytes().filter(|b| *b != 13))); + + #[cfg(not(windows))] + assert_eq!(result, txt.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_write_empty_element_attrs() -> Result<()> { + let str_from = r#""#; + let expected = r#""#; + let mut reader = AsyncReader::from_str(str_from); + reader.expand_empty_elements(false); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(String::from_utf8(result).unwrap(), expected); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_write_attrs() -> Result<()> { + let str_from = r#""#; + let expected = r#""#; + let mut reader = AsyncReader::from_str(str_from); + reader.trim_text(true); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + let event = match reader.read_event(&mut buf).await? { + Eof => break, + Start(elem) => { + let mut attrs = elem.attributes().collect::>>().unwrap(); + attrs.extend_from_slice(&[("a", "b").into(), ("c", "d").into()]); + let mut elem = BytesStart::owned(b"copy".to_vec(), 4); + elem.extend_attributes(attrs); + elem.push_attribute(("x", "y\"z")); + Start(elem) + } + End(_) => End(BytesEnd::borrowed(b"copy")), + e => e, + }; + assert!(writer.write_event(event).is_ok()); + } + + let result = writer.into_inner().into_inner(); + assert_eq!(result, expected.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_new_xml_decl_full() { + let mut writer = Writer::new(Vec::new()); + writer + .write_event(Decl(BytesDecl::new(b"1.2", Some(b"utf-X"), Some(b"yo")))) + .expect("writing xml decl should succeed"); + + let result = writer.into_inner(); + assert_eq!( + String::from_utf8(result).expect("utf-8 output"), + "".to_owned(), + "writer output (LHS)" + ); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_new_xml_decl_standalone() { + let mut writer = Writer::new(Vec::new()); + writer + .write_event(Decl(BytesDecl::new(b"1.2", None, Some(b"yo")))) + .expect("writing xml decl should succeed"); + + let result = writer.into_inner(); + assert_eq!( + String::from_utf8(result).expect("utf-8 output"), + "".to_owned(), + "writer output (LHS)" + ); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_new_xml_decl_encoding() { + let mut writer = Writer::new(Vec::new()); + writer + .write_event(Decl(BytesDecl::new(b"1.2", Some(b"utf-X"), None))) + .expect("writing xml decl should succeed"); + + let result = writer.into_inner(); + assert_eq!( + String::from_utf8(result).expect("utf-8 output"), + "".to_owned(), + "writer output (LHS)" + ); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_new_xml_decl_version() { + let mut writer = Writer::new(Vec::new()); + writer + .write_event(Decl(BytesDecl::new(b"1.2", None, None))) + .expect("writing xml decl should succeed"); + + let result = writer.into_inner(); + assert_eq!( + String::from_utf8(result).expect("utf-8 output"), + "".to_owned(), + "writer output (LHS)" + ); +} + +/// This test ensures that empty XML declaration attribute values are not a problem. +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_new_xml_decl_empty() { + let mut writer = Writer::new(Vec::new()); + // An empty version should arguably be an error, but we don't expect anyone to actually supply + // an empty version. + writer + .write_event(Decl(BytesDecl::new(b"", Some(b""), Some(b"")))) + .expect("writing xml decl should succeed"); + + let result = writer.into_inner(); + assert_eq!( + String::from_utf8(result).expect("utf-8 output"), + "".to_owned(), + "writer output (LHS)" + ); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_buf_position_err_end_element() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true).check_end_names(true); + + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Err(_) if r.buffer_position() == 2 => (), // error at char 2: no opening tag + Err(e) => panic!( + "expecting buf_pos = 2, found {}, err: {:?}", + r.buffer_position(), + e + ), + e => panic!("expecting error, found {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_buf_position_err_comment() { + let mut r = AsyncReader::from_str(" tag found + assert!(true); + } + Err(e) => panic!( + "expecting buf_pos = 5, found {}, err: {:?}", + r.buffer_position(), + e + ), + e => assert!(false, "expecting error, found {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_buf_position_err_comment_2_buf() { + let mut r = AsyncReader::from_str(" tag found + assert!(true); + } + Err(e) => panic!( + "expecting buf_pos = 4, found {}, err: {:?}", + r.buffer_position(), + e + ), + e => assert!(false, "expecting error, found {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_buf_position_err_comment_trim_text() { + let mut r = AsyncReader::from_str("\r\n tag found + assert!(true); + } + Err(e) => panic!( + "expecting buf_pos = 5, found {}, err: {:?}", + r.buffer_position(), + e + ), + e => assert!(false, "expecting error, found {:?}", e), + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_namespace() { + let mut r = AsyncReader::from_str("in namespace!"); + r.trim_text(true); + + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + assert!(false, "expecting start element with no namespace"); + } + + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + if &*a == b"www1" { + assert!(true); + } else { + assert!(false, "expecting namespace to resolve to 'www1'"); + } + } else { + assert!(false, "expecting namespace resolution"); + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_default_namespace() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + // + if let Ok((None, Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + assert!(false, "expecting outer start element with no namespace"); + } + + // + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + if &*a == b"www1" { + assert!(true); + } else { + assert!(false, "expecting namespace to resolve to 'www1'"); + } + } else { + assert!(false, "expecting namespace resolution"); + } + + // + if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + if &*a == b"www1" { + assert!(true); + } else { + assert!(false, "expecting namespace to resolve to 'www1'"); + } + } else { + assert!(false, "expecting namespace resolution"); + } + + // very important: a should not be in any namespace. The default namespace only applies to + // the sub-document it is defined on. + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + assert!(false, "expecting outer end element with no namespace"); + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_default_namespace_reset() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + + let mut buf = Vec::new(); + let mut ns_buf = Vec::new(); + + if let Ok((Some(a), Start(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + assert_eq!( + &a[..], + b"www1", + "expecting outer start element with to resolve to 'www1'" + ); + } else { + panic!("expecting outer start element with to resolve to 'www1'"); + } + + match r.read_namespaced_event(&mut buf, &mut ns_buf).await { + Ok((None, Start(_))) => (), + e => panic!("expecting inner start element, got {:?}", e), + } + + if let Ok((None, End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + } else { + assert!(false, "expecting inner end element"); + } + + if let Ok((Some(a), End(_))) = r.read_namespaced_event(&mut buf, &mut ns_buf).await { + assert_eq!( + &a[..], + b"www1", + "expecting outer end element with to resolve to 'www1'" + ); + } else { + panic!("expecting outer end element with to resolve to 'www1'"); + } +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_escaped_content() { + let mut r = AsyncReader::from_str("<test>"); + r.trim_text(true); + next_eq!(r, Start, b"a"); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Text(e)) => { + if &*e != b"<test>" { + panic!( + "content unexpected: expecting '<test>', got '{:?}'", + from_utf8(&*e) + ); + } + match e.unescaped() { + Ok(ref c) => { + if &**c != b"" { + panic!( + "unescaped content unexpected: expecting '<test<', got '{:?}'", + from_utf8(c) + ) + } + } + Err(e) => panic!( + "cannot escape content at position {}: {:?}", + r.buffer_position(), + e + ), + } + } + Ok(e) => panic!("Expecting text event, got {:?}", e), + Err(e) => panic!( + "Cannot get next event at position {}: {:?}", + r.buffer_position(), + e + ), + } + next_eq!(r, End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_read_write_roundtrip_results_in_identity() -> Result<()> { + let input = r#" + +
+
+
+
data
+
+ "#; + + let mut reader = AsyncReader::from_str(input); + reader.trim_text(false).expand_empty_elements(false); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(result, input.as_bytes()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_read_write_roundtrip() -> Result<()> { + let input = r#" + +
+
+
+
data <escaped>
+
+ "#; + + let mut reader = AsyncReader::from_str(input); + reader.trim_text(false).expand_empty_elements(false); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_read_write_roundtrip_escape() -> Result<()> { + let input = r#" + +
+
+
+
data <escaped>
+
+ "#; + + let mut reader = AsyncReader::from_str(input); + reader.trim_text(false).expand_empty_elements(false); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + Text(e) => { + let t = e.escaped(); + assert!(writer + .write_event(Event::Text(BytesText::from_escaped(t.to_vec()))) + .is_ok()); + } + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_read_write_roundtrip_escape_text() -> Result<()> { + let input = r#" + +
+
+
+
data <escaped>
+
+ "#; + + let mut reader = AsyncReader::from_str(input); + reader.trim_text(false).expand_empty_elements(false); + let mut writer = Writer::new(Cursor::new(Vec::new())); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await? { + Eof => break, + Text(e) => { + let t = e.unescape_and_decode(&reader).unwrap(); + assert!(writer + .write_event(Event::Text(BytesText::from_plain_str(&t))) + .is_ok()); + } + e => assert!(writer.write_event(e).is_ok()), + } + } + + let result = writer.into_inner().into_inner(); + assert_eq!(String::from_utf8(result).unwrap(), input.to_string()); + + Ok(()) +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_closing_bracket_in_single_quote_attr() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Start(e)) => { + let mut attrs = e.attributes(); + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), ">".as_bytes()).into()), + x => panic!("expected attribute 'attr', got {:?}", x), + } + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "2".as_bytes()).into()), + x => panic!("expected attribute 'check', got {:?}", x), + } + assert!(attrs.next().is_none(), "expected only two attributes"); + } + x => panic!("expected , got {:?}", x), + } + next_eq!(r, End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_closing_bracket_in_double_quote_attr() { + let mut r = AsyncReader::from_str("\" check=\"2\">"); + r.trim_text(true); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Start(e)) => { + let mut attrs = e.attributes(); + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), ">".as_bytes()).into()), + x => panic!("expected attribute 'attr', got {:?}", x), + } + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "2".as_bytes()).into()), + x => panic!("expected attribute 'check', got {:?}", x), + } + assert!(attrs.next().is_none(), "expected only two attributes"); + } + x => panic!("expected , got {:?}", x), + } + next_eq!(r, End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_closing_bracket_in_double_quote_mixed() { + let mut r = AsyncReader::from_str("'\" check=\"'2'\">"); + r.trim_text(true); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Start(e)) => { + let mut attrs = e.attributes(); + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), "'>'".as_bytes()).into()), + x => panic!("expected attribute 'attr', got {:?}", x), + } + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "'2'".as_bytes()).into()), + x => panic!("expected attribute 'check', got {:?}", x), + } + assert!(attrs.next().is_none(), "expected only two attributes"); + } + x => panic!("expected , got {:?}", x), + } + next_eq!(r, End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +async fn test_closing_bracket_in_single_quote_mixed() { + let mut r = AsyncReader::from_str(""); + r.trim_text(true); + let mut buf = Vec::new(); + + match r.read_event(&mut buf).await { + Ok(Start(e)) => { + let mut attrs = e.attributes(); + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("attr".as_bytes(), "\">\"".as_bytes()).into()), + x => panic!("expected attribute 'attr', got {:?}", x), + } + match attrs.next() { + Some(Ok(attr)) => assert_eq!(attr, ("check".as_bytes(), "\"2\"".as_bytes()).into()), + x => panic!("expected attribute 'check', got {:?}", x), + } + assert!(attrs.next().is_none(), "expected only two attributes"); + } + x => panic!("expected , got {:?}", x), + } + next_eq!(r, End, b"a"); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +#[cfg(not(feature = "encoding"))] +async fn test_unescape_and_decode_without_bom_removes_utf8_bom() { + let input: &str = std::str::from_utf8(b"\xEF\xBB\xBF").unwrap(); + + let mut reader = AsyncReader::from_str(&input); + reader.trim_text(true); + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await { + Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&reader).unwrap()), + Ok(Event::Eof) => break, + _ => (), + } + } + assert_eq!(txt, vec![""]); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +#[cfg(feature = "encoding")] +async fn test_unescape_and_decode_without_bom_removes_utf16be_bom() { + let mut reader = AsyncReader::from_file("./tests/documents/utf16be.xml") + .await + .unwrap(); + reader.trim_text(true); + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await { + Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), + Ok(Event::Eof) => break, + _ => (), + } + } + assert_eq!(txt[0], ""); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +#[cfg(feature = "encoding")] +async fn test_unescape_and_decode_without_bom_removes_utf16le_bom() { + let mut reader = AsyncReader::from_file("./tests/documents/utf16le.xml") + .await + .unwrap(); + reader.trim_text(true); + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await { + Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), + Ok(Event::Eof) => break, + _ => (), + } + } + assert_eq!(txt[0], ""); +} + +#[cfg(feature = "asynchronous")] +#[tokio::test] +#[cfg(not(feature = "encoding"))] +async fn test_unescape_and_decode_without_bom_does_nothing_if_no_bom_exists() { + let input: &str = std::str::from_utf8(b"").unwrap(); + + let mut reader = AsyncReader::from_str(&input); + reader.trim_text(true); + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event(&mut buf).await { + Ok(Event::Text(e)) => txt.push(e.unescape_and_decode_without_bom(&mut reader).unwrap()), + Ok(Event::Eof) => break, + _ => (), + } + } + assert_eq!(txt.is_empty(), true); +} diff --git a/tests/xmlrs_reader_tests.rs b/tests/xmlrs_reader_tests.rs index 120a02fb..fd8d6dd9 100644 --- a/tests/xmlrs_reader_tests.rs +++ b/tests/xmlrs_reader_tests.rs @@ -1,6 +1,8 @@ extern crate quick_xml; use quick_xml::events::{BytesStart, Event}; +#[cfg(feature = "asynchronous")] +use quick_xml::AsyncReader; use quick_xml::{Reader, Result}; use std::str::from_utf8; #[cfg(feature = "asynchronous")] @@ -299,11 +301,9 @@ fn default_namespace_applies_to_end_elem() { ); } -fn test(input: &[u8], output: &[u8], is_short: bool) { - #[cfg(feature = "asynchronous")] - let mut runtime = Runtime::new().expect("Runtime cannot be initialized"); - +fn test_sync(input: &[u8], output: &[u8], is_short: bool) { let mut reader = Reader::from_reader(input); + reader .trim_text(is_short) .check_comments(true) @@ -315,24 +315,12 @@ fn test(input: &[u8], output: &[u8], is_short: bool) { if !is_short { // discard first whitespace - - #[cfg(feature = "asynchronous")] - runtime.block_on(async { - reader.read_event(&mut buf).await.unwrap(); - }); - - #[cfg(not(feature = "asynchronous"))] reader.read_event(&mut buf).unwrap(); } loop { buf.clear(); - #[cfg(feature = "asynchronous")] - let event = runtime - .block_on(async { reader.read_namespaced_event(&mut buf, &mut ns_buffer).await }); - - #[cfg(not(feature = "asynchronous"))] let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer); let line = xmlrs_display(&event); @@ -363,13 +351,72 @@ fn test(input: &[u8], output: &[u8], is_short: bool) { let mut buf = Vec::new(); - #[cfg(feature = "asynchronous")] - let event = runtime.block_on(async { reader.read_event(&mut buf).await }); + if let Ok(Event::Text(ref e)) = reader.read_event(&mut buf) { + if e.iter().any(|b| match *b { + b' ' | b'\r' | b'\n' | b'\t' => false, + _ => true, + }) { + panic!("Reader expects empty Text event after a StartDocument"); + } + } else { + panic!("Reader expects empty Text event after a StartDocument"); + } + } + } +} + +#[cfg(feature = "asynchronous")] +async fn test_async(input: &[u8], output: &[u8], is_short: bool) { + let mut reader = AsyncReader::from_reader(input); + + reader + .trim_text(is_short) + .check_comments(true) + .expand_empty_elements(false); + + let mut spec_lines = SpecIter(output).enumerate(); + let mut buf = Vec::new(); + let mut ns_buffer = Vec::new(); + + if !is_short { + // discard first whitespace + reader.read_event(&mut buf).await.unwrap(); + } + + loop { + buf.clear(); + + let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer).await; - #[cfg(not(feature = "asynchronous"))] - let event = reader.read_event(&mut buf); + let line = xmlrs_display(&event); + if let Some((n, spec)) = spec_lines.next() { + if spec.trim() == "EndDocument" { + break; + } + if line.trim() != spec.trim() { + panic!( + "\n-------------------\n\ + Unexpected event at line {}:\n\ + Expected: {}\nFound: {}\n\ + -------------------\n", + n + 1, + spec, + line + ); + } + } else { + if line == "EndDocument" { + break; + } + panic!("Unexpected event: {}", line); + } + + if !is_short && line.starts_with("StartDocument") { + // advance next Characters(empty space) ... + + let mut buf = Vec::new(); - if let Ok(Event::Text(ref e)) = event { + if let Ok(Event::Text(ref e)) = reader.read_event(&mut buf).await { if e.iter().any(|b| match *b { b' ' | b'\r' | b'\n' | b'\t' => false, _ => true, @@ -383,6 +430,16 @@ fn test(input: &[u8], output: &[u8], is_short: bool) { } } +fn test(input: &[u8], output: &[u8], is_short: bool) { + test_sync(input, output, is_short); + + #[cfg(feature = "asynchronous")] + let runtime = Runtime::new().expect("Runtime cannot be initialized"); + + #[cfg(feature = "asynchronous")] + runtime.block_on(async { test_async(input, output, is_short).await }); +} + fn namespace_name(n: &Option<&[u8]>, name: &[u8]) -> String { match *n { Some(n) => format!("{{{}}}{}", from_utf8(n).unwrap(), from_utf8(name).unwrap()), @@ -430,12 +487,16 @@ fn xmlrs_display(opt_event: &Result<(Option<&[u8]>, Event)>) -> String { Ok((ref n, Event::End(ref e))) => format!("EndElement({})", namespace_name(n, e.name())), Ok((_, Event::Comment(ref e))) => format!("Comment({})", from_utf8(e).unwrap()), Ok((_, Event::CData(ref e))) => format!("CData({})", from_utf8(e).unwrap()), - Ok((_, Event::Text(ref e))) => match e.unescaped() { - Ok(c) => match from_utf8(&*c) { - Ok(c) => format!("Characters({})", c), - Err(ref err) => format!("InvalidUtf8({:?}; {})", e.escaped(), err), - }, - Err(ref err) => format!("FailedUnescape({:?}; {})", e.escaped(), err), + Ok((_, Event::Text(ref e))) => { + match e.unescaped() { + Ok(c) => { + match from_utf8(&*c) { + Ok(c) => format!("Characters({})", c), + Err(ref err) => format!("InvalidUtf8({:?}; {})", e.escaped(), err), + } + }, + Err(ref err) => format!("FailedUnescape({:?}; {})", e.escaped(), err), + } }, Ok((_, Event::Decl(ref e))) => { let version_cow = e.version().unwrap();