Skip to content

Commit 8c58416

Browse files
committed
move multiple dependency block check and test to ty_test
1 parent 190b4af commit 8c58416

5 files changed

Lines changed: 72 additions & 75 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/mdtest/src/parser.rs

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,7 @@ use rustc_stable_hash::{FromStableHash, SipHasher128Hash, StableSipHasher128};
1818
use serde::Deserialize;
1919

2020
/// Trait for mdtest configuration types.
21-
///
22-
/// This allows the parser to be generic over different configuration types
23-
/// (e.g. ty's `MarkdownTestConfig` vs ruff's `Options`) while still being
24-
/// able to enforce constraints during parsing.
25-
pub trait MdtestConfig: Clone + Default + for<'de> Deserialize<'de> {
26-
/// Whether this configuration specifies external dependencies.
27-
///
28-
/// Used by the parser to enforce that at most one section per file
29-
/// specifies dependencies.
30-
fn has_dependencies(&self) -> bool;
31-
}
21+
pub trait MdtestConfig: Clone + Default + for<'de> Deserialize<'de> {}
3222

3323
/// Parse the Markdown `source` as a test suite with given `title`.
3424
pub fn parse<'s, C: MdtestConfig>(
@@ -517,10 +507,6 @@ struct Parser<'s, C> {
517507

518508
/// Whether or not the current section has a config block.
519509
current_section_has_config: bool,
520-
521-
/// Whether or not any section in the file has external dependencies.
522-
/// Only one section per file is allowed to have dependencies (for lockfile support).
523-
file_has_dependencies: bool,
524510
}
525511

526512
impl<'s, C: MdtestConfig> Parser<'s, C> {
@@ -543,7 +529,6 @@ impl<'s, C: MdtestConfig> Parser<'s, C> {
543529
stack: SectionStack::new(root_section_id),
544530
current_section_files: IndexMap::default(),
545531
current_section_has_config: false,
546-
file_has_dependencies: false,
547532
}
548533
}
549534

@@ -927,16 +912,6 @@ impl<'s, C: MdtestConfig> Parser<'s, C> {
927912

928913
let config: C = toml::from_str(code).context("Error while parsing Markdown TOML config")?;
929914

930-
if config.has_dependencies() {
931-
if self.file_has_dependencies {
932-
bail!(
933-
"Multiple sections with `[project]` dependencies in the same file are not allowed. \
934-
External dependencies must be specified in a single top-level configuration block."
935-
);
936-
}
937-
self.file_has_dependencies = true;
938-
}
939-
940915
let current_section = &mut self.sections[self.stack.top()];
941916
current_section.config = config;
942917

@@ -1105,14 +1080,18 @@ mod tests {
11051080
/// ```
11061081
#[derive(Clone, Debug, Default, Deserialize)]
11071082
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
1083+
#[expect(
1084+
unused,
1085+
reason = "These fields are only used for testing deserialization and never read"
1086+
)]
11081087
struct TestConfig {
11091088
project: Option<Project>,
1110-
#[expect(unused)]
11111089
environment: Option<Environment>,
11121090
}
11131091

11141092
#[derive(Clone, Debug, Default, Deserialize)]
11151093
struct Project {
1094+
#[expect(unused)]
11161095
dependencies: Option<Vec<String>>,
11171096
}
11181097

@@ -1122,13 +1101,7 @@ mod tests {
11221101
typeshed: Option<String>,
11231102
}
11241103

1125-
impl super::MdtestConfig for TestConfig {
1126-
fn has_dependencies(&self) -> bool {
1127-
self.project
1128-
.as_ref()
1129-
.is_some_and(|project| project.dependencies.is_some())
1130-
}
1131-
}
1104+
impl super::MdtestConfig for TestConfig {}
11321105

