11use anyhow:: { anyhow, Context , Result } ;
2- use quick_xml:: events:: BytesStart ;
2+ use quick_xml:: events:: { BytesCData , BytesEnd , BytesStart } ;
33use serde_json:: Value ;
44use sha1:: { Digest , Sha1 } ;
55use std:: {
@@ -213,7 +213,19 @@ fn upload_bep_json_file(
213213 uploader. upload_artifact ( dry, None , build_event_json_file, mode)
214214}
215215
216- fn parse_test_xml ( path : & Path , label : & str ) -> Result < Option < Vec < u8 > > > {
216+ fn gen_error_content ( bazelci_task : & str , label : & str , name : & str , test_log : & str ) -> String {
217+ let mut buf = String :: new ( ) ;
218+ buf. push_str ( & format ! ( "BAZELCI_TASK={}\n " , bazelci_task) ) ;
219+ buf. push_str ( & format ! ( "TEST_TARGET={}\n " , label) ) ;
220+ buf. push_str ( & format ! ( "TEST_NAME={}\n " , name) ) ;
221+ buf. push_str ( "\n " ) ;
222+ buf. push_str ( & format ! ( "bazel test {} --test_filter={}\n " , & label, & name) ) ;
223+ buf. push_str ( "\n \n " ) ;
224+ buf. push_str ( test_log) ;
225+ buf
226+ }
227+
228+ fn parse_test_xml ( path : & Path , bazelci_task : & str , label : & str ) -> Result < Option < Vec < u8 > > > {
217229 use quick_xml:: { events:: Event , reader:: Reader , writer:: Writer } ;
218230 use std:: io:: Cursor ;
219231
@@ -222,10 +234,17 @@ fn parse_test_xml(path: &Path, label: &str) -> Result<Option<Vec<u8>>> {
222234 let mut buf = Vec :: new ( ) ;
223235 let mut reader = Reader :: from_file ( & path) ?;
224236 let fallback_classname = label. replace ( "//" , "" ) . replace ( "/" , "." ) . replace ( ":" , "." ) ;
237+ let mut in_error_tag = false ;
238+ let mut error_tag_stack = 0 ;
239+ let mut name = String :: new ( ) ;
225240 loop {
226241 match reader. read_event_into ( & mut buf) ? {
227242 Event :: Eof => break ,
228243 Event :: Start ( tag) => {
244+ if in_error_tag {
245+ error_tag_stack += 1 ;
246+ }
247+
229248 let tag = match tag. name ( ) . as_ref ( ) {
230249 b"testcase" => {
231250 has_testcase = true ;
@@ -241,6 +260,8 @@ fn parse_test_xml(path: &Path, label: &str) -> Result<Option<Vec<u8>>> {
241260 if attr. value . len ( ) == 0 {
242261 attr. value = fallback_classname. clone ( ) . into_bytes ( ) . into ( ) ;
243262 }
263+ } else if attr. key . as_ref ( ) == b"name" {
264+ name = String :: from_utf8_lossy ( & attr. value ) . to_string ( ) ;
244265 }
245266 new_tag. push_attribute ( attr) ;
246267 }
@@ -251,10 +272,52 @@ fn parse_test_xml(path: &Path, label: &str) -> Result<Option<Vec<u8>>> {
251272
252273 new_tag
253274 }
275+ b"failure" => {
276+ in_error_tag = true ;
277+ // replace failure with error
278+ let mut new_tag = BytesStart :: new ( "error" ) ;
279+ new_tag. push_attribute ( ( "message" , "" ) ) ;
280+ new_tag
281+ }
282+ b"error" => {
283+ in_error_tag = true ;
284+ tag
285+ }
254286 _ => tag,
255287 } ;
256288 writer. write_event ( Event :: Start ( tag) ) ?;
257289 }
290+ Event :: CData ( mut cdata) => {
291+ if in_error_tag {
292+ let test_log = String :: from_utf8_lossy ( & * cdata) ;
293+ let new_content = gen_error_content ( bazelci_task, label, & name, & test_log) ;
294+ cdata = BytesCData :: new ( new_content) ;
295+ }
296+
297+ writer. write_event ( Event :: CData ( cdata) ) ?;
298+ }
299+ Event :: Text ( text) => {
300+ if in_error_tag {
301+ let test_log = String :: from_utf8_lossy ( & * text) ;
302+ let new_content = gen_error_content ( bazelci_task, label, & name, & test_log) ;
303+ let cdata = BytesCData :: new ( new_content) ;
304+ writer. write_event ( Event :: CData ( cdata) ) ?;
305+ } else {
306+ writer. write_event ( Event :: Text ( text) ) ?;
307+ }
308+ }
309+ Event :: End ( mut tag) => {
310+ if in_error_tag {
311+ if error_tag_stack > 0 {
312+ error_tag_stack -= 1 ;
313+ } else {
314+ in_error_tag = false ;
315+ tag = BytesEnd :: new ( "error" ) ;
316+ }
317+ }
318+
319+ writer. write_event ( Event :: End ( tag) ) ?;
320+ }
258321 e => writer. write_event ( e) ?,
259322 }
260323 }
@@ -366,18 +429,19 @@ impl Uploader {
366429 dry : bool ,
367430 cwd : Option < & Path > ,
368431 token : & str ,
369- data : & Path ,
432+ bazelci_task : & str ,
433+ test_xml : & Path ,
370434 label : & str ,
371435 format : & str ,
372436 forms : & [ ( impl AsRef < str > , impl AsRef < str > ) ] ,
373437 ) -> Result < ( ) > {
374438 let full_path = if let Some ( cwd) = cwd {
375- cwd. join ( data )
439+ cwd. join ( test_xml )
376440 } else {
377- data . to_path_buf ( )
441+ test_xml . to_path_buf ( )
378442 } ;
379443
380- let content = parse_test_xml ( & full_path, label) ?;
444+ let content = parse_test_xml ( & full_path, & bazelci_task , label) ?;
381445 if content. is_none ( ) {
382446 return Ok ( ( ) ) ;
383447 }
@@ -441,18 +505,21 @@ impl Uploader {
441505 let token = maybe_get_env ( "BUILDKITE_ANALYTICS_TOKEN" ) ;
442506 let build_id = maybe_get_env ( "BUILDKITE_BUILD_ID" ) ;
443507 let step_id = maybe_get_env ( "BUILDKITE_STEP_ID" ) ;
444- if token. is_none ( ) || build_id. is_none ( ) || step_id. is_none ( ) {
508+ let bazelci_task = maybe_get_env ( "BAZELCI_TASK" ) ;
509+ if token. is_none ( ) || build_id. is_none ( ) || step_id. is_none ( ) || bazelci_task. is_none ( ) {
445510 return Ok ( ( ) ) ;
446511 }
447512
448513 let token = token. unwrap ( ) ;
449514 let build_id = build_id. unwrap ( ) ;
450515 let step_id = step_id. unwrap ( ) ;
516+ let bazelci_task = bazelci_task. unwrap ( ) ;
451517
452518 let mut forms = vec ! [
453519 ( "run_env[CI]" , "buildkite" . to_string( ) ) ,
454520 ( "run_env[key]" , format!( "{}/{}" , & build_id, & step_id) ) ,
455521 ( "run_env[build_id]" , build_id) ,
522+ ( "run_env[tags][]" , bazelci_task. clone( ) ) ,
456523 ] ;
457524
458525 for ( name, env) in [
@@ -462,14 +529,22 @@ impl Uploader {
462529 ( "run_env[number]" , "BUILDKITE_BUILD_NUMBER" ) ,
463530 ( "run_env[job_id]" , "BUILDKITE_JOB_ID" ) ,
464531 ( "run_env[message]" , "BUILDKITE_MESSAGE" ) ,
465- ( "run_env[tags][]" , "BAZELCI_TASK" ) ,
466532 ] {
467533 if let Some ( value) = maybe_get_env ( env) {
468534 forms. push ( ( name, value) ) ;
469535 }
470536 }
471537
472- self . upload_test_analytics ( dry, cwd, & token, test_xml, label, "junit" , & forms)
538+ self . upload_test_analytics (
539+ dry,
540+ cwd,
541+ & token,
542+ & bazelci_task,
543+ test_xml,
544+ label,
545+ "junit" ,
546+ & forms,
547+ )
473548 }
474549}
475550
0 commit comments