Refactor Rust lint test structure to use RuffTestFixture (#20689)

Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
Renkai Ge 2025-10-07 17:28:00 +08:00 committed by GitHub
parent b66a3e7451
commit 76f8e5b755
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 6304 additions and 6598 deletions

1
Cargo.lock generated
View File

@ -2777,6 +2777,7 @@ dependencies = [
"ruff_python_ast", "ruff_python_ast",
"ruff_python_formatter", "ruff_python_formatter",
"ruff_python_parser", "ruff_python_parser",
"ruff_python_trivia",
"ruff_server", "ruff_server",
"ruff_source_file", "ruff_source_file",
"ruff_text_size", "ruff_text_size",

View File

@ -72,6 +72,7 @@ dunce = { workspace = true }
indoc = { workspace = true } indoc = { workspace = true }
insta = { workspace = true, features = ["filters", "json"] } insta = { workspace = true, features = ["filters", "json"] }
insta-cmd = { workspace = true } insta-cmd = { workspace = true }
ruff_python_trivia = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
test-case = { workspace = true } test-case = { workspace = true }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,177 @@
//! Test fixture utilities for ruff CLI tests
//!
//! The core concept is borrowed from ty/tests/cli/main.rs and can be extended
//! with more functionality from there in the future if needed.
#![cfg(not(target_family = "wasm"))]
use anyhow::{Context as _, Result};
use insta::internals::SettingsBindDropGuard;
use insta_cmd::get_cargo_bin;
use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
use tempfile::TempDir;
mod lint;
const BIN_NAME: &str = "ruff";
/// Creates a regex filter for replacing temporary directory paths in snapshots
pub(crate) fn tempdir_filter(path: impl AsRef<str>) -> String {
format!(r"{}[\\/]?", regex::escape(path.as_ref()))
}
/// A test fixture for running ruff CLI tests with temporary directories and files.
///
/// This fixture provides:
/// - Temporary directory management
/// - File creation utilities
/// - Proper snapshot filtering for cross-platform compatibility
/// - Pre-configured ruff command creation
///
/// # Example
///
/// ```rust,no_run
/// use crate::common::RuffTestFixture;
///
/// let fixture = RuffTestFixture::with_file("ruff.toml", "select = ['E']")?;
/// let output = fixture.command().args(["check", "."]).output()?;
/// ```
pub(crate) struct CliTest {
_temp_dir: TempDir,
_settings_scope: SettingsBindDropGuard,
project_dir: PathBuf,
}
impl CliTest {
/// Creates a new test fixture with an empty temporary directory.
///
/// This sets up:
/// - A temporary directory that's automatically cleaned up
/// - Insta snapshot filters for cross-platform path compatibility
/// - Environment isolation for consistent test behavior
pub(crate) fn new() -> Result<Self> {
Self::with_settings(|_, settings| settings)
}
pub(crate) fn with_settings(
setup_settings: impl FnOnce(&Path, insta::Settings) -> insta::Settings,
) -> Result<Self> {
let temp_dir = TempDir::new()?;
// Canonicalize the tempdir path because macOS uses symlinks for tempdirs
// and that doesn't play well with our snapshot filtering.
// Simplify with dunce because otherwise we get UNC paths on Windows.
let project_dir = dunce::simplified(
&temp_dir
.path()
.canonicalize()
.context("Failed to canonicalize project path")?,
)
.to_path_buf();
let mut settings = setup_settings(&project_dir, insta::Settings::clone_current());
settings.add_filter(&tempdir_filter(project_dir.to_str().unwrap()), "[TMP]/");
settings.add_filter(r#"\\([\w&&[^nr"]]\w|\s|\.)"#, "/$1");
settings.add_filter(r"(Panicked at) [^:]+:\d+:\d+", "$1 <location>");
settings.add_filter(ruff_linter::VERSION, "[VERSION]");
settings.add_filter(
r#"The system cannot find the file specified."#,
"No such file or directory",
);
let settings_scope = settings.bind_to_scope();
Ok(Self {
project_dir,
_temp_dir: temp_dir,
_settings_scope: settings_scope,
})
}
/// Creates a test fixture with a single file.
///
/// # Arguments
///
/// * `path` - The relative path for the file
/// * `content` - The content to write to the file
///
/// # Example
///
/// ```rust,no_run
/// let fixture = RuffTestFixture::with_file("ruff.toml", "select = ['E']")?;
/// ```
pub(crate) fn with_file(path: impl AsRef<Path>, content: &str) -> Result<Self> {
let fixture = Self::new()?;
fixture.write_file(path, content)?;
Ok(fixture)
}
/// Ensures that the parent directory of a path exists.
fn ensure_parent_directory(path: &Path) -> Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create directory `{}`", parent.display()))?;
}
Ok(())
}
/// Writes a file to the test directory.
///
/// Parent directories are created automatically if they don't exist.
/// Content is dedented to remove common leading whitespace for cleaner test code.
///
/// # Arguments
///
/// * `path` - The relative path for the file
/// * `content` - The content to write to the file
pub(crate) fn write_file(&self, path: impl AsRef<Path>, content: &str) -> Result<()> {
let path = path.as_ref();
let file_path = self.project_dir.join(path);
Self::ensure_parent_directory(&file_path)?;
let content = ruff_python_trivia::textwrap::dedent(content);
fs::write(&file_path, content.as_ref())
.with_context(|| format!("Failed to write file `{}`", file_path.display()))?;
Ok(())
}
/// Returns the path to the test directory root.
pub(crate) fn root(&self) -> &Path {
&self.project_dir
}
/// Creates a pre-configured ruff command for testing.
///
/// The command is set up with:
/// - The correct ruff binary path
/// - Working directory set to the test directory
/// - Clean environment variables for consistent behavior
///
/// You can chain additional arguments and options as needed.
///
/// # Example
///
/// ```rust,no_run
/// let output = fixture
/// .command()
/// .args(["check", "--select", "E"])
/// .arg(".")
/// .output()?;
/// ```
pub(crate) fn command(&self) -> Command {
let mut command = Command::new(get_cargo_bin(BIN_NAME));
command.current_dir(&self.project_dir);
// Unset all environment variables because they can affect test behavior.
command.env_clear();
command
}
}

View File

@ -1,5 +1,5 @@
--- ---
source: crates/ruff/tests/lint.rs source: crates/ruff/tests/cli/lint.rs
info: info:
program: ruff program: ruff
args: args:
@ -12,6 +12,7 @@ info:
- "--target-version" - "--target-version"
- py39 - py39
- input.py - input.py
snapshot_kind: text
--- ---
success: false success: false
exit_code: 1 exit_code: 1

File diff suppressed because it is too large Load Diff