diff --git a/Cargo.lock b/Cargo.lock index 451ee4215d..8a08f5f2e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2033,6 +2033,7 @@ dependencies = [ "oxc_ast", "oxc_diagnostics", "oxc_formatter_core", + "oxc_formatter_tests", "oxc_jsdoc", "oxc_parser", "oxc_semantic", @@ -2058,7 +2059,6 @@ dependencies = [ "oxc_data_structures", "oxc_span", "rustc-hash", - "serde_json", "unicode-width", ] @@ -2072,6 +2072,7 @@ dependencies = [ "oxc_allocator", "oxc_diagnostics", "oxc_formatter_core", + "oxc_formatter_tests", "oxc_span", "phf", "pico-args", @@ -2087,6 +2088,7 @@ dependencies = [ "oxc_allocator", "oxc_diagnostics", "oxc_formatter_core", + "oxc_formatter_tests", "oxc_span", "oxc_tasks_common", "pico-args", @@ -2102,6 +2104,7 @@ dependencies = [ "oxc_ast", "oxc_diagnostics", "oxc_formatter_core", + "oxc_formatter_tests", "oxc_parser", "oxc_span", "oxc_syntax", @@ -2109,6 +2112,14 @@ dependencies = [ "pico-args", ] +[[package]] +name = "oxc_formatter_tests" +version = "0.62.0" +dependencies = [ + "oxc_formatter_core", + "serde_json", +] + [[package]] name = "oxc_formatter_yaml" version = "0.63.0" @@ -2119,6 +2130,7 @@ dependencies = [ "oxc_allocator", "oxc_diagnostics", "oxc_formatter_core", + "oxc_formatter_tests", "oxc_span", "oxc_tasks_common", "pico-args", diff --git a/Cargo.toml b/Cargo.toml index 9b04b9d30b..af1bf3073d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,6 +163,7 @@ oxc_formatter_core = { path = "crates/oxc_formatter_core" } # Language-agnostic oxc_formatter_css = { path = "crates/oxc_formatter_css" } # CSS formatter oxc_formatter_graphql = { path = "crates/oxc_formatter_graphql" } # GraphQL formatter oxc_formatter_json = { path = "crates/oxc_formatter_json" } # JSON formatter +oxc_formatter_tests = { path = "crates/oxc_formatter_tests" } # Formatter test infrastructure (fixture codegen + harness) oxc_formatter_yaml = { path = "crates/oxc_formatter_yaml" } # YAML formatter oxc_language_server = { path = "crates/oxc_language_server", default-features = false } # Language server oxc_lexer = { path = "crates/oxc_lexer" } # JS/TS lexer (incubating; x86_64 SIMD, stub elsewhere) diff --git a/crates/oxc_formatter/Cargo.toml b/crates/oxc_formatter/Cargo.toml index b3375aa179..535b02ae90 100644 --- a/crates/oxc_formatter/Cargo.toml +++ b/crates/oxc_formatter/Cargo.toml @@ -41,14 +41,14 @@ unicode-width = { workspace = true } [dev-dependencies] insta = { workspace = true } -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } oxc_tasks_common = { workspace = true } pico-args = { workspace = true } serde_json = { workspace = true } serde = { workspace = true, features = ["derive"] } [build-dependencies] -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } [features] default = [] diff --git a/crates/oxc_formatter/build.rs b/crates/oxc_formatter/build.rs index c0a3dc8904..94b76fcef1 100644 --- a/crates/oxc_formatter/build.rs +++ b/crates/oxc_formatter/build.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_formatter_core::test_support::{GenerateConfig, generate_tests}; +use oxc_formatter_tests::{GenerateConfig, generate_tests}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); diff --git a/crates/oxc_formatter/tests/fixtures/mod.rs b/crates/oxc_formatter/tests/fixtures/mod.rs index 3c1b45c089..ba211d8e41 100644 --- a/crates/oxc_formatter/tests/fixtures/mod.rs +++ b/crates/oxc_formatter/tests/fixtures/mod.rs @@ -5,7 +5,7 @@ use oxc_formatter::{ ArrowParentheses, BracketSameLine, BracketSpacing, JsFormatOptions, JsdocOptions, QuoteProperties, QuoteStyle, Semicolons, TrailingCommas, }; -use oxc_formatter_core::test_support::{ +use oxc_formatter_tests::{ FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, }; use oxc_span::SourceType; diff --git a/crates/oxc_formatter_core/AGENTS.md b/crates/oxc_formatter_core/AGENTS.md index 183473a06c..b9156881af 100644 --- a/crates/oxc_formatter_core/AGENTS.md +++ b/crates/oxc_formatter_core/AGENTS.md @@ -160,18 +160,11 @@ core owns the span-ordered cursor mechanics; what the items mean (comments) and Quote-style options, comment rules, and the like are likewise consumer-owned. -## Cargo features - -`test_harness` exposes `test_support/` (fixture test generation) to downstream crates. -Consumers still need their own `insta` dev-dep so the recorded `source:` header points to the consumer crate. - ## Verification -Feature configs to check: - ```sh cargo c -p oxc_formatter_core -cargo c -p oxc_formatter_core --features test_harness ``` This crate has basic tests only of its own, it is exercised through the conformance/snapshot tests of its consumers. +The fixture-test infrastructure those consumers use lives in `crates/oxc_formatter_tests`. diff --git a/crates/oxc_formatter_core/Cargo.toml b/crates/oxc_formatter_core/Cargo.toml index 1fe82db1bb..e331830fb7 100644 --- a/crates/oxc_formatter_core/Cargo.toml +++ b/crates/oxc_formatter_core/Cargo.toml @@ -25,14 +25,5 @@ cow-utils = { workspace = true } rustc-hash = { workspace = true } unicode-width = { workspace = true } -serde_json = { workspace = true, optional = true } - -[features] -# Enables `test_support` (used from build.rs and tests/) in downstream formatter -# crates. Consumer crates still need a direct `insta` dev-dep — the snapshot macro -# is invoked from each consumer's `tests/fixtures/mod.rs` so the recorded `source:` -# header points to the consumer, not the shared harness. -test_harness = ["dep:serde_json"] - [lib] doctest = false diff --git a/crates/oxc_formatter_core/src/lib.rs b/crates/oxc_formatter_core/src/lib.rs index 71a28d1a40..f08fa4b8c0 100644 --- a/crates/oxc_formatter_core/src/lib.rs +++ b/crates/oxc_formatter_core/src/lib.rs @@ -35,9 +35,6 @@ pub mod spec; mod state; mod traits; -#[cfg(feature = "test_harness")] -pub mod test_support; - pub use arguments::{Argument, Arguments}; pub use buffer::{ AccumulatorBuffer, Buffer, BufferExtensions, HeapVecBuffer, Inspect, PreambleBuffer, Recorded, diff --git a/crates/oxc_formatter_css/Cargo.toml b/crates/oxc_formatter_css/Cargo.toml index bf73abead4..6a868e67eb 100644 --- a/crates/oxc_formatter_css/Cargo.toml +++ b/crates/oxc_formatter_css/Cargo.toml @@ -27,11 +27,11 @@ oxc-css-parser = { workspace = true } [dev-dependencies] insta = { workspace = true } -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } pico-args = { workspace = true } [build-dependencies] -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } [lib] doctest = false diff --git a/crates/oxc_formatter_css/build.rs b/crates/oxc_formatter_css/build.rs index b8c14f5a5e..cfc0b37329 100644 --- a/crates/oxc_formatter_css/build.rs +++ b/crates/oxc_formatter_css/build.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_formatter_core::test_support::{GenerateConfig, generate_tests}; +use oxc_formatter_tests::{GenerateConfig, generate_tests}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); diff --git a/crates/oxc_formatter_css/tests/fixtures/mod.rs b/crates/oxc_formatter_css/tests/fixtures/mod.rs index b930f97626..e177c97645 100644 --- a/crates/oxc_formatter_css/tests/fixtures/mod.rs +++ b/crates/oxc_formatter_css/tests/fixtures/mod.rs @@ -6,10 +6,10 @@ use std::path::Path; use oxc_allocator::{Allocator, ArenaVec}; -use oxc_formatter_core::test_support::{ +use oxc_formatter_css::{CssFormatOptions, CssVariant, format}; +use oxc_formatter_tests::{ FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, }; -use oxc_formatter_css::{CssFormatOptions, CssVariant, format}; struct CssHarness; diff --git a/crates/oxc_formatter_graphql/Cargo.toml b/crates/oxc_formatter_graphql/Cargo.toml index c52073f873..9f0c034deb 100644 --- a/crates/oxc_formatter_graphql/Cargo.toml +++ b/crates/oxc_formatter_graphql/Cargo.toml @@ -26,12 +26,12 @@ oxc_span = { workspace = true } [dev-dependencies] insta = { workspace = true } -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } oxc_tasks_common = { workspace = true } pico-args = { workspace = true } [build-dependencies] -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } [lib] doctest = false diff --git a/crates/oxc_formatter_graphql/build.rs b/crates/oxc_formatter_graphql/build.rs index 098c0ba764..eee2baa8bf 100644 --- a/crates/oxc_formatter_graphql/build.rs +++ b/crates/oxc_formatter_graphql/build.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_formatter_core::test_support::{GenerateConfig, generate_tests}; +use oxc_formatter_tests::{GenerateConfig, generate_tests}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); diff --git a/crates/oxc_formatter_graphql/tests/fixtures/mod.rs b/crates/oxc_formatter_graphql/tests/fixtures/mod.rs index e71ef3eb1d..85aa7d4675 100644 --- a/crates/oxc_formatter_graphql/tests/fixtures/mod.rs +++ b/crates/oxc_formatter_graphql/tests/fixtures/mod.rs @@ -1,10 +1,10 @@ use std::path::Path; use oxc_allocator::Allocator; -use oxc_formatter_core::test_support::{ +use oxc_formatter_graphql::{GraphqlFormatOptions, format}; +use oxc_formatter_tests::{ FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, }; -use oxc_formatter_graphql::{GraphqlFormatOptions, format}; struct GraphqlHarness; diff --git a/crates/oxc_formatter_json/Cargo.toml b/crates/oxc_formatter_json/Cargo.toml index 6d6f882da2..590fcf36b8 100644 --- a/crates/oxc_formatter_json/Cargo.toml +++ b/crates/oxc_formatter_json/Cargo.toml @@ -28,12 +28,12 @@ oxc_syntax = { workspace = true } [dev-dependencies] insta = { workspace = true } -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } oxc_tasks_common = { workspace = true } pico-args = { workspace = true } [build-dependencies] -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } [lib] doctest = false diff --git a/crates/oxc_formatter_json/build.rs b/crates/oxc_formatter_json/build.rs index 5e8e9a0cc2..cd9cb7cc02 100644 --- a/crates/oxc_formatter_json/build.rs +++ b/crates/oxc_formatter_json/build.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_formatter_core::test_support::{GenerateConfig, generate_tests}; +use oxc_formatter_tests::{GenerateConfig, generate_tests}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); diff --git a/crates/oxc_formatter_json/tests/fixtures/mod.rs b/crates/oxc_formatter_json/tests/fixtures/mod.rs index 0714928872..d2f697f125 100644 --- a/crates/oxc_formatter_json/tests/fixtures/mod.rs +++ b/crates/oxc_formatter_json/tests/fixtures/mod.rs @@ -1,12 +1,12 @@ use std::path::Path; use oxc_allocator::Allocator; -use oxc_formatter_core::test_support::{ - FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, -}; use oxc_formatter_json::{ BracketSpacing, Expand, JsonFormatOptions, JsonVariant, QuoteProps, TrailingCommas, format, }; +use oxc_formatter_tests::{ + FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, +}; struct JsonHarness; diff --git a/crates/oxc_formatter_tests/AGENTS.md b/crates/oxc_formatter_tests/AGENTS.md new file mode 100644 index 0000000000..b8b7ee90fe --- /dev/null +++ b/crates/oxc_formatter_tests/AGENTS.md @@ -0,0 +1,24 @@ +# Coding agent guides for `crates/oxc_formatter_tests` + +Test infrastructure shared by the formatter crates (e.g. `oxc_formatter`, `oxc_formatter_json`, `oxc_formatter_css`, etc). + +- `codegen`: build-script helper — consumers call `generate_tests` from `build.rs` via `[build-dependencies]` to emit one `#[test]` per fixture file +- `harness`: fixture runtime — consumers implement `FixtureFormatter` in `tests/fixtures/mod.rs` via `[dev-dependencies]` + +## Dependency rule + +This crate depends only on `oxc_formatter_core` — NEVER on language crates. +Consumers use it from both `[build-dependencies]` and `[dev-dependencies]`; a dependency on a language crate would make that a build-dep cycle, which cargo rejects. + +## insta stays consumer-side + +`build_fixture_snapshot` returns the snapshot body without calling `insta::assert_snapshot!`. +Each consumer invokes the macro from its own `tests/fixtures/mod.rs` so the recorded `source:` header points to the consumer crate (required by `INSTA_REQUIRE_FULL_MATCH=1` in CI). Consumers therefore need their own `insta` dev-dep. + +## Verification + +```sh +cargo c -p oxc_formatter_tests +``` + +Exercised through the fixture tests of its consumers (`cargo test -p `). diff --git a/crates/oxc_formatter_tests/CLAUDE.md b/crates/oxc_formatter_tests/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/crates/oxc_formatter_tests/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/crates/oxc_formatter_tests/Cargo.toml b/crates/oxc_formatter_tests/Cargo.toml new file mode 100644 index 0000000000..e9cc673fc6 --- /dev/null +++ b/crates/oxc_formatter_tests/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "oxc_formatter_tests" +version = "0.62.0" +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +include = ["/src"] +keywords.workspace = true +license.workspace = true +publish = false +repository.workspace = true +rust-version.workspace = true +description.workspace = true + +[lints] +workspace = true + +[dependencies] +oxc_formatter_core = { workspace = true } + +serde_json = { workspace = true } + +[lib] +doctest = false +test = false diff --git a/crates/oxc_formatter_core/src/test_support/codegen.rs b/crates/oxc_formatter_tests/src/codegen.rs similarity index 100% rename from crates/oxc_formatter_core/src/test_support/codegen.rs rename to crates/oxc_formatter_tests/src/codegen.rs diff --git a/crates/oxc_formatter_core/src/test_support/harness.rs b/crates/oxc_formatter_tests/src/harness.rs similarity index 98% rename from crates/oxc_formatter_core/src/test_support/harness.rs rename to crates/oxc_formatter_tests/src/harness.rs index 7c0a0d47be..ecfbb77e3b 100644 --- a/crates/oxc_formatter_core/src/test_support/harness.rs +++ b/crates/oxc_formatter_tests/src/harness.rs @@ -17,7 +17,9 @@ use std::{ path::{Path, PathBuf}, }; -use crate::{CoreFormatOptions, FormatOptions, IndentStyle, IndentWidth, LineEnding, LineWidth}; +use oxc_formatter_core::{ + CoreFormatOptions, FormatOptions, IndentStyle, IndentWidth, LineEnding, LineWidth, +}; /// A single `options.json` entry: a JSON object of per-test format options. pub type OptionSet = serde_json::Map; diff --git a/crates/oxc_formatter_core/src/test_support/mod.rs b/crates/oxc_formatter_tests/src/lib.rs similarity index 56% rename from crates/oxc_formatter_core/src/test_support/mod.rs rename to crates/oxc_formatter_tests/src/lib.rs index 6c2e1421cd..4918b39c32 100644 --- a/crates/oxc_formatter_core/src/test_support/mod.rs +++ b/crates/oxc_formatter_tests/src/lib.rs @@ -1,9 +1,11 @@ -//! Fixture-test infrastructure shared across formatter crates. -//! -//! Gated behind the `test_harness` Cargo feature. Split into: +//! Test infrastructure shared across formatter crates. //! //! - `harness`: runtime that walks fixtures, drives format passes, and assembles snapshots. //! - `codegen`: build-script helper that emits `#[test]` functions for each fixture. +//! +//! Consumer crates use `codegen` from `build.rs` (via `[build-dependencies]`) and `harness` +//! from `tests/fixtures/mod.rs` (via `[dev-dependencies]`). This crate depends only on +//! `oxc_formatter_core`, never on language crates, so both directions stay cycle-free. mod codegen; mod harness; diff --git a/crates/oxc_formatter_yaml/Cargo.toml b/crates/oxc_formatter_yaml/Cargo.toml index 60d91466f4..67edf2f3f3 100644 --- a/crates/oxc_formatter_yaml/Cargo.toml +++ b/crates/oxc_formatter_yaml/Cargo.toml @@ -26,12 +26,12 @@ oxc_span = { workspace = true } [dev-dependencies] insta = { workspace = true } -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } oxc_tasks_common = { workspace = true } pico-args = { workspace = true } [build-dependencies] -oxc_formatter_core = { workspace = true, features = ["test_harness"] } +oxc_formatter_tests = { workspace = true } [lib] doctest = false diff --git a/crates/oxc_formatter_yaml/build.rs b/crates/oxc_formatter_yaml/build.rs index 4a3e27bd7d..d63f2595de 100644 --- a/crates/oxc_formatter_yaml/build.rs +++ b/crates/oxc_formatter_yaml/build.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_formatter_core::test_support::{GenerateConfig, generate_tests}; +use oxc_formatter_tests::{GenerateConfig, generate_tests}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); diff --git a/crates/oxc_formatter_yaml/tests/fixtures/mod.rs b/crates/oxc_formatter_yaml/tests/fixtures/mod.rs index 7583d9958c..1f4998962e 100644 --- a/crates/oxc_formatter_yaml/tests/fixtures/mod.rs +++ b/crates/oxc_formatter_yaml/tests/fixtures/mod.rs @@ -1,9 +1,9 @@ use std::path::Path; use oxc_allocator::Allocator; -use oxc_formatter_core::{ - LineEnding, - test_support::{FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot}, +use oxc_formatter_core::LineEnding; +use oxc_formatter_tests::{ + FixtureFormatter, OptionSet, apply_core_options, build_fixture_snapshot, }; use oxc_formatter_yaml::{ProseWrap, YamlFormatOptions, format};