Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions cli/src/summary_output/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use super::{
ErrorSummary, FailedTest, Outcome, Quarantining, UploadReport,
json::Summary,
text::{
FAILED_WITHOUT_RUNNING_TESTS, FAILURE_MESSAGE_MAX_LINES, NO_TEST_FAILURES, NO_TESTS_FOUND,
QUARANTINE_LOOKUP_FAILED, SOME_TESTS_MAY_NOT_HAVE_RUN, UNAUTHORIZED_HINT,
BAZEL_BEP_HINT, FAILED_WITHOUT_RUNNING_TESTS, FAILURE_MESSAGE_MAX_LINES, NO_TEST_FAILURES,
NO_TESTS_FOUND, QUARANTINE_LOOKUP_FAILED, SOME_TESTS_MAY_NOT_HAVE_RUN, UNAUTHORIZED_HINT,
old_timestamps_message,
},
};
use crate::{
Expand Down Expand Up @@ -74,6 +75,12 @@ fn upload_annotations(report: &UploadReport) -> Vec<Annotation> {
}
annotations.push(Annotation::warning(error_message(error)));
}
if report.files_with_old_timestamps > 0 {
annotations.push(Annotation::warning(format!(
"{}, but the test counts include them.\n{BAZEL_BEP_HINT}",
old_timestamps_message(report.files_with_old_timestamps)
)));
}

let exit_code = report.cli_exit_code();
match report.outcome {
Expand Down
36 changes: 36 additions & 0 deletions cli/src/summary_output/text.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::path::{Path, PathBuf};

use context::junit::validator::TIMESTAMP_OLD_HOURS;
use pluralizer::pluralize;
use superconsole::{
Hyperlink, Line, Lines, Span, SuperConsole,
Expand Down Expand Up @@ -81,6 +82,16 @@ pub(crate) const SOME_TESTS_MAY_NOT_HAVE_RUN: &str = "This may indicate that som
pub(crate) const QUARANTINE_LOOKUP_FAILED: &str = "We were unable to determine the quarantine status for tests. Any failing tests will be reported as failures";
pub(crate) const FAILURE_MESSAGE_MAX_LINES: usize = 20;
pub(crate) const UNAUTHORIZED_HINT: &str = "Hint: You can find it under the settings page at";
pub(crate) const BAZEL_BEP_HINT: &str = "If Bazel restored these files from its cache, upload with --bazel-bep-path to skip cached results: https://docs.trunk.io/flaky-tests/get-started/frameworks/bazel";

/// Shown whatever `--validation-report` says, since Trunk's ingestion drops these files' results.
pub(crate) fn old_timestamps_message(files: usize) -> String {
format!(
"{} {} test results older than {TIMESTAMP_OLD_HOURS} hours. Trunk will drop these results",
pluralize("file", files as isize, true),
if files == 1 { "has" } else { "have" },
)
}

pub fn render_error(error: &ErrorSummary) -> anyhow::Result<Vec<Line>> {
let mut lines = Vec::new();
Expand Down Expand Up @@ -209,6 +220,12 @@ pub fn render_upload(report: &UploadReport, options: &TextOptions) -> anyhow::Re
.attribute(Attribute::Bold),
)?]));
}
if report.files_with_old_timestamps > 0 {
output.extend(render_old_timestamps(format!(
"{}, but the test counts above include them.",
old_timestamps_message(report.files_with_old_timestamps)
))?);
}
output.push(Line::default());

if quarantined > 0 {
Expand Down Expand Up @@ -310,6 +327,17 @@ pub fn render_upload(report: &UploadReport, options: &TextOptions) -> anyhow::Re
Ok(output)
}

fn render_old_timestamps(message: String) -> anyhow::Result<Vec<Line>> {
Ok(vec![
Line::from_iter([Span::new_styled_lossy(
style(format!(" ⚠️ {message}"))
.with(Color::Yellow)
.attribute(Attribute::Bold),
)]),
Line::from_iter([Span::new_unstyled(format!(" {BAZEL_BEP_HINT}"))?]),
])
}

