diff --git a/src/chatstates.rs b/src/chatstates.rs index 2cb657f6fcc2c3f885eb18516e3afd33ef9ef11e..9a63a28d23fbcf4fab8e150c46212f1dd91a1f7d 100644 --- a/src/chatstates.rs +++ b/src/chatstates.rs @@ -38,7 +38,8 @@ impl TryFrom for ChatState { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::CHATSTATES) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::CHATSTATES) { return Err(Error::ParseError("This is not a chatstate element.")); } for _ in elem.children() { diff --git a/src/compare_elements.rs b/src/compare_elements.rs new file mode 100644 index 0000000000000000000000000000000000000000..d133c85c2c51698b518893458c1cada19c0007a7 --- /dev/null +++ b/src/compare_elements.rs @@ -0,0 +1,118 @@ +use minidom::{Node, Element}; + +pub trait NamespaceAwareCompare { + /// Namespace-aware comparison for tests + fn compare_to(&self, other: &Self) -> bool; +} + +impl NamespaceAwareCompare for Node { + fn compare_to(&self, other: &Self) -> bool { + match (self, other) { + (&Node::Element(ref elem1), &Node::Element(ref elem2)) => + Element::compare_to(elem1, elem2), + (&Node::Text(ref text1), &Node::Text(ref text2)) => + text1 == text2, + _ => false, + } + } +} + +impl NamespaceAwareCompare for Element { + fn compare_to(&self, other: &Self) -> bool { + if self.name() == other.name() && + self.ns() == other.ns() && + self.attrs().eq(other.attrs()) + { + let child_elems = self.children().count(); + let text_is_whitespace = self.texts() + .all(|text| text.chars().all(char::is_whitespace)); + if child_elems > 0 && text_is_whitespace { + // Ignore all the whitespace text nodes + self.children().zip(other.children()) + .all(|(node1, node2)| node1.compare_to(node2)) + } else { + // Compare with text nodes + self.nodes().zip(other.nodes()) + .all(|(node1, node2)| node1.compare_to(node2)) + } + } else { + false + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use minidom::Element; + + #[test] + fn simple() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_name() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_attr_value() { + let elem1: Element = "x 3".parse().unwrap(); + let elem2: Element = "x 3".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn attr_order() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_texts() { + let elem1: Element = "foo".parse().unwrap(); + let elem2: Element = "bar".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn wrong_children() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_wrong() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(!elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_other_prefix() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } + + #[test] + fn xmlns_dup() { + let elem1: Element = "".parse().unwrap(); + let elem2: Element = "".parse().unwrap(); + assert!(elem1.compare_to(&elem2)); + } +} diff --git a/src/disco.rs b/src/disco.rs index b1d21680691242ab22fe6d05e4c2738510debcf0..e67e7973a6b73d38a2c2e37afc2ed27bbd9f96b8 100644 --- a/src/disco.rs +++ b/src/disco.rs @@ -370,6 +370,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; use std::str::FromStr; #[test] @@ -394,7 +395,7 @@ mod tests { assert_eq!(query.extensions[0].form_type, Some(String::from("example"))); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/ibr.rs b/src/ibr.rs index c51153e65f2f72bfd503d8ff3bbed07cc501fc3d..05281f333255b2d52b6ecdaf63c5284d116bb238 100644 --- a/src/ibr.rs +++ b/src/ibr.rs @@ -81,6 +81,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -157,7 +158,7 @@ mod tests { assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok()); assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err()); let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -190,6 +191,6 @@ mod tests { panic!(); } let elem2 = query.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/iq.rs b/src/iq.rs index 7e919985b0bdb6fb27978f008b1d37285af0b0fe..65c13a2411a04894835b873d5199a1be5192e8f1 100644 --- a/src/iq.rs +++ b/src/iq.rs @@ -297,6 +297,7 @@ impl From for Element { mod tests { use super::*; use stanza_error::{ErrorType, DefinedCondition}; + use compare_elements::NamespaceAwareCompare; #[test] fn test_require_type() { @@ -328,7 +329,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Get(element) => element == query, + IqType::Get(element) => element.compare_to(&query), _ => false }); } @@ -349,7 +350,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Set(element) => element == vcard, + IqType::Set(element) => element.compare_to(&vcard), _ => false }); } @@ -386,7 +387,7 @@ mod tests { assert_eq!(iq.to, None); assert_eq!(iq.id, None); assert!(match iq.payload { - IqType::Result(Some(element)) => element == query, + IqType::Result(Some(element)) => element.compare_to(&query), _ => false, }); } diff --git a/src/jingle.rs b/src/jingle.rs index d36d0862b951794c5da49e39bc6bf649422e2fa2..d20ed056c5053f36b76b18a7e95dcf03cba47b37 100644 --- a/src/jingle.rs +++ b/src/jingle.rs @@ -208,7 +208,8 @@ impl TryFrom for ReasonElement { let mut reason = None; let mut text = None; for child in elem.children() { - if child.ns() != Some(ns::JINGLE) { + let child_ns = child.ns(); + if child_ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE) { return Err(Error::ParseError("Reason contains a foreign element.")); } match child.name() { diff --git a/src/jingle_message.rs b/src/jingle_message.rs index 216e1877386fa185894a9bb0936c111b833693b9..57cebe8012e097b401d199441aa76c05ea6a729d 100644 --- a/src/jingle_message.rs +++ b/src/jingle_message.rs @@ -47,7 +47,8 @@ impl TryFrom for JingleMI { type Err = Error; fn try_from(elem: Element) -> Result { - if elem.ns() != Some(ns::JINGLE_MESSAGE) { + let ns = elem.ns(); + if ns.as_ref().map(|ns| ns.as_str()) != Some(ns::JINGLE_MESSAGE) { return Err(Error::ParseError("This is not a Jingle message element.")); } Ok(match elem.name() { diff --git a/src/jingle_s5b.rs b/src/jingle_s5b.rs index d454a3d99e060473c3348fe1c55736ec679f0904..6f8bb902106ebcdbdd1dedae5f00fda9c57d53da 100644 --- a/src/jingle_s5b.rs +++ b/src/jingle_s5b.rs @@ -181,6 +181,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -205,7 +206,7 @@ mod tests { payload: TransportPayload::Activated(String::from("coucou")), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -225,6 +226,6 @@ mod tests { })), }; let elem2: Element = transport.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/lib.rs b/src/lib.rs index cc9a8c31aa76313664a271fc907b3755f7c78bae..75b092fb56de96e1e07b674d9a9afc5781496a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,6 +171,10 @@ pub mod error; /// XML namespace definitions used through XMPP. pub mod ns; +#[cfg(test)] +/// Namespace-aware comparison for tests +mod compare_elements; + /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core diff --git a/src/message.rs b/src/message.rs index 5dba839eacd095851d031a7e898b0e2629c4dcbb..3a4795b15c59df2a482fe7671347b5e532ecca6f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -243,6 +243,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -281,7 +282,7 @@ mod tests { assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] @@ -293,7 +294,7 @@ mod tests { let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap())); message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] @@ -307,7 +308,7 @@ mod tests { assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap()); let elem2 = message.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/muc/user.rs b/src/muc/user.rs index 9e141ad2defe1fac3aeb41e91861477f41e22044..322525cbe2a3cadbe0a292f23f401947d8c15b3f 100644 --- a/src/muc/user.rs +++ b/src/muc/user.rs @@ -387,6 +387,7 @@ impl From for Element { mod tests { use super::*; use std::error::Error as StdError; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -418,7 +419,7 @@ mod tests { ".parse().unwrap(); let muc = MucUser { status: vec!(), items: vec!() }; let elem2 = muc.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/presence.rs b/src/presence.rs index a1f11fa690319437c1af43cbcc54385d3ddddf1c..e48e68509dbc8c6f79673527b3fee31d9ecc01d1 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -340,6 +340,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -363,7 +364,7 @@ mod tests { let elem: Element = "/>".parse().unwrap(); let presence = Presence::new(Type::Unavailable); let elem2 = presence.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } #[test] diff --git a/src/pubsub/event.rs b/src/pubsub/event.rs index 108d0ab1321cb1067b7f89374f48d8f18c4e0e3a..d055476d5b66ce4a034f1b8d6178bb6579915eda 100644 --- a/src/pubsub/event.rs +++ b/src/pubsub/event.rs @@ -272,6 +272,7 @@ impl From for Element { mod tests { use super::*; use std::str::FromStr; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -417,6 +418,6 @@ mod tests { } let elem2: Element = event.into(); - assert_eq!(elem, elem2); + assert!(elem.compare_to(&elem2)); } } diff --git a/src/roster.rs b/src/roster.rs index 9e2bba31db54f240188b913581e81227e2d5c8be..ae26c3d25971cf023c1930cf1e91e16535badb0e 100644 --- a/src/roster.rs +++ b/src/roster.rs @@ -137,6 +137,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_get() { @@ -206,7 +207,7 @@ mod tests { assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap()); assert_eq!(roster.items[0].groups[1], Group::from_str("B").unwrap()); let elem2 = roster.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } #[test] diff --git a/src/rsm.rs b/src/rsm.rs index fa6f6c814fdbcedf798cb7588cc8da5d37706b33..97e7abc6182852a55f2ec36f2d732e3452d6e191 100644 --- a/src/rsm.rs +++ b/src/rsm.rs @@ -122,6 +122,7 @@ impl From for Element { #[cfg(test)] mod tests { use super::*; + use compare_elements::NamespaceAwareCompare; #[test] fn test_simple() { @@ -197,6 +198,6 @@ mod tests { max: None, }; let elem2 = set2.into(); - assert_eq!(elem1, elem2); + assert!(elem1.compare_to(&elem2)); } } diff --git a/src/stanza_error.rs b/src/stanza_error.rs index 8ca9c86289aead4f6ec8d756cb26c9bafcf83784..1ed542c14239bdf9d44eccffefd2109f303a7afc 100644 --- a/src/stanza_error.rs +++ b/src/stanza_error.rs @@ -136,6 +136,7 @@ impl TryFrom for StanzaError { let mut other = None; for child in elem.children() { + let child_ns = child.ns(); if child.is("text", ns::XMPP_STANZAS) { for _ in child.children() { return Err(Error::ParseError("Unknown element in error text.")); @@ -144,7 +145,7 @@ impl TryFrom for StanzaError { if texts.insert(lang, child.text()).is_some() { return Err(Error::ParseError("Text element present twice for the same xml:lang.")); } - } else if child.ns() == Some(ns::XMPP_STANZAS) { + } else if child_ns.as_ref().map(|ns| ns.as_str()) == Some(ns::XMPP_STANZAS) { if defined_condition.is_some() { return Err(Error::ParseError("Error must not have more than one defined-condition.")); }