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
+}