Merge #4331
4331: Fixture improvements r=TimoFreiberg a=TimoFreiberg As mentioned in [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/resolve_path.20between.20fixture.20files) :) I think always allowing unindented first lines is friendlier than making the user fix it and I don't see any drawbacks. Co-authored-by: Timo Freiberg <timo.freiberg@gmail.com>
This commit is contained in:
commit
b832dfc917
@ -1,4 +1,48 @@
|
||||
//! FIXME: write short doc here
|
||||
//! Fixtures are strings containing rust source code with optional metadata.
|
||||
//! A fixture without metadata is parsed into a single source file.
|
||||
//! Use this to test functionality local to one file.
|
||||
//!
|
||||
//! Example:
|
||||
//! ```
|
||||
//! r#"
|
||||
//! fn main() {
|
||||
//! println!("Hello World")
|
||||
//! }
|
||||
//! "#
|
||||
//! ```
|
||||
//!
|
||||
//! Metadata can be added to a fixture after a `//-` comment.
|
||||
//! The basic form is specifying filenames,
|
||||
//! which is also how to define multiple files in a single test fixture
|
||||
//!
|
||||
//! Example:
|
||||
//! ```
|
||||
//! "
|
||||
//! //- /main.rs
|
||||
//! mod foo;
|
||||
//! fn main() {
|
||||
//! foo::bar();
|
||||
//! }
|
||||
//!
|
||||
//! //- /foo.rs
|
||||
//! pub fn bar() {}
|
||||
//! "
|
||||
//! ```
|
||||
//!
|
||||
//! Metadata allows specifying all settings and variables
|
||||
//! that are available in a real rust project:
|
||||
//! - crate names via `crate:cratename`
|
||||
//! - dependencies via `deps:dep1,dep2`
|
||||
//! - configuration settings via `cfg:dbg=false,opt_level=2`
|
||||
//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
|
||||
//!
|
||||
//! Example:
|
||||
//! ```
|
||||
//! "
|
||||
//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
|
||||
//! fn insert_source_code_here() {}
|
||||
//! "
|
||||
//! ```
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
@ -155,7 +155,7 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
|
||||
res
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct FixtureEntry {
|
||||
pub meta: String,
|
||||
pub text: String,
|
||||
@ -170,19 +170,26 @@ pub struct FixtureEntry {
|
||||
/// // - other meta
|
||||
/// ```
|
||||
pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
|
||||
let margin = fixture
|
||||
.lines()
|
||||
.filter(|it| it.trim_start().starts_with("//-"))
|
||||
.map(|it| it.len() - it.trim_start().len())
|
||||
.next()
|
||||
.expect("empty fixture");
|
||||
let fixture = indent_first_line(fixture);
|
||||
let margin = fixture_margin(&fixture);
|
||||
|
||||
let mut lines = fixture
|
||||
.split('\n') // don't use `.lines` to not drop `\r\n`
|
||||
.filter_map(|line| {
|
||||
.enumerate()
|
||||
.filter_map(|(ix, line)| {
|
||||
if line.len() >= margin {
|
||||
assert!(line[..margin].trim().is_empty());
|
||||
Some(&line[margin..])
|
||||
let line_content = &line[margin..];
|
||||
if !line_content.starts_with("//-") {
|
||||
assert!(
|
||||
!line_content.contains("//-"),
|
||||
r#"Metadata line {} has invalid indentation. All metadata lines need to have the same indentation.
|
||||
The offending line: {:?}"#,
|
||||
ix,
|
||||
line
|
||||
);
|
||||
}
|
||||
Some(line_content)
|
||||
} else {
|
||||
assert!(line.trim().is_empty());
|
||||
None
|
||||
@ -202,6 +209,85 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
|
||||
res
|
||||
}
|
||||
|
||||
/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
|
||||
/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
|
||||
/// the other lines visually:
|
||||
/// ```
|
||||
/// let fixture = "//- /lib.rs
|
||||
/// mod foo;
|
||||
/// //- /foo.rs
|
||||
/// fn bar() {}
|
||||
/// ";
|
||||
/// assert_eq!(fixture_margin(fixture),
|
||||
/// " //- /lib.rs
|
||||
/// mod foo;
|
||||
/// //- /foo.rs
|
||||
/// fn bar() {}
|
||||
/// ")
|
||||
/// ```
|
||||
fn indent_first_line(fixture: &str) -> String {
|
||||
if fixture.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let mut lines = fixture.lines();
|
||||
let first_line = lines.next().unwrap();
|
||||
if first_line.contains("//-") {
|
||||
let rest = lines.collect::<Vec<_>>().join("\n");
|
||||
let fixed_margin = fixture_margin(&rest);
|
||||
let fixed_indent = fixed_margin - indent_len(first_line);
|
||||
format!("\n{}{}\n{}", " ".repeat(fixed_indent), first_line, rest)
|
||||
} else {
|
||||
fixture.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn fixture_margin(fixture: &str) -> usize {
|
||||
fixture
|
||||
.lines()
|
||||
.filter(|it| it.trim_start().starts_with("//-"))
|
||||
.map(indent_len)
|
||||
.next()
|
||||
.expect("empty fixture")
|
||||
}
|
||||
|
||||
fn indent_len(s: &str) -> usize {
|
||||
s.len() - s.trim_start().len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn parse_fixture_checks_further_indented_metadata() {
|
||||
parse_fixture(
|
||||
r"
|
||||
//- /lib.rs
|
||||
mod bar;
|
||||
|
||||
fn foo() {}
|
||||
//- /bar.rs
|
||||
pub fn baz() {}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_fixture_can_handle_unindented_first_line() {
|
||||
let fixture = "//- /lib.rs
|
||||
mod foo;
|
||||
//- /foo.rs
|
||||
struct Bar;
|
||||
";
|
||||
assert_eq!(
|
||||
parse_fixture(fixture),
|
||||
parse_fixture(
|
||||
"//- /lib.rs
|
||||
mod foo;
|
||||
//- /foo.rs
|
||||
struct Bar;
|
||||
"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Same as `parse_fixture`, except it allow empty fixture
|
||||
pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> {
|
||||
if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
|
||||
|
@ -136,7 +136,6 @@ fn finish(self) {
|
||||
}
|
||||
|
||||
let whitelist = [
|
||||
"ra_db",
|
||||
"ra_hir",
|
||||
"ra_hir_expand",
|
||||
"ra_ide",
|
||||
|
Loading…
Reference in New Issue
Block a user