@@ -6,7 +6,7 @@ use crate::{
6
6
console:: { ConsoleTestState , OutputLocation } ,
7
7
test_result:: TestResult ,
8
8
time,
9
- types:: TestDesc ,
9
+ types:: { TestDesc , TestType } ,
10
10
} ;
11
11
12
12
pub struct JunitFormatter < T > {
@@ -70,13 +70,15 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
70
70
state. failed, state. total, state. ignored
71
71
) ) ?;
72
72
for ( desc, result, duration) in std:: mem:: replace ( & mut self . results , Vec :: new ( ) ) {
73
+ let ( class_name, test_name) = parse_class_name ( & desc) ;
73
74
match result {
74
75
TestResult :: TrIgnored => { /* no-op */ }
75
76
TestResult :: TrFailed => {
76
77
self . write_message ( & * format ! (
77
- "<testcase classname=\" test.global \" \
78
+ "<testcase classname=\" {} \" \
78
79
name=\" {}\" time=\" {}\" >",
79
- desc. name. as_slice( ) ,
80
+ class_name,
81
+ test_name,
80
82
duration. as_secs( )
81
83
) ) ?;
82
84
self . write_message ( "<failure type=\" assert\" />" ) ?;
@@ -85,9 +87,10 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
85
87
86
88
TestResult :: TrFailedMsg ( ref m) => {
87
89
self . write_message ( & * format ! (
88
- "<testcase classname=\" test.global \" \
90
+ "<testcase classname=\" {} \" \
89
91
name=\" {}\" time=\" {}\" >",
90
- desc. name. as_slice( ) ,
92
+ class_name,
93
+ test_name,
91
94
duration. as_secs( )
92
95
) ) ?;
93
96
self . write_message ( & * format ! ( "<failure message=\" {}\" type=\" assert\" />" , m) ) ?;
@@ -96,9 +99,10 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
96
99
97
100
TestResult :: TrTimedFail => {
98
101
self . write_message ( & * format ! (
99
- "<testcase classname=\" test.global \" \
102
+ "<testcase classname=\" {} \" \
100
103
name=\" {}\" time=\" {}\" >",
101
- desc. name. as_slice( ) ,
104
+ class_name,
105
+ test_name,
102
106
duration. as_secs( )
103
107
) ) ?;
104
108
self . write_message ( "<failure type=\" timeout\" />" ) ?;
@@ -107,18 +111,18 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
107
111
108
112
TestResult :: TrBench ( ref b) => {
109
113
self . write_message ( & * format ! (
110
- "<testcase classname=\" benchmark.global \" \
114
+ "<testcase classname=\" benchmark::{} \" \
111
115
name=\" {}\" time=\" {}\" />",
112
- desc. name. as_slice( ) ,
113
- b. ns_iter_summ. sum
116
+ class_name, test_name, b. ns_iter_summ. sum
114
117
) ) ?;
115
118
}
116
119
117
120
TestResult :: TrOk | TestResult :: TrAllowedFail => {
118
121
self . write_message ( & * format ! (
119
- "<testcase classname=\" test.global \" \
122
+ "<testcase classname=\" {} \" \
120
123
name=\" {}\" time=\" {}\" />",
121
- desc. name. as_slice( ) ,
124
+ class_name,
125
+ test_name,
122
126
duration. as_secs( )
123
127
) ) ?;
124
128
}
@@ -132,3 +136,39 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
132
136
Ok ( state. failed == 0 )
133
137
}
134
138
}
139
+
140
+ fn parse_class_name ( desc : & TestDesc ) -> ( String , String ) {
141
+ match desc. test_type {
142
+ TestType :: UnitTest => parse_class_name_unit ( desc) ,
143
+ TestType :: DocTest => parse_class_name_doc ( desc) ,
144
+ TestType :: IntegrationTest => parse_class_name_integration ( desc) ,
145
+ TestType :: Unknown => ( String :: from ( "unknown" ) , String :: from ( desc. name . as_slice ( ) ) ) ,
146
+ }
147
+ }
148
+
149
+ fn parse_class_name_unit ( desc : & TestDesc ) -> ( String , String ) {
150
+ // Module path => classname
151
+ // Function name => name
152
+ let module_segments: Vec < & str > = desc. name . as_slice ( ) . split ( "::" ) . collect ( ) ;
153
+ let ( class_name, test_name) = match module_segments[ ..] {
154
+ [ test] => ( String :: from ( "crate" ) , String :: from ( test) ) ,
155
+ [ ref path @ .., test] => ( path. join ( "::" ) , String :: from ( test) ) ,
156
+ [ ..] => unreachable ! ( ) ,
157
+ } ;
158
+ ( class_name, test_name)
159
+ }
160
+
161
+ fn parse_class_name_doc ( desc : & TestDesc ) -> ( String , String ) {
162
+ // File path => classname
163
+ // Line # => test name
164
+ let segments: Vec < & str > = desc. name . as_slice ( ) . split ( " - " ) . collect ( ) ;
165
+ let ( class_name, test_name) = match segments[ ..] {
166
+ [ file, line] => ( String :: from ( file. trim ( ) ) , String :: from ( line. trim ( ) ) ) ,
167
+ [ ..] => unreachable ! ( ) ,
168
+ } ;
169
+ ( class_name, test_name)
170
+ }
171
+
172
+ fn parse_class_name_integration ( desc : & TestDesc ) -> ( String , String ) {
173
+ ( String :: from ( "integration" ) , String :: from ( desc. name . as_slice ( ) ) )
174
+ }
0 commit comments