diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs index 539c4435b24..15f70c0903f 100644 --- a/crates/ra_syntax/tests/test.rs +++ b/crates/ra_syntax/tests/test.rs @@ -24,7 +24,7 @@ fn lexer_tests() { #[test] fn parser_tests() { - dir_tests(&["parser/inline", "parser/ok"], |text, path| { + dir_tests(&["parser/inline/ok", "parser/ok"], |text, path| { let file = SourceFileNode::parse(text); let errors = file.errors(); assert_eq!( @@ -35,7 +35,7 @@ fn parser_tests() { ); dump_tree(file.syntax()) }); - dir_tests(&["parser/err"], |text, path| { + dir_tests(&["parser/err", "parser/inline/err"], |text, path| { let file = SourceFileNode::parse(text); let errors = file.errors(); assert_ne!( diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 95d6e08f048..66fca5bef18 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -21,6 +21,7 @@ const TOOLCHAIN: &str = "1.31.0"; pub struct Test { pub name: String, pub text: String, + pub ok: bool, } pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { @@ -38,11 +39,16 @@ pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { } let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..])); + let mut ok = true; let (start_line, name) = loop { match block.next() { Some((idx, line)) if line.starts_with("test ") => { break (idx, line["test ".len()..].to_string()); } + Some((idx, line)) if line.starts_with("test_fail ") => { + ok = false; + break (idx, line["test_fail ".len()..].to_string()); + } Some(_) => (), None => continue 'outer, } @@ -52,7 +58,7 @@ pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { "\n", ); assert!(!text.trim().is_empty() && text.ends_with('\n')); - res.push((start_line, Test { name, text })) + res.push((start_line, Test { name, text, ok })) } res } diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs index 8e5e2036d6d..08b21f7afc8 100644 --- a/crates/tools/src/main.rs +++ b/crates/tools/src/main.rs @@ -7,10 +7,11 @@ use std::{ use clap::{App, Arg, SubCommand}; use failure::bail; -use tools::{collect_tests, generate, install_format_hook, run, run_rustfmt, Mode, Overwrite, Result, Test, Verify}; +use tools::{collect_tests, generate, install_format_hook, run, run_rustfmt, Mode, Overwrite, Result, Test, Verify, project_root}; -const GRAMMAR_DIR: &str = "./crates/ra_syntax/src/grammar"; -const INLINE_TESTS_DIR: &str = "./crates/ra_syntax/tests/data/parser/inline"; +const GRAMMAR_DIR: &str = "crates/ra_syntax/src/grammar"; +const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok"; +const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/err"; fn main() -> Result<()> { let matches = App::new("tasks") @@ -48,34 +49,43 @@ fn main() -> Result<()> { fn gen_tests(mode: Mode) -> Result<()> { let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?; + fn install_tests(tests: &HashMap, into: &str, mode: Mode) -> Result<()> { + let tests_dir = project_root().join(into); + if !tests_dir.is_dir() { + fs::create_dir_all(&tests_dir)?; + } + // ok is never is actually read, but it needs to be specified to create a Test in existing_tests + let existing = existing_tests(&tests_dir, true)?; + for t in existing.keys().filter(|&t| !tests.contains_key(t)) { + panic!("Test is deleted: {}", t); + } - let inline_tests_dir = Path::new(INLINE_TESTS_DIR); - if !inline_tests_dir.is_dir() { - fs::create_dir_all(inline_tests_dir)?; + let mut new_idx = existing.len() + 2; + for (name, test) in tests { + let path = match existing.get(name) { + Some((path, _test)) => path.clone(), + None => { + let file_name = format!("{:04}_{}.rs", new_idx, name); + new_idx += 1; + tests_dir.join(file_name) + } + }; + teraron::update(&path, &test.text, mode)?; + } + Ok(()) } - let existing = existing_tests(inline_tests_dir)?; - - for t in existing.keys().filter(|&t| !tests.contains_key(t)) { - panic!("Test is deleted: {}", t); - } - - let mut new_idx = existing.len() + 2; - for (name, test) in tests { - let path = match existing.get(&name) { - Some((path, _test)) => path.clone(), - None => { - let file_name = format!("{:04}_{}.rs", new_idx, name); - new_idx += 1; - inline_tests_dir.join(file_name) - } - }; - teraron::update(&path, &test.text, mode)?; - } - Ok(()) + install_tests(&tests.ok, OK_INLINE_TESTS_DIR, mode)?; + install_tests(&tests.err, ERR_INLINE_TESTS_DIR, mode) } -fn tests_from_dir(dir: &Path) -> Result> { - let mut res = HashMap::new(); +#[derive(Default, Debug)] +struct Tests { + pub ok: HashMap, + pub err: HashMap, +} + +fn tests_from_dir(dir: &Path) -> Result { + let mut res = Tests::default(); for entry in ::walkdir::WalkDir::new(dir) { let entry = entry.unwrap(); if !entry.file_type().is_file() { @@ -89,19 +99,25 @@ fn tests_from_dir(dir: &Path) -> Result> { let grammar_rs = dir.parent().unwrap().join("grammar.rs"); process_file(&mut res, &grammar_rs)?; return Ok(res); - fn process_file(res: &mut HashMap, path: &Path) -> Result<()> { + fn process_file(res: &mut Tests, path: &Path) -> Result<()> { let text = fs::read_to_string(path)?; for (_, test) in collect_tests(&text) { - if let Some(old_test) = res.insert(test.name.clone(), test) { - bail!("Duplicate test: {}", old_test.name) + if test.ok { + if let Some(old_test) = res.ok.insert(test.name.clone(), test) { + bail!("Duplicate test: {}", old_test.name) + } + } else { + if let Some(old_test) = res.err.insert(test.name.clone(), test) { + bail!("Duplicate test: {}", old_test.name) + } } } Ok(()) } } -fn existing_tests(dir: &Path) -> Result> { +fn existing_tests(dir: &Path, ok: bool) -> Result> { let mut res = HashMap::new(); for file in fs::read_dir(dir)? { let file = file?; @@ -117,6 +133,7 @@ fn existing_tests(dir: &Path) -> Result> { let test = Test { name: name.clone(), text, + ok, }; if let Some(old) = res.insert(name, (path, test)) { println!("Duplicate test: {:?}", old);