From 42b972753aa9737140d3c715dc1fa2a4d20eba02 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 18 Dec 2025 19:05:02 +0100 Subject: [PATCH] [ty] Use datatest instead of dirtest (#21937) --- Cargo.lock | 23 +------- Cargo.toml | 1 - crates/ty_python_semantic/Cargo.toml | 8 ++- crates/ty_python_semantic/build.rs | 4 -- crates/ty_python_semantic/mdtest.py | 25 ++------- crates/ty_python_semantic/tests/mdtest.rs | 68 ++++++++--------------- crates/ty_test/README.md | 13 ----- crates/ty_test/src/lib.rs | 17 +++--- 8 files changed, 42 insertions(+), 117 deletions(-) delete mode 100644 crates/ty_python_semantic/build.rs diff --git a/Cargo.lock b/Cargo.lock index cda8a8261d..0cdc6e1039 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,27 +1004,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dir-test" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c013fe825864f3e4593f36426c1fa7a74f5603f13ca8d1af7a990c1cd94a79" -dependencies = [ - "dir-test-macros", -] - -[[package]] -name = "dir-test-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d42f54d7b4a6bc2400fe5b338e35d1a335787585375322f49c5d5fe7b243da7e" -dependencies = [ - "glob", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dirs" version = "6.0.0" @@ -4513,7 +4492,7 @@ dependencies = [ "camino", "colored 3.0.0", "compact_str", - "dir-test", + "datatest-stable", "drop_bomb", "get-size2", "glob", diff --git a/Cargo.toml b/Cargo.toml index bd06571cd3..b2915ae754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,6 @@ criterion = { version = "0.7.0", default-features = false } crossbeam = { version = "0.8.4" } dashmap = { version = "6.0.1" } datatest-stable = { version = "0.3.3" } -dir-test = { version = "0.4.0" } dunce = { version = "1.0.5" } drop_bomb = { version = "0.1.5" } etcetera = { version = "0.11.0" } diff --git a/crates/ty_python_semantic/Cargo.toml b/crates/ty_python_semantic/Cargo.toml index 957126cf3a..30d0a26fad 100644 --- a/crates/ty_python_semantic/Cargo.toml +++ b/crates/ty_python_semantic/Cargo.toml @@ -33,7 +33,7 @@ camino = { workspace = true } colored = { workspace = true } compact_str = { workspace = true } drop_bomb = { workspace = true } -get-size2 = { workspace = true, features = ["indexmap", "ordermap"]} +get-size2 = { workspace = true, features = ["indexmap", "ordermap"] } indexmap = { workspace = true } itertools = { workspace = true } ordermap = { workspace = true } @@ -62,7 +62,7 @@ ty_test = { workspace = true } ty_vendored = { workspace = true } anyhow = { workspace = true } -dir-test = { workspace = true } +datatest-stable = { workspace = true } glob = { workspace = true } indoc = { workspace = true } insta = { workspace = true } @@ -76,5 +76,9 @@ schemars = ["dep:schemars", "dep:serde_json"] serde = ["ruff_db/serde", "dep:serde", "ruff_python_ast/serde"] testing = [] +[[test]] +name = "mdtest" +harness = false + [lints] workspace = true diff --git a/crates/ty_python_semantic/build.rs b/crates/ty_python_semantic/build.rs deleted file mode 100644 index d9c90fc60b..0000000000 --- a/crates/ty_python_semantic/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Rebuild the crate if a test file is added or removed from -pub fn main() { - println!("cargo::rerun-if-changed=resources/mdtest"); -} diff --git a/crates/ty_python_semantic/mdtest.py b/crates/ty_python_semantic/mdtest.py index 2acc6f452b..8f6b4f11d4 100644 --- a/crates/ty_python_semantic/mdtest.py +++ b/crates/ty_python_semantic/mdtest.py @@ -129,17 +129,8 @@ class MDTestRunner: check=False, ) - def _mangle_path(self, markdown_file: Path) -> str: - return ( - markdown_file.as_posix() - .replace("/", "_") - .replace("-", "_") - .removesuffix(".md") - ) - def _run_mdtests_for_file(self, markdown_file: Path) -> None: - path_mangled = self._mangle_path(markdown_file) - test_name = f"mdtest__{path_mangled}" + test_name = f"mdtest::{markdown_file}" output = self._run_mdtest(["--exact", test_name], capture_output=True) @@ -245,16 +236,10 @@ class MDTestRunner: if rust_code_has_changed: if self._recompile_tests("Rust code has changed, recompiling tests..."): self._run_mdtest(self.filters) - elif vendored_typeshed_has_changed: - if self._recompile_tests( - "Vendored typeshed has changed, recompiling tests..." - ): - self._run_mdtest(self.filters) - elif new_md_files: - files = " ".join(file.as_posix() for file in new_md_files) - self._recompile_tests( - f"New Markdown test [yellow]{files}[/yellow] detected, recompiling tests..." - ) + elif vendored_typeshed_has_changed and self._recompile_tests( + "Vendored typeshed has changed, recompiling tests..." + ): + self._run_mdtest(self.filters) for path in new_md_files | changed_md_files: self._run_mdtests_for_file(path) diff --git a/crates/ty_python_semantic/tests/mdtest.rs b/crates/ty_python_semantic/tests/mdtest.rs index 343ded06c7..62579bb74a 100644 --- a/crates/ty_python_semantic/tests/mdtest.rs +++ b/crates/ty_python_semantic/tests/mdtest.rs @@ -1,24 +1,25 @@ +use anyhow::anyhow; use camino::Utf8Path; -use dir_test::{Fixture, dir_test}; use ty_static::EnvVars; use ty_test::OutputFormat; /// See `crates/ty_test/README.md` for documentation on these tests. -#[dir_test( - dir: "$CARGO_MANIFEST_DIR/resources/mdtest", - glob: "**/*.md" -)] #[expect(clippy::needless_pass_by_value)] -fn mdtest(fixture: Fixture<&str>) { - let absolute_fixture_path = Utf8Path::new(fixture.path()); +fn mdtest(fixture_path: &Utf8Path, content: String) -> datatest_stable::Result<()> { + let short_title = fixture_path + .file_name() + .ok_or_else(|| anyhow!("Expected fixture path to have a file name"))?; + let crate_dir = Utf8Path::new(env!("CARGO_MANIFEST_DIR")); let snapshot_path = crate_dir.join("resources").join("mdtest").join("snapshots"); - let workspace_root = crate_dir.ancestors().nth(2).unwrap(); + let absolute_fixture_path = crate_dir.join(fixture_path); + let workspace_relative_fixture_path = Utf8Path::new("crates/ty_python_semantic") + .join(fixture_path.strip_prefix(".").unwrap_or(fixture_path)); - let relative_fixture_path = absolute_fixture_path.strip_prefix(workspace_root).unwrap(); - let short_title = absolute_fixture_path.file_name().unwrap(); - - let test_name = test_name("mdtest", absolute_fixture_path); + let test_name = fixture_path + .strip_prefix("./resources/mdtest") + .unwrap_or(fixture_path) + .as_str(); let output_format = if std::env::var(EnvVars::MDTEST_GITHUB_ANNOTATIONS_FORMAT).is_ok() { OutputFormat::GitHub @@ -27,43 +28,18 @@ fn mdtest(fixture: Fixture<&str>) { }; ty_test::run( - absolute_fixture_path, - relative_fixture_path, + &absolute_fixture_path, + &workspace_relative_fixture_path, + &content, &snapshot_path, short_title, - &test_name, + test_name, output_format, - ); + )?; + + Ok(()) } -/// Constructs the test name used for individual markdown files -/// -/// This code is copied from -/// and should be updated if they diverge -fn test_name(test_func_name: &str, fixture_path: &Utf8Path) -> String { - assert!(fixture_path.is_file()); - - let dir_path = format!("{}/resources/mdtest", std::env!("CARGO_MANIFEST_DIR")); - let rel_path = fixture_path.strip_prefix(dir_path).unwrap(); - assert!(rel_path.is_relative()); - - let mut test_name = test_func_name.to_owned(); - test_name.push_str("__"); - - for component in rel_path.parent().unwrap().components() { - let component = component - .as_str() - .replace(|c: char| c.is_ascii_punctuation(), "_"); - test_name.push_str(&component); - test_name.push('_'); - } - - test_name.push_str( - &rel_path - .file_stem() - .unwrap() - .replace(|c: char| c.is_ascii_punctuation(), "_"), - ); - - test_name +datatest_stable::harness! { + { test = mdtest, root = "./resources/mdtest", pattern = r"\.md$" }, } diff --git a/crates/ty_test/README.md b/crates/ty_test/README.md index b31a45e877..200a197660 100644 --- a/crates/ty_test/README.md +++ b/crates/ty_test/README.md @@ -34,19 +34,6 @@ syntax, it's just how this README embeds an example mdtest Markdown document.) See actual example mdtest suites in [`crates/ty_python_semantic/resources/mdtest`](https://github.com/astral-sh/ruff/tree/main/crates/ty_python_semantic/resources/mdtest). -> [!NOTE] -> If you use `dir-test`, `rstest` or similar to generate a separate test for all Markdown files in a certain directory, -> as with the example in `crates/ty_python_semantic/tests/mdtest.rs`, -> you will likely want to also make sure that the crate the tests are in is rebuilt every time a -> Markdown file is added or removed from the directory. See -> [`crates/ty_python_semantic/build.rs`](https://github.com/astral-sh/ruff/tree/main/crates/ty_python_semantic/build.rs) -> for an example of how to do this. -> -> This is because these macros generate their tests at build time rather than at runtime. -> Without the `build.rs` file to force a rebuild when a Markdown file is added or removed, -> a new Markdown test suite might not be run unless some other change in the crate caused a rebuild -> following the addition of the new test file. - ## Assertions Two kinds of assertions are supported: `# revealed:` (shown above) and `# error:`. diff --git a/crates/ty_test/src/lib.rs b/crates/ty_test/src/lib.rs index 312a00fc32..f717811624 100644 --- a/crates/ty_test/src/lib.rs +++ b/crates/ty_test/src/lib.rs @@ -1,6 +1,7 @@ use crate::config::Log; use crate::db::Db; use crate::parser::{BacktickOffsets, EmbeddedFileSourceMap}; +use anyhow::anyhow; use camino::Utf8Path; use colored::Colorize; use config::SystemKind; @@ -41,18 +42,14 @@ use ty_static::EnvVars; pub fn run( absolute_fixture_path: &Utf8Path, relative_fixture_path: &Utf8Path, + source: &str, snapshot_path: &Utf8Path, short_title: &str, test_name: &str, output_format: OutputFormat, -) { - let source = std::fs::read_to_string(absolute_fixture_path).unwrap(); - let suite = match test_parser::parse(short_title, &source) { - Ok(suite) => suite, - Err(err) => { - panic!("Error parsing `{absolute_fixture_path}`: {err:?}") - } - }; +) -> anyhow::Result<()> { + let suite = test_parser::parse(short_title, source) + .map_err(|err| anyhow!("Failed to parse fixture: {err}"))?; let mut db = db::Db::setup(); @@ -86,7 +83,7 @@ pub fn run( } if let Err(failures) = result { - let md_index = LineIndex::from_source_text(&source); + let md_index = LineIndex::from_source_text(source); for test_failures in failures { let source_map = @@ -156,6 +153,8 @@ pub fn run( println!("\n{}\n", "-".repeat(50)); assert!(!any_failures, "Some tests failed."); + + Ok(()) } /// Defines the format in which mdtest should print an error to the terminal