mirror of https://github.com/astral-sh/ruff
Refactor Rust lint test structure to use RuffTestFixture (#20689)
Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
b66a3e7451
commit
76f8e5b755
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
Loading…
Reference in New Issue