diff options
Diffstat (limited to '')
-rw-r--r-- | src/tools/rust-analyzer/crates/parser/src/tests/sourcegen_inline_tests.rs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/sourcegen_inline_tests.rs b/src/tools/rust-analyzer/crates/parser/src/tests/sourcegen_inline_tests.rs new file mode 100644 index 000000000..7b2b703de --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/tests/sourcegen_inline_tests.rs @@ -0,0 +1,123 @@ +//! This module greps parser's code for specially formatted comments and turns +//! them into tests. + +use std::{ + collections::HashMap, + fs, iter, + path::{Path, PathBuf}, +}; + +#[test] +fn sourcegen_parser_tests() { + let grammar_dir = sourcegen::project_root().join(Path::new("crates/parser/src/grammar")); + let tests = tests_from_dir(&grammar_dir); + + install_tests(&tests.ok, "crates/parser/test_data/parser/inline/ok"); + install_tests(&tests.err, "crates/parser/test_data/parser/inline/err"); + + fn install_tests(tests: &HashMap<String, Test>, into: &str) { + let tests_dir = sourcegen::project_root().join(into); + if !tests_dir.is_dir() { + fs::create_dir_all(&tests_dir).unwrap(); + } + // ok is never 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 mut new_idx = existing.len() + 1; + 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) + } + }; + sourcegen::ensure_file_contents(&path, &test.text); + } + } +} + +#[derive(Debug)] +struct Test { + name: String, + text: String, + ok: bool, +} + +#[derive(Default, Debug)] +struct Tests { + ok: HashMap<String, Test>, + err: HashMap<String, Test>, +} + +fn collect_tests(s: &str) -> Vec<Test> { + let mut res = Vec::new(); + for comment_block in sourcegen::CommentBlock::extract_untagged(s) { + let first_line = &comment_block.contents[0]; + let (name, ok) = if let Some(name) = first_line.strip_prefix("test ") { + (name.to_string(), true) + } else if let Some(name) = first_line.strip_prefix("test_err ") { + (name.to_string(), false) + } else { + continue; + }; + let text: String = comment_block.contents[1..] + .iter() + .cloned() + .chain(iter::once(String::new())) + .collect::<Vec<_>>() + .join("\n"); + assert!(!text.trim().is_empty() && text.ends_with('\n')); + res.push(Test { name, text, ok }) + } + res +} + +fn tests_from_dir(dir: &Path) -> Tests { + let mut res = Tests::default(); + for entry in sourcegen::list_rust_files(dir) { + process_file(&mut res, entry.as_path()); + } + let grammar_rs = dir.parent().unwrap().join("grammar.rs"); + process_file(&mut res, &grammar_rs); + return res; + + fn process_file(res: &mut Tests, path: &Path) { + let text = fs::read_to_string(path).unwrap(); + + for test in collect_tests(&text) { + if test.ok { + if let Some(old_test) = res.ok.insert(test.name.clone(), test) { + panic!("Duplicate test: {}", old_test.name); + } + } else if let Some(old_test) = res.err.insert(test.name.clone(), test) { + panic!("Duplicate test: {}", old_test.name); + } + } + } +} + +fn existing_tests(dir: &Path, ok: bool) -> HashMap<String, (PathBuf, Test)> { + let mut res = HashMap::default(); + for file in fs::read_dir(dir).unwrap() { + let file = file.unwrap(); + let path = file.path(); + if path.extension().unwrap_or_default() != "rs" { + continue; + } + let name = { + let file_name = path.file_name().unwrap().to_str().unwrap(); + file_name[5..file_name.len() - 3].to_string() + }; + let text = fs::read_to_string(&path).unwrap(); + let test = Test { name: name.clone(), text, ok }; + if let Some(old) = res.insert(name, (path, test)) { + println!("Duplicate test: {:?}", old); + } + } + res +} |