1use crate::results::{Event, EventKind};
4use crate::errors::SuityError;
5use std::io::{Write,self};
6use xml_writer::XmlWriter;
7
8#[derive(Debug, Eq, PartialEq)]
12pub struct Failure {
13 pub message: String,
15}
16
17#[derive(Debug, Eq, PartialEq)]
19pub struct TestCase {
20 pub name: String,
22 pub failure: Option<Failure>,
24}
25
26#[derive(Debug, Eq, PartialEq)]
27pub struct TestSuite {
28 pub name: String,
30 pub errors: u64,
32 pub failures: u64,
34 pub tests: u64,
36 pub test_cases: Vec<TestCase>,
37}
38
39
40
41impl TestSuite {
42 pub fn new(events: Vec<Event>, name: String) -> Result<TestSuite,SuityError> {
46
47 let mut suite = TestSuite {
48 name,
49 errors: 0,
50 failures:0,
51 tests: 0,
52 test_cases: Vec::new()
53 };
54
55 let mut counter = 0;
56
57
58 for event in events {
59 match event {
60 Event::Suite(s) => {
61 match s.event {
62 EventKind::Ignored => { },
63 EventKind::Started => {
64 suite.tests = s.test_count.unwrap();
65 counter += 1;
66 if counter > 1 {
67 return Err(SuityError::MultipleTestRuns);
68 }
69 },
70 EventKind::Failed | EventKind::Ok => {
71 suite.failures = s.failed.unwrap();
72 }
73 }
74 },
75 Event::Test(t) => {
76 match t.event {
77 EventKind::Started => { },
78 EventKind::Ignored => { },
79 EventKind::Ok => {
80 suite.test_cases.push(
81 TestCase {
82 name: t.name,
83 failure: None
84 }
85 )
86 }
87 EventKind::Failed => {
88 suite.test_cases.push(
89 TestCase {
90 name: t.name,
91 failure: Some(Failure{
92 message: t.stdout.unwrap()
93 })
94 }
95 )
96 }
97 }
98
99 }
100 }
101 }
102 Ok(suite)
103 }
104}
105
106
107pub fn write_as_xml<W: Write>(suites: &Vec<TestSuite>, writer: W) -> Result<(),io::Error> {
108 let mut xml = XmlWriter::new(writer);
109 xml.dtd("utf-8")?;
110 xml.begin_elem("testsuites")?;
111 for suite in suites {
112 xml.begin_elem("testsuite")?;
113 xml.attr_esc("name", &suite.name)?;
114 xml.attr("errors", suite.errors.to_string().as_str())?;
115 xml.attr("failures", suite.failures.to_string().as_str())?;
116 xml.attr("tests", suite.tests.to_string().as_str())?;
117 for testcase in &suite.test_cases {
118 xml.begin_elem("testcase")?;
119 xml.attr("name", &testcase.name)?;
120 if let Some(ref failure) = &testcase.failure {
121 xml.begin_elem("failure")?;
122 xml.attr_esc("message", &failure.message)?;
123 xml.end_elem()?;
124 }
125 xml.end_elem()?;
126 }
127 xml.end_elem()?;
128 }
129 xml.end_elem()?;
130 xml.close()?;
131 xml.flush()
132}
133
134#[cfg(test)]
135mod tests {
136
137 use crate::results::parse_test_results;
138 use super::{TestSuite, TestCase, Failure, write_as_xml};
139
140 #[test]
141 fn test_simple_output() {
142 let stdout = r#"{ "type": "suite", "event": "started", "test_count": 1 }
143{ "type": "test", "event": "started", "name": "parsers::test::test_zpools_on_single_zpool" }
144{ "type": "test", "name": "parsers::test::test_zpools_on_single_zpool", "event": "ok" }
145{ "type": "suite", "event": "ok", "passed": 1, "failed": 0, "allowed_fail": 0, "ignored": 0, "measured": 0, "filtered_out": 40 }"#;
146
147 let events = parse_test_results(stdout);
148
149 let name = String::from("Doc Tests");
150 let test_name = String::from("parsers::test::test_zpools_on_single_zpool");
151 let expected_test_case = TestCase {
152 name: test_name.clone(),
153 failure: None,
154 };
155 let expected = TestSuite {
156 name: name.clone(),
157 errors: 0,
158 failures: 0,
159 tests: 1,
160 test_cases: vec![expected_test_case]
161 };
162 let suite = TestSuite::new(events, name).unwrap();
163
164 assert_eq!(expected, suite);
165 }
166
167 #[test]
168 fn test_failed_output() {
169 let stdout = r#"{ "type": "suite", "event": "started", "test_count": 2 }
170{ "type": "test", "event": "started", "name": "parsers::test::test_zpools_on_single_zpool" }
171{ "type": "test", "name": "parsers::test::test_zpools_on_single_zpool", "event": "ok" }
172{ "type": "test", "event": "started", "name": "failed" }
173{ "type": "test", "name": "failed", "event": "failed", "stdout": "idk dawg" }
174{ "type": "suite", "event": "ok", "passed": 1, "failed": 1, "allowed_fail": 0, "ignored": 0, "measured": 0, "filtered_out": 40 }"#;
175
176 let events = parse_test_results(stdout);
177
178 let name = String::from("Doc Tests");
179 let expected_test_case = TestCase {
180 name: String::from("parsers::test::test_zpools_on_single_zpool"),
181 failure: None,
182 };
183 let expected_test_case2 = TestCase {
184 name: String::from("failed"),
185 failure: Some(Failure {
186 message: String::from("idk dawg")
187 }),
188 };
189 let expected = TestSuite {
190 name: name.clone(),
191 errors: 0,
192 failures: 1,
193 tests: 2,
194 test_cases: vec![expected_test_case, expected_test_case2]
195 };
196 let suite = TestSuite::new(events, name).unwrap();
197
198 assert_eq!(expected, suite);
199 }
200
201
202 #[test]
203 fn test_generate_xml_no_error_single_testsuite() {
204 let stdout = r#"{ "type": "suite", "event": "started", "test_count": 2 }
205{ "type": "test", "event": "started", "name": "parsers::test::test_zpools_on_single_zpool" }
206{ "type": "test", "name": "parsers::test::test_zpools_on_single_zpool", "event": "ok" }
207{ "type": "test", "event": "started", "name": "failed" }
208{ "type": "test", "name": "failed", "event": "failed", "stdout": "idk dawg" }
209{ "type": "suite", "event": "ok", "passed": 1, "failed": 1, "allowed_fail": 0, "ignored": 0, "measured": 0, "filtered_out": 40 }"#;
210
211 let name = String::from("Doc Tests");
212 let events = parse_test_results(stdout);
213 let suite = TestSuite::new(events, name).unwrap();
214
215 let suites = vec![suite];
216
217 let mut output = Vec::with_capacity(128);
218
219 write_as_xml(&suites, &mut output).unwrap();
220 }
221 #[test]
222 fn test_multiple_outputs() {
223 let stdout = r#"{ "type": "suite", "event": "started", "test_count": 1 }
224{ "type": "test", "event": "started", "name": "parsers::test::test_zpools_on_single_zpool" }
225{ "type": "test", "name": "parsers::test::test_zpools_on_single_zpool", "event": "ok" }
226{ "type": "suite", "event": "ok", "passed": 1, "failed": 0, "allowed_fail": 0, "ignored": 0, "measured": 0, "filtered_out": 40 }
227{ "type": "suite", "event": "started", "test_count": 1 }
228{ "type": "test", "event": "started", "name": "parsers::test::test_zpools_on_single_zpool" }
229{ "type": "test", "name": "parsers::test::test_zpools_on_single_zpool", "event": "ok" }
230{ "type": "suite", "event": "ok", "passed": 1, "failed": 0, "allowed_fail": 0, "ignored": 0, "measured": 0, "filtered_out": 40 }"#;
231 let events = parse_test_results(stdout);
232 let suite = TestSuite::new(events, String::from("should fail"));
233 assert!(suite.is_err());
234 }
235}