Skip to content

Commit

Permalink
init2
Browse files Browse the repository at this point in the history
  • Loading branch information
gnalh committed Dec 19, 2024
1 parent 3a850e6 commit eefd81a
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

7 changes: 6 additions & 1 deletion bundle/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,15 @@ impl FileSet {

// Save file under junit/0, junit/1, etc.
// This is to avoid having to deal with potential file name collisions.
let path_formatted = match original_path_abs.ends_with(".xml") {
// we currently support junit and internal binary files
true => format!("junit/{}", file_counter.count_file()),
false => format!("bin/{}", file_counter.count_file()),
};
files.push(BundledFile {
original_path: original_path_abs,
original_path_rel: Some(original_path_rel),
path: format!("junit/{}", file_counter.count_file()),
path: path_formatted,
#[cfg(not(feature = "wasm"))]
last_modified_epoch_ns: path
.metadata()?
Expand Down
2 changes: 1 addition & 1 deletion constants/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub const ALLOW_LIST: &[&str] = &[r".*\.xml$", r".*\.junit$"];
pub const ALLOW_LIST: &[&str] = &[r".*\.xml$", r".*\.junit$", r".*\.bin$"];

pub const EXIT_SUCCESS: i32 = 0;
pub const EXIT_FAILURE: i32 = 1;
Expand Down
2 changes: 2 additions & 0 deletions context-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ context = { path = "../context", features = ["git-access", "pyo3"] }
pyo3-stub-gen = "0.6.0"
futures-io = "0.3.31"
tokio = { version = "*", default-features = false, features = ["rt"] }
proto = { path = "../proto" }
prost = "0.13.4"

[target.'cfg(target_os = "linux")'.dependencies]
pyo3 = { version = "0.22.5", features = ["abi3-py39", "extension-module"] }
Expand Down
10 changes: 10 additions & 0 deletions context-py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use bundle::{
};
use codeowners::{BindingsOwners, CodeOwners};
use context::{env, junit, meta, repo};
use prost::Message;
use pyo3::{exceptions::PyTypeError, prelude::*};
use pyo3_stub_gen::{define_stub_info_gatherer, derive::gen_stub_pyfunction};

Expand Down Expand Up @@ -80,6 +81,14 @@ fn junit_parse(xml: Vec<u8>) -> PyResult<Vec<junit::bindings::BindingsReport>> {
.collect())
}

#[gen_stub_pyfunction]
#[pyfunction]
fn bin_parse(bin: Vec<u8>) -> PyResult<Vec<junit::bindings::BindingsReport>> {
let test_result = proto::test_context::test_run::TestResult::decode(&*bin).unwrap();
let bindings_report = junit::bindings::BindingsReport::from(test_result);
Ok(vec![bindings_report])
}

#[gen_stub_pyfunction]
#[pyfunction]
fn junit_validate(
Expand Down Expand Up @@ -199,6 +208,7 @@ fn context_py(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<junit::validator::JunitValidationLevel>()?;
m.add_class::<junit::validator::JunitValidationType>()?;
m.add_function(wrap_pyfunction!(junit_parse, m)?)?;
m.add_function(wrap_pyfunction!(bin_parse, m)?)?;
m.add_function(wrap_pyfunction!(junit_validate, m)?)?;
m.add_function(wrap_pyfunction!(junit_validation_level_to_string, m)?)?;
m.add_function(wrap_pyfunction!(junit_validation_type_to_string, m)?)?;
Expand Down
1 change: 1 addition & 0 deletions context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ thiserror = "1.0.63"
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 }
proto = { path = "../proto" }

[target.'cfg(target_os = "linux")'.dependencies]
pyo3 = { version = "0.22.5", optional = true, features = [
Expand Down
173 changes: 173 additions & 0 deletions context/src/junit/bindings.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{collections::HashMap, time::Duration};

use chrono::{DateTime, TimeDelta};
use proto::test_context::test_run::{TestCaseRun, TestCaseRunStatus, TestResult};
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
#[cfg(feature = "pyo3")]
Expand Down Expand Up @@ -31,6 +32,178 @@ pub struct BindingsReport {
pub test_suites: Vec<BindingsTestSuite>,
}

impl From<TestCaseRunStatus> for BindingsTestCaseStatusStatus {
fn from(value: TestCaseRunStatus) -> Self {
match value {
TestCaseRunStatus::Success => BindingsTestCaseStatusStatus::Success,
TestCaseRunStatus::Failure => BindingsTestCaseStatusStatus::NonSuccess,
TestCaseRunStatus::Skipped => BindingsTestCaseStatusStatus::Skipped,
TestCaseRunStatus::Unspecified => todo!(),
}
}
}

impl From<TestResult> for BindingsReport {
fn from(
TestResult {
test_case_runs,
uploader_metadata,
}: TestResult,
) -> Self {
let test_cases: Vec<BindingsTestCase> = test_case_runs
.into_iter()
.map(|testcase| BindingsTestCase::from(testcase))
.collect();
let parent_name_map: HashMap<String, Vec<BindingsTestCase>> =
test_cases.iter().fold(HashMap::new(), |mut acc, testcase| {
let parent_name = testcase.extra.get("parent_name").unwrap();
acc.entry(parent_name.clone())
.or_insert_with(Vec::new)
.push(testcase.to_owned());
acc
});
let mut report_time = 0.0;
let mut report_failures = 0;
let mut report_tests = 0;
let test_suites = parent_name_map
.into_iter()
.map(|(name, testcases)| {
let tests = testcases.len();
let disabled = testcases
.iter()
.filter(|tc| tc.status.status == BindingsTestCaseStatusStatus::Skipped)
.count();
let failures = testcases
.iter()
.filter(|tc| tc.status.status == BindingsTestCaseStatusStatus::NonSuccess)
.count();
let timestamp = testcases.iter().map(|tc| tc.timestamp.unwrap_or(0)).max();
let timestamp_micros = testcases
.iter()
.map(|tc| tc.timestamp_micros.unwrap_or(0))
.max();
let time = testcases.iter().map(|tc| tc.time.unwrap_or(0.0)).sum();
report_time += time;
report_failures += failures;
report_tests += tests;
BindingsTestSuite {
name,
tests,
disabled,
errors: 0,
failures,
timestamp,
timestamp_micros,
time: Some(time),
test_cases: testcases,
properties: vec![],
system_out: None,
system_err: None,
extra: HashMap::new(),
}
})
.collect();
let (name, timestamp, timestamp_micros) = match uploader_metadata {
Some(t) => (
t.origin,
Some(t.upload_time.unwrap_or_default().seconds),
Some(t.upload_time.unwrap_or_default().nanos as i64 * 1000),
),
None => ("Unknown".to_string(), None, None),
};
BindingsReport {
name,
test_suites,
time: Some(report_time),
uuid: None,
timestamp,
timestamp_micros,
errors: 0,
failures: report_failures,
tests: report_tests,
}
}
}

impl From<TestCaseRun> for BindingsTestCase {
fn from(
TestCaseRun {
name,
parent_name,
classname,
started_at,
finished_at,
status,
status_output_message,
id,
file,
line,
attempt_number,
}: TestCaseRun,
) -> Self {
let (timestamp, timestamp_micros) = match started_at {
Some(started_at) => (
Some(started_at.seconds),
Some(started_at.nanos as i64 * 1000),
),
None => (None, None),
};
let time = match (started_at, finished_at) {
(Some(started_at), Some(finished_at)) => Some(
(finished_at.seconds - started_at.seconds) as f64
+ (finished_at.nanos - started_at.nanos) as f64 / 1_000_000_000.0,
),
_ => None,
};
let typed_status = TestCaseRunStatus::try_from(status).ok().unwrap();
Self {
name,
classname: Some(classname),
assertions: None,
timestamp,
timestamp_micros,
time,
status: BindingsTestCaseStatus {
status: typed_status.into(),
success: {
match typed_status == TestCaseRunStatus::Success {
true => Some(BindingsTestCaseStatusSuccess { flaky_runs: vec![] }),
false => None,
}
},
non_success: match typed_status == TestCaseRunStatus::Success {
false => Some(BindingsTestCaseStatusNonSuccess {
kind: BindingsNonSuccessKind::Failure,
message: Some(status_output_message.clone()),
ty: None,
description: None,
reruns: vec![],
}),
true => None,
},
skipped: match typed_status == TestCaseRunStatus::Skipped {
true => Some(BindingsTestCaseStatusSkipped {
message: Some(status_output_message.clone()),
ty: None,
description: None,
}),
false => None,
},
},
system_err: None,
system_out: None,
extra: HashMap::from([
("id".to_string(), id.to_string()),
("file".to_string(), file),
("line".to_string(), line.to_string()),
("attempt_number".to_string(), attempt_number.to_string()),
("parent_name".to_string(), parent_name),
]),
properties: vec![],
}
}
}

impl From<Report> for BindingsReport {
fn from(
Report {
Expand Down
8 changes: 4 additions & 4 deletions context/src/junit/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ pub fn validate(report: &Report) -> JunitReportValidation {
if let Some(raw_test_case_id) = test_case.extra.get("id") {
let test_case_id = uuid::Uuid::parse_str(raw_test_case_id).unwrap_or_default();
if test_case_id.get_version() != Some(uuid::Version::Sha1) {
test_case_validation.add_issue(JunitValidationIssue::Invalid(
JunitTestCaseValidationIssueInvalid::TestCaseInvalidId(
test_case_validation.add_issue(JunitValidationIssue::SubOptimal(
JunitTestCaseValidationIssueSubOptimal::TestCaseInvalidId(
raw_test_case_id.to_string().clone(),
),
));
Expand Down Expand Up @@ -607,12 +607,12 @@ pub enum JunitTestCaseValidationIssueSubOptimal {
TIMESTAMP_STALE_HOURS
)]
TestCaseStaleTimestamp(DateTime<FixedOffset>),
#[error("test case id is not a valid uuidv5")]
TestCaseInvalidId(String),
}

#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum JunitTestCaseValidationIssueInvalid {
#[error("test case name too short")]
TestCaseNameTooShort(String),
#[error("test case id is not a valid uuidv5")]
TestCaseInvalidId(String),
}

0 comments on commit eefd81a

Please sign in to comment.