aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-03-08 17:27:08 +0000
committerAleksey Kladov <[email protected]>2021-03-08 17:37:20 +0000
commitda73c93c7f6d841496e75a16531d9be4aa23cada (patch)
tree360aae1dc872b22a71014f253e3a98f21a3502a6 /crates
parentd57c9f79801762aa23adc21adbb638678268e96b (diff)
Don't punish every crate with serde-json
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs97
-rw-r--r--crates/test_utils/Cargo.toml1
-rw-r--r--crates/test_utils/src/lib.rs96
3 files changed, 96 insertions, 98 deletions
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;
13use rust_analyzer::{config::Config, lsp_ext, main_loop}; 13use rust_analyzer::{config::Config, lsp_ext, main_loop};
14use serde::Serialize; 14use serde::Serialize;
15use serde_json::{json, to_string_pretty, Value}; 15use serde_json::{json, to_string_pretty, Value};
16use test_utils::{find_mismatch, Fixture}; 16use test_utils::Fixture;
17use vfs::AbsPathBuf; 17use vfs::AbsPathBuf;
18 18
19use crate::testdir::TestDir; 19use crate::testdir::TestDir;
@@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver<Message>) -> Result<Option<Message>, Timeout
279 recv(after(timeout)) -> _ => Err(Timeout), 279 recv(after(timeout)) -> _ => Err(Timeout),
280 } 280 }
281} 281}
282
283// Comparison functionality borrowed from cargo:
284
285/// Compares JSON object for approximate equality.
286/// You can use `[..]` wildcard in strings (useful for OS dependent things such
287/// as paths). You can use a `"{...}"` string literal as a wildcard for
288/// arbitrary nested JSON. Arrays are sorted before comparison.
289fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
290 match (expected, actual) {
291 (Value::Number(l), Value::Number(r)) if l == r => None,
292 (Value::Bool(l), Value::Bool(r)) if l == r => None,
293 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
294 (Value::Array(l), Value::Array(r)) => {
295 if l.len() != r.len() {
296 return Some((expected, actual));
297 }
298
299 let mut l = l.iter().collect::<Vec<_>>();
300 let mut r = r.iter().collect::<Vec<_>>();
301
302 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
303 Some(i) => {
304 r.remove(i);
305 false
306 }
307 None => true,
308 });
309
310 if !l.is_empty() {
311 assert!(!r.is_empty());
312 Some((&l[0], &r[0]))
313 } else {
314 assert_eq!(r.len(), 0);
315 None
316 }
317 }
318 (Value::Object(l), Value::Object(r)) => {
319 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
320 let mut entries = obj.iter().collect::<Vec<_>>();
321 entries.sort_by_key(|it| it.0);
322 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
323 }
324
325 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
326 if !same_keys {
327 return Some((expected, actual));
328 }
329
330 let l = sorted_values(l);
331 let r = sorted_values(r);
332
333 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
334 }
335 (Value::Null, Value::Null) => None,
336 // magic string literal "{...}" acts as wildcard for any sub-JSON
337 (Value::String(l), _) if l == "{...}" => None,
338 _ => Some((expected, actual)),
339 }
340}
341
342/// Compare a line with an expected pattern.
343/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
344/// (similar to `.*` in a regex).
345fn lines_match(expected: &str, actual: &str) -> bool {
346 // Let's not deal with / vs \ (windows...)
347 // First replace backslash-escaped backslashes with forward slashes
348 // which can occur in, for example, JSON output
349 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
350 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
351 for (i, part) in expected.split("[..]").enumerate() {
352 match actual.find(part) {
353 Some(j) => {
354 if i == 0 && j != 0 {
355 return false;
356 }
357 actual = &actual[j + part.len()..];
358 }
359 None => return false,
360 }
361 }
362 actual.is_empty() || expected.ends_with("[..]")
363}
364
365#[test]
366fn lines_match_works() {
367 assert!(lines_match("a b", "a b"));
368 assert!(lines_match("a[..]b", "a b"));
369 assert!(lines_match("a[..]", "a b"));
370 assert!(lines_match("[..]", "a b"));
371 assert!(lines_match("[..]b", "a b"));
372
373 assert!(!lines_match("[..]b", "c"));
374 assert!(!lines_match("b", "c"));
375 assert!(!lines_match("b", "cb"));
376}
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
13# Avoid adding deps here, this crate is widely used in tests it should compile fast! 13# Avoid adding deps here, this crate is widely used in tests it should compile fast!
14dissimilar = "1.0.2" 14dissimilar = "1.0.2"
15text-size = "1.0.0" 15text-size = "1.0.0"
16serde_json = "1.0.48"
17rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
18 17
19stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { 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::{
18}; 18};
19 19
20use profile::StopWatch; 20use profile::StopWatch;
21use serde_json::Value;
22use stdx::lines_with_ends; 21use stdx::lines_with_ends;
23use text_size::{TextRange, TextSize}; 22use text_size::{TextRange, TextSize};
24 23
@@ -281,101 +280,6 @@ fn main() {
281 ); 280 );
282} 281}
283 282
284// Comparison functionality borrowed from cargo:
285
286/// Compare a line with an expected pattern.
287/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
288/// (similar to `.*` in a regex).
289pub fn lines_match(expected: &str, actual: &str) -> bool {
290 // Let's not deal with / vs \ (windows...)
291 // First replace backslash-escaped backslashes with forward slashes
292 // which can occur in, for example, JSON output
293 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
294 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
295 for (i, part) in expected.split("[..]").enumerate() {
296 match actual.find(part) {
297 Some(j) => {
298 if i == 0 && j != 0 {
299 return false;
300 }
301 actual = &actual[j + part.len()..];
302 }
303 None => return false,
304 }
305 }
306 actual.is_empty() || expected.ends_with("[..]")
307}
308
309#[test]
310fn lines_match_works() {
311 assert!(lines_match("a b", "a b"));
312 assert!(lines_match("a[..]b", "a b"));
313 assert!(lines_match("a[..]", "a b"));
314 assert!(lines_match("[..]", "a b"));
315 assert!(lines_match("[..]b", "a b"));
316
317 assert!(!lines_match("[..]b", "c"));
318 assert!(!lines_match("b", "c"));
319 assert!(!lines_match("b", "cb"));
320}
321
322/// Compares JSON object for approximate equality.
323/// You can use `[..]` wildcard in strings (useful for OS dependent things such
324/// as paths). You can use a `"{...}"` string literal as a wildcard for
325/// arbitrary nested JSON. Arrays are sorted before comparison.
326pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
327 match (expected, actual) {
328 (Value::Number(l), Value::Number(r)) if l == r => None,
329 (Value::Bool(l), Value::Bool(r)) if l == r => None,
330 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
331 (Value::Array(l), Value::Array(r)) => {
332 if l.len() != r.len() {
333 return Some((expected, actual));
334 }
335
336 let mut l = l.iter().collect::<Vec<_>>();
337 let mut r = r.iter().collect::<Vec<_>>();
338
339 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
340 Some(i) => {
341 r.remove(i);
342 false
343 }
344 None => true,
345 });
346
347 if !l.is_empty() {
348 assert!(!r.is_empty());
349 Some((&l[0], &r[0]))
350 } else {
351 assert_eq!(r.len(), 0);
352 None
353 }
354 }
355 (Value::Object(l), Value::Object(r)) => {
356 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
357 let mut entries = obj.iter().collect::<Vec<_>>();
358 entries.sort_by_key(|it| it.0);
359 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
360 }
361
362 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
363 if !same_keys {
364 return Some((expected, actual));
365 }
366
367 let l = sorted_values(l);
368 let r = sorted_values(r);
369
370 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
371 }
372 (Value::Null, Value::Null) => None,
373 // magic string literal "{...}" acts as wildcard for any sub-JSON
374 (Value::String(l), _) if l == "{...}" => None,
375 _ => Some((expected, actual)),
376 }
377}
378
379/// Returns `false` if slow tests should not run, otherwise returns `true` and 283/// Returns `false` if slow tests should not run, otherwise returns `true` and
380/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag 284/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
381/// that slow tests did run. 285/// that slow tests did run.