From 66e0c9f840bc60fa132b285312136293301cc2c0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 21 Aug 2020 13:15:56 +0200 Subject: Remove expect crate --- crates/expect/src/lib.rs | 356 ----------------------------------------------- 1 file changed, 356 deletions(-) delete mode 100644 crates/expect/src/lib.rs (limited to 'crates/expect/src/lib.rs') diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs deleted file mode 100644 index bd83895f7..000000000 --- a/crates/expect/src/lib.rs +++ /dev/null @@ -1,356 +0,0 @@ -//! Snapshot testing library, see -//! https://github.com/rust-analyzer/rust-analyzer/pull/5101 -use std::{ - collections::HashMap, - env, fmt, fs, mem, - ops::Range, - panic, - path::{Path, PathBuf}, - sync::Mutex, -}; - -use difference::Changeset; -use once_cell::sync::Lazy; -use stdx::{lines_with_ends, trim_indent}; - -const HELP: &str = " -You can update all `expect![[]]` tests by running: - - env UPDATE_EXPECT=1 cargo test - -To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer. -"; - -fn update_expect() -> bool { - env::var("UPDATE_EXPECT").is_ok() -} - -/// expect![[r#"inline snapshot"#]] -#[macro_export] -macro_rules! expect { - [[$data:literal]] => {$crate::Expect { - position: $crate::Position { - file: file!(), - line: line!(), - column: column!(), - }, - data: $data, - }}; - [[]] => { $crate::expect![[""]] }; -} - -/// expect_file!["/crates/foo/test_data/bar.html"] -#[macro_export] -macro_rules! expect_file { - [$path:expr] => {$crate::ExpectFile { - path: std::path::PathBuf::from($path) - }}; -} - -#[derive(Debug)] -pub struct Expect { - pub position: Position, - pub data: &'static str, -} - -#[derive(Debug)] -pub struct ExpectFile { - pub path: PathBuf, -} - -#[derive(Debug)] -pub struct Position { - pub file: &'static str, - pub line: u32, - pub column: u32, -} - -impl fmt::Display for Position { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}:{}", self.file, self.line, self.column) - } -} - -impl Expect { - pub fn assert_eq(&self, actual: &str) { - let trimmed = self.trimmed(); - if trimmed == actual { - return; - } - Runtime::fail_expect(self, &trimmed, actual); - } - pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { - let actual = format!("{:#?}\n", actual); - self.assert_eq(&actual) - } - - fn trimmed(&self) -> String { - if !self.data.contains('\n') { - return self.data.to_string(); - } - trim_indent(self.data) - } - - fn locate(&self, file: &str) -> Location { - let mut target_line = None; - let mut line_start = 0; - for (i, line) in lines_with_ends(file).enumerate() { - if i == self.position.line as usize - 1 { - let pat = "expect![["; - let offset = line.find(pat).unwrap(); - let literal_start = line_start + offset + pat.len(); - let indent = line.chars().take_while(|&it| it == ' ').count(); - target_line = Some((literal_start, indent)); - break; - } - line_start += line.len(); - } - let (literal_start, line_indent) = target_line.unwrap(); - let literal_length = - file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`."); - let literal_range = literal_start..literal_start + literal_length; - Location { line_indent, literal_range } - } -} - -impl ExpectFile { - pub fn assert_eq(&self, actual: &str) { - let expected = self.read(); - if actual == expected { - return; - } - Runtime::fail_file(self, &expected, actual); - } - pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { - let actual = format!("{:#?}\n", actual); - self.assert_eq(&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)] -struct Runtime { - help_printed: bool, - per_file: HashMap<&'static str, FileRuntime>, -} -static RT: Lazy> = Lazy::new(Default::default); - -impl Runtime { - fn fail_expect(expect: &Expect, 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.position); - rt.per_file - .entry(expect.position.file) - .or_insert_with(|| FileRuntime::new(expect)) - .update(expect, actual); - return; - } - 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.display()); - expect.write(actual); - return; - } - rt.panic(expect.path.display().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 diff = Changeset::new(actual, expected, "\n"); - - println!( - "\n -\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m - \x1b[1m\x1b[34m-->\x1b[0m {} -{} -\x1b[1mExpect\x1b[0m: ----- -{} ----- - -\x1b[1mActual\x1b[0m: ----- -{} ----- - -\x1b[1mDiff\x1b[0m: ----- -{} ----- -", - position, help, expected, actual, diff - ); - // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. - panic::resume_unwind(Box::new(())); - } -} - -struct FileRuntime { - path: PathBuf, - original_text: String, - patchwork: Patchwork, -} - -impl FileRuntime { - fn new(expect: &Expect) -> FileRuntime { - let path = WORKSPACE_ROOT.join(expect.position.file); - let original_text = fs::read_to_string(&path).unwrap(); - let patchwork = Patchwork::new(original_text.clone()); - FileRuntime { path, original_text, patchwork } - } - fn update(&mut self, expect: &Expect, actual: &str) { - let loc = expect.locate(&self.original_text); - let patch = format_patch(loc.line_indent.clone(), actual); - self.patchwork.patch(loc.literal_range, &patch); - fs::write(&self.path, &self.patchwork.text).unwrap() - } -} - -#[derive(Debug)] -struct Location { - line_indent: usize, - literal_range: Range, -} - -#[derive(Debug)] -struct Patchwork { - text: String, - indels: Vec<(Range, usize)>, -} - -impl Patchwork { - fn new(text: String) -> Patchwork { - Patchwork { text, indels: Vec::new() } - } - fn patch(&mut self, mut range: Range, patch: &str) { - self.indels.push((range.clone(), patch.len())); - self.indels.sort_by_key(|(delete, _insert)| delete.start); - - let (delete, insert) = self - .indels - .iter() - .take_while(|(delete, _)| delete.start < range.start) - .map(|(delete, insert)| (delete.end - delete.start, insert)) - .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2)); - - for pos in &mut [&mut range.start, &mut range.end] { - **pos -= delete; - **pos += insert; - } - - self.text.replace_range(range, &patch); - } -} - -fn format_patch(line_indent: usize, patch: &str) -> String { - let mut max_hashes = 0; - let mut cur_hashes = 0; - for byte in patch.bytes() { - if byte != b'#' { - cur_hashes = 0; - continue; - } - cur_hashes += 1; - max_hashes = max_hashes.max(cur_hashes); - } - let hashes = &"#".repeat(max_hashes + 1); - let indent = &" ".repeat(line_indent); - let is_multiline = patch.contains('\n'); - - let mut buf = String::new(); - buf.push('r'); - buf.push_str(hashes); - buf.push('"'); - if is_multiline { - buf.push('\n'); - } - let mut final_newline = false; - for line in lines_with_ends(patch) { - if is_multiline && !line.trim().is_empty() { - buf.push_str(indent); - buf.push_str(" "); - } - buf.push_str(line); - final_newline = line.ends_with('\n'); - } - if final_newline { - buf.push_str(indent); - } - buf.push('"'); - buf.push_str(hashes); - buf -} - -static WORKSPACE_ROOT: Lazy = Lazy::new(|| { - let my_manifest = - env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()); - // Heuristic, see https://github.com/rust-lang/cargo/issues/3946 - Path::new(&my_manifest) - .ancestors() - .filter(|it| it.join("Cargo.toml").exists()) - .last() - .unwrap() - .to_path_buf() -}); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_format_patch() { - let patch = format_patch(0, "hello\nworld\n"); - expect![[r##" - r#" - hello - world - "#"##]] - .assert_eq(&patch); - - let patch = format_patch(4, "single line"); - expect![[r##"r#"single line"#"##]].assert_eq(&patch); - } - - #[test] - fn test_patchwork() { - let mut patchwork = Patchwork::new("one two three".to_string()); - patchwork.patch(4..7, "zwei"); - patchwork.patch(0..3, "один"); - patchwork.patch(8..13, "3"); - expect![[r#" - Patchwork { - text: "один zwei 3", - indels: [ - ( - 0..3, - 8, - ), - ( - 4..7, - 4, - ), - ( - 8..13, - 1, - ), - ], - } - "#]] - .assert_debug_eq(&patchwork); - } -} -- cgit v1.2.3