From da73c93c7f6d841496e75a16531d9be4aa23cada Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 8 Mar 2021 20:27:08 +0300 Subject: Don't punish every crate with serde-json --- .../rust-analyzer/tests/rust-analyzer/support.rs | 97 +++++++++++++++++++++- crates/test_utils/Cargo.toml | 1 - crates/test_utils/src/lib.rs | 96 --------------------- 3 files changed, 96 insertions(+), 98 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 6b774073d..cd0c91481 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs @@ -13,7 +13,7 @@ use project_model::ProjectManifest; use rust_analyzer::{config::Config, lsp_ext, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; -use test_utils::{find_mismatch, Fixture}; +use test_utils::Fixture; use vfs::AbsPathBuf; use crate::testdir::TestDir; @@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver) -> Result, Timeout recv(after(timeout)) -> _ => Err(Timeout), } } + +// Comparison functionality borrowed from cargo: + +/// Compares JSON object for approximate equality. +/// You can use `[..]` wildcard in strings (useful for OS dependent things such +/// as paths). You can use a `"{...}"` string literal as a wildcard for +/// arbitrary nested JSON. Arrays are sorted before comparison. +fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { + match (expected, actual) { + (Value::Number(l), Value::Number(r)) if l == r => None, + (Value::Bool(l), Value::Bool(r)) if l == r => None, + (Value::String(l), Value::String(r)) if lines_match(l, r) => None, + (Value::Array(l), Value::Array(r)) => { + if l.len() != r.len() { + return Some((expected, actual)); + } + + let mut l = l.iter().collect::>(); + let mut r = r.iter().collect::>(); + + l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { + Some(i) => { + r.remove(i); + false + } + None => true, + }); + + if !l.is_empty() { + assert!(!r.is_empty()); + Some((&l[0], &r[0])) + } else { + assert_eq!(r.len(), 0); + None + } + } + (Value::Object(l), Value::Object(r)) => { + fn sorted_values(obj: &serde_json::Map) -> Vec<&Value> { + let mut entries = obj.iter().collect::>(); + entries.sort_by_key(|it| it.0); + entries.into_iter().map(|(_k, v)| v).collect::>() + } + + let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); + if !same_keys { + return Some((expected, actual)); + } + + let l = sorted_values(l); + let r = sorted_values(r); + + l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() + } + (Value::Null, Value::Null) => None, + // magic string literal "{...}" acts as wildcard for any sub-JSON + (Value::String(l), _) if l == "{...}" => None, + _ => Some((expected, actual)), + } +} + +/// Compare a line with an expected pattern. +/// - Use `[..]` as a wildcard to match 0 or more characters on the same line +/// (similar to `.*` in a regex). +fn lines_match(expected: &str, actual: &str) -> bool { + // Let's not deal with / vs \ (windows...) + // First replace backslash-escaped backslashes with forward slashes + // which can occur in, for example, JSON output + let expected = expected.replace(r"\\", "/").replace(r"\", "/"); + let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); + for (i, part) in expected.split("[..]").enumerate() { + match actual.find(part) { + Some(j) => { + if i == 0 && j != 0 { + return false; + } + actual = &actual[j + part.len()..]; + } + None => return false, + } + } + actual.is_empty() || expected.ends_with("[..]") +} + +#[test] +fn lines_match_works() { + assert!(lines_match("a b", "a b")); + assert!(lines_match("a[..]b", "a b")); + assert!(lines_match("a[..]", "a b")); + assert!(lines_match("[..]", "a b")); + assert!(lines_match("[..]b", "a b")); + + assert!(!lines_match("[..]b", "c")); + assert!(!lines_match("b", "c")); + assert!(!lines_match("b", "cb")); +} diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 2a65000b8..87bab7a08 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml @@ -13,7 +13,6 @@ doctest = false # Avoid adding deps here, this crate is widely used in tests it should compile fast! dissimilar = "1.0.2" text-size = "1.0.0" -serde_json = "1.0.48" rustc-hash = "1.1.0" stdx = { path = "../stdx", version = "0.0.0" } diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 5be4a64fc..27b05e34b 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -18,7 +18,6 @@ use std::{ }; use profile::StopWatch; -use serde_json::Value; use stdx::lines_with_ends; use text_size::{TextRange, TextSize}; @@ -281,101 +280,6 @@ fn main() { ); } -// Comparison functionality borrowed from cargo: - -/// Compare a line with an expected pattern. -/// - Use `[..]` as a wildcard to match 0 or more characters on the same line -/// (similar to `.*` in a regex). -pub fn lines_match(expected: &str, actual: &str) -> bool { - // Let's not deal with / vs \ (windows...) - // First replace backslash-escaped backslashes with forward slashes - // which can occur in, for example, JSON output - let expected = expected.replace(r"\\", "/").replace(r"\", "/"); - let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); - for (i, part) in expected.split("[..]").enumerate() { - match actual.find(part) { - Some(j) => { - if i == 0 && j != 0 { - return false; - } - actual = &actual[j + part.len()..]; - } - None => return false, - } - } - actual.is_empty() || expected.ends_with("[..]") -} - -#[test] -fn lines_match_works() { - assert!(lines_match("a b", "a b")); - assert!(lines_match("a[..]b", "a b")); - assert!(lines_match("a[..]", "a b")); - assert!(lines_match("[..]", "a b")); - assert!(lines_match("[..]b", "a b")); - - assert!(!lines_match("[..]b", "c")); - assert!(!lines_match("b", "c")); - assert!(!lines_match("b", "cb")); -} - -/// Compares JSON object for approximate equality. -/// You can use `[..]` wildcard in strings (useful for OS dependent things such -/// as paths). You can use a `"{...}"` string literal as a wildcard for -/// arbitrary nested JSON. Arrays are sorted before comparison. -pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { - match (expected, actual) { - (Value::Number(l), Value::Number(r)) if l == r => None, - (Value::Bool(l), Value::Bool(r)) if l == r => None, - (Value::String(l), Value::String(r)) if lines_match(l, r) => None, - (Value::Array(l), Value::Array(r)) => { - if l.len() != r.len() { - return Some((expected, actual)); - } - - let mut l = l.iter().collect::>(); - let mut r = r.iter().collect::>(); - - l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { - Some(i) => { - r.remove(i); - false - } - None => true, - }); - - if !l.is_empty() { - assert!(!r.is_empty()); - Some((&l[0], &r[0])) - } else { - assert_eq!(r.len(), 0); - None - } - } - (Value::Object(l), Value::Object(r)) => { - fn sorted_values(obj: &serde_json::Map) -> Vec<&Value> { - let mut entries = obj.iter().collect::>(); - entries.sort_by_key(|it| it.0); - entries.into_iter().map(|(_k, v)| v).collect::>() - } - - let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); - if !same_keys { - return Some((expected, actual)); - } - - let l = sorted_values(l); - let r = sorted_values(r); - - l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() - } - (Value::Null, Value::Null) => None, - // magic string literal "{...}" acts as wildcard for any sub-JSON - (Value::String(l), _) if l == "{...}" => None, - _ => Some((expected, actual)), - } -} - /// Returns `false` if slow tests should not run, otherwise returns `true` and /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag /// that slow tests did run. -- cgit v1.2.3