fn render_groups(groups: &[FailureGroup], options: &TextOptions) -> anyhow::Result<Lines> {
let mut output = Vec::new();
for group in groups {
Expand Down Expand Up @@ -663,6 +691,7 @@ pub fn render_validate(report: &ValidateReport) -> anyhow::Result<Vec<Line>> {
files_with_errors,
warnings,
errors,
files_with_old_timestamps,
} = report.counts;
output.push(Line::from_iter([
Span::new_styled(style(format!("{}", valid_files)).with(Color::Green))?,
Expand Down Expand Up @@ -694,6 +723,13 @@ pub fn render_validate(report: &ValidateReport) -> anyhow::Result<Vec<Line>> {
Span::new_styled(style(format!("{}", errors)).with(Color::Red))?,
Span::new_unstyled(format!(" {}", pluralize("error", errors as isize, false)))?,
]));
if files_with_old_timestamps > 0 {
output.push(Line::default());
output.extend(render_old_timestamps(format!(
"{} when they're uploaded.",
old_timestamps_message(files_with_old_timestamps)
))?);
}

Ok(output)
}
Expand Down
20 changes: 19 additions & 1 deletion cli/src/summary_output/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ pub struct UploadReport {
pub upload_url: Option<String>,
/// One entry per test report file.
pub validations: Vec<FileValidation>,
/// Files whose results Trunk will drop for having old timestamps. `counts` includes their
/// tests.
#[serde(default)]
pub files_with_old_timestamps: usize,
pub counts: TestCounts,
pub quarantining: Quarantining,
/// Failed tests that were quarantined, grouped like `failures`.
Expand Down Expand Up @@ -114,6 +118,9 @@ pub struct FileValidation {
pub test_suite_count: Option<usize>,
/// Null when the file couldn't be parsed.
pub test_case_count: Option<usize>,
/// The file has a test older than 24 hours, so Trunk will drop all of its results.
#[serde(default)]
pub old_timestamps: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
Expand Down Expand Up @@ -179,14 +186,16 @@ impl UploadReport {
UploadCommand::RSpec => None,
};
let bundle_upload_id = &result.meta.base_props.bundle_upload_id;
let validations = file_validations(&result.validations);

Self {
command,
error,
dry_run: result.dry_run,
bundle_upload_id: Some(bundle_upload_id.clone()).filter(|id| !id.is_empty()),
upload_url: upload_url(result),
validations: file_validations(&result.validations),
files_with_old_timestamps: files_with_old_timestamps(&validations),
validations,
counts,
quarantining: quarantine_context.quarantining(result.disable_quarantining),
quarantined: group(quarantined, |test| failed_test(result, test, true)),
Expand Down Expand Up @@ -228,6 +237,13 @@ impl TestCounts {
}
}

pub(super) fn files_with_old_timestamps(validations: &[FileValidation]) -> usize {
validations
.iter()
.filter(|validation| validation.old_timestamps)
.count()
}

impl FileValidation {
pub fn has_issues(&self) -> bool {
self.parse_error.is_some() || !self.errors.is_empty() || !self.warnings.is_empty()
Expand Down Expand Up @@ -336,6 +352,7 @@ fn file_validations(validations: &JunitReportValidations) -> Vec<FileValidation>
warnings: Vec::new(),
test_suite_count: None,
test_case_count: None,
old_timestamps: false,
},
Ok(validation) => {
let issues = validation.all_issues_flat();
Expand All @@ -353,6 +370,7 @@ fn file_validations(validations: &JunitReportValidations) -> Vec<FileValidation>
warnings: messages(JunitValidationLevel::SubOptimal),
test_suite_count: Some(validation.test_suites().len()),
test_case_count: Some(validation.test_cases().len()),
old_timestamps: validation.has_old_timestamps(),
}
}
})
Expand Down
7 changes: 6 additions & 1 deletion cli/src/summary_output/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use context::junit::validator::JunitReportValidationFlatIssue;
use schemars::JsonSchema;
use serde::Serialize;

use super::{FileValidation, Notice, ParseError};
use super::{FileValidation, Notice, ParseError, upload::files_with_old_timestamps};
use crate::{
exit_code::ExitCode,
validate_command::{ParseIssues, TestIssues, ValidateRunResult},
Expand Down Expand Up @@ -49,6 +49,8 @@ pub struct ValidationCounts {
pub files_with_errors: usize,
pub warnings: usize,
pub errors: usize,
/// Files whose results Trunk will drop for having old timestamps.
pub files_with_old_timestamps: usize,
}

impl ValidateReport {
Expand Down Expand Up @@ -132,6 +134,7 @@ impl ValidationCounts {
files_with_errors: files_with_errors.len(),
warnings,
errors,
files_with_old_timestamps: files_with_old_timestamps(validations),
}
}
}
Expand All @@ -145,6 +148,7 @@ fn parse_validation(issues: &ParseIssues) -> FileValidation {
warnings: issues.warnings.iter().map(ToString::to_string).collect(),
test_suite_count: None,
test_case_count: None,
old_timestamps: false,
}
}

Expand All @@ -162,5 +166,6 @@ fn test_validation(issues: &TestIssues) -> FileValidation {
warnings: messages(&issues.warnings),
test_suite_count: Some(issues.num_test_suites),
test_case_count: Some(issues.num_test_cases),
old_timestamps: issues.old_timestamps,
}
}
2 changes: 2 additions & 0 deletions cli/src/validate_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub struct TestIssues {
pub(crate) num_test_cases: usize,
pub(crate) errors: Vec<JunitReportValidationFlatIssue>,
pub(crate) warnings: Vec<JunitReportValidationFlatIssue>,
pub(crate) old_timestamps: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -448,6 +449,7 @@ fn gen_test_issues(report_validations: &JunitFileToValidation) -> Vec<TestIssues
num_test_cases: report_validation.test_cases().len(),
errors,
warnings,
old_timestamps: report_validation.has_old_timestamps(),
}
})
.collect()
Expand Down
38 changes: 37 additions & 1 deletion cli/tests/summary/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use trunk_analytics_cli::{

use super::{
unauthorized_error,
upload::{UPLOAD_ERROR_MESSAGE, failure, upload_result},
upload::{UPLOAD_ERROR_MESSAGE, failure, old_timestamp_validations, upload_result},
};

/// Writes the summary as `upload` would, then reads it back the way the command does.
Expand Down Expand Up @@ -233,6 +233,42 @@ fn warns_about_an_upload_error_before_the_failures() {
);
}

