Skip to content

Conversation

@ntBre
Copy link
Contributor

@ntBre ntBre commented Jul 15, 2025

Summary

This PR moves the JUnit output format to the new rendering infrastructure. As I
mention in a TODO in the code, there's some code that will be shared with the
grouped output format. Hopefully I'll have that PR up too by the time this one
is reviewed.

Test Plan

Existing tests moved to ruff_db

Summary
--

This PR moves the JUnit output format to the new rendering infrastructure. As I
mention in a TODO in the code, there's some code that will be shared with the
`grouped` output format. Hopefully I'll have that PR up too by the time this one
is reviewed.

Test Plan
--

Existing tests moved to `ruff_db`
@ntBre ntBre added internal An internal refactor or improvement diagnostics Related to reporting of diagnostics. labels Jul 15, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Jul 15, 2025

mypy_primer results

No ecosystem changes detected ✅
No memory usage changes detected ✅

@github-actions
Copy link
Contributor

github-actions bot commented Jul 15, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

warning: Detected debug build without --no-cache.
error: Failed to parse examples/mcp/databricks_mcp_cookbook.ipynb:12:1:8: Simple statements must be separated by newlines or semicolons

Formatter (preview)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

ruff format --preview

warning: Detected debug build without --no-cache.
error: Failed to parse examples/mcp/databricks_mcp_cookbook.ipynb:12:1:8: Simple statements must be separated by newlines or semicolons

@ntBre ntBre marked this pull request as ready for review July 15, 2025 21:18

use crate::diagnostic::{Diagnostic, SecondaryCode, render::FileResolver};

pub struct JunitRenderer<'a> {
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a link to the format specification?

Comment on lines 50 to 55
status.set_description(format!(
"line {row}, col {col}, {body}",
row = location.line,
col = location.column,
body = diagnostic.body()
));
Copy link
Member

Choose a reason for hiding this comment

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

Given that this is a free text field, I'd prefer if we omit row and column for diagnostics without a location rather than making up a location. For the future, I could see us use the full diagnostic renderer for the description similar to https://www.ibm.com/docs/en/developer-for-zos/16.0.x?topic=formats-junit-xml-format

Comment on lines 64 to 72
case.extra.insert(
XmlString::new("line"),
XmlString::new(location.line.to_string()),
);
case.extra.insert(
XmlString::new("column"),
XmlString::new(location.column.to_string()),
);

Copy link
Member

Choose a reason for hiding this comment

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

Same here, I think I'd prefer not to insert a made-up line and column information if they are missing

let code = diagnostic
.secondary_code()
.map_or_else(|| diagnostic.name(), SecondaryCode::as_str);
let mut case = TestCase::new(format!("org.ruff.{code}"), status);
Copy link
Member

Choose a reason for hiding this comment

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

I know that's pre-existing but I wonder if the test case name is supposed to be unique (but it isn't if a file contains multiple diagnostics with the same code). But this isn't something that we need to solve in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good point, it does seem like it's probably supposed to be unique. This site says it's the test method name in the test's class specified by classname.

Comment on lines 79 to 89
// Safety: this should be infallible as long as the data we put in the report is valid
// UTF-8.
//
// It's a bit of a shame to call `to_string`, but `Report` otherwise only exposes a
// `serialize` method, which expects an `io::Write`, not a `fmt::Write`. `to_string`
// currently (2025-07-15) serializes into a `Vec<u8>` and converts to a `String` from there.
f.write_str(
&report
.to_string()
.expect("Failed to serialize JUnit report"),
)
Copy link
Member

Choose a reason for hiding this comment

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

This has now come up a couple of times (JSON reports too).

We could write a io::Write to fmt::Write adapter instead. It's slightly less efficient because it needs to validate that the string is valid UTF8

struct FmtAdapter<'a> {
    fmt: &'a mut dyn std::fmt::Write,
}

impl std::io::Write for FmtAdapter<'_> {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.fmt
            .write_str(str::from_utf8(buf).map_err(|_| {
                std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    "Invalid UTF-8 in JUnit report",
                )
            })?)
            .map_err(io_error)?;

        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }

    fn write_fmt(&mut self, args: Arguments<'_>) -> std::io::Result<()> {
        self.fmt.write_fmt(args).map_err(io_error)
    }
}

fn io_error(error: std::fmt::Error) -> std::io::Error {
    std::io::Error::new(std::io::ErrorKind::Other, error)
}

The other alternative (but this might be worth a separate PR) is to explore if we can pass an std::io::Write instead of an std::fmt::Write

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I can add this for now. I poked a bit at passing a std::io::Write to all of these formats before, but I think it would justify its own PR like you said.

@ntBre ntBre merged commit 997dc2e into main Jul 17, 2025
37 checks passed
@ntBre ntBre deleted the brent/junit branch July 17, 2025 22:24
dcreager added a commit that referenced this pull request Jul 18, 2025
* main:
  [ty] Use `…` as the "cut" indicator in diagnostic rendering (#19420)
  [ty] synthesize __setattr__ for frozen dataclasses (#19307)
  [ty] Fixed bug in semantic token provider for parameters. (#19418)
  [ty] Shrink reachability constraints (#19410)
  Move JUnit rendering to `ruff_db` (#19370)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

diagnostics Related to reporting of diagnostics. internal An internal refactor or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants