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