#[test]
fn warns_that_results_with_old_timestamps_will_be_dropped() {
let mut result = upload_result(5);
result.validations = old_timestamp_validations(2);

assert_eq!(
upload_annotations(&result),
[
"::warning::2 files have test results older than 24 hours. Trunk will drop these results, but the test counts include them.%0AIf Bazel restored these files from its cache, upload with --bazel-bep-path to skip cached results: https://docs.trunk.io/flaky-tests/get-started/frameworks/bazel"
]
);
}

#[test]
fn reads_a_summary_written_before_old_timestamps_were_counted() {
let mut result = upload_result(5);
result.validations = old_timestamp_validations(1);
let report = UploadReport::new(&result, UploadCommand::Upload);
let mut written: serde_json::Value =
serde_json::from_str(&Report::Upload(&report).to_json().unwrap()).unwrap();
written
.as_object_mut()
.unwrap()
.remove("filesWithOldTimestamps");
written["validations"][0]
.as_object_mut()
.unwrap()
.remove("oldTimestamps");

let json::Summary::Upload(read) = written.to_string().parse().unwrap() else {
panic!("expected an upload summary");
};
assert_eq!(read.files_with_old_timestamps, 0);
assert!(!read.validations[0].old_timestamps);
}

#[test]
fn annotates_a_run_that_failed_outright() {
let report = ErrorReport::new(
Expand Down
Loading
Loading