@@ -11,7 +11,7 @@ use crate::{
11
11
12
12
pub struct JunitFormatter < T > {
13
13
out : OutputLocation < T > ,
14
- results : Vec < ( TestDesc , TestResult , Duration ) > ,
14
+ results : Vec < ( TestDesc , TestResult , Duration , Vec < u8 > ) > ,
15
15
}
16
16
17
17
impl < T : Write > JunitFormatter < T > {
@@ -26,6 +26,18 @@ impl<T: Write> JunitFormatter<T> {
26
26
}
27
27
}
28
28
29
+ fn str_to_cdata ( s : & str ) -> String {
30
+ // Drop the stdout in a cdata. Unfortunately, you can't put either of `]]>` or
31
+ // `<?'` in a CDATA block, so the escaping gets a little weird.
32
+ let escaped_output = s. replace ( "]]>" , "]]]]><![CDATA[>" ) ;
33
+ let escaped_output = escaped_output. replace ( "<?" , "<]]><![CDATA[?" ) ;
34
+ // We also smuggle newlines as 
 so as to keep all the output on one line
35
+ let escaped_output = escaped_output. replace ( "\n " , "]]>
<![CDATA[" ) ;
36
+ // Prune empty CDATA blocks resulting from any escaping
37
+ let escaped_output = escaped_output. replace ( "<![CDATA[]]>" , "" ) ;
38
+ format ! ( "<![CDATA[{}]]>" , escaped_output)
39
+ }
40
+
29
41
impl < T : Write > OutputFormatter for JunitFormatter < T > {
30
42
fn write_discovery_start ( & mut self ) -> io:: Result < ( ) > {
31
43
Err ( io:: Error :: new ( io:: ErrorKind :: NotFound , "Not yet implemented!" ) )
@@ -63,14 +75,14 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
63
75
desc : & TestDesc ,
64
76
result : & TestResult ,
65
77
exec_time : Option < & time:: TestExecTime > ,
66
- _stdout : & [ u8 ] ,
78
+ stdout : & [ u8 ] ,
67
79
_state : & ConsoleTestState ,
68
80
) -> io:: Result < ( ) > {
69
81
// Because the testsuite node holds some of the information as attributes, we can't write it
70
82
// until all of the tests have finished. Instead of writing every result as they come in, we add
71
83
// them to a Vec and write them all at once when run is complete.
72
84
let duration = exec_time. map ( |t| t. 0 ) . unwrap_or_default ( ) ;
73
- self . results . push ( ( desc. clone ( ) , result. clone ( ) , duration) ) ;
85
+ self . results . push ( ( desc. clone ( ) , result. clone ( ) , duration, stdout . to_vec ( ) ) ) ;
74
86
Ok ( ( ) )
75
87
}
76
88
fn write_run_finish ( & mut self , state : & ConsoleTestState ) -> io:: Result < bool > {
@@ -85,7 +97,7 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
85
97
>",
86
98
state. failed, state. total, state. ignored
87
99
) ) ?;
88
- for ( desc, result, duration) in std:: mem:: take ( & mut self . results ) {
100
+ for ( desc, result, duration, stdout ) in std:: mem:: take ( & mut self . results ) {
89
101
let ( class_name, test_name) = parse_class_name ( & desc) ;
90
102
match result {
91
103
TestResult :: TrIgnored => { /* no-op */ }
@@ -98,6 +110,11 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
98
110
duration. as_secs_f64( )
99
111
) ) ?;
100
112
self . write_message ( "<failure type=\" assert\" />" ) ?;
113
+ if !stdout. is_empty ( ) {
114
+ self . write_message ( "<system-out>" ) ?;
115
+ self . write_message ( & str_to_cdata ( & String :: from_utf8_lossy ( & stdout) ) ) ?;
116
+ self . write_message ( "</system-out>" ) ?;
117
+ }
101
118
self . write_message ( "</testcase>" ) ?;
102
119
}
103
120
@@ -110,6 +127,11 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
110
127
duration. as_secs_f64( )
111
128
) ) ?;
112
129
self . write_message ( & format ! ( "<failure message=\" {m}\" type=\" assert\" />" ) ) ?;
130
+ if !stdout. is_empty ( ) {
131
+ self . write_message ( "<system-out>" ) ?;
132
+ self . write_message ( & str_to_cdata ( & String :: from_utf8_lossy ( & stdout) ) ) ?;
133
+ self . write_message ( "</system-out>" ) ?;
134
+ }
113
135
self . write_message ( "</testcase>" ) ?;
114
136
}
115
137
@@ -136,11 +158,19 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
136
158
TestResult :: TrOk => {
137
159
self . write_message ( & format ! (
138
160
"<testcase classname=\" {}\" \
139
- name=\" {}\" time=\" {}\" /> ",
161
+ name=\" {}\" time=\" {}\" ",
140
162
class_name,
141
163
test_name,
142
164
duration. as_secs_f64( )
143
165
) ) ?;
166
+ if stdout. is_empty ( ) || !state. options . display_output {
167
+ self . write_message ( "/>" ) ?;
168
+ } else {
169
+ self . write_message ( "><system-out>" ) ?;
170
+ self . write_message ( & str_to_cdata ( & String :: from_utf8_lossy ( & stdout) ) ) ?;
171
+ self . write_message ( "</system-out>" ) ?;
172
+ self . write_message ( "</testcase>" ) ?;
173
+ }
144
174
}
145
175
}
146
176
}
0 commit comments