From dfba29e4fb66457d101db295e3c356a932ac005e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Oct 2018 22:34:31 +0300 Subject: Add MockAnalysis to make testing easier --- crates/ra_analysis/src/mock_analysis.rs | 84 +++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) (limited to 'crates/ra_analysis/src/mock_analysis.rs') diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 1c1dbee7c..f72911192 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs @@ -2,11 +2,19 @@ use std::sync::Arc; use relative_path::{RelativePath, RelativePathBuf}; +use ra_syntax::TextUnit; +use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; use crate::{ AnalysisChange, Analysis, AnalysisHost, FileId, FileResolver, }; +#[derive(Debug)] +pub struct FilePosition { + pub file_id: FileId, + pub offset: TextUnit, +} + /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. #[derive(Debug, Default)] @@ -18,11 +26,57 @@ impl MockAnalysis { pub fn new() -> MockAnalysis { MockAnalysis::default() } - pub fn with_files(files: &[(&str, &str)]) -> MockAnalysis { - let files = files.iter() - .map(|it| (it.0.to_string(), it.1.to_string())) - .collect(); - MockAnalysis { files } + /// Creates `MockAnalysis` using a fixture data in the following format: + /// + /// ```notrust + /// //- /main.rs + /// mod foo; + /// fn main() {} + /// + /// //- /foo.rs + /// struct Baz; + /// ``` + pub fn with_files(fixture: &str) -> MockAnalysis { + let mut res = MockAnalysis::new(); + for entry in parse_fixture(fixture) { + res.add_file(&entry.meta, &entry.text); + } + res + } + + /// Same as `with_files`, but requires that a single file contains a `<|>` marker, + /// whose position is also returned. + pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { + let mut position = None; + let mut res = MockAnalysis::new(); + for entry in parse_fixture(fixture) { + if entry.text.contains(CURSOR_MARKER) { + assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); + position = Some(res.add_file_with_position(&entry.meta, &entry.text)); + } else { + res.add_file(&entry.meta, &entry.text); + } + } + let position = position.expect("expected a marker (<|>)"); + (res, position) + } + + pub fn add_file(&mut self, path: &str, text: &str) -> FileId { + let file_id = FileId((self.files.len() + 1) as u32); + self.files.push((path.to_string(), text.to_string())); + file_id + } + pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { + let (offset, text) = extract_offset(text); + let file_id = FileId((self.files.len() + 1) as u32); + self.files.push((path.to_string(), text.to_string())); + FilePosition { file_id, offset } + } + pub fn id_of(&self, path: &str) -> FileId { + let (idx, _) = self.files.iter().enumerate() + .find(|(_, (p, _text))| path == p) + .expect("no file in this mock"); + FileId(idx as u32 + 1) } pub fn analysis_host(self) -> AnalysisHost { let mut host = AnalysisHost::new(); @@ -44,6 +98,26 @@ impl MockAnalysis { } } +/// Creates analysis from a multi-file fixture, returns positions marked with <|>. +pub fn analysis_and_position(fixture: &str) -> (Analysis, FilePosition) { + let (mock, position) = MockAnalysis::with_files_and_position(fixture); + (mock.analysis(), position) +} + +/// Creates analysis for a single file. +pub fn single_file(code: &str) -> (Analysis, FileId) { + let mut mock = MockAnalysis::new(); + let file_id = mock.add_file("/main.rs", code); + (mock.analysis(), file_id) +} + +/// Creates analysis for a single file, returns position marked with <|>. +pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { + let mut mock = MockAnalysis::new(); + let pos = mock.add_file_with_position("/main.rs", code); + (mock.analysis(), pos) +} + #[derive(Debug)] struct FileMap(Vec<(FileId, RelativePathBuf)>); -- cgit v1.2.3