diff options
Diffstat (limited to 'crates/test_utils')
-rw-r--r-- | crates/test_utils/src/lib.rs | 106 | ||||
-rw-r--r-- | crates/test_utils/src/mark.rs (renamed from crates/test_utils/src/marks.rs) | 46 |
2 files changed, 113 insertions, 39 deletions
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b1365444a..be2cfbaa2 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | //! * marks (see the eponymous module). | 7 | //! * marks (see the eponymous module). |
8 | 8 | ||
9 | #[macro_use] | 9 | #[macro_use] |
10 | pub mod marks; | 10 | pub mod mark; |
11 | 11 | ||
12 | use std::{ | 12 | use std::{ |
13 | fs, | 13 | fs, |
@@ -155,7 +155,7 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { | |||
155 | res | 155 | res |
156 | } | 156 | } |
157 | 157 | ||
158 | #[derive(Debug)] | 158 | #[derive(Debug, Eq, PartialEq)] |
159 | pub struct FixtureEntry { | 159 | pub struct FixtureEntry { |
160 | pub meta: String, | 160 | pub meta: String, |
161 | pub text: String, | 161 | pub text: String, |
@@ -170,19 +170,26 @@ pub struct FixtureEntry { | |||
170 | /// // - other meta | 170 | /// // - other meta |
171 | /// ``` | 171 | /// ``` |
172 | pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { | 172 | pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { |
173 | let margin = fixture | 173 | let fixture = indent_first_line(fixture); |
174 | .lines() | 174 | let margin = fixture_margin(&fixture); |
175 | .filter(|it| it.trim_start().starts_with("//-")) | ||
176 | .map(|it| it.len() - it.trim_start().len()) | ||
177 | .next() | ||
178 | .expect("empty fixture"); | ||
179 | 175 | ||
180 | let mut lines = fixture | 176 | let mut lines = fixture |
181 | .split('\n') // don't use `.lines` to not drop `\r\n` | 177 | .split('\n') // don't use `.lines` to not drop `\r\n` |
182 | .filter_map(|line| { | 178 | .enumerate() |
179 | .filter_map(|(ix, line)| { | ||
183 | if line.len() >= margin { | 180 | if line.len() >= margin { |
184 | assert!(line[..margin].trim().is_empty()); | 181 | assert!(line[..margin].trim().is_empty()); |
185 | Some(&line[margin..]) | 182 | let line_content = &line[margin..]; |
183 | if !line_content.starts_with("//-") { | ||
184 | assert!( | ||
185 | !line_content.contains("//-"), | ||
186 | r#"Metadata line {} has invalid indentation. All metadata lines need to have the same indentation. | ||
187 | The offending line: {:?}"#, | ||
188 | ix, | ||
189 | line | ||
190 | ); | ||
191 | } | ||
192 | Some(line_content) | ||
186 | } else { | 193 | } else { |
187 | assert!(line.trim().is_empty()); | 194 | assert!(line.trim().is_empty()); |
188 | None | 195 | None |
@@ -202,6 +209,85 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { | |||
202 | res | 209 | res |
203 | } | 210 | } |
204 | 211 | ||
212 | /// 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 | ||
214 | /// the other lines visually: | ||
215 | /// ``` | ||
216 | /// let fixture = "//- /lib.rs | ||
217 | /// mod foo; | ||
218 | /// //- /foo.rs | ||
219 | /// fn bar() {} | ||
220 | /// "; | ||
221 | /// assert_eq!(fixture_margin(fixture), | ||
222 | /// " //- /lib.rs | ||
223 | /// mod foo; | ||
224 | /// //- /foo.rs | ||
225 | /// fn bar() {} | ||
226 | /// ") | ||
227 | /// ``` | ||
228 | fn indent_first_line(fixture: &str) -> String { | ||
229 | if fixture.is_empty() { | ||
230 | return String::new(); | ||
231 | } | ||
232 | let mut lines = fixture.lines(); | ||
233 | let first_line = lines.next().unwrap(); | ||
234 | if first_line.contains("//-") { | ||
235 | let rest = lines.collect::<Vec<_>>().join("\n"); | ||
236 | let fixed_margin = fixture_margin(&rest); | ||
237 | let fixed_indent = fixed_margin - indent_len(first_line); | ||
238 | format!("\n{}{}\n{}", " ".repeat(fixed_indent), first_line, rest) | ||
239 | } else { | ||
240 | fixture.to_owned() | ||
241 | } | ||
242 | } | ||
243 | |||
244 | fn fixture_margin(fixture: &str) -> usize { | ||
245 | fixture | ||
246 | .lines() | ||
247 | .filter(|it| it.trim_start().starts_with("//-")) | ||
248 | .map(indent_len) | ||
249 | .next() | ||
250 | .expect("empty fixture") | ||
251 | } | ||
252 | |||
253 | fn indent_len(s: &str) -> usize { | ||
254 | s.len() - s.trim_start().len() | ||
255 | } | ||
256 | |||
257 | #[test] | ||
258 | #[should_panic] | ||
259 | fn parse_fixture_checks_further_indented_metadata() { | ||
260 | parse_fixture( | ||
261 | r" | ||
262 | //- /lib.rs | ||
263 | mod bar; | ||
264 | |||
265 | fn foo() {} | ||
266 | //- /bar.rs | ||
267 | pub fn baz() {} | ||
268 | ", | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
273 | fn parse_fixture_can_handle_dedented_first_line() { | ||
274 | let fixture = "//- /lib.rs | ||
275 | mod foo; | ||
276 | //- /foo.rs | ||
277 | struct Bar; | ||
278 | "; | ||
279 | assert_eq!( | ||
280 | parse_fixture(fixture), | ||
281 | parse_fixture( | ||
282 | "//- /lib.rs | ||
283 | mod foo; | ||
284 | //- /foo.rs | ||
285 | struct Bar; | ||
286 | " | ||
287 | ) | ||
288 | ) | ||
289 | } | ||
290 | |||
205 | /// Same as `parse_fixture`, except it allow empty fixture | 291 | /// Same as `parse_fixture`, except it allow empty fixture |
206 | pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { | 292 | pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { |
207 | if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { | 293 | if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { |
diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/mark.rs index c3185e860..7c309a894 100644 --- a/crates/test_utils/src/marks.rs +++ b/crates/test_utils/src/mark.rs | |||
@@ -7,18 +7,18 @@ | |||
7 | //! ``` | 7 | //! ``` |
8 | //! #[test] | 8 | //! #[test] |
9 | //! fn test_foo() { | 9 | //! fn test_foo() { |
10 | //! covers!(test_foo); | 10 | //! mark::check!(test_foo); |
11 | //! } | 11 | //! } |
12 | //! ``` | 12 | //! ``` |
13 | //! | 13 | //! |
14 | //! and in the code under test you write | 14 | //! and in the code under test you write |
15 | //! | 15 | //! |
16 | //! ``` | 16 | //! ``` |
17 | //! # use test_utils::tested_by; | 17 | //! # use test_utils::mark; |
18 | //! # fn some_condition() -> bool { true } | 18 | //! # fn some_condition() -> bool { true } |
19 | //! fn foo() { | 19 | //! fn foo() { |
20 | //! if some_condition() { | 20 | //! if some_condition() { |
21 | //! tested_by!(test_foo); | 21 | //! mark::hit!(test_foo); |
22 | //! } | 22 | //! } |
23 | //! } | 23 | //! } |
24 | //! ``` | 24 | //! ``` |
@@ -29,43 +29,31 @@ | |||
29 | use std::sync::atomic::{AtomicUsize, Ordering}; | 29 | use std::sync::atomic::{AtomicUsize, Ordering}; |
30 | 30 | ||
31 | #[macro_export] | 31 | #[macro_export] |
32 | macro_rules! tested_by { | 32 | macro_rules! _hit { |
33 | ($ident:ident; force) => {{ | ||
34 | { | ||
35 | // sic! use call-site crate | ||
36 | crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
37 | } | ||
38 | }}; | ||
39 | ($ident:ident) => {{ | 33 | ($ident:ident) => {{ |
40 | #[cfg(test)] | 34 | #[cfg(test)] |
41 | { | 35 | { |
42 | // sic! use call-site crate | 36 | extern "C" { |
43 | crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | 37 | #[no_mangle] |
38 | static $ident: std::sync::atomic::AtomicUsize; | ||
39 | } | ||
40 | unsafe { | ||
41 | $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
42 | } | ||
44 | } | 43 | } |
45 | }}; | 44 | }}; |
46 | } | 45 | } |
46 | pub use _hit as hit; | ||
47 | 47 | ||
48 | #[macro_export] | 48 | #[macro_export] |
49 | macro_rules! covers { | 49 | macro_rules! _check { |
50 | // sic! use call-site crate | ||
51 | ($ident:ident) => { | 50 | ($ident:ident) => { |
52 | $crate::covers!(crate::$ident) | 51 | #[no_mangle] |
53 | }; | 52 | static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); |
54 | ($krate:ident :: $ident:ident) => { | 53 | let _checker = $crate::mark::MarkChecker::new(&$ident); |
55 | let _checker = $crate::marks::MarkChecker::new(&$krate::marks::$ident); | ||
56 | }; | ||
57 | } | ||
58 | |||
59 | #[macro_export] | ||
60 | macro_rules! marks { | ||
61 | ($($ident:ident)*) => { | ||
62 | $( | ||
63 | #[allow(bad_style)] | ||
64 | pub static $ident: std::sync::atomic::AtomicUsize = | ||
65 | std::sync::atomic::AtomicUsize::new(0); | ||
66 | )* | ||
67 | }; | 54 | }; |
68 | } | 55 | } |
56 | pub use _check as check; | ||
69 | 57 | ||
70 | pub struct MarkChecker { | 58 | pub struct MarkChecker { |
71 | mark: &'static AtomicUsize, | 59 | mark: &'static AtomicUsize, |