Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Feat): Parse BEP TestSummary #242

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion bundle/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{format, time::SystemTime};

use codeowners::{CodeOwners, Owners, OwnersOfPath};
use constants::ALLOW_LIST;
use context::junit::junit_path::{JunitReportFileWithStatus, JunitReportStatus};
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
#[cfg(feature = "pyo3")]
Expand Down Expand Up @@ -39,6 +40,8 @@ pub struct FileSet {
pub file_set_type: FileSetType,
pub files: Vec<BundledFile>,
pub glob: String,
/// Added in v0.6.11. Populated when parsing from BEP, not from junit globs
pub test_runner_status: Option<JunitReportStatus>,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up making it optional again since that allows us to better deserialize. Otherwise we need to have some versioned logic here for deserializing from meta.json, which doesn't seem worth the effort for this particular case

}

impl FileSet {
Expand All @@ -47,12 +50,16 @@ impl FileSet {
///
pub fn scan_from_glob(
repo_root: &str,
glob_path: String,
glob_with_status: JunitReportFileWithStatus,
file_counter: &mut FileSetCounter,
team: Option<String>,
codeowners: &Option<CodeOwners>,
start: Option<SystemTime>,
) -> anyhow::Result<FileSet> {
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)
Expand Down Expand Up @@ -147,6 +154,7 @@ impl FileSet {
file_set_type: FileSetType::Junit,
files,
glob: glob_path,
test_runner_status,
})
}
}
1 change: 1 addition & 0 deletions bundle/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub struct Test {
pub class_name: Option<String>,
pub file: Option<String>,
pub id: String,
/// Added in v0.6.9
pub timestamp_millis: Option<i64>,
}

Expand Down
55 changes: 55 additions & 0 deletions cli-tests/src/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,61 @@ async fn upload_bundle_success_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");
// 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")
Expand Down
11 changes: 10 additions & 1 deletion cli-tests/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -54,6 +57,12 @@ pub fn generate_mock_bazel_bep<T: AsRef<Path>>(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();
Expand Down
32 changes: 32 additions & 0 deletions cli-tests/test_fixtures/bep_retries
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
81 changes: 81 additions & 0 deletions cli-tests/test_fixtures/bep_retries_timestamp
Original file line number Diff line number Diff line change
@@ -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"
}
}
10 changes: 8 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ 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::JunitReportFileWithStatus,
repo::BundleRepo,
};
use trunk_analytics_cli::{
api_client::ApiClient,
print::print_bep_results,
Expand Down Expand Up @@ -235,7 +238,10 @@ async fn run(cli: Cli) -> anyhow::Result<i32> {
print_bep_results(&bep_result);
bep_result.uncached_xml_files()
}
None => junit_paths,
None => junit_paths
.into_iter()
.map(JunitReportFileWithStatus::from)
.collect(),
};
validate(junit_file_paths, show_warnings, codeowners_path).await
}
Expand Down
Loading
Loading