Add file support to expect
This commit is contained in:
parent
82838f5eda
commit
adf624b433
@ -14,7 +14,7 @@
|
|||||||
use stdx::{lines_with_ends, trim_indent};
|
use stdx::{lines_with_ends, trim_indent};
|
||||||
|
|
||||||
const HELP: &str = "
|
const HELP: &str = "
|
||||||
You can update all `expect![[]]` tests by:
|
You can update all `expect![[]]` tests by running:
|
||||||
|
|
||||||
env UPDATE_EXPECT=1 cargo test
|
env UPDATE_EXPECT=1 cargo test
|
||||||
|
|
||||||
@ -25,26 +25,37 @@ fn update_expect() -> bool {
|
|||||||
env::var("UPDATE_EXPECT").is_ok()
|
env::var("UPDATE_EXPECT").is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// expect![[""]]
|
/// expect![[r#"inline snapshot"#]]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! expect {
|
macro_rules! expect {
|
||||||
[[$lit:literal]] => {$crate::Expect {
|
[[$data:literal]] => {$crate::Expect {
|
||||||
position: $crate::Position {
|
position: $crate::Position {
|
||||||
file: file!(),
|
file: file!(),
|
||||||
line: line!(),
|
line: line!(),
|
||||||
column: column!(),
|
column: column!(),
|
||||||
},
|
},
|
||||||
data: $lit,
|
data: $data,
|
||||||
}};
|
}};
|
||||||
[[]] => { $crate::expect![[""]] };
|
[[]] => { $crate::expect![[""]] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// expect_file!["/crates/foo/test_data/foo.rs"]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! expect_file {
|
||||||
|
[$path:literal] => {$crate::ExpectFile { path: $path }};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expect {
|
pub struct Expect {
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub data: &'static str,
|
pub data: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExpectFile {
|
||||||
|
pub path: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub file: &'static str,
|
pub file: &'static str,
|
||||||
@ -64,7 +75,7 @@ pub fn assert_eq(&self, actual: &str) {
|
|||||||
if &trimmed == actual {
|
if &trimmed == actual {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Runtime::fail(self, &trimmed, actual);
|
Runtime::fail_expect(self, &trimmed, actual);
|
||||||
}
|
}
|
||||||
pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
|
pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
|
||||||
let actual = format!("{:#?}\n", actual);
|
let actual = format!("{:#?}\n", actual);
|
||||||
@ -100,6 +111,25 @@ fn locate(&self, file: &str) -> Location {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExpectFile {
|
||||||
|
pub fn assert_eq(&self, actual: &str) {
|
||||||
|
let expected = self.read();
|
||||||
|
if actual == expected {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Runtime::fail_file(self, &expected, actual);
|
||||||
|
}
|
||||||
|
fn read(&self) -> String {
|
||||||
|
fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n")
|
||||||
|
}
|
||||||
|
fn write(&self, contents: &str) {
|
||||||
|
fs::write(self.abs_path(), contents).unwrap()
|
||||||
|
}
|
||||||
|
fn abs_path(&self) -> PathBuf {
|
||||||
|
workspace_root().join(self.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Runtime {
|
struct Runtime {
|
||||||
help_printed: bool,
|
help_printed: bool,
|
||||||
@ -108,7 +138,7 @@ struct Runtime {
|
|||||||
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);
|
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);
|
||||||
|
|
||||||
impl Runtime {
|
impl Runtime {
|
||||||
fn fail(expect: &Expect, expected: &str, actual: &str) {
|
fn fail_expect(expect: &Expect, expected: &str, actual: &str) {
|
||||||
let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
|
let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||||
if update_expect() {
|
if update_expect() {
|
||||||
println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
|
println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
|
||||||
@ -118,7 +148,21 @@ fn fail(expect: &Expect, expected: &str, actual: &str) {
|
|||||||
.update(expect, actual);
|
.update(expect, actual);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let print_help = !mem::replace(&mut rt.help_printed, true);
|
rt.panic(expect.position.to_string(), expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) {
|
||||||
|
let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||||
|
if update_expect() {
|
||||||
|
println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path);
|
||||||
|
expect.write(actual);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rt.panic(expect.path.to_string(), expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panic(&mut self, position: String, expected: &str, actual: &str) {
|
||||||
|
let print_help = !mem::replace(&mut self.help_printed, true);
|
||||||
let help = if print_help { HELP } else { "" };
|
let help = if print_help { HELP } else { "" };
|
||||||
|
|
||||||
let diff = Changeset::new(actual, expected, "\n");
|
let diff = Changeset::new(actual, expected, "\n");
|
||||||
@ -143,7 +187,7 @@ fn fail(expect: &Expect, expected: &str, actual: &str) {
|
|||||||
{}
|
{}
|
||||||
----
|
----
|
||||||
",
|
",
|
||||||
expect.position, help, expected, actual, diff
|
position, help, expected, actual, diff
|
||||||
);
|
);
|
||||||
// Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
|
// Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
|
||||||
panic::resume_unwind(Box::new(()));
|
panic::resume_unwind(Box::new(()));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use test_utils::{assert_eq_text, project_dir, read_text};
|
use expect::{expect_file, ExpectFile};
|
||||||
|
use test_utils::project_dir;
|
||||||
|
|
||||||
use crate::{mock_analysis::single_file, FileRange, TextRange};
|
use crate::{mock_analysis::single_file, FileRange, TextRange};
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
|
|||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/highlighting.html",
|
expect_file!["crates/ra_ide/src/snapshots/highlighting.html"],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ fn bar() {
|
|||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/rainbow_highlighting.html",
|
expect_file!["crates/ra_ide/src/snapshots/rainbow_highlighting.html"],
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -167,7 +168,7 @@ fn foo() {
|
|||||||
);
|
);
|
||||||
}"##
|
}"##
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/highlight_injection.html",
|
expect_file!["crates/ra_ide/src/snapshots/highlight_injection.html"],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -250,7 +251,7 @@ fn main() {
|
|||||||
println!("{ничоси}", ничоси = 92);
|
println!("{ничоси}", ничоси = 92);
|
||||||
}"#
|
}"#
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/highlight_strings.html",
|
expect_file!["crates/ra_ide/src/snapshots/highlight_strings.html"],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -278,7 +279,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/highlight_unsafe.html",
|
expect_file!["crates/ra_ide/src/snapshots/highlight_unsafe.html"],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -354,7 +355,7 @@ macro_rules! noop {
|
|||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.trim(),
|
.trim(),
|
||||||
"crates/ra_ide/src/snapshots/highlight_doctest.html",
|
expect_file!["crates/ra_ide/src/snapshots/highlight_doctest.html"],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -362,11 +363,8 @@ macro_rules! noop {
|
|||||||
/// Highlights the code given by the `ra_fixture` argument, renders the
|
/// Highlights the code given by the `ra_fixture` argument, renders the
|
||||||
/// result as HTML, and compares it with the HTML file given as `snapshot`.
|
/// result as HTML, and compares it with the HTML file given as `snapshot`.
|
||||||
/// Note that the `snapshot` file is overwritten by the rendered HTML.
|
/// Note that the `snapshot` file is overwritten by the rendered HTML.
|
||||||
fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
|
fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
|
||||||
let (analysis, file_id) = single_file(ra_fixture);
|
let (analysis, file_id) = single_file(ra_fixture);
|
||||||
let dst_file = project_dir().join(snapshot);
|
|
||||||
let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
|
let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
|
||||||
let expected_html = &read_text(&dst_file);
|
expect.assert_eq(actual_html)
|
||||||
fs::write(dst_file, &actual_html).unwrap();
|
|
||||||
assert_eq_text!(expected_html, actual_html);
|
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ pub(crate) fn handle_runnables(
|
|||||||
let source_file = snap.analysis.parse(file_id)?;
|
let source_file = snap.analysis.parse(file_id)?;
|
||||||
algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
|
algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
|
||||||
.and_then(|it| it.path()?.segment()?.name_ref())
|
.and_then(|it| it.path()?.segment()?.name_ref())
|
||||||
.map_or(false, |it| it.text() == "expect")
|
.map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user