junit_report/
lib.rs

1/*
2 * Copyright (c) 2018 Pascal Bach
3 * Copyright (c) 2021 Siemens Mobility GmbH
4 *
5 * SPDX-License-Identifier:     MIT
6 */
7
8//! Create JUnit compatible XML reports.
9//!
10//! ## Example
11//!
12//! ```rust
13//!     use junit_report::{datetime, Duration, ReportBuilder, TestCase, TestCaseBuilder, TestSuiteBuilder};
14//!
15//!     let timestamp = datetime!(1970-01-01 01:01 UTC);
16//!
17//!     let test_success = TestCase::success("good test", Duration::seconds(15));
18//!     let test_error = TestCase::error(
19//!         "error test",
20//!         Duration::seconds(5),
21//!         "git error",
22//!         "unable to fetch",
23//!     );
24//!     let test_failure = TestCaseBuilder::failure(
25//!         "failure test",
26//!         Duration::seconds(10),
27//!         "assert_eq",
28//!         "not equal",
29//!     ).set_classname("classname").set_filepath("./foo.rs")
30//!     .build();
31//!
32//!     let ts1 = TestSuiteBuilder::new("ts1").set_timestamp(timestamp).build();
33//!
34//!     let ts2 = TestSuiteBuilder::new("ts2").set_timestamp(timestamp)
35//!       .add_testcase(test_success)
36//!       .add_testcase(test_error)
37//!       .add_testcase(test_failure)
38//!       .build();
39//!
40//!     let r = ReportBuilder::new()
41//!       .add_testsuite(ts1)
42//!       .add_testsuite(ts2)
43//!       .build();
44//!
45//!     let mut out: Vec<u8> = Vec::new();
46//!
47//!     r.write_xml(&mut out).unwrap();
48//! ```
49
50mod collections;
51mod reports;
52
53pub use quick_xml::Error;
54pub use time::{macros::datetime, Duration, OffsetDateTime};
55
56pub use crate::{
57    collections::{TestCase, TestCaseBuilder, TestResult, TestSuite, TestSuiteBuilder},
58    reports::{Report, ReportBuilder},
59};
60
61#[cfg(test)]
62mod tests {
63    use crate::{
64        datetime, Duration, Report, ReportBuilder, TestCase, TestCaseBuilder, TestSuite,
65        TestSuiteBuilder,
66    };
67
68    #[test]
69    fn empty_testsuites() {
70        let r = Report::new();
71
72        let mut out: Vec<u8> = Vec::new();
73
74        r.write_xml(&mut out).unwrap();
75
76        // language=xml
77        assert_eq!(
78            String::from_utf8(out).unwrap(),
79            "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites/>",
80        );
81    }
82
83    #[test]
84    fn add_empty_testsuite_single() {
85        let timestamp = datetime!(1970-01-01 01:01 UTC);
86
87        let ts1 = TestSuiteBuilder::new("ts1")
88            .set_timestamp(timestamp)
89            .build();
90        let mut tsb = TestSuiteBuilder::new("ts2");
91        tsb.set_timestamp(timestamp);
92        let ts2 = tsb.build();
93
94        let r = ReportBuilder::new()
95            .add_testsuite(ts1)
96            .add_testsuite(ts2)
97            .build();
98
99        let mut out: Vec<u8> = Vec::new();
100
101        r.write_xml(&mut out).unwrap();
102
103        // language=xml
104        assert_eq!(
105            String::from_utf8(out).unwrap(),
106            "\
107<?xml version=\"1.0\" encoding=\"utf-8\"?>\
108<testsuites>\
109  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
110  <testsuite id=\"1\" name=\"ts2\" package=\"testsuite/ts2\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
111</testsuites>",
112        );
113    }
114
115    #[test]
116    fn add_empty_testsuite_single_with_sysout() {
117        let timestamp = datetime!(1970-01-01 01:01 UTC);
118
119        let ts1 = TestSuiteBuilder::new("ts1")
120            .set_system_out("Test sysout")
121            .set_timestamp(timestamp)
122            .build();
123
124        let r = ReportBuilder::new().add_testsuite(ts1).build();
125
126        let mut out: Vec<u8> = Vec::new();
127
128        r.write_xml(&mut out).unwrap();
129
130        // language=xml
131        assert_eq!(
132            String::from_utf8(out).unwrap(),
133            "\
134<?xml version=\"1.0\" encoding=\"utf-8\"?>\
135<testsuites>\
136  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\">\
137    <system-out><![CDATA[Test sysout]]></system-out>\
138  </testsuite>\
139</testsuites>",
140        );
141    }
142
143    #[test]
144    fn add_empty_testsuite_single_with_syserror() {
145        let timestamp = datetime!(1970-01-01 01:01 UTC);
146
147        let ts1 = TestSuiteBuilder::new("ts1")
148            .set_system_err("Test syserror")
149            .set_timestamp(timestamp)
150            .build();
151
152        let r = ReportBuilder::new().add_testsuite(ts1).build();
153
154        let mut out: Vec<u8> = Vec::new();
155
156        r.write_xml(&mut out).unwrap();
157
158        // language=xml
159        assert_eq!(
160            String::from_utf8(out).unwrap(),
161            "\
162<?xml version=\"1.0\" encoding=\"utf-8\"?>\
163<testsuites>\
164  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\">\
165    <system-err><![CDATA[Test syserror]]></system-err>\
166  </testsuite>\
167</testsuites>",
168        );
169    }
170
171    #[test]
172    fn add_empty_testsuite_batch() {
173        let timestamp = datetime!(1970-01-01 01:01 UTC);
174
175        let ts1 = TestSuiteBuilder::new("ts1")
176            .set_timestamp(timestamp)
177            .build();
178        let ts2 = TestSuiteBuilder::new("ts2")
179            .set_timestamp(timestamp)
180            .build();
181
182        let v = vec![ts1, ts2];
183
184        let r = ReportBuilder::new().add_testsuites(v).build();
185
186        let mut out: Vec<u8> = Vec::new();
187
188        r.write_xml(&mut out).unwrap();
189
190        // language=xml
191        assert_eq!(
192            String::from_utf8(out).unwrap(),
193            "\
194<?xml version=\"1.0\" encoding=\"utf-8\"?>\
195<testsuites>\
196  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
197  <testsuite id=\"1\" name=\"ts2\" package=\"testsuite/ts2\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
198</testsuites>",
199        );
200    }
201
202    #[test]
203    fn count_tests() {
204        let mut ts = TestSuite::new("ts");
205
206        let tc1 = TestCase::success("mysuccess", Duration::milliseconds(6001));
207        let tc2 = TestCase::error(
208            "myerror",
209            Duration::seconds(6),
210            "Some Error",
211            "An Error happened",
212        );
213        let tc3 = TestCase::failure(
214            "myerror",
215            Duration::seconds(6),
216            "Some failure",
217            "A Failure happened",
218        );
219
220        assert_eq!(0, ts.tests());
221        assert_eq!(0, ts.errors());
222        assert_eq!(0, ts.failures());
223
224        ts.add_testcase(tc1);
225
226        assert_eq!(1, ts.tests());
227        assert_eq!(0, ts.errors());
228        assert_eq!(0, ts.failures());
229
230        ts.add_testcase(tc2);
231
232        assert_eq!(2, ts.tests());
233        assert_eq!(1, ts.errors());
234        assert_eq!(0, ts.failures());
235
236        ts.add_testcase(tc3);
237
238        assert_eq!(3, ts.tests());
239        assert_eq!(1, ts.errors());
240        assert_eq!(1, ts.failures());
241    }
242
243    #[test]
244    fn testcases_no_stdout_stderr() {
245        let timestamp = datetime!(1970-01-01 01:01 UTC);
246
247        let test_success = TestCaseBuilder::success("good test", Duration::milliseconds(15001))
248            .set_classname("MyClass")
249            .set_filepath("./foo.rs")
250            .build();
251        let test_error = TestCaseBuilder::error(
252            "error test",
253            Duration::seconds(5),
254            "git error",
255            "unable to fetch",
256        )
257        .build();
258        let test_failure = TestCaseBuilder::failure(
259            "failure test",
260            Duration::seconds(10),
261            "assert_eq",
262            "not equal",
263        )
264        .build();
265
266        let ts1 = TestSuiteBuilder::new("ts1")
267            .set_timestamp(timestamp)
268            .build();
269        let ts2 = TestSuiteBuilder::new("ts2")
270            .set_timestamp(timestamp)
271            .add_testcase(test_success)
272            .add_testcase(test_error)
273            .add_testcase(test_failure)
274            .build();
275
276        let r = ReportBuilder::new()
277            .add_testsuite(ts1)
278            .add_testsuite(ts2)
279            .build();
280
281        let mut out: Vec<u8> = Vec::new();
282
283        r.write_xml(&mut out).unwrap();
284
285        // language=xml
286        assert_eq!(
287            String::from_utf8(out).unwrap(),
288            "\
289<?xml version=\"1.0\" encoding=\"utf-8\"?>\
290<testsuites>\
291  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
292  <testsuite id=\"1\" name=\"ts2\" package=\"testsuite/ts2\" tests=\"3\" errors=\"1\" failures=\"1\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"30.001\">\
293    <testcase name=\"good test\" time=\"15.001\" classname=\"MyClass\" file=\"./foo.rs\"/>\
294    <testcase name=\"error test\" time=\"5\">\
295      <error type=\"git error\" message=\"unable to fetch\"/>\
296    </testcase>\
297    <testcase name=\"failure test\" time=\"10\">\
298      <failure type=\"assert_eq\" message=\"not equal\"/>\
299    </testcase>\
300  </testsuite>\
301</testsuites>",
302        );
303    }
304
305    #[test]
306    fn test_cases_with_sysout_and_syserr() {
307        let timestamp = datetime!(1970-01-01 01:01 UTC);
308
309        let test_success = TestCaseBuilder::success("good test", Duration::milliseconds(15001))
310            .set_classname("MyClass")
311            .set_filepath("./foo.rs")
312            .set_system_out("Some sysout message")
313            .build();
314        let test_error = TestCaseBuilder::error(
315            "error test",
316            Duration::seconds(5),
317            "git error",
318            "unable to fetch",
319        )
320        .set_system_err("Some syserror message")
321        .build();
322        let test_failure = TestCaseBuilder::failure(
323            "failure test",
324            Duration::seconds(10),
325            "assert_eq",
326            "not equal",
327        )
328        .set_system_out("System out or error message")
329        .set_system_err("Another system error message")
330        .build();
331
332        let ts1 = TestSuiteBuilder::new("ts1")
333            .set_timestamp(timestamp)
334            .build();
335        let ts2 = TestSuiteBuilder::new("ts2")
336            .set_timestamp(timestamp)
337            .add_testcase(test_success)
338            .add_testcase(test_error)
339            .add_testcase(test_failure)
340            .build();
341
342        let r = ReportBuilder::new()
343            .add_testsuite(ts1)
344            .add_testsuite(ts2)
345            .build();
346
347        let mut out: Vec<u8> = Vec::new();
348
349        r.write_xml(&mut out).unwrap();
350
351        // language=xml
352        assert_eq!(
353            String::from_utf8(out).unwrap(),
354            "\
355<?xml version=\"1.0\" encoding=\"utf-8\"?>\
356<testsuites>\
357  <testsuite id=\"0\" name=\"ts1\" package=\"testsuite/ts1\" tests=\"0\" errors=\"0\" failures=\"0\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"0\"/>\
358  <testsuite id=\"1\" name=\"ts2\" package=\"testsuite/ts2\" tests=\"3\" errors=\"1\" failures=\"1\" hostname=\"localhost\" timestamp=\"1970-01-01T01:01:00Z\" time=\"30.001\">\
359    <testcase name=\"good test\" time=\"15.001\" classname=\"MyClass\" file=\"./foo.rs\">\
360      <system-out><![CDATA[Some sysout message]]></system-out>\
361    </testcase>\
362    <testcase name=\"error test\" time=\"5\">\
363      <error type=\"git error\" message=\"unable to fetch\"><![CDATA[Some syserror message]]></error>\
364    </testcase>\
365    <testcase name=\"failure test\" time=\"10\">\
366      <failure type=\"assert_eq\" message=\"not equal\"><![CDATA[System out or error message]]><![CDATA[Another system error message]]></failure>\
367    </testcase>\
368  </testsuite>\
369</testsuites>",
370        );
371    }
372}