aboutsummaryrefslogtreecommitdiff
path: root/crates/test_utils
diff options
context:
space:
mode:
Diffstat (limited to 'crates/test_utils')
-rw-r--r--crates/test_utils/Cargo.toml4
-rw-r--r--crates/test_utils/src/lib.rs160
2 files changed, 158 insertions, 6 deletions
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 8ec986bcb..4d185b01c 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -11,3 +11,7 @@ doctest = false
11difference = "2.0.0" 11difference = "2.0.0"
12text-size = "1.0.0" 12text-size = "1.0.0"
13serde_json = "1.0.48" 13serde_json = "1.0.48"
14relative-path = "1.0.0"
15rustc-hash = "1.1.0"
16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index be2cfbaa2..1bd97215c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -14,6 +14,10 @@ use std::{
14 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
15}; 15};
16 16
17pub use ra_cfg::CfgOptions;
18
19pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap;
17use serde_json::Value; 21use serde_json::Value;
18use text_size::{TextRange, TextSize}; 22use text_size::{TextRange, TextSize};
19 23
@@ -157,10 +161,82 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
157 161
158#[derive(Debug, Eq, PartialEq)] 162#[derive(Debug, Eq, PartialEq)]
159pub struct FixtureEntry { 163pub struct FixtureEntry {
160 pub meta: String, 164 pub meta: FixtureMeta,
161 pub text: String, 165 pub text: String,
162} 166}
163 167
168#[derive(Debug, Eq, PartialEq)]
169pub enum FixtureMeta {
170 Root { path: RelativePathBuf },
171 File(FileMeta),
172}
173
174#[derive(Debug, Eq, PartialEq)]
175pub struct FileMeta {
176 pub path: RelativePathBuf,
177 pub crate_name: Option<String>,
178 pub deps: Vec<String>,
179 pub cfg: CfgOptions,
180 pub edition: Option<String>,
181 pub env: FxHashMap<String, String>,
182}
183
184impl FixtureMeta {
185 pub fn path(&self) -> &RelativePath {
186 match self {
187 FixtureMeta::Root { path } => &path,
188 FixtureMeta::File(f) => &f.path,
189 }
190 }
191
192 pub fn crate_name(&self) -> Option<&String> {
193 match self {
194 FixtureMeta::File(f) => f.crate_name.as_ref(),
195 _ => None,
196 }
197 }
198
199 pub fn cfg_options(&self) -> Option<&CfgOptions> {
200 match self {
201 FixtureMeta::File(f) => Some(&f.cfg),
202 _ => None,
203 }
204 }
205
206 pub fn edition(&self) -> Option<&String> {
207 match self {
208 FixtureMeta::File(f) => f.edition.as_ref(),
209 _ => None,
210 }
211 }
212
213 pub fn env(&self) -> impl Iterator<Item = (&String, &String)> {
214 struct EnvIter<'a> {
215 iter: Option<std::collections::hash_map::Iter<'a, String, String>>,
216 }
217
218 impl<'a> EnvIter<'a> {
219 fn new(meta: &'a FixtureMeta) -> Self {
220 Self {
221 iter: match meta {
222 FixtureMeta::File(f) => Some(f.env.iter()),
223 _ => None,
224 },
225 }
226 }
227 }
228
229 impl<'a> Iterator for EnvIter<'a> {
230 type Item = (&'a String, &'a String);
231 fn next(&mut self) -> Option<Self::Item> {
232 self.iter.as_mut().and_then(|i| i.next())
233 }
234 }
235
236 EnvIter::new(self)
237 }
238}
239
164/// Parses text which looks like this: 240/// Parses text which looks like this:
165/// 241///
166/// ```not_rust 242/// ```not_rust
@@ -169,8 +245,8 @@ pub struct FixtureEntry {
169/// line 2 245/// line 2
170/// // - other meta 246/// // - other meta
171/// ``` 247/// ```
172pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 248pub fn parse_fixture(ra_fixture: &str) -> Vec<FixtureEntry> {
173 let fixture = indent_first_line(fixture); 249 let fixture = indent_first_line(ra_fixture);
174 let margin = fixture_margin(&fixture); 250 let margin = fixture_margin(&fixture);
175 251
176 let mut lines = fixture 252 let mut lines = fixture
@@ -200,6 +276,7 @@ The offending line: {:?}"#,
200 for line in lines.by_ref() { 276 for line in lines.by_ref() {
201 if line.starts_with("//-") { 277 if line.starts_with("//-") {
202 let meta = line["//-".len()..].trim().to_string(); 278 let meta = line["//-".len()..].trim().to_string();
279 let meta = parse_meta(&meta);
203 res.push(FixtureEntry { meta, text: String::new() }) 280 res.push(FixtureEntry { meta, text: String::new() })
204 } else if let Some(entry) = res.last_mut() { 281 } else if let Some(entry) = res.last_mut() {
205 entry.text.push_str(line); 282 entry.text.push_str(line);
@@ -209,6 +286,57 @@ The offending line: {:?}"#,
209 res 286 res
210} 287}
211 288
289//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
290fn parse_meta(meta: &str) -> FixtureMeta {
291 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
292
293 if components[0] == "root" {
294 let path: RelativePathBuf = components[1].into();
295 assert!(path.starts_with("/") && path.ends_with("/"));
296 return FixtureMeta::Root { path };
297 }
298
299 let path: RelativePathBuf = components[0].into();
300 assert!(path.starts_with("/"));
301
302 let mut krate = None;
303 let mut deps = Vec::new();
304 let mut edition = None;
305 let mut cfg = CfgOptions::default();
306 let mut env = FxHashMap::default();
307 for component in components[1..].iter() {
308 let (key, value) = split1(component, ':').unwrap();
309 match key {
310 "crate" => krate = Some(value.to_string()),
311 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
312 "edition" => edition = Some(value.to_string()),
313 "cfg" => {
314 for key in value.split(',') {
315 match split1(key, '=') {
316 None => cfg.insert_atom(key.into()),
317 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
318 }
319 }
320 }
321 "env" => {
322 for key in value.split(',') {
323 if let Some((k, v)) = split1(key, '=') {
324 env.insert(k.into(), v.into());
325 }
326 }
327 }
328 _ => panic!("bad component: {:?}", component),
329 }
330 }
331
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333}
334
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
212/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 340/// 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 341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
214/// the other lines visually: 342/// the other lines visually:
@@ -288,13 +416,33 @@ struct Bar;
288 ) 416 )
289} 417}
290 418
419#[test]
420fn parse_fixture_gets_full_meta() {
421 let parsed = parse_fixture(
422 r"
423 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
424 mod m;
425 ",
426 );
427 assert_eq!(1, parsed.len());
428
429 let parsed = &parsed[0];
430 assert_eq!("mod m;\n\n", parsed.text);
431
432 let meta = &parsed.meta;
433 assert_eq!("foo", meta.crate_name().unwrap());
434 assert_eq!("/lib.rs", meta.path());
435 assert!(meta.cfg_options().is_some());
436 assert_eq!(2, meta.env().count());
437}
438
291/// Same as `parse_fixture`, except it allow empty fixture 439/// Same as `parse_fixture`, except it allow empty fixture
292pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { 440pub fn parse_single_fixture(ra_fixture: &str) -> Option<FixtureEntry> {
293 if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { 441 if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
294 return None; 442 return None;
295 } 443 }
296 444
297 let fixtures = parse_fixture(fixture); 445 let fixtures = parse_fixture(ra_fixture);
298 if fixtures.len() > 1 { 446 if fixtures.len() > 1 {
299 panic!("too many fixtures"); 447 panic!("too many fixtures");
300 } 448 }