From 68d96542447c9cf3c31bac92fd189fb11e067bc7 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Thu, 12 Dec 2024 15:23:01 -0800 Subject: [PATCH 01/16] parsing labels --- context/src/bazel_bep/parser.rs | 119 +++++++++++++++++++------------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index 160606a9..4ce514d2 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -1,8 +1,9 @@ -use std::path::PathBuf; - use anyhow::Ok; -use bazel_bep::types::build_event_stream::{build_event::Payload, file::File::Uri, BuildEvent}; +use bazel_bep::types::build_event_stream::{ + build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, BuildEventId, TestStatus, +}; use serde_json::Deserializer; +use std::{collections::HashSet, path::PathBuf}; #[derive(Debug, Clone, Default)] pub struct TestResult { @@ -68,59 +69,81 @@ impl BazelBepParser { let file = std::fs::File::open(&self.bazel_bep_path)?; let reader = std::io::BufReader::new(file); - let (errors, test_results, bep_test_events) = Deserializer::from_reader(reader) - .into_iter::() - .fold( - ( - Vec::::new(), - Vec::::new(), - Vec::::new(), - ), - |(mut errors, mut test_results, mut bep_test_events), parse_event| { - match parse_event { - Result::Err(ref err) => { - errors.push(format!("Error parsing build event: {}", err)); - } - Result::Ok(build_event) => { - if let Some(Payload::TestResult(test_result)) = &build_event.payload { - let xml_files: Vec = test_result - .test_action_output - .iter() - .filter_map(|action_output| { - if action_output.name.ends_with(".xml") { - action_output.file.clone().and_then(|f| { - if let Uri(uri) = f { - Some( - uri.strip_prefix(FILE_URI_PREFIX) - .unwrap_or(&uri) - .to_string(), - ) - } else { - None - } - }) - } else { - None - } - }) - .collect(); - - let cached = - if let Some(execution_info) = &test_result.execution_info { + let (errors, test_results, pass_labels, bep_test_events) = + Deserializer::from_reader(reader) + .into_iter::() + .fold( + ( + Vec::::new(), + Vec::::new(), + HashSet::::new(), + Vec::::new(), + ), + |(mut errors, mut test_results, mut pass_labels, mut bep_test_events), + parse_event| { + match parse_event { + Result::Err(ref err) => { + errors.push(format!("Error parsing build event: {}", err)); + } + Result::Ok(build_event) => { + if let ( + Some(Payload::TestSummary(test_summary)), + Some(Id::TestSummary(id)), + ) = (&build_event.payload, &build_event.id.and_then(|id| id.id)) + { + // These are the cases in which bazel marks a test as passing + if test_summary.overall_status == TestStatus::Passed as i32 + || test_summary.overall_status == TestStatus::Flaky as i32 + { + pass_labels.insert(id.label.clone()); + } + bep_test_events.push(build_event); + } else if let ( + Some(Payload::TestResult(test_result)), + Some(Id::TestResult(id)), + ) = + (&build_event.payload, &build_event.id.and_then(|id| id.id)) + { + // TODO: TYLER GRAB ID TOO + let xml_files = test_result + .test_action_output + .iter() + .filter_map(|action_output| { + if action_output.name.ends_with(".xml") { + action_output.file.clone().and_then(|f| { + if let Uri(uri) = f { + Some( + uri.strip_prefix(FILE_URI_PREFIX) + .unwrap_or(&uri) + .to_string(), + ) + } else { + None + } + }) + } else { + None + } + }) + .collect(); + + let cached = if let Some(execution_info) = + &test_result.execution_info + { execution_info.cached_remotely || test_result.cached_locally } else { test_result.cached_locally }; - bep_test_events.push(build_event); - test_results.push(TestResult { cached, xml_files }); + bep_test_events.push(build_event); + test_results.push(TestResult { cached, xml_files }); + } } } - } - (errors, test_results, bep_test_events) - }, - ); + (errors, test_results, pass_labels, bep_test_events) + }, + ); Ok(BepParseResult { bep_test_events, From 6c3a06096ce4ffddb16abcf20b6805d0c1535e11 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Thu, 12 Dec 2024 17:04:09 -0800 Subject: [PATCH 02/16] validated an upload was happening correctly, now just need to attach to junit --- context/src/bazel_bep/parser.rs | 110 +++++++++++++++++--------------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index 4ce514d2..fda84dd4 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -7,6 +7,7 @@ use std::{collections::HashSet, path::PathBuf}; #[derive(Debug, Clone, Default)] pub struct TestResult { + label: String, pub cached: bool, pub xml_files: Vec, } @@ -86,57 +87,66 @@ impl BazelBepParser { errors.push(format!("Error parsing build event: {}", err)); } Result::Ok(build_event) => { - if let ( - Some(Payload::TestSummary(test_summary)), - Some(Id::TestSummary(id)), - ) = (&build_event.payload, &build_event.id.and_then(|id| id.id)) - { - // These are the cases in which bazel marks a test as passing - if test_summary.overall_status == TestStatus::Passed as i32 - || test_summary.overall_status == TestStatus::Flaky as i32 - { - pass_labels.insert(id.label.clone()); + match ( + &build_event.payload, + &build_event.clone().id.and_then(|id| id.id), + ) { + ( + Some(Payload::TestSummary(test_summary)), + Some(Id::TestSummary(id)), + ) => { + // These are the cases in which bazel marks a test as passing + if test_summary.overall_status == TestStatus::Passed as i32 + || test_summary.overall_status + == TestStatus::Flaky as i32 + { + pass_labels.insert(id.label.clone()); + } + bep_test_events.push(build_event); } - bep_test_events.push(build_event); - } else if let ( - Some(Payload::TestResult(test_result)), - Some(Id::TestResult(id)), - ) = - (&build_event.payload, &build_event.id.and_then(|id| id.id)) - { - // TODO: TYLER GRAB ID TOO - let xml_files = test_result - .test_action_output - .iter() - .filter_map(|action_output| { - if action_output.name.ends_with(".xml") { - action_output.file.clone().and_then(|f| { - if let Uri(uri) = f { - Some( - uri.strip_prefix(FILE_URI_PREFIX) - .unwrap_or(&uri) - .to_string(), - ) - } else { - None - } - }) - } else { - None - } - }) - .collect(); - - let cached = if let Some(execution_info) = - &test_result.execution_info - { - execution_info.cached_remotely || test_result.cached_locally - } else { - test_result.cached_locally - }; - - bep_test_events.push(build_event); - test_results.push(TestResult { cached, xml_files }); + ( + Some(Payload::TestResult(test_result)), + Some(Id::TestResult(id)), + ) => { + let xml_files = test_result + .test_action_output + .iter() + .filter_map(|action_output| { + if action_output.name.ends_with(".xml") { + action_output.file.clone().and_then(|f| { + if let Uri(uri) = f { + Some( + uri.strip_prefix(FILE_URI_PREFIX) + .unwrap_or(&uri) + .to_string(), + ) + } else { + None + } + }) + } else { + None + } + }) + .collect(); + + let cached = if let Some(execution_info) = + &test_result.execution_info + { + execution_info.cached_remotely + || test_result.cached_locally + } else { + test_result.cached_locally + }; + + test_results.push(TestResult { + label: id.label.clone(), + cached, + xml_files, + }); + bep_test_events.push(build_event); + } + _ => {} } } } From 37cc44ce2c17beacfe530ebb35f024a1f5204bc7 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Fri, 13 Dec 2024 12:06:40 -0800 Subject: [PATCH 03/16] set override pass --- context/src/bazel_bep/parser.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index fda84dd4..d1ec5875 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -1,15 +1,16 @@ use anyhow::Ok; use bazel_bep::types::build_event_stream::{ - build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, BuildEventId, TestStatus, + build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, TestStatus, }; use serde_json::Deserializer; use std::{collections::HashSet, path::PathBuf}; #[derive(Debug, Clone, Default)] pub struct TestResult { - label: String, + pub label: String, pub cached: bool, pub xml_files: Vec, + pub override_pass: bool, } const FILE_URI_PREFIX: &str = "file://"; @@ -143,6 +144,7 @@ impl BazelBepParser { label: id.label.clone(), cached, xml_files, + override_pass: false, }); bep_test_events.push(build_event); } @@ -158,7 +160,16 @@ impl BazelBepParser { Ok(BepParseResult { bep_test_events, errors, - test_results, + test_results: test_results.iter().map(|test_result| { + let mut override_pass = false; + if pass_labels.contains(&test_result.label.to_string()) { + override_pass = true; + } + TestResult { + override_pass, + ..test_result.clone() + } + }).collect(), }) } } From b30df0d2877e90172548a7b77de655f6b9405987 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 19:10:20 -0800 Subject: [PATCH 04/16] small test fix --- cli-tests/src/upload.rs | 1 + cli-tests/src/utils.rs | 12 +++++++++++- cli/src/runner.rs | 1 + context/src/bazel_bep/parser.rs | 31 +++++++++++++++++-------------- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/cli-tests/src/upload.rs b/cli-tests/src/upload.rs index 81ff98a3..542b0001 100644 --- a/cli-tests/src/upload.rs +++ b/cli-tests/src/upload.rs @@ -251,6 +251,7 @@ async fn upload_bundle_using_bep() { async fn upload_bundle_success_status_code() { let temp_dir = tempdir().unwrap(); generate_mock_git_repo(&temp_dir); + // DONOTLAND TODO: TYLER NEED TO FIX THIS TO INCLUDE SUMMARIES let test_bep_path = get_test_file_path("test_fixtures/bep_retries"); let uri_fail = format!( "file://{}", diff --git a/cli-tests/src/utils.rs b/cli-tests/src/utils.rs index 94b070af..7c31a162 100644 --- a/cli-tests/src/utils.rs +++ b/cli-tests/src/utils.rs @@ -1,5 +1,8 @@ use bazel_bep::types::build_event_stream::{ - build_event::Payload, file::File::Uri, BuildEvent, File, TestResult, + build_event::Payload, + build_event_id::{Id, TestResultId}, + file::File::Uri, + BuildEvent, BuildEventId, File, TestResult, }; use chrono::{TimeDelta, Utc}; use escargot::{CargoBuild, CargoRun}; @@ -43,6 +46,7 @@ pub fn generate_mock_valid_junit_xmls>(directory: T) -> Vec>(directory: T) { let mock_junits = generate_mock_valid_junit_xmls(&directory); + // TODO: TYLER SHOULD WE MAKE TESTSUMMARY EVENTS TOO? DONOTLAND let build_events: Vec = mock_junits .iter() .map(|junit| { @@ -54,6 +58,12 @@ pub fn generate_mock_bazel_bep>(directory: T) { ..Default::default() }]; build_event.payload = Some(Payload::TestResult(payload)); + build_event.id = Some(BuildEventId { + id: Some(Id::TestResult(TestResultId { + label: "//path:test".to_string(), + ..Default::default() + })), + }); build_event }) .collect(); diff --git a/cli/src/runner.rs b/cli/src/runner.rs index d8308481..53a7ea21 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -84,6 +84,7 @@ async fn run_test_and_get_exit_code(command: &String, args: Vec<&String>) -> any Ok(result) } +// TODO: TYLER FILESET AND THE INPUT TO THIS NEEDS TO INCLUDE TEST RUNNER STATUS pub fn build_filesets( repo_root: &str, junit_paths: &[String], diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index d1ec5875..df76d418 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -88,10 +88,9 @@ impl BazelBepParser { errors.push(format!("Error parsing build event: {}", err)); } Result::Ok(build_event) => { - match ( - &build_event.payload, - &build_event.clone().id.and_then(|id| id.id), - ) { + let payload = &build_event.payload; + let id = build_event.id.clone().and_then(|id| id.id); + match (payload, id) { ( Some(Payload::TestSummary(test_summary)), Some(Id::TestSummary(id)), @@ -160,16 +159,19 @@ impl BazelBepParser { Ok(BepParseResult { bep_test_events, errors, - test_results: test_results.iter().map(|test_result| { - let mut override_pass = false; - if pass_labels.contains(&test_result.label.to_string()) { - override_pass = true; - } - TestResult { - override_pass, - ..test_result.clone() - } - }).collect(), + test_results: test_results + .iter() + .map(|test_result| { + let mut override_pass = false; + if pass_labels.contains(&test_result.label.to_string()) { + override_pass = true; + } + TestResult { + override_pass, + ..test_result.clone() + } + }) + .collect(), }) } } @@ -182,6 +184,7 @@ mod tests { const SIMPLE_EXAMPLE: &str = "test_fixtures/bep_example"; const EMPTY_EXAMPLE: &str = "test_fixtures/bep_empty"; const PARTIAL_EXAMPLE: &str = "test_fixtures/bep_partially_valid"; + // DONOTLAND TODO: TYLER NEED TO ADD A TEST CASE FOR SUMMARY STATUS #[test] fn test_parse_simple_bep() { From 81e099a7be95a18fe510fb48b170342f025323e1 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 20:28:07 -0800 Subject: [PATCH 05/16] finish implementing --- Cargo.lock | 1 + bundle/src/files.rs | 4 ++ cli/src/main.rs | 13 +++++- cli/src/runner.rs | 38 ++++++++++++++---- cli/src/upload.rs | 27 +++++++++---- cli/src/validate.rs | 3 +- context/Cargo.toml | 3 +- context/src/bazel_bep/parser.rs | 70 +++++++++++++++++++++------------ context/src/junit/junit_path.rs | 29 ++++++++++++++ context/src/junit/mod.rs | 1 + 10 files changed, 145 insertions(+), 44 deletions(-) create mode 100644 context/src/junit/junit_path.rs diff --git a/Cargo.lock b/Cargo.lock index cbdaaf7d..4676289a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -931,6 +931,7 @@ dependencies = [ "tempfile", "test_utils", "thiserror 1.0.69", + "tsify-next", "uuid", "wasm-bindgen", ] diff --git a/bundle/src/files.rs b/bundle/src/files.rs index f082d3dc..7041b4f4 100644 --- a/bundle/src/files.rs +++ b/bundle/src/files.rs @@ -2,6 +2,7 @@ use std::{format, time::SystemTime}; use codeowners::{CodeOwners, Owners, OwnersOfPath}; use constants::ALLOW_LIST; +use context::junit::junit_path::TestRunnerJunitStatus; #[cfg(feature = "pyo3")] use pyo3::prelude::*; #[cfg(feature = "pyo3")] @@ -39,6 +40,7 @@ pub struct FileSet { pub file_set_type: FileSetType, pub files: Vec, pub glob: String, + pub test_runner_status: Option, } impl FileSet { @@ -48,6 +50,7 @@ impl FileSet { pub fn scan_from_glob( repo_root: &str, glob_path: String, + test_runner_status: Option, file_counter: &mut FileSetCounter, team: Option, codeowners: &Option, @@ -147,6 +150,7 @@ impl FileSet { file_set_type: FileSetType::Junit, files, glob: glob_path, + test_runner_status, }) } } diff --git a/cli/src/main.rs b/cli/src/main.rs index d88df7cf..475dd067 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -5,7 +5,9 @@ use bundle::RunResult; use clap::{Args, Parser, Subcommand}; use codeowners::CodeOwners; use constants::{EXIT_FAILURE, SENTRY_DSN}; -use context::{bazel_bep::parser::BazelBepParser, repo::BundleRepo}; +use context::{ + bazel_bep::parser::BazelBepParser, junit::junit_path::JunitPathWrapper, repo::BundleRepo, +}; use trunk_analytics_cli::{ api_client::ApiClient, print::print_bep_results, @@ -235,8 +237,15 @@ async fn run(cli: Cli) -> anyhow::Result { print_bep_results(&bep_result); bep_result.uncached_xml_files() } - None => junit_paths, + None => junit_paths + .into_iter() + .map(|p| JunitPathWrapper { + junit_path: p, + status: None, + }) + .collect(), }; + dbg!(&junit_file_paths); // DONOTLAND TODO: REMOVE validate(junit_file_paths, show_warnings, codeowners_path).await } } diff --git a/cli/src/runner.rs b/cli/src/runner.rs index 53a7ea21..e583347f 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -9,7 +9,14 @@ use bundle::{ }; use codeowners::CodeOwners; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; -use context::{bazel_bep::parser::BazelBepParser, junit::parser::JunitParser, repo::BundleRepo}; +use context::{ + bazel_bep::parser::BazelBepParser, + junit::{ + junit_path::{JunitPathWrapper, TestRunnerJunitStatus}, + parser::JunitParser, + }, + repo::BundleRepo, +}; use crate::{api_client::ApiClient, print::print_bep_results}; @@ -32,7 +39,13 @@ pub async fn run_test_command( log::info!("Command exit code: {}", exit_code); let output_paths = match junit_spec { - JunitSpec::Paths(paths) => paths, + JunitSpec::Paths(paths) => paths + .into_iter() + .map(|p| JunitPathWrapper { + junit_path: p, + status: None, + }) + .collect(), JunitSpec::BazelBep(bep_path) => { let mut parser = BazelBepParser::new(bep_path); let bep_result = parser.parse()?; @@ -87,7 +100,7 @@ async fn run_test_and_get_exit_code(command: &String, args: Vec<&String>) -> any // TODO: TYLER FILESET AND THE INPUT TO THIS NEEDS TO INCLUDE TEST RUNNER STATUS pub fn build_filesets( repo_root: &str, - junit_paths: &[String], + junit_paths: &[JunitPathWrapper], team: Option, codeowners: &Option, exec_start: Option, @@ -95,10 +108,11 @@ pub fn build_filesets( let mut file_counter = FileSetCounter::default(); let mut file_sets = junit_paths .iter() - .map(|path| { + .map(|junit_wrapper| { FileSet::scan_from_glob( repo_root, - path.to_string(), + junit_wrapper.junit_path.to_string(), + junit_wrapper.status.clone(), &mut file_counter, team.clone(), codeowners, @@ -111,8 +125,8 @@ pub fn build_filesets( if file_counter.get_count() == 0 { file_sets = junit_paths .iter() - .map(|path| { - let mut path = path.clone(); + .map(|junit_wrapper| { + let mut path = junit_wrapper.junit_path.clone(); if !path.ends_with('/') { path.push('/'); } @@ -120,6 +134,7 @@ pub fn build_filesets( FileSet::scan_from_glob( repo_root, path.to_string(), + junit_wrapper.status.clone(), &mut file_counter, team.clone(), codeowners, @@ -169,6 +184,11 @@ pub async fn extract_failed_tests( let mut successes: HashMap = HashMap::new(); for file_set in file_sets { + if let Some(test_runner_status) = &file_set.test_runner_status { + if test_runner_status != &TestRunnerJunitStatus::Failed { + continue; + } + } for file in &file_set.files { let file = match std::fs::File::open(&file.original_path) { Ok(file) => file, @@ -348,6 +368,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), + test_runner_status: None, }]; let retried_failures = @@ -370,6 +391,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), + test_runner_status: None, }]; let retried_failures = @@ -392,6 +414,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), + test_runner_status: None, }]; let mut multi_failures = @@ -425,6 +448,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), + test_runner_status: None, }]; let some_failures = diff --git a/cli/src/upload.rs b/cli/src/upload.rs index f86de44f..cac198e5 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -20,7 +20,7 @@ use constants::{EXIT_FAILURE, EXIT_SUCCESS}; use context::repo::RepoUrlParts; use context::{ bazel_bep::parser::{BazelBepParser, BepParseResult}, - junit::parser::JunitParser, + junit::{junit_path::JunitPathWrapper, parser::JunitParser}, repo::BundleRepo, }; @@ -121,7 +121,7 @@ pub async fn run_upload( exec_start: Option, ) -> anyhow::Result { let UploadArgs { - mut junit_paths, + junit_paths, #[cfg(target_os = "macos")] xcresult_path, bazel_bep_path, @@ -140,6 +140,13 @@ pub async fn run_upload( team, codeowners_path, } = upload_args; + let mut junit_path_wrappers = junit_paths + .into_iter() + .map(|p| JunitPathWrapper { + junit_path: p, + status: None, + }) + .collect(); let repo = BundleRepo::new( repo_root, @@ -163,7 +170,7 @@ pub async fn run_upload( let mut parser = BazelBepParser::new(bazel_bep_path); let bep_parse_result = parser.parse()?; print_bep_results(&bep_parse_result); - junit_paths = bep_parse_result.uncached_xml_files(); + junit_path_wrappers = bep_parse_result.uncached_xml_files(); bep_result = Some(bep_parse_result); } @@ -174,7 +181,7 @@ pub async fn run_upload( { let temp_paths = handle_xcresult(&junit_temp_dir, xcresult_path, &repo.repo, &org_url_slug)?; - junit_paths = [junit_paths.as_slice(), temp_paths.as_slice()].concat(); + junit_path_wrappers = [junit_paths.as_slice(), temp_paths.as_slice()].concat(); if junit_paths.is_empty() && !allow_empty_test_results { return Err(anyhow::anyhow!( "No tests found in the provided XCResult path." @@ -186,7 +193,7 @@ pub async fn run_upload( let (file_sets, file_counter) = build_filesets( &repo.repo_root, - &junit_paths, + &junit_path_wrappers, team.clone(), &codeowners, exec_start, @@ -197,6 +204,7 @@ pub async fn run_upload( } let failures = extract_failed_tests(&repo, &org_url_slug, &file_sets).await; + dbg!(&failures); // TODO: REMOVE // Run the quarantine step and update the exit code. let exit_code = if failures.is_empty() { @@ -295,7 +303,7 @@ pub async fn run_upload( if file_counter.get_count() == 0 { log::warn!( "No JUnit files found to pack and upload using globs: {:?}", - junit_paths + junit_path_wrappers.iter().map(|j| &j.junit_path) ); } @@ -370,7 +378,7 @@ fn handle_xcresult( xcresult_path: Option, repo: &RepoUrlParts, org_url_slug: &str, -) -> Result, anyhow::Error> { +) -> Result, anyhow::Error> { let mut temp_paths = Vec::new(); if let Some(xcresult_path) = xcresult_path { let xcresult = XCResult::new(xcresult_path, repo, org_url_slug.to_string()); @@ -389,7 +397,10 @@ fn handle_xcresult( .map_err(|e| anyhow::anyhow!("Failed to write junit file: {}", e))?; let junit_temp_path_str = junit_temp_path.to_str(); if let Some(junit_temp_path_string) = junit_temp_path_str { - temp_paths.push(junit_temp_path_string.to_string()); + temp_paths.push(JunitPathWrapper { + junit_path: junit_temp_path_string.to_string(), + status: None, + }); } else { return Err(anyhow::anyhow!( "Failed to convert junit temp path to string." diff --git a/cli/src/validate.rs b/cli/src/validate.rs index 30cc1926..9b964f3f 100644 --- a/cli/src/validate.rs +++ b/cli/src/validate.rs @@ -7,6 +7,7 @@ use colored::{ColoredString, Colorize}; use console::Emoji; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; use context::junit::{ + junit_path::JunitPathWrapper, parser::{JunitParseError, JunitParser}, validator::{ validate as validate_report, JunitReportValidation, JunitReportValidationFlatIssue, @@ -21,7 +22,7 @@ type JunitFileToReportAndErrors = BTreeMap, Vec< type JunitFileToValidation = BTreeMap>; pub async fn validate( - junit_paths: Vec, + junit_paths: Vec, show_warnings: bool, codeowners_path: Option, ) -> anyhow::Result { diff --git a/context/Cargo.toml b/context/Cargo.toml index 47b0e54e..cf43fede 100644 --- a/context/Cargo.toml +++ b/context/Cargo.toml @@ -28,6 +28,7 @@ serde = { version = "1.0.215", default-features = false, features = ["derive"] } serde_json = "1.0.133" speedate = "0.14.4" thiserror = "1.0.63" +tsify-next = { version = "0.5.4", optional = true } uuid = { version = "1.10.0", features = ["v5"] } wasm-bindgen = { version = "0.2.95", optional = true } magnus = { version = "0.7.1", optional = true, default-features = false } @@ -47,5 +48,5 @@ default = ["git-access"] git-access = ["dep:gix", "dep:openssl"] bindings = [] pyo3 = ["bindings", "dep:pyo3", "dep:pyo3-stub-gen"] -wasm = ["bindings", "dep:wasm-bindgen", "dep:js-sys"] +wasm = ["bindings", "dep:wasm-bindgen", "dep:js-sys", "dep:tsify-next"] ruby = ["bindings", "dep:magnus"] diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index df76d418..1f23de5a 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -1,16 +1,17 @@ +use crate::junit::junit_path::{JunitPathWrapper, TestRunnerJunitStatus}; use anyhow::Ok; use bazel_bep::types::build_event_stream::{ build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, TestStatus, }; use serde_json::Deserializer; -use std::{collections::HashSet, path::PathBuf}; +use std::{collections::HashMap, path::PathBuf}; #[derive(Debug, Clone, Default)] pub struct TestResult { pub label: String, pub cached: bool, pub xml_files: Vec, - pub override_pass: bool, + pub summary_status: Option, } const FILE_URI_PREFIX: &str = "file://"; @@ -37,20 +38,46 @@ impl BepParseResult { (xml_count, cached_xml_count) } - pub fn uncached_xml_files(&self) -> Vec { + pub fn uncached_xml_files(&self) -> Vec { self.test_results .iter() .filter_map(|r| { if r.cached { return None; } - Some(r.xml_files.clone()) + Some( + r.xml_files + .iter() + .map(|f| JunitPathWrapper { + junit_path: f.clone(), + status: r.summary_status.clone(), + }) + .collect::>(), + ) }) .flatten() .collect() } } +fn convert_test_summary_status(status: TestStatus) -> Option { + // DONOTLAND TODO: REMOVE + // // These are the cases in which bazel marks a test as passing + // if test_summary.overall_status == TestStatus::Passed as i32 + // || test_summary.overall_status + // == TestStatus::Flaky as i32 + // { + // summary_statuses.insert(id.label.clone(), TestRunnerJunitStatus::Passed); + // } + + match status { + TestStatus::Passed => Some(TestRunnerJunitStatus::Passed), + TestStatus::Failed => Some(TestRunnerJunitStatus::Failed), + TestStatus::Flaky => Some(TestRunnerJunitStatus::Flaky), + _ => None, + } +} + /// Uses proto spec /// https://github.com/TylerJang27/bazel-bep/blob/master/proto/build_event_stream.proto based on /// https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto @@ -71,17 +98,17 @@ impl BazelBepParser { let file = std::fs::File::open(&self.bazel_bep_path)?; let reader = std::io::BufReader::new(file); - let (errors, test_results, pass_labels, bep_test_events) = + let (errors, test_results, summary_statuses, bep_test_events) = Deserializer::from_reader(reader) .into_iter::() .fold( ( Vec::::new(), Vec::::new(), - HashSet::::new(), + HashMap::::new(), Vec::::new(), ), - |(mut errors, mut test_results, mut pass_labels, mut bep_test_events), + |(mut errors, mut test_results, mut summary_statuses, mut bep_test_events), parse_event| { match parse_event { Result::Err(ref err) => { @@ -95,12 +122,11 @@ impl BazelBepParser { Some(Payload::TestSummary(test_summary)), Some(Id::TestSummary(id)), ) => { - // These are the cases in which bazel marks a test as passing - if test_summary.overall_status == TestStatus::Passed as i32 - || test_summary.overall_status - == TestStatus::Flaky as i32 - { - pass_labels.insert(id.label.clone()); + let summary_status = convert_test_summary_status( + test_summary.overall_status(), + ); + if let Some(status) = summary_status { + summary_statuses.insert(id.label.clone(), status); } bep_test_events.push(build_event); } @@ -143,7 +169,7 @@ impl BazelBepParser { label: id.label.clone(), cached, xml_files, - override_pass: false, + summary_status: None, }); bep_test_events.push(build_event); } @@ -152,7 +178,7 @@ impl BazelBepParser { } } - (errors, test_results, pass_labels, bep_test_events) + (errors, test_results, summary_statuses, bep_test_events) }, ); @@ -160,16 +186,10 @@ impl BazelBepParser { bep_test_events, errors, test_results: test_results - .iter() - .map(|test_result| { - let mut override_pass = false; - if pass_labels.contains(&test_result.label.to_string()) { - override_pass = true; - } - TestResult { - override_pass, - ..test_result.clone() - } + .into_iter() + .map(|test_result| TestResult { + summary_status: summary_statuses.get(&test_result.label).cloned(), + ..test_result }) .collect(), }) diff --git a/context/src/junit/junit_path.rs b/context/src/junit/junit_path.rs new file mode 100644 index 00000000..e167f6b0 --- /dev/null +++ b/context/src/junit/junit_path.rs @@ -0,0 +1,29 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; +#[cfg(feature = "pyo3")] +use pyo3_stub_gen::derive::gen_stub_pyclass_enum; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "wasm")] +use tsify_next::Tsify; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "pyo3", gen_stub_pyclass_enum, pyclass(eq, eq_int))] +#[cfg_attr(feature = "wasm", derive(Tsify))] +pub enum TestRunnerJunitStatus { + #[default] + Passed, + Failed, + Flaky, +} + +/// Encapsulates the glob path for a junit and, if applicable, the flakiness already +/// assigned by the user's test runner. See bazel_bep/parser.rs for more. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct JunitPathWrapper { + /// Path or glob pattern to the junit file. + pub junit_path: String, + /// Refers to an optional status parsed from the test runner's output, before junits have been parsed. + pub status: Option, +} diff --git a/context/src/junit/mod.rs b/context/src/junit/mod.rs index 13185ddd..23716c81 100644 --- a/context/src/junit/mod.rs +++ b/context/src/junit/mod.rs @@ -1,5 +1,6 @@ #[cfg(feature = "bindings")] pub mod bindings; mod date_parser; +pub mod junit_path; pub mod parser; pub mod validator; From 6f4bb2ea8a78cfe936867d9064019a16dee4c056 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 21:43:10 -0800 Subject: [PATCH 06/16] fix existing tests --- context/src/bazel_bep/parser.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index 1f23de5a..a3905e48 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -215,7 +215,10 @@ mod tests { let empty_vec: Vec = Vec::new(); assert_eq!( parse_result.uncached_xml_files(), - vec!["/tmp/hello_test/test.xml"] + vec![JunitPathWrapper { + junit_path: "/tmp/hello_test/test.xml".to_string(), + status: None + }] ); assert_eq!(parse_result.xml_file_counts(), (1, 0)); assert_eq!(*parse_result.errors, empty_vec); @@ -227,10 +230,11 @@ mod tests { let mut parser = BazelBepParser::new(input_file); let parse_result = parser.parse().unwrap(); - let empty_vec: Vec = Vec::new(); - assert_eq!(parse_result.uncached_xml_files(), empty_vec); + let empty_xml_vec: Vec = Vec::new(); + let empty_errors_vec: Vec = Vec::new(); + assert_eq!(parse_result.uncached_xml_files(), empty_xml_vec); assert_eq!(parse_result.xml_file_counts(), (0, 0)); - assert_eq!(*parse_result.errors, empty_vec); + assert_eq!(*parse_result.errors, empty_errors_vec); } #[test] @@ -241,7 +245,16 @@ mod tests { assert_eq!( parse_result.uncached_xml_files(), - vec!["/tmp/hello_test/test.xml", "/tmp/client_test/test.xml"] + vec![ + JunitPathWrapper { + junit_path: "/tmp/hello_test/test.xml".to_string(), + status: Some(TestRunnerJunitStatus::Passed) + }, + JunitPathWrapper { + junit_path: "/tmp/client_test/test.xml".to_string(), + status: Some(TestRunnerJunitStatus::Passed) + } + ] ); assert_eq!(parse_result.xml_file_counts(), (3, 1)); assert_eq!( From c7ea240df20937649a99a4941eacb58a88fa194f Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 22:03:16 -0800 Subject: [PATCH 07/16] add integration test --- context/src/bazel_bep/parser.rs | 41 +++++++++++++++++++------ context/test_fixtures/bep_flaky_summary | 39 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 context/test_fixtures/bep_flaky_summary diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index a3905e48..28e0e3c2 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -61,15 +61,6 @@ impl BepParseResult { } fn convert_test_summary_status(status: TestStatus) -> Option { - // DONOTLAND TODO: REMOVE - // // These are the cases in which bazel marks a test as passing - // if test_summary.overall_status == TestStatus::Passed as i32 - // || test_summary.overall_status - // == TestStatus::Flaky as i32 - // { - // summary_statuses.insert(id.label.clone(), TestRunnerJunitStatus::Passed); - // } - match status { TestStatus::Passed => Some(TestRunnerJunitStatus::Passed), TestStatus::Failed => Some(TestRunnerJunitStatus::Failed), @@ -204,7 +195,7 @@ mod tests { const SIMPLE_EXAMPLE: &str = "test_fixtures/bep_example"; const EMPTY_EXAMPLE: &str = "test_fixtures/bep_empty"; const PARTIAL_EXAMPLE: &str = "test_fixtures/bep_partially_valid"; - // DONOTLAND TODO: TYLER NEED TO ADD A TEST CASE FOR SUMMARY STATUS + const FLAKY_SUMMARY_EXAMPLE: &str = "test_fixtures/bep_flaky_summary"; #[test] fn test_parse_simple_bep() { @@ -262,4 +253,34 @@ mod tests { vec!["Error parsing build event: EOF while parsing a value at line 108 column 0"] ); } + + #[test] + fn test_parse_flaky_summary_bep() { + let input_file = get_test_file_path(FLAKY_SUMMARY_EXAMPLE); + let mut parser = BazelBepParser::new(input_file); + let parse_result = parser.parse().unwrap(); + + assert_eq!( + parse_result.uncached_xml_files(), + vec![ + JunitPathWrapper { + junit_path: "/tmp/hello_test/test_attempts/attempt_1.xml".to_string(), + status: Some(TestRunnerJunitStatus::Flaky) + }, + JunitPathWrapper { + junit_path: "/tmp/hello_test/test_attempts/attempt_2.xml".to_string(), + status: Some(TestRunnerJunitStatus::Flaky) + }, + JunitPathWrapper { + junit_path: "/tmp/hello_test/test.xml".to_string(), + status: Some(TestRunnerJunitStatus::Flaky) + }, + JunitPathWrapper { + junit_path: "/tmp/client_test/test.xml".to_string(), + status: Some(TestRunnerJunitStatus::Failed) + } + ] + ); + assert_eq!(parse_result.xml_file_counts(), (4, 0)); + } } diff --git a/context/test_fixtures/bep_flaky_summary b/context/test_fixtures/bep_flaky_summary new file mode 100644 index 00000000..e20d2e0c --- /dev/null +++ b/context/test_fixtures/bep_flaky_summary @@ -0,0 +1,39 @@ +{"id":{"started":{}},"children":[{"progress":{}},{"unstructuredCommandLine":{}},{"structuredCommandLine":{"commandLineLabel":"original"}},{"structuredCommandLine":{"commandLineLabel":"canonical"}},{"structuredCommandLine":{"commandLineLabel":"tool"}},{"buildMetadata":{}},{"optionsParsed":{}},{"workspaceStatus":{}},{"pattern":{"pattern":["//trunk/hello_world/cc:hello_test","//trunk/hello_world/cc_grpc:client_test"]}},{"buildFinished":{}}],"started":{"uuid":"337be0c6-0b6a-47fa-a573-19a33412c719","startTimeMillis":"1734408654987","buildToolVersion":"7.4.0","optionsDescription":"","command":"test","workingDirectory":"/repos/trunk","workspaceDirectory":"/repos/trunk","serverPid":"57611","startTime":"2024-12-17T04:10:54.987Z"}} +{"id":{"buildMetadata":{}},"buildMetadata":{}} +{"id":{"unstructuredCommandLine":{}},"unstructuredCommandLine":{"args":[]}} +{"id":{"optionsParsed":{}},"optionsParsed":{"startupOptions":[],"explicitStartupOptions":[],"cmdLine":[],"explicitCmdLine":[],"invocationPolicy":{}}} +{"id":{"structuredCommandLine":{"commandLineLabel":"original"}},"structuredCommandLine":{"commandLineLabel":"original","sections":[]}} +{"id":{"structuredCommandLine":{"commandLineLabel":"canonical"}},"structuredCommandLine":{"commandLineLabel":"canonical","sections":[]}} +{"id":{"structuredCommandLine":{"commandLineLabel":"tool"}},"structuredCommandLine":{}} +{"id":{"pattern":{"pattern":["//trunk/hello_world/cc:hello_test","//trunk/hello_world/cc_grpc:client_test"]}},"children":[{"targetConfigured":{"label":"//trunk/hello_world/cc:hello_test"}},{"targetConfigured":{"label":"//trunk/hello_world/cc_grpc:client_test"}}],"expanded":{}} +{"id":{"progress":{}},"children":[{"progress":{"opaqueCount":1}},{"workspace":{}}],"progress":{"stderr":""}} +{"id":{"workspace":{}},"workspaceInfo":{"localExecRoot":"/tmp/.cache/bazel/deadbeef/execroot/_main"}} +{"id":{"progress":{"opaqueCount":1}},"children":[{"progress":{"opaqueCount":2}},{"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}],"progress":{}} +{"id":{"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}},"configuration":{"mnemonic":"k8-fastbuild","platformName":"k8","cpu":"k8","makeVariable":{"TRUNK_RELEASE":"0.0.0","METRICS_DATABASE_URL":"","IMAGE_TAG":"null","SENTRY_AUTH_TOKEN":"","TARGET_CPU":"k8","GENDIR":"bazel-out/k8-fastbuild/bin","FLAKY_TESTS_OVERRIDE_REPO_FULL_NAME":"","AWS_PROFILE":"","SERVICE_JEST_ARGS":"--testMatch\u003d**/__tests__/**/*.test.js","ANALYZE":"","BINDIR":"bazel-out/k8-fastbuild/bin","COMPILATION_MODE":"fastbuild","DATABASE_URL":"","absl":"1","SPDLOG_COMPILE_LEVEL":"SPDLOG_LEVEL_TRACE","SENTRY_LOG_LEVEL":"","AWS_ECR_URL":"null","FLAKY_TESTS_OVERRIDE_ORG_SLUG":""}}} +{"id":{"targetConfigured":{"label":"//trunk/hello_world/cc:hello_test"}},"children":[{"targetCompleted":{"label":"//trunk/hello_world/cc:hello_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"configured":{"targetKind":"cc_test rule","testSize":"MEDIUM","tag":["__CC_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__"]}} +{"id":{"targetConfigured":{"label":"//trunk/hello_world/cc_grpc:client_test"}},"children":[{"targetCompleted":{"label":"//trunk/hello_world/cc_grpc:client_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"configured":{"targetKind":"cc_test rule","testSize":"MEDIUM","tag":["__CC_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__"]}} +{"id":{"workspaceStatus":{}},"workspaceStatus":{"item":[{"key":"BUILD_EMBED_LABEL"},{"key":"BUILD_HOST","value":"tyler-Precision-3260"},{"key":"BUILD_TIMESTAMP","value":"1734408655"},{"key":"BUILD_USER","value":"tyler"},{"key":"FORMATTED_DATE","value":"2024 Dec 17 04 10 55 Tue"}]}} +{"id":{"progress":{"opaqueCount":2}},"children":[{"progress":{"opaqueCount":3}},{"namedSet":{"id":"0"}}],"progress":{"stderr":""}} +{"id":{"namedSet":{"id":"0"}},"namedSetOfFiles":{"files":[{"name":"trunk/hello_world/cc_grpc/client_test","uri":"file:///tmp/.cache/bazel/deadbeef/execroot/_main/bazel-out/k8-fastbuild/bin/trunk/hello_world/cc_grpc/client_test","pathPrefix":["bazel-out","k8-fastbuild","bin"],"digest":"e666f897f5dd4747c254bee145f40bdc3a7787bdfd9ef87afdb15bab94b8ff73","length":"5536640"}]}} +{"id":{"targetCompleted":{"label":"//trunk/hello_world/cc_grpc:client_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"children":[{"testResult":{"label":"//trunk/hello_world/cc_grpc:client_test","run":1,"shard":1,"attempt":1,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},{"testSummary":{"label":"//trunk/hello_world/cc_grpc:client_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"completed":{"success":true,"outputGroup":[{"name":"default","fileSets":[{"id":"0"}]}],"tag":["__CC_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__","medium","moderate","noflaky","nolocal"],"importantOutput":[{"name":"trunk/hello_world/cc_grpc/client_test","uri":"file:///tmp/.cache/bazel/deadbeef/execroot/_main/bazel-out/k8-fastbuild/bin/trunk/hello_world/cc_grpc/client_test","pathPrefix":["bazel-out","k8-fastbuild","bin"],"digest":"e666f897f5dd4747c254bee145f40bdc3a7787bdfd9ef87afdb15bab94b8ff73","length":"5536640"}],"testTimeoutSeconds":"300","testTimeout":"300s"}} +{"id":{"progress":{"opaqueCount":3}},"children":[{"progress":{"opaqueCount":4}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":4}},"children":[{"progress":{"opaqueCount":5}},{"namedSet":{"id":"1"}}],"progress":{"stderr":""}} +{"id":{"namedSet":{"id":"1"}},"namedSetOfFiles":{"files":[{"name":"trunk/hello_world/cc/hello_test","uri":"file:///tmp/.cache/bazel/deadbeef/execroot/_main/bazel-out/k8-fastbuild/bin/trunk/hello_world/cc/hello_test","pathPrefix":["bazel-out","k8-fastbuild","bin"],"digest":"665d899ac16983beb71217dec6958c494a4858add581745e0b72816462c9f793","length":"2692552"}]}} +{"id":{"targetCompleted":{"label":"//trunk/hello_world/cc:hello_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"children":[{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":1,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},{"testSummary":{"label":"//trunk/hello_world/cc:hello_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"completed":{"success":true,"outputGroup":[{"name":"default","fileSets":[{"id":"1"}]}],"tag":["__CC_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__","medium","moderate","noflaky","nolocal"],"importantOutput":[{"name":"trunk/hello_world/cc/hello_test","uri":"file:///tmp/.cache/bazel/deadbeef/execroot/_main/bazel-out/k8-fastbuild/bin/trunk/hello_world/cc/hello_test","pathPrefix":["bazel-out","k8-fastbuild","bin"],"digest":"665d899ac16983beb71217dec6958c494a4858add581745e0b72816462c9f793","length":"2692552"}],"testTimeoutSeconds":"300","testTimeout":"300s"}} +{"id":{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":1,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"children":[{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":2,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"testResult":{"testActionOutput":[{"name":"test.log","uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test_attempts/attempt_1.log"},{"name":"test.xml","uri":"file:///tmp/hello_test/test_attempts/attempt_1.xml"}],"testAttemptDurationMillis":"46","status":"FAILED","testAttemptStartMillisEpoch":"1734408655311","executionInfo":{"strategy":"linux-sandbox","timingBreakdown":{"child":[{"name":"parseTime","time":"0s"},{"name":"fetchTime","time":"0s"},{"name":"queueTime","time":"0s"},{"name":"uploadTime","time":"0s"},{"name":"setupTime","time":"0s"},{"name":"executionWallTime","time":"0.046s"},{"name":"processOutputsTime","time":"0s"},{"name":"networkTime","time":"0s"}],"name":"totalTime","time":"0.046s"}},"testAttemptStart":"2024-12-17T04:10:55.311Z","testAttemptDuration":"0.046s"}} +{"id":{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":2,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"children":[{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":3,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}}],"testResult":{"testActionOutput":[{"name":"test.log","uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test_attempts/attempt_2.log"},{"name":"test.xml","uri":"file:///tmp/hello_test/test_attempts/attempt_2.xml"}],"testAttemptDurationMillis":"44","status":"FAILED","testAttemptStartMillisEpoch":"1734408655377","executionInfo":{"strategy":"linux-sandbox","timingBreakdown":{"child":[{"name":"parseTime","time":"0s"},{"name":"fetchTime","time":"0s"},{"name":"queueTime","time":"0s"},{"name":"uploadTime","time":"0s"},{"name":"setupTime","time":"0s"},{"name":"executionWallTime","time":"0.044s"},{"name":"processOutputsTime","time":"0s"},{"name":"networkTime","time":"0s"}],"name":"totalTime","time":"0.044s"}},"testAttemptStart":"2024-12-17T04:10:55.377Z","testAttemptDuration":"0.044s"}} +{"id":{"testResult":{"label":"//trunk/hello_world/cc:hello_test","run":1,"shard":1,"attempt":3,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"testResult":{"testActionOutput":[{"name":"test.log","uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test.log"},{"name":"test.xml","uri":"file:///tmp/hello_test/test.xml"}],"testAttemptDurationMillis":"41","status":"PASSED","testAttemptStartMillisEpoch":"1734408655425","executionInfo":{"strategy":"linux-sandbox","timingBreakdown":{"child":[{"name":"parseTime","time":"0s"},{"name":"fetchTime","time":"0s"},{"name":"queueTime","time":"0s"},{"name":"uploadTime","time":"0s"},{"name":"setupTime","time":"0s"},{"name":"executionWallTime","time":"0.041s"},{"name":"processOutputsTime","time":"0s"},{"name":"networkTime","time":"0s"}],"name":"totalTime","time":"0.041s"}},"testAttemptStart":"2024-12-17T04:10:55.425Z","testAttemptDuration":"0.041s"}} +{"id":{"testSummary":{"label":"//trunk/hello_world/cc:hello_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"testSummary":{"totalRunCount":3,"passed":[{"uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test.log"}],"failed":[{"uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test_attempts/attempt_1.log"},{"uri":"file:///repos/trunk/trunk/hello_world/cc/hello_test/test_attempts/attempt_2.log"}],"overallStatus":"FLAKY","firstStartTimeMillis":"1734408655425","lastStopTimeMillis":"1734408655466","totalRunDurationMillis":"41","runCount":1,"totalRunDuration":"0.041s","firstStartTime":"2024-12-17T04:10:55.425Z","lastStopTime":"2024-12-17T04:10:55.466Z","attemptCount":3}} +{"id":{"testResult":{"label":"//trunk/hello_world/cc_grpc:client_test","run":1,"shard":1,"attempt":1,"configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"testResult":{"testActionOutput":[{"name":"test.log","uri":"file:///repos/trunk/trunk/hello_world/cc_grpc/client_test/test.log"},{"name":"test.xml","uri":"file:///tmp/client_test/test.xml"}],"testAttemptDurationMillis":"1061","status":"FAILED","testAttemptStartMillisEpoch":"1734408655322","executionInfo":{"strategy":"linux-sandbox","timingBreakdown":{"child":[{"name":"parseTime","time":"0s"},{"name":"fetchTime","time":"0s"},{"name":"queueTime","time":"0s"},{"name":"uploadTime","time":"0s"},{"name":"setupTime","time":"0s"},{"name":"executionWallTime","time":"1.061s"},{"name":"processOutputsTime","time":"0s"},{"name":"networkTime","time":"0s"}],"name":"totalTime","time":"1.061s"}},"testAttemptStart":"2024-12-17T04:10:55.322Z","testAttemptDuration":"1.061s"}} +{"id":{"testSummary":{"label":"//trunk/hello_world/cc_grpc:client_test","configuration":{"id":"2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6"}}},"testSummary":{"totalRunCount":1,"failed":[{"uri":"file:///repos/trunk/trunk/hello_world/cc_grpc/client_test/test.log"}],"overallStatus":"FAILED","firstStartTimeMillis":"1734408655322","lastStopTimeMillis":"1734408656383","totalRunDurationMillis":"1061","runCount":1,"totalRunDuration":"1.061s","firstStartTime":"2024-12-17T04:10:55.322Z","lastStopTime":"2024-12-17T04:10:56.383Z","attemptCount":1}} +{"id":{"progress":{"opaqueCount":5}},"children":[{"progress":{"opaqueCount":6}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":6}},"children":[{"progress":{"opaqueCount":7}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":7}},"children":[{"progress":{"opaqueCount":8}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":8}},"children":[{"progress":{"opaqueCount":9}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":9}},"children":[{"progress":{"opaqueCount":10}}],"progress":{"stdout":"","stderr":""}} +{"id":{"progress":{"opaqueCount":10}},"children":[{"progress":{"opaqueCount":11}}],"progress":{"stdout":"","stderr":""}} +{"id":{"buildFinished":{}},"children":[{"buildToolLogs":{}}],"finished":{"overallSuccess":true,"finishTimeMillis":"1734408656395","exitCode":{"name":"SUCCESS"},"finishTime":"2024-12-17T04:10:56.395Z"}} +{"id":{"progress":{"opaqueCount":11}},"children":[{"progress":{"opaqueCount":12}},{"buildMetrics":{}}],"progress":{"stderr":""}} +{"id":{"buildMetrics":{}},"buildMetrics":{"actionSummary":{"actionsExecuted":"3","actionData":[{"mnemonic":"TestRunner","actionsExecuted":"2","firstStartedMs":"1734408655311","lastEndedMs":"1734408656393","systemTime":"0.132s","userTime":"1.023s"},{"mnemonic":"BazelWorkspaceStatusAction","actionsExecuted":"1","firstStartedMs":"1734408655309","lastEndedMs":"1734408655310"}],"runnerCount":[{"name":"total","count":3},{"name":"linux-sandbox","count":4,"execKind":"Local"}],"actionCacheStatistics":{"sizeInBytes":"21710463","hits":20,"misses":3,"missDetails":[{},{"reason":"DIFFERENT_DEPS"},{"reason":"DIFFERENT_ENVIRONMENT"},{"reason":"DIFFERENT_FILES"},{"reason":"CORRUPTED_CACHE_ENTRY"},{"reason":"NOT_CACHED"},{"reason":"UNCONDITIONAL_EXECUTION","count":3}]}},"memoryMetrics":{},"targetMetrics":{},"packageMetrics":{},"timingMetrics":{"cpuTimeInMs":"2040","wallTimeInMs":"1401","analysisPhaseTimeInMs":"9","executionPhaseTimeInMs":"1084"},"cumulativeMetrics":{"numAnalyses":7,"numBuilds":7},"artifactMetrics":{"sourceArtifactsRead":{},"outputArtifactsSeen":{"sizeInBytes":"716839","count":26},"outputArtifactsFromActionCache":{"sizeInBytes":"714374","count":20},"topLevelArtifacts":{}},"buildGraphMetrics":{"postInvocationSkyframeNodeCount":266751},"workerPoolMetrics":{}}} +{"id":{"buildToolLogs":{}},"buildToolLogs":{"log":[{"name":"elapsed time","contents":""},{"name":"critical path","contents":""},{"name":"process stats","contents":""},{"name":"command.profile.gz","uri":"file:///tmp/.cache/bazel/deadbeef/command.profile.gz"}]}} +{"id":{"progress":{"opaqueCount":12}},"progress":{},"lastMessage":true} From 7556bd481d7d09bd0e02ee892f05c8a9b899a469 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 22:12:17 -0800 Subject: [PATCH 08/16] add upload quarantine integration test --- cli-tests/src/upload.rs | 56 ++++++++++++- cli-tests/src/utils.rs | 1 - cli-tests/test_fixtures/bep_retries | 32 ++++++++ cli-tests/test_fixtures/bep_retries_timestamp | 81 +++++++++++++++++++ cli/src/main.rs | 1 - cli/src/runner.rs | 1 - cli/src/upload.rs | 1 - 7 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 cli-tests/test_fixtures/bep_retries_timestamp diff --git a/cli-tests/src/upload.rs b/cli-tests/src/upload.rs index 542b0001..a3572d98 100644 --- a/cli-tests/src/upload.rs +++ b/cli-tests/src/upload.rs @@ -251,8 +251,62 @@ async fn upload_bundle_using_bep() { async fn upload_bundle_success_status_code() { let temp_dir = tempdir().unwrap(); generate_mock_git_repo(&temp_dir); - // DONOTLAND TODO: TYLER NEED TO FIX THIS TO INCLUDE SUMMARIES let test_bep_path = get_test_file_path("test_fixtures/bep_retries"); + // The test cases need not match up or have timestamps, so long as there is a testSummary + // That indicates a flake or pass + let uri_fail = format!( + "file://{}", + get_test_file_path("../cli/test_fixtures/junit1_fail.xml") + ); + let uri_pass = format!( + "file://{}", + get_test_file_path("../cli/test_fixtures/junit0_pass.xml") + ); + + let bep_content = fs::read_to_string(&test_bep_path) + .unwrap() + .replace("${URI_FAIL}", &uri_fail) + .replace("${URI_PASS}", &uri_pass); + let bep_path = temp_dir.path().join("bep.json"); + fs::write(&bep_path, bep_content).unwrap(); + + let state = MockServerBuilder::new().spawn_mock_server().await; + + let args = &[ + "upload", + "--bazel-bep-path", + "./bep.json", + "--org-url-slug", + "test-org", + "--token", + "test-token", + ]; + + // Even though the junits contain failures, they contain retries that succeeded, + // so the upload command should have a successful exit code + let assert = Command::new(CARGO_RUN.path()) + .current_dir(&temp_dir) + .env("TRUNK_PUBLIC_API_ADDRESS", &state.host) + .env("CI", "1") + .env("GITHUB_JOB", "test-job") + .args(args) + .assert() + .code(0) + .success(); + + // No quarantine request + let requests = state.requests.lock().unwrap().clone(); + assert_eq!(requests.len(), 4); + + // HINT: View CLI output with `cargo test -- --nocapture` + println!("{assert}"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn upload_bundle_success_timestamp_status_code() { + let temp_dir = tempdir().unwrap(); + generate_mock_git_repo(&temp_dir); + let test_bep_path = get_test_file_path("test_fixtures/bep_retries_timestamp"); let uri_fail = format!( "file://{}", get_test_file_path("../cli/test_fixtures/junit0_fail.xml") diff --git a/cli-tests/src/utils.rs b/cli-tests/src/utils.rs index 7c31a162..e11b5c4a 100644 --- a/cli-tests/src/utils.rs +++ b/cli-tests/src/utils.rs @@ -46,7 +46,6 @@ pub fn generate_mock_valid_junit_xmls>(directory: T) -> Vec>(directory: T) { let mock_junits = generate_mock_valid_junit_xmls(&directory); - // TODO: TYLER SHOULD WE MAKE TESTSUMMARY EVENTS TOO? DONOTLAND let build_events: Vec = mock_junits .iter() .map(|junit| { diff --git a/cli-tests/test_fixtures/bep_retries b/cli-tests/test_fixtures/bep_retries index 0955a16c..fc30978c 100644 --- a/cli-tests/test_fixtures/bep_retries +++ b/cli-tests/test_fixtures/bep_retries @@ -78,4 +78,36 @@ "testAttemptStart": "2024-12-10T06:17:24.011Z", "testAttemptDuration": "0.044s" } +} +{ + "id": { + "testSummary": { + "label": "//trunk/hello_world/cc:hello_test", + "configuration": { + "id": "2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6" + } + } + }, + "testSummary": { + "totalRunCount": 2, + "passed": [ + { + "uri": "file:///repos/trunk/trunk/hello_world/cc/hello_test/test.log" + } + ], + "failed": [ + { + "uri": "file:///repos/trunk/trunk/hello_world/cc/hello_test/test_attempts/attempt_1.log" + } + ], + "overallStatus": "FLAKY", + "firstStartTimeMillis": "1734408655425", + "lastStopTimeMillis": "1734408655466", + "totalRunDurationMillis": "41", + "runCount": 1, + "totalRunDuration": "0.041s", + "firstStartTime": "2024-12-17T04:10:55.425Z", + "lastStopTime": "2024-12-17T04:10:55.466Z", + "attemptCount": 2 + } } \ No newline at end of file diff --git a/cli-tests/test_fixtures/bep_retries_timestamp b/cli-tests/test_fixtures/bep_retries_timestamp new file mode 100644 index 00000000..0955a16c --- /dev/null +++ b/cli-tests/test_fixtures/bep_retries_timestamp @@ -0,0 +1,81 @@ +{ + "id": { + "testResult": { + "label": "//trunk/hello_world/cc:hello_test", + "run": 1, + "shard": 1, + "attempt": 1, + "configuration": { + "id": "2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6" + } + } + }, + "children": [ + { + "testResult": { + "label": "//trunk/hello_world/cc:hello_test", + "run": 1, + "shard": 1, + "attempt": 2, + "configuration": { + "id": "2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6" + } + } + } + ], + "testResult": { + "testActionOutput": [ + { + "name": "test.xml", + "uri": "${URI_FAIL}" + } + ], + "testAttemptDurationMillis": "43", + "status": "FAILED", + "testAttemptStartMillisEpoch": "1733811443963", + "executionInfo": { + "strategy": "linux-sandbox", + "timingBreakdown": { + "child": [], + "name": "totalTime", + "time": "0.043s" + } + }, + "testAttemptStart": "2024-12-10T06:17:23.963Z", + "testAttemptDuration": "0.043s" + } +} +{ + "id": { + "testResult": { + "label": "//trunk/hello_world/cc:hello_test", + "run": 1, + "shard": 1, + "attempt": 2, + "configuration": { + "id": "2136e65c67d9ba6b2652accfbf6533486c16692a484f4dc7ea7756de0edc13a6" + } + } + }, + "testResult": { + "testActionOutput": [ + { + "name": "test.xml", + "uri": "${URI_PASS}" + } + ], + "testAttemptDurationMillis": "44", + "status": "PASSED", + "testAttemptStartMillisEpoch": "1733811444011", + "executionInfo": { + "strategy": "linux-sandbox", + "timingBreakdown": { + "child": [], + "name": "totalTime", + "time": "0.044s" + } + }, + "testAttemptStart": "2024-12-10T06:17:24.011Z", + "testAttemptDuration": "0.044s" + } +} \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 475dd067..d5212318 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -245,7 +245,6 @@ async fn run(cli: Cli) -> anyhow::Result { }) .collect(), }; - dbg!(&junit_file_paths); // DONOTLAND TODO: REMOVE validate(junit_file_paths, show_warnings, codeowners_path).await } } diff --git a/cli/src/runner.rs b/cli/src/runner.rs index e583347f..0dc09c31 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -97,7 +97,6 @@ async fn run_test_and_get_exit_code(command: &String, args: Vec<&String>) -> any Ok(result) } -// TODO: TYLER FILESET AND THE INPUT TO THIS NEEDS TO INCLUDE TEST RUNNER STATUS pub fn build_filesets( repo_root: &str, junit_paths: &[JunitPathWrapper], diff --git a/cli/src/upload.rs b/cli/src/upload.rs index cac198e5..51a64c37 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -204,7 +204,6 @@ pub async fn run_upload( } let failures = extract_failed_tests(&repo, &org_url_slug, &file_sets).await; - dbg!(&failures); // TODO: REMOVE // Run the quarantine step and update the exit code. let exit_code = if failures.is_empty() { From bc51900c7609f5cb8eec01501aad3d0ca622ee79 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 22:17:15 -0800 Subject: [PATCH 09/16] additional unit test --- cli/src/runner.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/cli/src/runner.rs b/cli/src/runner.rs index 0dc09c31..9a7ccec5 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -455,4 +455,43 @@ mod tests { assert_eq!(some_failures.len(), 1); assert_eq!(some_failures[0].name, "Goodbye"); } + + #[tokio::test(start_paused = true)] + async fn test_extract_multi_failed_tests_with_runner_status() { + let file_sets = vec![ + FileSet { + file_set_type: FileSetType::Junit, + files: vec![BundledFile { + original_path: get_test_file_path(JUNIT1_FAIL), + ..BundledFile::default() + }], + glob: String::from("1/*.xml"), + test_runner_status: Some(TestRunnerJunitStatus::Passed), + }, + FileSet { + file_set_type: FileSetType::Junit, + files: vec![BundledFile { + original_path: get_test_file_path(JUNIT1_FAIL), + ..BundledFile::default() + }], + glob: String::from("2/*.xml"), + test_runner_status: Some(TestRunnerJunitStatus::Flaky), + }, + FileSet { + file_set_type: FileSetType::Junit, + files: vec![BundledFile { + original_path: get_test_file_path(JUNIT0_FAIL), + ..BundledFile::default() + }], + glob: String::from("3/*.xml"), + test_runner_status: Some(TestRunnerJunitStatus::Failed), + }, + ]; + + let mut multi_failures = + extract_failed_tests(&BundleRepo::default(), ORG_SLUG, &file_sets).await; + multi_failures.sort_by(|a, b| a.name.cmp(&b.name)); + assert_eq!(multi_failures.len(), 1); + assert_eq!(multi_failures[0].name, "Hello"); + } } From cf9cac333f9076ca8c92b7e08f360125de4a44b6 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 22:27:45 -0800 Subject: [PATCH 10/16] add versioning notes --- bundle/src/files.rs | 1 + bundle/src/types.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/bundle/src/files.rs b/bundle/src/files.rs index 7041b4f4..ea5d944a 100644 --- a/bundle/src/files.rs +++ b/bundle/src/files.rs @@ -40,6 +40,7 @@ pub struct FileSet { pub file_set_type: FileSetType, pub files: Vec, pub glob: String, + /// Added in v0.6.11. Populated when parsing from BEP, not from junit globs pub test_runner_status: Option, } diff --git a/bundle/src/types.rs b/bundle/src/types.rs index edd85347..5f2ad136 100644 --- a/bundle/src/types.rs +++ b/bundle/src/types.rs @@ -31,6 +31,7 @@ pub struct Test { pub class_name: Option, pub file: Option, pub id: String, + /// Added in v0.6.9 pub timestamp_millis: Option, } From feac42ffa4d9e91fe31de598c83aadaea666ca57 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 22:30:54 -0800 Subject: [PATCH 11/16] fix wasm tests --- context-js/tests/parse_compressed_bundle.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/context-js/tests/parse_compressed_bundle.test.ts b/context-js/tests/parse_compressed_bundle.test.ts index 2c2e471a..b323f4e0 100644 --- a/context-js/tests/parse_compressed_bundle.test.ts +++ b/context-js/tests/parse_compressed_bundle.test.ts @@ -35,6 +35,7 @@ const generateBundleMeta = (): TestBundleMeta => ({ }, ], glob: "**/*.xml", + test_runner_status: null, }, ], org: faker.company.name(), From 1862d2a77eca4725bbc83d354ed5c9f15d3b0056 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 23:07:01 -0800 Subject: [PATCH 12/16] fix mac --- cli/src/upload.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/upload.rs b/cli/src/upload.rs index 51a64c37..bc8a2095 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -181,7 +181,7 @@ pub async fn run_upload( { let temp_paths = handle_xcresult(&junit_temp_dir, xcresult_path, &repo.repo, &org_url_slug)?; - junit_path_wrappers = [junit_paths.as_slice(), temp_paths.as_slice()].concat(); + junit_path_wrappers = [junit_path_wrappers.as_slice(), temp_paths.as_slice()].concat(); if junit_paths.is_empty() && !allow_empty_test_results { return Err(anyhow::anyhow!( "No tests found in the provided XCResult path." From a49628200714821cdeeedb74613257b8b3c7df79 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Mon, 16 Dec 2024 23:19:13 -0800 Subject: [PATCH 13/16] fix mac again --- cli/src/upload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/upload.rs b/cli/src/upload.rs index bc8a2095..245678ad 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -182,11 +182,11 @@ pub async fn run_upload( let temp_paths = handle_xcresult(&junit_temp_dir, xcresult_path, &repo.repo, &org_url_slug)?; junit_path_wrappers = [junit_path_wrappers.as_slice(), temp_paths.as_slice()].concat(); - if junit_paths.is_empty() && !allow_empty_test_results { + if junit_path_wrappers.is_empty() && !allow_empty_test_results { return Err(anyhow::anyhow!( "No tests found in the provided XCResult path." )); - } else if junit_paths.is_empty() && allow_empty_test_results { + } else if junit_path_wrappers.is_empty() && allow_empty_test_results { log::warn!("No tests found in the provided XCResult path."); } } From 6715bd7b450c3e6e92867557d1add71a6c66d480 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Tue, 17 Dec 2024 18:12:30 -0800 Subject: [PATCH 14/16] address comments --- bundle/src/files.rs | 11 ++++-- cli/src/main.rs | 8 ++-- cli/src/runner.rs | 41 ++++++++++----------- cli/src/upload.rs | 11 ++---- cli/src/validate.rs | 4 +- context/src/bazel_bep/parser.rs | 65 +++++++++++++++------------------ context/src/junit/junit_path.rs | 34 +++++++++++++++-- 7 files changed, 95 insertions(+), 79 deletions(-) diff --git a/bundle/src/files.rs b/bundle/src/files.rs index ea5d944a..e673467f 100644 --- a/bundle/src/files.rs +++ b/bundle/src/files.rs @@ -2,7 +2,7 @@ use std::{format, time::SystemTime}; use codeowners::{CodeOwners, Owners, OwnersOfPath}; use constants::ALLOW_LIST; -use context::junit::junit_path::TestRunnerJunitStatus; +use context::junit::junit_path::{JunitReportFileWithStatus, JunitReportStatus}; #[cfg(feature = "pyo3")] use pyo3::prelude::*; #[cfg(feature = "pyo3")] @@ -41,7 +41,7 @@ pub struct FileSet { pub files: Vec, pub glob: String, /// Added in v0.6.11. Populated when parsing from BEP, not from junit globs - pub test_runner_status: Option, + pub test_runner_status: JunitReportStatus, } impl FileSet { @@ -50,13 +50,16 @@ impl FileSet { /// pub fn scan_from_glob( repo_root: &str, - glob_path: String, - test_runner_status: Option, + glob_with_status: JunitReportFileWithStatus, file_counter: &mut FileSetCounter, team: Option, codeowners: &Option, start: Option, ) -> anyhow::Result { + let JunitReportFileWithStatus { + junit_path: glob_path, + status: test_runner_status, + } = glob_with_status; let path_to_scan = if !std::path::Path::new(&glob_path).is_absolute() { std::path::Path::new(repo_root) .join(&glob_path) diff --git a/cli/src/main.rs b/cli/src/main.rs index d5212318..3b2799d1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -6,7 +6,8 @@ use clap::{Args, Parser, Subcommand}; use codeowners::CodeOwners; use constants::{EXIT_FAILURE, SENTRY_DSN}; use context::{ - bazel_bep::parser::BazelBepParser, junit::junit_path::JunitPathWrapper, repo::BundleRepo, + bazel_bep::parser::BazelBepParser, junit::junit_path::JunitReportFileWithStatus, + repo::BundleRepo, }; use trunk_analytics_cli::{ api_client::ApiClient, @@ -239,10 +240,7 @@ async fn run(cli: Cli) -> anyhow::Result { } None => junit_paths .into_iter() - .map(|p| JunitPathWrapper { - junit_path: p, - status: None, - }) + .map(JunitReportFileWithStatus::from) .collect(), }; validate(junit_file_paths, show_warnings, codeowners_path).await diff --git a/cli/src/runner.rs b/cli/src/runner.rs index 9a7ccec5..39d49cbf 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -12,7 +12,7 @@ use constants::{EXIT_FAILURE, EXIT_SUCCESS}; use context::{ bazel_bep::parser::BazelBepParser, junit::{ - junit_path::{JunitPathWrapper, TestRunnerJunitStatus}, + junit_path::{JunitReportFileWithStatus, JunitReportStatus}, parser::JunitParser, }, repo::BundleRepo, @@ -41,10 +41,7 @@ pub async fn run_test_command( let output_paths = match junit_spec { JunitSpec::Paths(paths) => paths .into_iter() - .map(|p| JunitPathWrapper { - junit_path: p, - status: None, - }) + .map(JunitReportFileWithStatus::from) .collect(), JunitSpec::BazelBep(bep_path) => { let mut parser = BazelBepParser::new(bep_path); @@ -99,7 +96,7 @@ async fn run_test_and_get_exit_code(command: &String, args: Vec<&String>) -> any pub fn build_filesets( repo_root: &str, - junit_paths: &[JunitPathWrapper], + junit_paths: &[JunitReportFileWithStatus], team: Option, codeowners: &Option, exec_start: Option, @@ -110,8 +107,7 @@ pub fn build_filesets( .map(|junit_wrapper| { FileSet::scan_from_glob( repo_root, - junit_wrapper.junit_path.to_string(), - junit_wrapper.status.clone(), + junit_wrapper.clone(), &mut file_counter, team.clone(), codeowners, @@ -132,8 +128,10 @@ pub fn build_filesets( path.push_str("**/*.xml"); FileSet::scan_from_glob( repo_root, - path.to_string(), - junit_wrapper.status.clone(), + JunitReportFileWithStatus { + junit_path: path, + status: junit_wrapper.status.clone(), + }, &mut file_counter, team.clone(), codeowners, @@ -183,10 +181,11 @@ pub async fn extract_failed_tests( let mut successes: HashMap = HashMap::new(); for file_set in file_sets { - if let Some(test_runner_status) = &file_set.test_runner_status { - if test_runner_status != &TestRunnerJunitStatus::Failed { - continue; - } + // TODO(TRUNK-13911): We should populate the status for all junits, regardless of the presence of a test runner status. + if file_set.test_runner_status == JunitReportStatus::Passed + || file_set.test_runner_status == JunitReportStatus::Flaky + { + continue; } for file in &file_set.files { let file = match std::fs::File::open(&file.original_path) { @@ -367,7 +366,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: None, + test_runner_status: JunitReportStatus::Unknown, }]; let retried_failures = @@ -390,7 +389,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: None, + test_runner_status: JunitReportStatus::Unknown, }]; let retried_failures = @@ -413,7 +412,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: None, + test_runner_status: JunitReportStatus::Unknown, }]; let mut multi_failures = @@ -447,7 +446,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: None, + test_runner_status: JunitReportStatus::Unknown, }]; let some_failures = @@ -466,7 +465,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("1/*.xml"), - test_runner_status: Some(TestRunnerJunitStatus::Passed), + test_runner_status: JunitReportStatus::Passed, }, FileSet { file_set_type: FileSetType::Junit, @@ -475,7 +474,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("2/*.xml"), - test_runner_status: Some(TestRunnerJunitStatus::Flaky), + test_runner_status: JunitReportStatus::Flaky, }, FileSet { file_set_type: FileSetType::Junit, @@ -484,7 +483,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("3/*.xml"), - test_runner_status: Some(TestRunnerJunitStatus::Failed), + test_runner_status: JunitReportStatus::Failed, }, ]; diff --git a/cli/src/upload.rs b/cli/src/upload.rs index 245678ad..e8d8ee1f 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -20,7 +20,7 @@ use constants::{EXIT_FAILURE, EXIT_SUCCESS}; use context::repo::RepoUrlParts; use context::{ bazel_bep::parser::{BazelBepParser, BepParseResult}, - junit::{junit_path::JunitPathWrapper, parser::JunitParser}, + junit::{junit_path::JunitReportFileWithStatus, parser::JunitParser}, repo::BundleRepo, }; @@ -142,10 +142,7 @@ pub async fn run_upload( } = upload_args; let mut junit_path_wrappers = junit_paths .into_iter() - .map(|p| JunitPathWrapper { - junit_path: p, - status: None, - }) + .map(JunitReportFileWithStatus::from) .collect(); let repo = BundleRepo::new( @@ -377,7 +374,7 @@ fn handle_xcresult( xcresult_path: Option, repo: &RepoUrlParts, org_url_slug: &str, -) -> Result, anyhow::Error> { +) -> Result, anyhow::Error> { let mut temp_paths = Vec::new(); if let Some(xcresult_path) = xcresult_path { let xcresult = XCResult::new(xcresult_path, repo, org_url_slug.to_string()); @@ -396,7 +393,7 @@ fn handle_xcresult( .map_err(|e| anyhow::anyhow!("Failed to write junit file: {}", e))?; let junit_temp_path_str = junit_temp_path.to_str(); if let Some(junit_temp_path_string) = junit_temp_path_str { - temp_paths.push(JunitPathWrapper { + temp_paths.push(JunitReportFileWithStatus { junit_path: junit_temp_path_string.to_string(), status: None, }); diff --git a/cli/src/validate.rs b/cli/src/validate.rs index 9b964f3f..ed48e243 100644 --- a/cli/src/validate.rs +++ b/cli/src/validate.rs @@ -7,7 +7,7 @@ use colored::{ColoredString, Colorize}; use console::Emoji; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; use context::junit::{ - junit_path::JunitPathWrapper, + junit_path::JunitReportFileWithStatus, parser::{JunitParseError, JunitParser}, validator::{ validate as validate_report, JunitReportValidation, JunitReportValidationFlatIssue, @@ -22,7 +22,7 @@ type JunitFileToReportAndErrors = BTreeMap, Vec< type JunitFileToValidation = BTreeMap>; pub async fn validate( - junit_paths: Vec, + junit_paths: Vec, show_warnings: bool, codeowners_path: Option, ) -> anyhow::Result { diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index 28e0e3c2..6ce90658 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -1,7 +1,7 @@ -use crate::junit::junit_path::{JunitPathWrapper, TestRunnerJunitStatus}; +use crate::junit::junit_path::{JunitReportFileWithStatus, JunitReportStatus}; use anyhow::Ok; use bazel_bep::types::build_event_stream::{ - build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, TestStatus, + build_event::Payload, build_event_id::Id, file::File::Uri, BuildEvent, }; use serde_json::Deserializer; use std::{collections::HashMap, path::PathBuf}; @@ -11,7 +11,7 @@ pub struct TestResult { pub label: String, pub cached: bool, pub xml_files: Vec, - pub summary_status: Option, + pub summary_status: JunitReportStatus, } const FILE_URI_PREFIX: &str = "file://"; @@ -38,7 +38,7 @@ impl BepParseResult { (xml_count, cached_xml_count) } - pub fn uncached_xml_files(&self) -> Vec { + pub fn uncached_xml_files(&self) -> Vec { self.test_results .iter() .filter_map(|r| { @@ -48,11 +48,11 @@ impl BepParseResult { Some( r.xml_files .iter() - .map(|f| JunitPathWrapper { + .map(|f| JunitReportFileWithStatus { junit_path: f.clone(), status: r.summary_status.clone(), }) - .collect::>(), + .collect::>(), ) }) .flatten() @@ -60,15 +60,6 @@ impl BepParseResult { } } -fn convert_test_summary_status(status: TestStatus) -> Option { - match status { - TestStatus::Passed => Some(TestRunnerJunitStatus::Passed), - TestStatus::Failed => Some(TestRunnerJunitStatus::Failed), - TestStatus::Flaky => Some(TestRunnerJunitStatus::Flaky), - _ => None, - } -} - /// Uses proto spec /// https://github.com/TylerJang27/bazel-bep/blob/master/proto/build_event_stream.proto based on /// https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto @@ -96,7 +87,7 @@ impl BazelBepParser { ( Vec::::new(), Vec::::new(), - HashMap::::new(), + HashMap::::new(), Vec::::new(), ), |(mut errors, mut test_results, mut summary_statuses, mut bep_test_events), @@ -113,10 +104,9 @@ impl BazelBepParser { Some(Payload::TestSummary(test_summary)), Some(Id::TestSummary(id)), ) => { - let summary_status = convert_test_summary_status( + if let Result::Ok(status) = JunitReportStatus::try_from( test_summary.overall_status(), - ); - if let Some(status) = summary_status { + ) { summary_statuses.insert(id.label.clone(), status); } bep_test_events.push(build_event); @@ -160,7 +150,7 @@ impl BazelBepParser { label: id.label.clone(), cached, xml_files, - summary_status: None, + summary_status: JunitReportStatus::Unknown, }); bep_test_events.push(build_event); } @@ -179,7 +169,10 @@ impl BazelBepParser { test_results: test_results .into_iter() .map(|test_result| TestResult { - summary_status: summary_statuses.get(&test_result.label).cloned(), + summary_status: summary_statuses + .get(&test_result.label) + .cloned() + .unwrap_or(JunitReportStatus::Unknown), ..test_result }) .collect(), @@ -206,9 +199,9 @@ mod tests { let empty_vec: Vec = Vec::new(); assert_eq!( parse_result.uncached_xml_files(), - vec![JunitPathWrapper { + vec![JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: None + status: JunitReportStatus::Unknown }] ); assert_eq!(parse_result.xml_file_counts(), (1, 0)); @@ -221,7 +214,7 @@ mod tests { let mut parser = BazelBepParser::new(input_file); let parse_result = parser.parse().unwrap(); - let empty_xml_vec: Vec = Vec::new(); + let empty_xml_vec: Vec = Vec::new(); let empty_errors_vec: Vec = Vec::new(); assert_eq!(parse_result.uncached_xml_files(), empty_xml_vec); assert_eq!(parse_result.xml_file_counts(), (0, 0)); @@ -237,13 +230,13 @@ mod tests { assert_eq!( parse_result.uncached_xml_files(), vec![ - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: Some(TestRunnerJunitStatus::Passed) + status: JunitReportStatus::Passed }, - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/client_test/test.xml".to_string(), - status: Some(TestRunnerJunitStatus::Passed) + status: JunitReportStatus::Passed } ] ); @@ -263,21 +256,21 @@ mod tests { assert_eq!( parse_result.uncached_xml_files(), vec![ - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test_attempts/attempt_1.xml".to_string(), - status: Some(TestRunnerJunitStatus::Flaky) + status: JunitReportStatus::Flaky }, - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test_attempts/attempt_2.xml".to_string(), - status: Some(TestRunnerJunitStatus::Flaky) + status: JunitReportStatus::Flaky }, - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: Some(TestRunnerJunitStatus::Flaky) + status: JunitReportStatus::Flaky }, - JunitPathWrapper { + JunitReportFileWithStatus { junit_path: "/tmp/client_test/test.xml".to_string(), - status: Some(TestRunnerJunitStatus::Failed) + status: JunitReportStatus::Failed } ] ); diff --git a/context/src/junit/junit_path.rs b/context/src/junit/junit_path.rs index e167f6b0..ce6cbde2 100644 --- a/context/src/junit/junit_path.rs +++ b/context/src/junit/junit_path.rs @@ -8,22 +8,48 @@ use tsify_next::Tsify; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::*; +use bazel_bep::types::build_event_stream::TestStatus; + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "pyo3", gen_stub_pyclass_enum, pyclass(eq, eq_int))] #[cfg_attr(feature = "wasm", derive(Tsify))] -pub enum TestRunnerJunitStatus { - #[default] +pub enum JunitReportStatus { Passed, Failed, Flaky, + #[default] + Unknown, +} + +impl TryFrom for JunitReportStatus { + type Error = (); + + fn try_from(status: TestStatus) -> Result { + match status { + TestStatus::Passed => Ok(JunitReportStatus::Passed), + TestStatus::Failed => Ok(JunitReportStatus::Failed), + TestStatus::Flaky => Ok(JunitReportStatus::Flaky), + _ => Err(()), + } + } } /// Encapsulates the glob path for a junit and, if applicable, the flakiness already /// assigned by the user's test runner. See bazel_bep/parser.rs for more. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct JunitPathWrapper { +pub struct JunitReportFileWithStatus { /// Path or glob pattern to the junit file. pub junit_path: String, /// Refers to an optional status parsed from the test runner's output, before junits have been parsed. - pub status: Option, + /// TODO(TRUNK-13911): We should populate the status for all junits, regardless of the presence of a test runner status. + pub status: JunitReportStatus, +} + +impl From for JunitReportFileWithStatus { + fn from(junit_path: String) -> Self { + Self { + junit_path, + status: JunitReportStatus::Unknown, + } + } } From 3f3ee7818d0fd9336db9b34061d578def627fa4f Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Tue, 17 Dec 2024 18:19:20 -0800 Subject: [PATCH 15/16] fix test --- context-js/tests/parse_compressed_bundle.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context-js/tests/parse_compressed_bundle.test.ts b/context-js/tests/parse_compressed_bundle.test.ts index b323f4e0..652973cf 100644 --- a/context-js/tests/parse_compressed_bundle.test.ts +++ b/context-js/tests/parse_compressed_bundle.test.ts @@ -35,7 +35,7 @@ const generateBundleMeta = (): TestBundleMeta => ({ }, ], glob: "**/*.xml", - test_runner_status: null, + test_runner_status: "Unknown", }, ], org: faker.company.name(), From 102b20d0163a5a55b6582bde757c74e2569d2ec0 Mon Sep 17 00:00:00 2001 From: Tyler Jang Date: Tue, 17 Dec 2024 19:38:04 -0800 Subject: [PATCH 16/16] make enum optional again --- bundle/src/files.rs | 2 +- cli/src/runner.rs | 24 +++++++++---------- cli/src/upload.rs | 2 ++ .../tests/parse_compressed_bundle.test.ts | 2 +- context/src/bazel_bep/parser.rs | 23 ++++++++---------- context/src/junit/junit_path.rs | 7 +++--- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/bundle/src/files.rs b/bundle/src/files.rs index e673467f..d727821d 100644 --- a/bundle/src/files.rs +++ b/bundle/src/files.rs @@ -41,7 +41,7 @@ pub struct FileSet { pub files: Vec, pub glob: String, /// Added in v0.6.11. Populated when parsing from BEP, not from junit globs - pub test_runner_status: JunitReportStatus, + pub test_runner_status: Option, } impl FileSet { diff --git a/cli/src/runner.rs b/cli/src/runner.rs index 39d49cbf..65f692ca 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -181,11 +181,11 @@ pub async fn extract_failed_tests( let mut successes: HashMap = HashMap::new(); for file_set in file_sets { - // TODO(TRUNK-13911): We should populate the status for all junits, regardless of the presence of a test runner status. - if file_set.test_runner_status == JunitReportStatus::Passed - || file_set.test_runner_status == JunitReportStatus::Flaky - { - continue; + if let Some(test_runner_status) = &file_set.test_runner_status { + // TODO(TRUNK-13911): We should populate the status for all junits, regardless of the presence of a test runner status. + if test_runner_status != &JunitReportStatus::Failed { + continue; + } } for file in &file_set.files { let file = match std::fs::File::open(&file.original_path) { @@ -366,7 +366,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: JunitReportStatus::Unknown, + test_runner_status: None, }]; let retried_failures = @@ -389,7 +389,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: JunitReportStatus::Unknown, + test_runner_status: None, }]; let retried_failures = @@ -412,7 +412,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: JunitReportStatus::Unknown, + test_runner_status: None, }]; let mut multi_failures = @@ -446,7 +446,7 @@ mod tests { }, ], glob: String::from("**/*.xml"), - test_runner_status: JunitReportStatus::Unknown, + test_runner_status: None, }]; let some_failures = @@ -465,7 +465,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("1/*.xml"), - test_runner_status: JunitReportStatus::Passed, + test_runner_status: Some(JunitReportStatus::Passed), }, FileSet { file_set_type: FileSetType::Junit, @@ -474,7 +474,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("2/*.xml"), - test_runner_status: JunitReportStatus::Flaky, + test_runner_status: Some(JunitReportStatus::Flaky), }, FileSet { file_set_type: FileSetType::Junit, @@ -483,7 +483,7 @@ mod tests { ..BundledFile::default() }], glob: String::from("3/*.xml"), - test_runner_status: JunitReportStatus::Failed, + test_runner_status: Some(JunitReportStatus::Failed), }, ]; diff --git a/cli/src/upload.rs b/cli/src/upload.rs index e8d8ee1f..95e13a6d 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -17,6 +17,8 @@ use bundle::{ use codeowners::CodeOwners; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; #[cfg(target_os = "macos")] +use context::junit::junit_path::JunitReportStatus; +#[cfg(target_os = "macos")] use context::repo::RepoUrlParts; use context::{ bazel_bep::parser::{BazelBepParser, BepParseResult}, diff --git a/context-js/tests/parse_compressed_bundle.test.ts b/context-js/tests/parse_compressed_bundle.test.ts index 652973cf..b323f4e0 100644 --- a/context-js/tests/parse_compressed_bundle.test.ts +++ b/context-js/tests/parse_compressed_bundle.test.ts @@ -35,7 +35,7 @@ const generateBundleMeta = (): TestBundleMeta => ({ }, ], glob: "**/*.xml", - test_runner_status: "Unknown", + test_runner_status: null, }, ], org: faker.company.name(), diff --git a/context/src/bazel_bep/parser.rs b/context/src/bazel_bep/parser.rs index 6ce90658..7cad7fb0 100644 --- a/context/src/bazel_bep/parser.rs +++ b/context/src/bazel_bep/parser.rs @@ -11,7 +11,7 @@ pub struct TestResult { pub label: String, pub cached: bool, pub xml_files: Vec, - pub summary_status: JunitReportStatus, + pub summary_status: Option, } const FILE_URI_PREFIX: &str = "file://"; @@ -150,7 +150,7 @@ impl BazelBepParser { label: id.label.clone(), cached, xml_files, - summary_status: JunitReportStatus::Unknown, + summary_status: None, }); bep_test_events.push(build_event); } @@ -169,10 +169,7 @@ impl BazelBepParser { test_results: test_results .into_iter() .map(|test_result| TestResult { - summary_status: summary_statuses - .get(&test_result.label) - .cloned() - .unwrap_or(JunitReportStatus::Unknown), + summary_status: summary_statuses.get(&test_result.label).cloned(), ..test_result }) .collect(), @@ -201,7 +198,7 @@ mod tests { parse_result.uncached_xml_files(), vec![JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: JunitReportStatus::Unknown + status: None }] ); assert_eq!(parse_result.xml_file_counts(), (1, 0)); @@ -232,11 +229,11 @@ mod tests { vec![ JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: JunitReportStatus::Passed + status: Some(JunitReportStatus::Passed) }, JunitReportFileWithStatus { junit_path: "/tmp/client_test/test.xml".to_string(), - status: JunitReportStatus::Passed + status: Some(JunitReportStatus::Passed) } ] ); @@ -258,19 +255,19 @@ mod tests { vec![ JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test_attempts/attempt_1.xml".to_string(), - status: JunitReportStatus::Flaky + status: Some(JunitReportStatus::Flaky) }, JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test_attempts/attempt_2.xml".to_string(), - status: JunitReportStatus::Flaky + status: Some(JunitReportStatus::Flaky) }, JunitReportFileWithStatus { junit_path: "/tmp/hello_test/test.xml".to_string(), - status: JunitReportStatus::Flaky + status: Some(JunitReportStatus::Flaky) }, JunitReportFileWithStatus { junit_path: "/tmp/client_test/test.xml".to_string(), - status: JunitReportStatus::Failed + status: Some(JunitReportStatus::Failed) } ] ); diff --git a/context/src/junit/junit_path.rs b/context/src/junit/junit_path.rs index ce6cbde2..8956d1fa 100644 --- a/context/src/junit/junit_path.rs +++ b/context/src/junit/junit_path.rs @@ -14,11 +14,10 @@ use bazel_bep::types::build_event_stream::TestStatus; #[cfg_attr(feature = "pyo3", gen_stub_pyclass_enum, pyclass(eq, eq_int))] #[cfg_attr(feature = "wasm", derive(Tsify))] pub enum JunitReportStatus { + #[default] Passed, Failed, Flaky, - #[default] - Unknown, } impl TryFrom for JunitReportStatus { @@ -42,14 +41,14 @@ pub struct JunitReportFileWithStatus { pub junit_path: String, /// Refers to an optional status parsed from the test runner's output, before junits have been parsed. /// TODO(TRUNK-13911): We should populate the status for all junits, regardless of the presence of a test runner status. - pub status: JunitReportStatus, + pub status: Option, } impl From for JunitReportFileWithStatus { fn from(junit_path: String) -> Self { Self { junit_path, - status: JunitReportStatus::Unknown, + status: None, } } }