Skip to content

Commit 86fa80e

Browse files
committed
Allow fixture strings with unindented first line
This allows fixtures like "//- /lib.rs ... //- /foo.rs ... "
1 parent 8a5d144 commit 86fa80e

File tree

1 file changed

+95
-9
lines changed

1 file changed

+95
-9
lines changed

crates/test_utils/src/lib.rs

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
155155
res
156156
}
157157

158-
#[derive(Debug)]
158+
#[derive(Debug, Eq, PartialEq)]
159159
pub struct FixtureEntry {
160160
pub meta: String,
161161
pub text: String,
@@ -170,19 +170,26 @@ pub struct FixtureEntry {
170170
/// // - other meta
171171
/// ```
172172
pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
173-
let margin = fixture
174-
.lines()
175-
.filter(|it| it.trim_start().starts_with("//-"))
176-
.map(|it| it.len() - it.trim_start().len())
177-
.next()
178-
.expect("empty fixture");
173+
let fixture = indent_first_line(fixture);
174+
let margin = fixture_margin(&fixture);
179175

180176
let mut lines = fixture
181177
.split('\n') // don't use `.lines` to not drop `\r\n`
182-
.filter_map(|line| {
178+
.enumerate()
179+
.filter_map(|(ix, line)| {
183180
if line.len() >= margin {
184181
assert!(line[..margin].trim().is_empty());
185-
Some(&line[margin..])
182+
let line_content = &line[margin..];
183+
if !line_content.starts_with("//-") {
184+
assert!(
185+
!line_content.contains("//-"),
186+
r#"Metadata line {} has invalid indentation. All metadata lines need to have the same indentation.
187+
The offending line: {:?}"#,
188+
ix,
189+
line
190+
);
191+
}
192+
Some(line_content)
186193
} else {
187194
assert!(line.trim().is_empty());
188195
None
@@ -202,6 +209,85 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
202209
res
203210
}
204211

212+
/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
213+
/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
214+
/// the other lines visually:
215+
/// ```
216+
/// let fixture = "//- /lib.rs
217+
/// mod foo;
218+
/// //- /foo.rs
219+
/// fn bar() {}
220+
/// ";
221+
/// assert_eq!(fixture_margin(fixture),
222+
/// " //- /lib.rs
223+
/// mod foo;
224+
/// //- /foo.rs
225+
/// fn bar() {}
226+
/// ")
227+
/// ```
228+
fn indent_first_line(fixture: &str) -> String {
229+
if fixture.is_empty() {
230+
return String::new();
231+
}
232+
let mut lines = fixture.lines();
233+
let first_line = lines.next().unwrap();
234+
if first_line.contains("//-") {
235+
let rest = lines.collect::<Vec<_>>().join("\n");
236+
let fixed_margin = fixture_margin(&rest);
237+
let fixed_indent = fixed_margin - indent_len(first_line);
238+
format!("\n{}{}\n{}", " ".repeat(fixed_indent), first_line, rest)
239+
} else {
240+
fixture.to_owned()
241+
}
242+
}
243+
244+
fn fixture_margin(fixture: &str) -> usize {
245+
fixture
246+
.lines()
247+
.filter(|it| it.trim_start().starts_with("//-"))
248+
.map(indent_len)
249+
.next()
250+
.expect("empty fixture")
251+
}
252+
253+
fn indent_len(s: &str) -> usize {
254+
s.len() - s.trim_start().len()
255+
}
256+
257+
#[test]
258+
#[should_panic]
259+
fn parse_fixture_checks_further_indented_metadata() {
260+
parse_fixture(
261+
r"
262+
//- /lib.rs
263+
mod bar;
264+
265+
fn foo() {}
266+
//- /bar.rs
267+
pub fn baz() {}
268+
",
269+
);
270+
}
271+
272+
#[test]
273+
fn parse_fixture_can_handle_unindented_first_line() {
274+
let fixture = "//- /lib.rs
275+
mod foo;
276+
//- /foo.rs
277+
struct Bar;
278+
";
279+
assert_eq!(
280+
parse_fixture(fixture),
281+
parse_fixture(
282+
"//- /lib.rs
283+
mod foo;
284+
//- /foo.rs
285+
struct Bar;
286+
"
287+
)
288+
)
289+
}
290+
205291
/// Same as `parse_fixture`, except it allow empty fixture
206292
pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> {
207293
if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) {

0 commit comments

Comments
 (0)