@@ -6,7 +6,7 @@ use crate::{
66 console:: { ConsoleTestState , OutputLocation } ,
77 test_result:: TestResult ,
88 time,
9- types:: TestDesc ,
9+ types:: { TestDesc , TestType } ,
1010} ;
1111
1212pub struct JunitFormatter < T > {
@@ -70,13 +70,15 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
7070 state. failed, state. total, state. ignored
7171 ) ) ?;
7272 for ( desc, result, duration) in std:: mem:: replace ( & mut self . results , Vec :: new ( ) ) {
73+ let ( class_name, test_name) = parse_class_name ( & desc) ;
7374 match result {
7475 TestResult :: TrIgnored => { /* no-op */ }
7576 TestResult :: TrFailed => {
7677 self . write_message ( & * format ! (
77- "<testcase classname=\" test.global \" \
78+ "<testcase classname=\" {} \" \
7879 name=\" {}\" time=\" {}\" >",
79- desc. name. as_slice( ) ,
80+ class_name,
81+ test_name,
8082 duration. as_secs( )
8183 ) ) ?;
8284 self . write_message ( "<failure type=\" assert\" />" ) ?;
@@ -85,9 +87,10 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
8587
8688 TestResult :: TrFailedMsg ( ref m) => {
8789 self . write_message ( & * format ! (
88- "<testcase classname=\" test.global \" \
90+ "<testcase classname=\" {} \" \
8991 name=\" {}\" time=\" {}\" >",
90- desc. name. as_slice( ) ,
92+ class_name,
93+ test_name,
9194 duration. as_secs( )
9295 ) ) ?;
9396 self . write_message ( & * format ! ( "<failure message=\" {}\" type=\" assert\" />" , m) ) ?;
@@ -96,9 +99,10 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
9699
97100 TestResult :: TrTimedFail => {
98101 self . write_message ( & * format ! (
99- "<testcase classname=\" test.global \" \
102+ "<testcase classname=\" {} \" \
100103 name=\" {}\" time=\" {}\" >",
101- desc. name. as_slice( ) ,
104+ class_name,
105+ test_name,
102106 duration. as_secs( )
103107 ) ) ?;
104108 self . write_message ( "<failure type=\" timeout\" />" ) ?;
@@ -107,18 +111,18 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
107111
108112 TestResult :: TrBench ( ref b) => {
109113 self . write_message ( & * format ! (
110- "<testcase classname=\" benchmark.global \" \
114+ "<testcase classname=\" benchmark::{} \" \
111115 name=\" {}\" time=\" {}\" />",
112- desc. name. as_slice( ) ,
113- b. ns_iter_summ. sum
116+ class_name, test_name, b. ns_iter_summ. sum
114117 ) ) ?;
115118 }
116119
117120 TestResult :: TrOk | TestResult :: TrAllowedFail => {
118121 self . write_message ( & * format ! (
119- "<testcase classname=\" test.global \" \
122+ "<testcase classname=\" {} \" \
120123 name=\" {}\" time=\" {}\" />",
121- desc. name. as_slice( ) ,
124+ class_name,
125+ test_name,
122126 duration. as_secs( )
123127 ) ) ?;
124128 }
@@ -132,3 +136,39 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
132136 Ok ( state. failed == 0 )
133137 }
134138}
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