diff --git a/Changelog.md b/Changelog.md index ab8548b0..2d9f59af 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,9 +19,14 @@ ### Bug Fixes +- [#773]: Fixed reporting incorrect end position in `Reader::read_to_end` family + of methods and trimming of the trailing spaces in `Reader::read_text` when + `trim_text_start` is set and the last event is not a `Text` event. + ### Misc Changes [#772]: https://github.com/tafia/quick-xml/pull/772 +[#773]: https://github.com/tafia/quick-xml/pull/773 ## 0.34.0 -- 2024-06-25 diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 68853cb3..63a42574 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -394,28 +394,52 @@ macro_rules! read_until_close { /// Generalization of `read_to_end` method for buffered and borrowed readers macro_rules! read_to_end { ( + // $self: &mut Reader $self:expr, $end:expr, $buf:expr, $read_event:ident, // Code block that performs clearing of internal buffer after read of each event $clear:block $(, $await:ident)? ) => {{ + // Because we take position after the event before the End event, + // it is important that this position indicates beginning of the End event. + // If between last event and the End event would be only spaces, then we + // take position before the spaces, but spaces would be skipped without + // generating event if `trim_text_start` is set to `true`. To prevent that + // we temporary disable start text trimming. + // + // We also cannot take position after getting End event, because if + // `trim_markup_names_in_closing_tags` is set to `true` (which is the default), + // we do not known the real size of the End event that it is occupies in + // the source and cannot correct the position after the End event. + // So, we in any case should tweak parser configuration. + let config = $self.config_mut(); + let trim = config.trim_text_start; + config.trim_text_start = false; + let start = $self.buffer_position(); let mut depth = 0; loop { $clear let end = $self.buffer_position(); match $self.$read_event($buf) $(.$await)? { - Err(e) => return Err(e), + Err(e) => { + $self.config_mut().trim_text_start = trim; + return Err(e); + } Ok(Event::Start(e)) if e.name() == $end => depth += 1, Ok(Event::End(e)) if e.name() == $end => { if depth == 0 { + $self.config_mut().trim_text_start = trim; break start..end; } depth -= 1; } - Ok(Event::Eof) => return Err(Error::missed_end($end, $self.decoder())), + Ok(Event::Eof) => { + $self.config_mut().trim_text_start = trim; + return Err(Error::missed_end($end, $self.decoder())); + } _ => (), } } diff --git a/tests/async-tokio.rs b/tests/async-tokio.rs index 3d70c50a..3c267d90 100644 --- a/tests/async-tokio.rs +++ b/tests/async-tokio.rs @@ -1,13 +1,13 @@ use std::iter; use pretty_assertions::assert_eq; -use quick_xml::events::Event::*; +use quick_xml::events::{BytesStart, Event::*}; use quick_xml::name::QName; use quick_xml::reader::Reader; // Import `small_buffers_tests!` #[macro_use] -mod reader; +mod helpers; small_buffers_tests!( #[tokio::test] @@ -40,6 +40,53 @@ async fn test_sample() { assert_eq!((count, reads), (1247, 5245)); } +/// This tests checks that read_to_end() correctly returns span even when +/// text is trimmed from both sides +mod read_to_end { + use super::*; + use pretty_assertions::assert_eq; + + #[tokio::test] + async fn text() { + let mut r = Reader::from_str(" text "); + // ^0 ^5 ^11 + r.config_mut().trim_text(true); + + let mut buf = Vec::new(); + assert_eq!( + r.read_event_into_async(&mut buf).await.unwrap(), + Start(BytesStart::new("tag")) + ); + assert_eq!( + r.read_to_end_into_async(QName(b"tag"), &mut buf) + .await + .unwrap(), + 5..11 + ); + assert_eq!(r.read_event_into_async(&mut buf).await.unwrap(), Eof); + } + + #[tokio::test] + async fn tag() { + let mut r = Reader::from_str(" "); + // ^0 ^5 ^16 + r.config_mut().trim_text(true); + + let mut buf = Vec::new(); + assert_eq!( + r.read_event_into_async(&mut buf).await.unwrap(), + Start(BytesStart::new("tag")) + ); + assert_eq!( + r.read_to_end_into_async(QName(b"tag"), &mut buf) + .await + .unwrap(), + 5..16 + ); + assert_eq!(r.read_event_into_async(&mut buf).await.unwrap(), Eof); + } +} + /// Regression test for https://github.com/tafia/quick-xml/issues/751 /// /// Actually, that error was not found in async reader, but we would to test it as well. diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 1af3ea74..bbc7c0c0 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -1,24 +1,151 @@ //! Utility functions for integration tests -use quick_xml::de::Deserializer; -use quick_xml::DeError; -use serde::Deserialize; - -/// Deserialize an instance of type T from a string of XML text. -/// If deserialization was succeeded checks that all XML events was consumed -pub fn from_str<'de, T>(source: &'de str) -> Result -where - T: Deserialize<'de>, -{ - // Log XML that we try to deserialize to see it in the failed tests output - dbg!(source); - let mut de = Deserializer::from_str(source); - let result = T::deserialize(&mut de); - - // If type was deserialized, the whole XML document should be consumed - if let Ok(_) = result { - assert!(de.is_empty(), "the whole XML document should be consumed"); - } - - result +/// Tests for https://github.com/tafia/quick-xml/issues/469 +/// Exported to reuse in `async-tokio` tests. +#[macro_export] +macro_rules! small_buffers_tests { + ( + #[$test:meta] + $read_event:ident: $BufReader:ty + $(, $async:ident, $await:ident)? + ) => { + mod small_buffers { + use quick_xml::events::{BytesCData, BytesDecl, BytesPI, BytesStart, BytesText, Event}; + use quick_xml::reader::Reader; + use pretty_assertions::assert_eq; + + #[$test] + $($async)? fn decl() { + let xml = ""; + // ^^^^^^^ data that fit into buffer + let size = xml.match_indices("?>").next().unwrap().0 + 1; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml ", 3))) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn pi() { + let xml = ""; + // ^^^^^ data that fit into buffer + let size = xml.match_indices("?>").next().unwrap().0 + 1; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::PI(BytesPI::new("pi")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn empty() { + let xml = ""; + // ^^^^^^^ data that fit into buffer + let size = xml.match_indices("/>").next().unwrap().0 + 1; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Empty(BytesStart::new("empty")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn cdata1() { + let xml = ""; + // ^^^^^^^^^^^^^^^ data that fit into buffer + let size = xml.match_indices("]]>").next().unwrap().0 + 1; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::CData(BytesCData::new("cdata")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn cdata2() { + let xml = ""; + // ^^^^^^^^^^^^^^^^ data that fit into buffer + let size = xml.match_indices("]]>").next().unwrap().0 + 2; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::CData(BytesCData::new("cdata")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn comment1() { + let xml = ""; + // ^^^^^^^^^^^^ data that fit into buffer + let size = xml.match_indices("-->").next().unwrap().0 + 1; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Comment(BytesText::new("comment")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + + #[$test] + $($async)? fn comment2() { + let xml = ""; + // ^^^^^^^^^^^^^ data that fit into buffer + let size = xml.match_indices("-->").next().unwrap().0 + 2; + let br = <$BufReader>::with_capacity(size, xml.as_bytes()); + let mut reader = Reader::from_reader(br); + let mut buf = Vec::new(); + + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Comment(BytesText::new("comment")) + ); + assert_eq!( + reader.$read_event(&mut buf) $(.$await)? .unwrap(), + Event::Eof + ); + } + } + }; } diff --git a/tests/reader-errors.rs b/tests/reader-errors.rs index e5d4bfcd..18c08a23 100644 --- a/tests/reader-errors.rs +++ b/tests/reader-errors.rs @@ -5,7 +5,7 @@ use quick_xml::events::{BytesCData, BytesDecl, BytesEnd, BytesPI, BytesStart, By use quick_xml::reader::{NsReader, Reader}; macro_rules! ok { - ($test:ident($xml:literal) => $event:expr) => { + ($test:ident($xml:literal) => $pos:literal : $event:expr) => { mod $test { use super::*; @@ -18,6 +18,7 @@ macro_rules! ok { let mut reader = Reader::from_str($xml); reader.config_mut().enable_all_checks(true); assert_eq!(reader.read_event().unwrap(), $event); + assert_eq!(reader.buffer_position(), $pos); } #[test] @@ -26,6 +27,7 @@ macro_rules! ok { let mut reader = Reader::from_str($xml); reader.config_mut().enable_all_checks(true); assert_eq!(reader.read_event_into(&mut buf).unwrap(), $event); + assert_eq!(reader.buffer_position(), $pos); } #[cfg(feature = "async-tokio")] @@ -38,6 +40,7 @@ macro_rules! ok { reader.read_event_into_async(&mut buf).await.unwrap(), $event ); + assert_eq!(reader.buffer_position(), $pos); } } @@ -50,6 +53,7 @@ macro_rules! ok { let mut reader = NsReader::from_str($xml); reader.config_mut().enable_all_checks(true); assert_eq!(reader.read_resolved_event().unwrap().1, $event); + assert_eq!(reader.buffer_position(), $pos); } #[test] @@ -58,6 +62,7 @@ macro_rules! ok { let mut reader = NsReader::from_str($xml); reader.config_mut().enable_all_checks(true); assert_eq!(reader.read_resolved_event_into(&mut buf).unwrap().1, $event); + assert_eq!(reader.buffer_position(), $pos); } #[cfg(feature = "async-tokio")] @@ -74,6 +79,7 @@ macro_rules! ok { .1, $event ); + assert_eq!(reader.buffer_position(), $pos); } } } @@ -303,7 +309,8 @@ mod syntax { err!(unclosed09("") => SyntaxError::UnclosedComment); err!(unclosed10("") => Event::Comment(BytesText::new(""))); + ok!(normal1("") => 7: Event::Comment(BytesText::new(""))); + ok!(normal2("rest") => 7: Event::Comment(BytesText::new(""))); } /// https://www.w3.org/TR/xml11/#NT-CDSect @@ -336,7 +343,8 @@ mod syntax { err!(unclosed24(" SyntaxError::UnclosedCData); err!(unclosed25("") => SyntaxError::UnclosedCData); - ok!(normal("") => Event::CData(BytesCData::new(""))); + ok!(normal1("") => 12: Event::CData(BytesCData::new(""))); + ok!(normal2("rest") => 12: Event::CData(BytesCData::new(""))); } /// According to the grammar, only upper-case letters allowed for DOCTYPE writing. @@ -371,7 +379,8 @@ mod syntax { // According to the grammar, XML declaration MUST contain at least one space // and an element name, but we do not consider this as a _syntax_ error. - ok!(normal("") => Event::DocType(BytesText::new("e"))); + ok!(normal1("") => 12: Event::DocType(BytesText::new("e"))); + ok!(normal2("rest") => 12: Event::DocType(BytesText::new("e"))); } /// https://www.w3.org/TR/xml11/#NT-PI @@ -391,8 +400,10 @@ mod syntax { // According to the grammar, processing instruction MUST contain a non-empty // target name, but we do not consider this as a _syntax_ error. - ok!(normal_empty("") => Event::PI(BytesPI::new(""))); - ok!(normal_xmlx("") => Event::PI(BytesPI::new("xmlx"))); + ok!(normal_empty1("") => 4: Event::PI(BytesPI::new(""))); + ok!(normal_empty2("rest") => 4: Event::PI(BytesPI::new(""))); + ok!(normal_xmlx1("") => 8: Event::PI(BytesPI::new("xmlx"))); + ok!(normal_xmlx2("rest") => 8: Event::PI(BytesPI::new("xmlx"))); } /// https://www.w3.org/TR/xml11/#NT-prolog @@ -406,11 +417,12 @@ mod syntax { // According to the grammar, XML declaration MUST contain at least one space // and `version` attribute, but we do not consider this as a _syntax_ error. - ok!(normal1("") => Event::Decl(BytesDecl::from_start(BytesStart::new("xml")))); - ok!(normal2("") => Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml ", 3)))); - ok!(normal3("") => Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\t", 3)))); - ok!(normal4("") => Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\r", 3)))); - ok!(normal5("") => Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\n", 3)))); + ok!(normal1("") => 7: Event::Decl(BytesDecl::from_start(BytesStart::new("xml")))); + ok!(normal2("") => 8: Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml ", 3)))); + ok!(normal3("") => 8: Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\t", 3)))); + ok!(normal4("") => 8: Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\r", 3)))); + ok!(normal5("") => 8: Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\n", 3)))); + ok!(normal6("rest") => 8: Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml\n", 3)))); } } @@ -811,13 +823,13 @@ mod ill_formed { // ^= 9 err!(missing_doctype_name2("") => 13: IllFormedError::MissingDoctypeName); // ^= 13 - ok!(missing_doctype_name3("") => Event::DocType(BytesText::new("x"))); + ok!(missing_doctype_name3("") => 15: Event::DocType(BytesText::new("x"))); err!(unmatched_end_tag1("") => 0: IllFormedError::UnmatchedEndTag("".to_string())); err!(unmatched_end_tag2("") => 0: IllFormedError::UnmatchedEndTag("end".to_string())); err!(unmatched_end_tag3("") => 0: IllFormedError::UnmatchedEndTag("end".to_string())); - ok!(mismatched_end_tag1("") => Event::Start(BytesStart::new("start"))); + ok!(mismatched_end_tag1("") => 7: Event::Start(BytesStart::new("start"))); err2!(mismatched_end_tag2("") => 7: IllFormedError::MismatchedEndTag { // ^= 7 expected: "start".to_string(), @@ -834,7 +846,7 @@ mod ill_formed { found: "end".to_string(), }); - ok!(double_hyphen_in_comment1("") => Event::Comment(BytesText::new(""))); + ok!(double_hyphen_in_comment1("") => 7: Event::Comment(BytesText::new(""))); err!(double_hyphen_in_comment2("") => 4: IllFormedError::DoubleHyphenInComment); // ^= 4 err!(double_hyphen_in_comment3("") => 5: IllFormedError::DoubleHyphenInComment); diff --git a/tests/reader.rs b/tests/reader.rs index f1e58de7..4921cd87 100644 --- a/tests/reader.rs +++ b/tests/reader.rs @@ -1,159 +1,14 @@ use std::str::from_utf8; use quick_xml::events::{BytesCData, BytesEnd, BytesStart, BytesText, Event::*}; +use quick_xml::name::QName; use quick_xml::reader::Reader; use pretty_assertions::assert_eq; -/// Tests for https://github.com/tafia/quick-xml/issues/469 -/// Exported to reuse in `async-tokio` tests. -#[macro_export] -macro_rules! small_buffers_tests { - ( - #[$test:meta] - $read_event:ident: $BufReader:ty - $(, $async:ident, $await:ident)? - ) => { - mod small_buffers { - use quick_xml::events::{BytesCData, BytesDecl, BytesPI, BytesStart, BytesText, Event}; - use quick_xml::reader::Reader; - use pretty_assertions::assert_eq; - - #[$test] - $($async)? fn decl() { - let xml = ""; - // ^^^^^^^ data that fit into buffer - let size = xml.match_indices("?>").next().unwrap().0 + 1; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml ", 3))) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn pi() { - let xml = ""; - // ^^^^^ data that fit into buffer - let size = xml.match_indices("?>").next().unwrap().0 + 1; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::PI(BytesPI::new("pi")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn empty() { - let xml = ""; - // ^^^^^^^ data that fit into buffer - let size = xml.match_indices("/>").next().unwrap().0 + 1; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Empty(BytesStart::new("empty")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn cdata1() { - let xml = ""; - // ^^^^^^^^^^^^^^^ data that fit into buffer - let size = xml.match_indices("]]>").next().unwrap().0 + 1; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::CData(BytesCData::new("cdata")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn cdata2() { - let xml = ""; - // ^^^^^^^^^^^^^^^^ data that fit into buffer - let size = xml.match_indices("]]>").next().unwrap().0 + 2; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::CData(BytesCData::new("cdata")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn comment1() { - let xml = ""; - // ^^^^^^^^^^^^ data that fit into buffer - let size = xml.match_indices("-->").next().unwrap().0 + 1; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Comment(BytesText::new("comment")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - - #[$test] - $($async)? fn comment2() { - let xml = ""; - // ^^^^^^^^^^^^^ data that fit into buffer - let size = xml.match_indices("-->").next().unwrap().0 + 2; - let br = <$BufReader>::with_capacity(size, xml.as_bytes()); - let mut reader = Reader::from_reader(br); - let mut buf = Vec::new(); - - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Comment(BytesText::new("comment")) - ); - assert_eq!( - reader.$read_event(&mut buf) $(.$await)? .unwrap(), - Event::Eof - ); - } - } - }; -} +// Import `small_buffers_tests!` +#[macro_use] +mod helpers; small_buffers_tests!( #[test] @@ -411,3 +266,98 @@ mod double_dash { assert_eq!(r.read_event().unwrap(), End(BytesEnd::new("hello"))); } } + +/// This tests checks that read_to_end() correctly returns span even when +/// text is trimmed from both sides +mod read_to_end { + use super::*; + + mod borrowed { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn text() { + let mut r = Reader::from_str(" text "); + // ^0 ^5 ^11 + r.config_mut().trim_text(true); + + assert_eq!(r.read_event().unwrap(), Start(BytesStart::new("tag"))); + assert_eq!(r.read_to_end(QName(b"tag")).unwrap(), 5..11); + assert_eq!(r.read_event().unwrap(), Eof); + } + + #[test] + fn tag() { + let mut r = Reader::from_str(" "); + // ^0 ^5 ^16 + r.config_mut().trim_text(true); + + assert_eq!(r.read_event().unwrap(), Start(BytesStart::new("tag"))); + assert_eq!(r.read_to_end(QName(b"tag")).unwrap(), 5..16); + assert_eq!(r.read_event().unwrap(), Eof); + } + } + + mod buffered { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn text() { + let mut r = Reader::from_str(" text "); + // ^0 ^5 ^11 + r.config_mut().trim_text(true); + + let mut buf = Vec::new(); + assert_eq!( + r.read_event_into(&mut buf).unwrap(), + Start(BytesStart::new("tag")) + ); + assert_eq!(r.read_to_end_into(QName(b"tag"), &mut buf).unwrap(), 5..11); + assert_eq!(r.read_event_into(&mut buf).unwrap(), Eof); + } + + #[test] + fn tag() { + let mut r = Reader::from_str(" "); + // ^0 ^5 ^16 + r.config_mut().trim_text(true); + + let mut buf = Vec::new(); + assert_eq!( + r.read_event_into(&mut buf).unwrap(), + Start(BytesStart::new("tag")) + ); + assert_eq!(r.read_to_end_into(QName(b"tag"), &mut buf).unwrap(), 5..16); + assert_eq!(r.read_event_into(&mut buf).unwrap(), Eof); + } + } +} + +/// This tests checks that read_text() correctly returns text even when +/// text is trimmed from both sides +mod read_text { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn text() { + let mut r = Reader::from_str(" text "); + r.config_mut().trim_text(true); + + assert_eq!(r.read_event().unwrap(), Start(BytesStart::new("tag"))); + assert_eq!(r.read_text(QName(b"tag")).unwrap(), " text "); + assert_eq!(r.read_event().unwrap(), Eof); + } + + #[test] + fn tag() { + let mut r = Reader::from_str(" "); + r.config_mut().trim_text(true); + + assert_eq!(r.read_event().unwrap(), Start(BytesStart::new("tag"))); + assert_eq!(r.read_text(QName(b"tag")).unwrap(), " "); + assert_eq!(r.read_event().unwrap(), Eof); + } +} diff --git a/tests/serde-de-enum.rs b/tests/serde-de-enum.rs index 82028004..88832fd0 100644 --- a/tests/serde-de-enum.rs +++ b/tests/serde-de-enum.rs @@ -3,8 +3,8 @@ use quick_xml::DeError; use serde::Deserialize; -mod helpers; -use helpers::from_str; +mod serde_helpers; +use serde_helpers::from_str; #[derive(Debug, Deserialize, PartialEq)] struct Nested { diff --git a/tests/serde-de-seq.rs b/tests/serde-de-seq.rs index 24c50167..4ffa4105 100644 --- a/tests/serde-de-seq.rs +++ b/tests/serde-de-seq.rs @@ -3,8 +3,8 @@ use quick_xml::DeError; use serde::Deserialize; -mod helpers; -use helpers::from_str; +mod serde_helpers; +use serde_helpers::from_str; /// Check that top-level sequences can be deserialized from the multi-root XML documents mod top_level { diff --git a/tests/serde-de.rs b/tests/serde-de.rs index f0a4d07d..75526b40 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -8,8 +8,8 @@ use serde::de::IgnoredAny; use serde::serde_if_integer128; use serde::Deserialize; -mod helpers; -use helpers::from_str; +mod serde_helpers; +use serde_helpers::from_str; /// Tests for deserializing into specially named field `$text` which represent /// textual content of an XML element diff --git a/tests/serde_helpers/mod.rs b/tests/serde_helpers/mod.rs new file mode 100644 index 00000000..fa6432f1 --- /dev/null +++ b/tests/serde_helpers/mod.rs @@ -0,0 +1,24 @@ +//! Utility functions for serde integration tests + +use quick_xml::de::Deserializer; +use quick_xml::DeError; +use serde::Deserialize; + +/// Deserialize an instance of type T from a string of XML text. +/// If deserialization was succeeded checks that all XML events was consumed +pub fn from_str<'de, T>(source: &'de str) -> Result +where + T: Deserialize<'de>, +{ + // Log XML that we try to deserialize to see it in the failed tests output + dbg!(source); + let mut de = Deserializer::from_str(source); + let result = T::deserialize(&mut de); + + // If type was deserialized, the whole XML document should be consumed + if let Ok(_) = result { + assert!(de.is_empty(), "the whole XML document should be consumed"); + } + + result +}