11331106
fn parse<'s>(
11341107
title: &'s str,
@@ -2257,39 +2230,4 @@ mod tests {
22572230
let parse_result = parse("file.md", &source);
22582231
assert!(parse_result.is_ok(), "{parse_result:?}");
22592232
}
2260-
2261-
#[test]
2262-
fn multiple_sections_with_dependencies_not_allowed() {
2263-
let source = dedent(
2264-
r#"
2265-
# First section
2266-
2267-
```toml
2268-
[project]
2269-
dependencies = ["pydantic==2.12.2"]
2270-
```
2271-
2272-
```py
2273-
x = 1
2274-
```
2275-
2276-
# Second section
2277-
2278-
```toml
2279-
[project]
2280-
dependencies = ["numpy==2.0.0"]
2281-
```
2282-
2283-
```py
2284-
y = 2
2285-
```
2286-
"#,
2287-
);
2288-
let err = parse("file.md", &source).expect_err("Should fail to parse");
2289-
assert_eq!(
2290-
err.to_string(),
2291-
"Multiple sections with `[project]` dependencies in the same file are not allowed. \
2292-
External dependencies must be specified in a single top-level configuration block."
2293-
);
2294-
}
22952233
}

crates/ty_test/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,8 @@ serde = { workspace = true, features = ["derive"] }
3636
tempfile = { workspace = true }
3737
tracing = { workspace = true }
3838

39+
[dev-dependencies]
40+
ruff_python_trivia = { workspace = true }
41+
3942
[lints]
4043
workspace = true

crates/ty_test/src/config.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,7 @@ impl MarkdownTestConfig {
6969
}
7070
}
7171

72-
impl mdtest::parser::MdtestConfig for MarkdownTestConfig {
73-
fn has_dependencies(&self) -> bool {
74-
self.dependencies().is_some()
75-
}
76-
}
72+
impl mdtest::parser::MdtestConfig for MarkdownTestConfig {}
7773

7874
#[derive(Deserialize, Debug, Default, Clone)]
7975
#[serde(rename_all = "kebab-case", deny_unknown_fields)]

crates/ty_test/src/lib.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::config::{Log, MarkdownTestConfig, SystemKind};
22
use crate::db::Db;
3-
use anyhow::anyhow;
3+
use anyhow::{anyhow, bail};
44
use camino::Utf8Path;
55
use colored::Colorize;
66
use mdtest::matcher::{self, Failure};
@@ -52,6 +52,21 @@ pub fn run(
5252
let suite = parser::parse::<MarkdownTestConfig>(short_title, source)
5353
.map_err(|err| anyhow!("Failed to parse fixture: {err}"))?;
5454

55+
// Whether or not any section in the file has external dependencies.
56+
// Only one section per file is allowed to have dependencies (for lockfile support).
57+
let mut file_has_dependencies = false;
58+
for test in suite.tests() {
59+
if test.configuration().dependencies().is_some() {
60+
if file_has_dependencies {
61+
bail!(
62+
"Multiple sections with `[project]` dependencies in the same file are not allowed. \
63+
External dependencies must be specified in a single top-level configuration block."
64+
);
65+
}
66+
file_has_dependencies = true;
67+
}
68+
}
69+
5570
let mut db = Db::setup();
5671
let mut markdown_edits = vec![];
5772

@@ -776,3 +791,47 @@ impl AttemptTestError<'_> {
776791
}
777792
}
778793
}
794+
795+
#[cfg(test)]
796+
mod tests {
797+
use mdtest::parser::parse;
798+
use ruff_python_trivia::textwrap::dedent;
799+
800+
use crate::config::MarkdownTestConfig;
801+
802+
#[test]
803+
fn multiple_sections_with_dependencies_not_allowed() {
804+
let source = dedent(
805+
r#"
806+
# First section
807+
808+
```toml
809+
[project]
810+
dependencies = ["pydantic==2.12.2"]
811+
```
812+
813+
```py
814+
x = 1
815+
```
816+
817+
# Second section
818+
819+
```toml
820+
[project]
821+
dependencies = ["numpy==2.0.0"]
822+
```
823+
824+
```py
825+
y = 2
826+
```
827+
"#,
828+
);
829+
let err =
830+
parse::<MarkdownTestConfig>("file.md", &source).expect_err("Should fail to parse");
831+
assert_eq!(
832+
err.to_string(),
833+
"Multiple sections with `[project]` dependencies in the same file are not allowed. \
834+
External dependencies must be specified in a single top-level configuration block."
835+
);
836+
}
837+
}

0 commit comments

Comments
 (0)