From c72f04fa0e4639455c4510ba451e89dc7fd33be9 Mon Sep 17 00:00:00 2001 From: jcamiel Date: Wed, 1 Nov 2023 20:23:49 +0100 Subject: [PATCH] Remove xmltree/indexmap dependency. Due to xmltree re-exposing an older version of indexmap, we couldnt' upgrade to the latest version of indemap. xmltree is a tree in-memory representation of an XML document that we use for JUnit export. As xmltree is a thin layer above xml-rs, we re-implement a thin tree in-memory XML document using xml-rs directly and remove xmltree/indexmap dependency. --- Cargo.lock | 30 +------ bin/update_crates.sh | 4 +- integration/tests_ok/junit.out.pattern | 2 +- packages/hurl/Cargo.toml | 2 - packages/hurl/src/report/junit/mod.rs | 68 ++++++---------- packages/hurl/src/report/junit/testcase.rs | 86 ++++++-------------- packages/hurl/src/report/junit/xml/mod.rs | 6 ++ packages/hurl/src/report/junit/xml/reader.rs | 1 + packages/hurl/src/report/junit/xml/writer.rs | 7 +- 9 files changed, 66 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce26c1eaa0e..06c0e74f73a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,12 +474,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.13.2" @@ -525,7 +519,6 @@ dependencies = [ "hex", "hex-literal", "hurl_core", - "indexmap", "lazy_static", "libflate", "libxml", @@ -540,7 +533,6 @@ dependencies = [ "uuid", "winres", "xml-rs", - "xmltree", ] [[package]] @@ -597,16 +589,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "is-terminal" version = "0.4.9" @@ -665,7 +647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" dependencies = [ "core2", - "hashbrown 0.13.2", + "hashbrown", "rle-decode-fast", ] @@ -1362,16 +1344,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" -[[package]] -name = "xmltree" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" -dependencies = [ - "indexmap", - "xml-rs", -] - [[package]] name = "zerocopy" version = "0.7.21" diff --git a/bin/update_crates.sh b/bin/update_crates.sh index 97c7e706044..48ad136ceca 100755 --- a/bin/update_crates.sh +++ b/bin/update_crates.sh @@ -116,9 +116,7 @@ main() { check_args "${arg}" updated_count=0 - # xmltree-rs crate v0.10.3 doesn't build with the latest indexmap crates - # see - blacklisted="indexmap" + blacklisted="" # update toml for package in packages/*; do diff --git a/integration/tests_ok/junit.out.pattern b/integration/tests_ok/junit.out.pattern index 3be6fc6c1df..c75c4b3f70d 100644 --- a/integration/tests_ok/junit.out.pattern +++ b/integration/tests_ok/junit.out.pattern @@ -1,4 +1,4 @@ -Assert body value +Assert body value --> tests_ok/test.2.hurl:8:1 | 8 | `Goodbye World!` diff --git a/packages/hurl/Cargo.toml b/packages/hurl/Cargo.toml index 3c603efec59..2b238474c16 100644 --- a/packages/hurl/Cargo.toml +++ b/packages/hurl/Cargo.toml @@ -30,7 +30,6 @@ glob = "0.3.1" hex = "0.4.3" hex-literal = "0.4.1" hurl_core = { version = "4.2.0-SNAPSHOT", path = "../hurl_core" } -indexmap = "1.9.3" libflate = "2.0.0" libxml = "0.3.3" md5 = "0.7.0" @@ -40,7 +39,6 @@ serde = "1.0.190" serde_json = "1.0.108" sha2 = "0.10.8" url = "2.4.1" -xmltree = { version = "0.10.3", features = ["attribute-order"] } xml-rs = { version = "0.8.19"} lazy_static = "1.4.0" # uuid features: lets you generate random UUIDs and use a faster (but still sufficiently random) RNG diff --git a/packages/hurl/src/report/junit/mod.rs b/packages/hurl/src/report/junit/mod.rs index d0bf5abc78c..8e7f6c73d84 100644 --- a/packages/hurl/src/report/junit/mod.rs +++ b/packages/hurl/src/report/junit/mod.rs @@ -58,20 +58,16 @@ mod testcase; mod xml; use std::fs::File; -use indexmap::IndexMap; -use xmltree::{Element, XMLNode}; - +use crate::report::junit::xml::{Element, XmlDocument}; use crate::report::Error; pub use testcase::Testcase; /// Creates a JUnit from a list of `testcases`. pub fn write_report(filename: &str, testcases: &[Testcase]) -> Result<(), Error> { - let mut testsuites = vec![]; - // If there is an existing JUnit report, we parses it to insert a new testsuite. let path = std::path::Path::new(&filename); - if path.exists() { - let s = match std::fs::read_to_string(path) { + let mut root = if path.exists() { + let file = match File::open(path) { Ok(s) => s, Err(why) => { return Err(Error { @@ -79,24 +75,16 @@ pub fn write_report(filename: &str, testcases: &[Testcase]) -> Result<(), Error> }); } }; - let root = Element::parse(s.as_bytes()).unwrap(); - for child in root.children { - if let XMLNode::Element(_) = child.clone() { - testsuites.push(child.clone()); - } - } - } + let doc = XmlDocument::parse(file).unwrap(); + doc.root.unwrap() + } else { + Element::new("testsuites") + }; let testsuite = create_testsuite(testcases); - testsuites.push(XMLNode::Element(testsuite)); - let report = Element { - name: "testsuites".to_string(), - prefix: None, - namespace: None, - namespaces: None, - attributes: IndexMap::new(), - children: testsuites, - }; + root = root.add_child(testsuite); + + let doc = XmlDocument::new(root); let file = match File::create(filename) { Ok(f) => f, Err(e) => { @@ -105,7 +93,7 @@ pub fn write_report(filename: &str, testcases: &[Testcase]) -> Result<(), Error> }); } }; - match report.write(file) { + match doc.write(file) { Ok(_) => Ok(()), Err(e) => Err(Error { message: format!("Failed to produce Junit report: {e:?}"), @@ -115,7 +103,6 @@ pub fn write_report(filename: &str, testcases: &[Testcase]) -> Result<(), Error> /// Returns a testsuite as a XML object, from a list of `testcases`. fn create_testsuite(testcases: &[Testcase]) -> Element { - let mut attrs = IndexMap::new(); let mut tests = 0; let mut errors = 0; let mut failures = 0; @@ -126,26 +113,21 @@ fn create_testsuite(testcases: &[Testcase]) -> Element { failures += cases.get_fail_count(); } - attrs.insert("tests".to_string(), tests.to_string()); - attrs.insert("errors".to_string(), errors.to_string()); - attrs.insert("failures".to_string(), failures.to_string()); + let mut element = Element::new("testsuite") + .attr("tests", &tests.to_string()) + .attr("errors", &errors.to_string()) + .attr("failures", &failures.to_string()); - let children = testcases - .iter() - .map(|t| XMLNode::Element(t.to_xml())) - .collect(); - Element { - name: "testsuite".to_string(), - prefix: None, - namespace: None, - namespaces: None, - attributes: attrs, - children, + for testcase in testcases.iter() { + let child = testcase.to_xml(); + element = element.add_child(child); } + element } #[cfg(test)] mod tests { + use crate::report::junit::xml::XmlDocument; use crate::report::junit::{create_testsuite, Testcase}; use crate::runner::{EntryResult, Error, HurlResult, RunnerError}; use hurl_core::ast::SourceInfo; @@ -214,11 +196,11 @@ mod tests { let tc = Testcase::from(&res, content, filename); testcases.push(tc); - let mut buffer = Vec::new(); - create_testsuite(&testcases).write(&mut buffer).unwrap(); + let suite = create_testsuite(&testcases); + let doc = XmlDocument::new(suite); assert_eq!( - std::str::from_utf8(&buffer).unwrap(), - "\ + doc.to_string().unwrap(), + "\ \ \ \ diff --git a/packages/hurl/src/report/junit/testcase.rs b/packages/hurl/src/report/junit/testcase.rs index 1a68a62d6ce..d4331c7b619 100644 --- a/packages/hurl/src/report/junit/testcase.rs +++ b/packages/hurl/src/report/junit/testcase.rs @@ -15,11 +15,9 @@ * limitations under the License. * */ -use super::xml::XmlDocument; +use crate::report::junit::xml::Element; use crate::runner::HurlResult; use crate::util::logger; -use indexmap::map::IndexMap; -use xmltree::{Element, XMLNode}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Testcase { @@ -58,44 +56,21 @@ impl Testcase { /// Serializes this testcase to XML. pub fn to_xml(&self) -> Element { - let name = "testcase".to_string(); - let mut attributes = IndexMap::new(); - attributes.insert("id".to_string(), self.id.clone()); - attributes.insert("name".to_string(), self.name.clone()); let time_in_seconds = format!("{:.3}", self.time_in_ms as f64 / 1000.0); - attributes.insert("time".to_string(), time_in_seconds); - - let mut children = vec![]; - for message in self.failures.clone() { - let element = Element { - prefix: None, - namespace: None, - namespaces: None, - name: "failure".to_string(), - attributes: IndexMap::new(), - children: vec![XMLNode::Text(message)], - }; - children.push(XMLNode::Element(element)); - } - for message in self.errors.clone() { - let element = Element { - prefix: None, - namespace: None, - namespaces: None, - name: "error".to_string(), - attributes: IndexMap::new(), - children: vec![XMLNode::Text(message)], - }; - children.push(XMLNode::Element(element)); + + let mut element = Element::new("testcase") + .attr("id", &self.id) + .attr("name", &self.name) + .attr("time", &time_in_seconds); + + for failure in self.failures.iter() { + element = element.add_child(Element::new("failure").text(failure)) } - Element { - name, - prefix: None, - namespace: None, - namespaces: None, - attributes, - children, + + for error in self.errors.iter() { + element = element.add_child(Element::new("error").text(error)) } + element } pub fn get_error_count(&self) -> usize { @@ -112,6 +87,7 @@ mod test { use hurl_core::ast::SourceInfo; use crate::report::junit::testcase::Testcase; + use crate::report::junit::xml::XmlDocument; use crate::runner::{EntryResult, Error, HurlResult, RunnerError}; #[test] @@ -124,16 +100,13 @@ mod test { timestamp: 1, }; - let mut buffer = Vec::new(); let content = ""; let filename = "test.hurl"; - Testcase::from(&hurl_result, content, filename) - .to_xml() - .write(&mut buffer) - .unwrap(); + let element = Testcase::from(&hurl_result, content, filename).to_xml(); + let doc = XmlDocument::new(element); assert_eq!( - std::str::from_utf8(&buffer).unwrap(), - r#""# + doc.to_string().unwrap(), + r#""# ); } @@ -164,14 +137,12 @@ HTTP/1.0 200 cookies: vec![], timestamp: 1, }; - let mut buffer = Vec::new(); - Testcase::from(&hurl_result, content, filename) - .to_xml() - .write(&mut buffer) - .unwrap(); + + let element = Testcase::from(&hurl_result, content, filename).to_xml(); + let doc = XmlDocument::new(element); assert_eq!( - std::str::from_utf8(&buffer).unwrap(), - r#"Assert status code + doc.to_string().unwrap(), + r#"Assert status code --> test.hurl:2:10 | 2 | HTTP/1.0 200 @@ -205,14 +176,11 @@ HTTP/1.0 200 cookies: vec![], timestamp: 1, }; - let mut buffer = Vec::new(); - Testcase::from(&hurl_result, content, filename) - .to_xml() - .write(&mut buffer) - .unwrap(); + let element = Testcase::from(&hurl_result, content, filename).to_xml(); + let doc = XmlDocument::new(element); assert_eq!( - std::str::from_utf8(&buffer).unwrap(), - r#"HTTP connection + doc.to_string().unwrap(), + r#"HTTP connection --> test.hurl:1:5 | 1 | GET http://unknown diff --git a/packages/hurl/src/report/junit/xml/mod.rs b/packages/hurl/src/report/junit/xml/mod.rs index d955e0d066e..28f2b0e45fb 100644 --- a/packages/hurl/src/report/junit/xml/mod.rs +++ b/packages/hurl/src/report/junit/xml/mod.rs @@ -31,6 +31,12 @@ pub struct XmlDocument { pub root: Option, } +impl XmlDocument { + pub fn new(root: Element) -> XmlDocument { + XmlDocument { root: Some(root) } + } +} + #[derive(Clone, Eq, PartialEq, Debug)] pub enum XmlNode { /// An XML element. diff --git a/packages/hurl/src/report/junit/xml/reader.rs b/packages/hurl/src/report/junit/xml/reader.rs index d8335b61dfe..99a59fe6bc6 100644 --- a/packages/hurl/src/report/junit/xml/reader.rs +++ b/packages/hurl/src/report/junit/xml/reader.rs @@ -32,6 +32,7 @@ pub enum ParserError { /// document returned is a in-memory tree representation of the whole document. impl XmlDocument { /// Convenient associated method to read and parse a XML string `source`. + #[allow(dead_code)] pub fn parse_str(source: &str) -> Result { let bytes = source.as_bytes(); XmlDocument::parse(bytes) diff --git a/packages/hurl/src/report/junit/xml/writer.rs b/packages/hurl/src/report/junit/xml/writer.rs index 47383ad89cf..304df60af7f 100644 --- a/packages/hurl/src/report/junit/xml/writer.rs +++ b/packages/hurl/src/report/junit/xml/writer.rs @@ -52,8 +52,9 @@ impl From for WriterError { } impl XmlDocument { - /// Convenient method to seralize an XML document to a string. - pub fn write_string(&self) -> Result { + /// Convenient method to serialize an XML document to a string. + #[allow(dead_code)] + pub fn to_string(&self) -> Result { let buffer = vec![]; let buffer = self.write(buffer)?; let str = String::from_utf8(buffer)?; @@ -194,7 +195,7 @@ mod tests { ; let doc = XmlDocument { root: Some(root) }; assert_eq!( - doc.write_string().unwrap(), + doc.to_string().unwrap(), "\ \ \