diff options
-rw-r--r-- | crates/expect/src/lib.rs | 97 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/tests.rs | 22 | ||||
-rw-r--r-- | crates/ra_ide/test_data/highlight_doctest.html (renamed from crates/ra_ide/src/snapshots/highlight_doctest.html) | 0 | ||||
-rw-r--r-- | crates/ra_ide/test_data/highlight_injection.html (renamed from crates/ra_ide/src/snapshots/highlight_injection.html) | 0 | ||||
-rw-r--r-- | crates/ra_ide/test_data/highlight_strings.html (renamed from crates/ra_ide/src/snapshots/highlight_strings.html) | 0 | ||||
-rw-r--r-- | crates/ra_ide/test_data/highlight_unsafe.html (renamed from crates/ra_ide/src/snapshots/highlight_unsafe.html) | 0 | ||||
-rw-r--r-- | crates/ra_ide/test_data/highlighting.html (renamed from crates/ra_ide/src/snapshots/highlighting.html) | 0 | ||||
-rw-r--r-- | crates/ra_ide/test_data/rainbow_highlighting.html (renamed from crates/ra_ide/src/snapshots/rainbow_highlighting.html) | 0 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 2 |
9 files changed, 87 insertions, 34 deletions
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs index dd7b96aab..a5e26fade 100644 --- a/crates/expect/src/lib.rs +++ b/crates/expect/src/lib.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! https://github.com/rust-analyzer/rust-analyzer/pull/5101 | 2 | //! https://github.com/rust-analyzer/rust-analyzer/pull/5101 |
3 | use std::{ | 3 | use std::{ |
4 | collections::HashMap, | 4 | collections::HashMap, |
5 | env, fmt, fs, | 5 | env, fmt, fs, mem, |
6 | ops::Range, | 6 | ops::Range, |
7 | panic, | 7 | panic, |
8 | path::{Path, PathBuf}, | 8 | path::{Path, PathBuf}, |
@@ -14,7 +14,7 @@ use once_cell::sync::Lazy; | |||
14 | use stdx::{lines_with_ends, trim_indent}; | 14 | use stdx::{lines_with_ends, trim_indent}; |
15 | 15 | ||
16 | const HELP: &str = " | 16 | const HELP: &str = " |
17 | You can update all `expect![[]]` tests by: | 17 | You can update all `expect![[]]` tests by running: |
18 | 18 | ||
19 | env UPDATE_EXPECT=1 cargo test | 19 | env UPDATE_EXPECT=1 cargo test |
20 | 20 | ||
@@ -25,24 +25,48 @@ fn update_expect() -> bool { | |||
25 | env::var("UPDATE_EXPECT").is_ok() | 25 | env::var("UPDATE_EXPECT").is_ok() |
26 | } | 26 | } |
27 | 27 | ||
28 | /// expect![[""]] | 28 | /// expect![[r#"inline snapshot"#]] |
29 | #[macro_export] | 29 | #[macro_export] |
30 | macro_rules! expect { | 30 | macro_rules! expect { |
31 | [[$lit:literal]] => {$crate::Expect { | 31 | [[$data:literal]] => {$crate::Expect { |
32 | file: file!(), | 32 | position: $crate::Position { |
33 | line: line!(), | 33 | file: file!(), |
34 | column: column!(), | 34 | line: line!(), |
35 | data: $lit, | 35 | column: column!(), |
36 | }, | ||
37 | data: $data, | ||
36 | }}; | 38 | }}; |
37 | [[]] => { $crate::expect![[""]] }; | 39 | [[]] => { $crate::expect![[""]] }; |
38 | } | 40 | } |
39 | 41 | ||
42 | /// expect_file!["/crates/foo/test_data/bar.html"] | ||
43 | #[macro_export] | ||
44 | macro_rules! expect_file { | ||
45 | [$path:literal] => {$crate::ExpectFile { path: $path }}; | ||
46 | } | ||
47 | |||
40 | #[derive(Debug)] | 48 | #[derive(Debug)] |
41 | pub struct Expect { | 49 | pub struct Expect { |
50 | pub position: Position, | ||
51 | pub data: &'static str, | ||
52 | } | ||
53 | |||
54 | #[derive(Debug)] | ||
55 | pub struct ExpectFile { | ||
56 | pub path: &'static str, | ||
57 | } | ||
58 | |||
59 | #[derive(Debug)] | ||
60 | pub struct Position { | ||
42 | pub file: &'static str, | 61 | pub file: &'static str, |
43 | pub line: u32, | 62 | pub line: u32, |
44 | pub column: u32, | 63 | pub column: u32, |
45 | pub data: &'static str, | 64 | } |
65 | |||
66 | impl fmt::Display for Position { | ||
67 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
68 | write!(f, "{}:{}:{}", self.file, self.line, self.column) | ||
69 | } | ||
46 | } | 70 | } |
47 | 71 | ||
48 | impl Expect { | 72 | impl Expect { |
@@ -51,7 +75,7 @@ impl Expect { | |||
51 | if &trimmed == actual { | 75 | if &trimmed == actual { |
52 | return; | 76 | return; |
53 | } | 77 | } |
54 | Runtime::fail(self, &trimmed, actual); | 78 | Runtime::fail_expect(self, &trimmed, actual); |
55 | } | 79 | } |
56 | pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { | 80 | pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { |
57 | let actual = format!("{:#?}\n", actual); | 81 | let actual = format!("{:#?}\n", actual); |
@@ -69,7 +93,7 @@ impl Expect { | |||
69 | let mut target_line = None; | 93 | let mut target_line = None; |
70 | let mut line_start = 0; | 94 | let mut line_start = 0; |
71 | for (i, line) in lines_with_ends(file).enumerate() { | 95 | for (i, line) in lines_with_ends(file).enumerate() { |
72 | if i == self.line as usize - 1 { | 96 | if i == self.position.line as usize - 1 { |
73 | let pat = "expect![["; | 97 | let pat = "expect![["; |
74 | let offset = line.find(pat).unwrap(); | 98 | let offset = line.find(pat).unwrap(); |
75 | let literal_start = line_start + offset + pat.len(); | 99 | let literal_start = line_start + offset + pat.len(); |
@@ -87,6 +111,25 @@ impl Expect { | |||
87 | } | 111 | } |
88 | } | 112 | } |
89 | 113 | ||
114 | impl ExpectFile { | ||
115 | pub fn assert_eq(&self, actual: &str) { | ||
116 | let expected = self.read(); | ||
117 | if actual == expected { | ||
118 | return; | ||
119 | } | ||
120 | Runtime::fail_file(self, &expected, actual); | ||
121 | } | ||
122 | fn read(&self) -> String { | ||
123 | fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n") | ||
124 | } | ||
125 | fn write(&self, contents: &str) { | ||
126 | fs::write(self.abs_path(), contents).unwrap() | ||
127 | } | ||
128 | fn abs_path(&self) -> PathBuf { | ||
129 | workspace_root().join(self.path) | ||
130 | } | ||
131 | } | ||
132 | |||
90 | #[derive(Default)] | 133 | #[derive(Default)] |
91 | struct Runtime { | 134 | struct Runtime { |
92 | help_printed: bool, | 135 | help_printed: bool, |
@@ -95,27 +138,39 @@ struct Runtime { | |||
95 | static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default); | 138 | static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default); |
96 | 139 | ||
97 | impl Runtime { | 140 | impl Runtime { |
98 | fn fail(expect: &Expect, expected: &str, actual: &str) { | 141 | fn fail_expect(expect: &Expect, expected: &str, actual: &str) { |
99 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); | 142 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); |
100 | let mut updated = ""; | ||
101 | if update_expect() { | 143 | if update_expect() { |
102 | updated = " (updated)"; | 144 | println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position); |
103 | rt.per_file | 145 | rt.per_file |
104 | .entry(expect.file) | 146 | .entry(expect.position.file) |
105 | .or_insert_with(|| FileRuntime::new(expect)) | 147 | .or_insert_with(|| FileRuntime::new(expect)) |
106 | .update(expect, actual); | 148 | .update(expect, actual); |
149 | return; | ||
107 | } | 150 | } |
108 | let print_help = !rt.help_printed && !update_expect(); | 151 | rt.panic(expect.position.to_string(), expected, actual); |
109 | rt.help_printed = true; | 152 | } |
153 | |||
154 | fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) { | ||
155 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); | ||
156 | if update_expect() { | ||
157 | println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path); | ||
158 | expect.write(actual); | ||
159 | return; | ||
160 | } | ||
161 | rt.panic(expect.path.to_string(), expected, actual); | ||
162 | } | ||
110 | 163 | ||
164 | fn panic(&mut self, position: String, expected: &str, actual: &str) { | ||
165 | let print_help = !mem::replace(&mut self.help_printed, true); | ||
111 | let help = if print_help { HELP } else { "" }; | 166 | let help = if print_help { HELP } else { "" }; |
112 | 167 | ||
113 | let diff = Changeset::new(actual, expected, "\n"); | 168 | let diff = Changeset::new(actual, expected, "\n"); |
114 | 169 | ||
115 | println!( | 170 | println!( |
116 | "\n | 171 | "\n |
117 | \x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m{} | 172 | \x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m |
118 | \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{} | 173 | \x1b[1m\x1b[34m-->\x1b[0m {} |
119 | {} | 174 | {} |
120 | \x1b[1mExpect\x1b[0m: | 175 | \x1b[1mExpect\x1b[0m: |
121 | ---- | 176 | ---- |
@@ -132,7 +187,7 @@ impl Runtime { | |||
132 | {} | 187 | {} |
133 | ---- | 188 | ---- |
134 | ", | 189 | ", |
135 | updated, expect.file, expect.line, expect.column, help, expected, actual, diff | 190 | position, help, expected, actual, diff |
136 | ); | 191 | ); |
137 | // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. | 192 | // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. |
138 | panic::resume_unwind(Box::new(())); | 193 | panic::resume_unwind(Box::new(())); |
@@ -147,7 +202,7 @@ struct FileRuntime { | |||
147 | 202 | ||
148 | impl FileRuntime { | 203 | impl FileRuntime { |
149 | fn new(expect: &Expect) -> FileRuntime { | 204 | fn new(expect: &Expect) -> FileRuntime { |
150 | let path = workspace_root().join(expect.file); | 205 | let path = workspace_root().join(expect.position.file); |
151 | let original_text = fs::read_to_string(&path).unwrap(); | 206 | let original_text = fs::read_to_string(&path).unwrap(); |
152 | let patchwork = Patchwork::new(original_text.clone()); | 207 | let patchwork = Patchwork::new(original_text.clone()); |
153 | FileRuntime { path, original_text, patchwork } | 208 | FileRuntime { path, original_text, patchwork } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index b7fad9719..aa7c887d6 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::fs; | 1 | use std::fs; |
2 | 2 | ||
3 | use test_utils::{assert_eq_text, project_dir, read_text}; | 3 | use expect::{expect_file, ExpectFile}; |
4 | use test_utils::project_dir; | ||
4 | 5 | ||
5 | use crate::{mock_analysis::single_file, FileRange, TextRange}; | 6 | use crate::{mock_analysis::single_file, FileRange, TextRange}; |
6 | 7 | ||
@@ -91,7 +92,7 @@ impl<T> Option<T> { | |||
91 | } | 92 | } |
92 | "# | 93 | "# |
93 | .trim(), | 94 | .trim(), |
94 | "crates/ra_ide/src/snapshots/highlighting.html", | 95 | expect_file!["crates/ra_ide/test_data/highlighting.html"], |
95 | false, | 96 | false, |
96 | ); | 97 | ); |
97 | } | 98 | } |
@@ -114,7 +115,7 @@ fn bar() { | |||
114 | } | 115 | } |
115 | "# | 116 | "# |
116 | .trim(), | 117 | .trim(), |
117 | "crates/ra_ide/src/snapshots/rainbow_highlighting.html", | 118 | expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"], |
118 | true, | 119 | true, |
119 | ); | 120 | ); |
120 | } | 121 | } |
@@ -167,7 +168,7 @@ fn main() { | |||
167 | ); | 168 | ); |
168 | }"## | 169 | }"## |
169 | .trim(), | 170 | .trim(), |
170 | "crates/ra_ide/src/snapshots/highlight_injection.html", | 171 | expect_file!["crates/ra_ide/test_data/highlight_injection.html"], |
171 | false, | 172 | false, |
172 | ); | 173 | ); |
173 | } | 174 | } |
@@ -250,7 +251,7 @@ fn main() { | |||
250 | println!("{ничоси}", ничоси = 92); | 251 | println!("{ничоси}", ничоси = 92); |
251 | }"# | 252 | }"# |
252 | .trim(), | 253 | .trim(), |
253 | "crates/ra_ide/src/snapshots/highlight_strings.html", | 254 | expect_file!["crates/ra_ide/test_data/highlight_strings.html"], |
254 | false, | 255 | false, |
255 | ); | 256 | ); |
256 | } | 257 | } |
@@ -278,7 +279,7 @@ fn main() { | |||
278 | } | 279 | } |
279 | "# | 280 | "# |
280 | .trim(), | 281 | .trim(), |
281 | "crates/ra_ide/src/snapshots/highlight_unsafe.html", | 282 | expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"], |
282 | false, | 283 | false, |
283 | ); | 284 | ); |
284 | } | 285 | } |
@@ -354,7 +355,7 @@ macro_rules! noop { | |||
354 | } | 355 | } |
355 | "# | 356 | "# |
356 | .trim(), | 357 | .trim(), |
357 | "crates/ra_ide/src/snapshots/highlight_doctest.html", | 358 | expect_file!["crates/ra_ide/test_data/highlight_doctest.html"], |
358 | false, | 359 | false, |
359 | ); | 360 | ); |
360 | } | 361 | } |
@@ -362,11 +363,8 @@ macro_rules! noop { | |||
362 | /// Highlights the code given by the `ra_fixture` argument, renders the | 363 | /// Highlights the code given by the `ra_fixture` argument, renders the |
363 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | 364 | /// result as HTML, and compares it with the HTML file given as `snapshot`. |
364 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | 365 | /// Note that the `snapshot` file is overwritten by the rendered HTML. |
365 | fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) { | 366 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { |
366 | let (analysis, file_id) = single_file(ra_fixture); | 367 | let (analysis, file_id) = single_file(ra_fixture); |
367 | let dst_file = project_dir().join(snapshot); | ||
368 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); | 368 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); |
369 | let expected_html = &read_text(&dst_file); | 369 | expect.assert_eq(actual_html) |
370 | fs::write(dst_file, &actual_html).unwrap(); | ||
371 | assert_eq_text!(expected_html, actual_html); | ||
372 | } | 370 | } |
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html index e8155def7..e8155def7 100644 --- a/crates/ra_ide/src/snapshots/highlight_doctest.html +++ b/crates/ra_ide/test_data/highlight_doctest.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html index 1b0349bae..1b0349bae 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/test_data/highlight_injection.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/test_data/highlight_strings.html index d184b5691..d184b5691 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/test_data/highlight_strings.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html index 6936e949f..6936e949f 100644 --- a/crates/ra_ide/src/snapshots/highlight_unsafe.html +++ b/crates/ra_ide/test_data/highlight_unsafe.html | |||
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/test_data/highlighting.html index 8d0b38f95..8d0b38f95 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/test_data/highlighting.html | |||
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/test_data/rainbow_highlighting.html index 9516c7441..9516c7441 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/test_data/rainbow_highlighting.html | |||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 25bcd80af..607a95682 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -415,7 +415,7 @@ pub(crate) fn handle_runnables( | |||
415 | let source_file = snap.analysis.parse(file_id)?; | 415 | let source_file = snap.analysis.parse(file_id)?; |
416 | algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) | 416 | algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) |
417 | .and_then(|it| it.path()?.segment()?.name_ref()) | 417 | .and_then(|it| it.path()?.segment()?.name_ref()) |
418 | .map_or(false, |it| it.text() == "expect") | 418 | .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file") |
419 | } | 419 | } |
420 | None => false, | 420 | None => false, |
421 | }; | 421 | }; |