]> git.lizzy.rs Git - rust.git/blob - crates/test_utils/src/fixture.rs
Merge #7572
[rust.git] / crates / test_utils / src / fixture.rs
1 //! Defines `Fixture` -- a convenient way to describe the initial state of
2 //! rust-analyzer database from a single string.
3
4 use rustc_hash::FxHashMap;
5 use stdx::{lines_with_ends, split_once, trim_indent};
6
7 #[derive(Debug, Eq, PartialEq)]
8 pub struct Fixture {
9     pub path: String,
10     pub text: String,
11     pub krate: Option<String>,
12     pub deps: Vec<String>,
13     pub cfg_atoms: Vec<String>,
14     pub cfg_key_values: Vec<(String, String)>,
15     pub edition: Option<String>,
16     pub env: FxHashMap<String, String>,
17 }
18
19 impl Fixture {
20     /// Parses text which looks like this:
21     ///
22     ///  ```not_rust
23     ///  //- some meta
24     ///  line 1
25     ///  line 2
26     ///  // - other meta
27     ///  ```
28     pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
29         let fixture = trim_indent(ra_fixture);
30
31         let mut res: Vec<Fixture> = Vec::new();
32
33         let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") };
34
35         for (ix, line) in default.into_iter().chain(lines_with_ends(&fixture)).enumerate() {
36             if line.contains("//-") {
37                 assert!(
38                     line.starts_with("//-"),
39                     "Metadata line {} has invalid indentation. \
40                      All metadata lines need to have the same indentation.\n\
41                      The offending line: {:?}",
42                     ix,
43                     line
44                 );
45             }
46
47             if line.starts_with("//-") {
48                 let meta = Fixture::parse_meta_line(line);
49                 res.push(meta)
50             } else if let Some(entry) = res.last_mut() {
51                 entry.text.push_str(line);
52             }
53         }
54
55         res
56     }
57
58     //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
59     fn parse_meta_line(meta: &str) -> Fixture {
60         assert!(meta.starts_with("//-"));
61         let meta = meta["//-".len()..].trim();
62         let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
63
64         let path = components[0].to_string();
65         assert!(path.starts_with('/'));
66
67         let mut krate = None;
68         let mut deps = Vec::new();
69         let mut edition = None;
70         let mut cfg_atoms = Vec::new();
71         let mut cfg_key_values = Vec::new();
72         let mut env = FxHashMap::default();
73         for component in components[1..].iter() {
74             let (key, value) = split_once(component, ':').unwrap();
75             match key {
76                 "crate" => krate = Some(value.to_string()),
77                 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
78                 "edition" => edition = Some(value.to_string()),
79                 "cfg" => {
80                     for entry in value.split(',') {
81                         match split_once(entry, '=') {
82                             Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())),
83                             None => cfg_atoms.push(entry.to_string()),
84                         }
85                     }
86                 }
87                 "env" => {
88                     for key in value.split(',') {
89                         if let Some((k, v)) = split_once(key, '=') {
90                             env.insert(k.into(), v.into());
91                         }
92                     }
93                 }
94                 _ => panic!("bad component: {:?}", component),
95             }
96         }
97
98         Fixture { path, text: String::new(), krate, deps, cfg_atoms, cfg_key_values, edition, env }
99     }
100 }
101
102 #[test]
103 #[should_panic]
104 fn parse_fixture_checks_further_indented_metadata() {
105     Fixture::parse(
106         r"
107         //- /lib.rs
108           mod bar;
109
110           fn foo() {}
111           //- /bar.rs
112           pub fn baz() {}
113           ",
114     );
115 }
116
117 #[test]
118 fn parse_fixture_gets_full_meta() {
119     let parsed = Fixture::parse(
120         r"
121     //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
122     mod m;
123     ",
124     );
125     assert_eq!(1, parsed.len());
126
127     let meta = &parsed[0];
128     assert_eq!("mod m;\n", meta.text);
129
130     assert_eq!("foo", meta.krate.as_ref().unwrap());
131     assert_eq!("/lib.rs", meta.path);
132     assert_eq!(2, meta.env.len());
133 }