aboutsummaryrefslogtreecommitdiff
path: root/crates/test_utils
diff options
context:
space:
mode:
Diffstat (limited to 'crates/test_utils')
-rw-r--r--crates/test_utils/src/lib.rs106
-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]
10pub mod marks; 10pub mod mark;
11 11
12use std::{ 12use 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)]
159pub struct FixtureEntry { 159pub 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/// ```
172pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 172pub 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.
187The 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/// ```
228fn 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
244fn 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
253fn indent_len(s: &str) -> usize {
254 s.len() - s.trim_start().len()
255}
256
257#[test]
258#[should_panic]
259fn 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]
273fn 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
283mod foo;
284//- /foo.rs
285struct 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
206pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { 292pub 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 @@
29use std::sync::atomic::{AtomicUsize, Ordering}; 29use std::sync::atomic::{AtomicUsize, Ordering};
30 30
31#[macro_export] 31#[macro_export]
32macro_rules! tested_by { 32macro_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}
46pub use _hit as hit;
47 47
48#[macro_export] 48#[macro_export]
49macro_rules! covers { 49macro_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]
60macro_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}
56pub use _check as check;
69 57
70pub struct MarkChecker { 58pub struct MarkChecker {
71 mark: &'static AtomicUsize, 59 mark: &'static AtomicUsize,