diff --git a/Cargo.lock b/Cargo.lock index 82dbafd47d6..be9e8be98ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ dependencies = [ "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "strings 0.0.1 (git+https://github.com/nrc/strings.rs.git)", + "term 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -22,6 +23,15 @@ name = "diff" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "kernel32-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.1.8" @@ -60,6 +70,15 @@ name = "strings" version = "0.0.1" source = "git+https://github.com/nrc/strings.rs.git#6d748148fbe3bf2d9e5ac2ede65ac503d7491a4f" +[[package]] +name = "term" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.1.21" @@ -68,3 +87,13 @@ dependencies = [ "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/Cargo.toml b/Cargo.toml index a71d4abae86..4716be7098e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ rustc-serialize = "0.3.14" [dev-dependencies] diff = "0.1.0" regex = "0.1" +term = "0.2" diff --git a/tests/system.rs b/tests/system.rs index 19e98a64889..d6ba46c9cf5 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -13,14 +13,17 @@ extern crate rustfmt; extern crate diff; extern crate regex; +extern crate term; -use std::collections::HashMap; +use std::collections::{VecDeque, HashMap}; use std::fs; use std::io::{self, Read, BufRead, BufReader}; use std::thread; use rustfmt::*; use rustfmt::config::Config; +static DIFF_CONTEXT_SIZE: usize = 3; + fn get_path_string(dir_entry: io::Result) -> String { let path = dir_entry.ok().expect("Couldn't get DirEntry.").path(); @@ -87,16 +90,40 @@ fn check_files(files: I) -> (u32, u32) (count, fails) } -fn print_mismatches(result: HashMap) { - for (_, fmt_text) in result { - println!("{}", fmt_text); +fn print_mismatches(result: HashMap>) { + let mut t = term::stdout().unwrap(); + + for (file_name, diff) in result { + for mismatch in diff { + t.fg(term::color::BRIGHT_WHITE).unwrap(); + writeln!(t, "\nMismatch at {}:{}:", file_name, mismatch.line_number).unwrap(); + + for line in mismatch.lines { + match line { + DiffLine::Context(ref str) => { + t.fg(term::color::WHITE).unwrap(); + writeln!(t, " {}⏎", str).unwrap(); + } + DiffLine::Expected(ref str) => { + t.fg(term::color::GREEN).unwrap(); + writeln!(t, "+{}⏎", str).unwrap(); + } + DiffLine::Resulting(ref str) => { + t.fg(term::color::RED).unwrap(); + writeln!(t, "-{}⏎", str).unwrap(); + } + } + } + } } + + assert!(t.reset().unwrap()); } // Ick, just needed to get a &'static to handle_result. static HANDLE_RESULT: &'static Fn(HashMap) = &handle_result; -pub fn idempotent_check(filename: String) -> Result<(), HashMap> { +pub fn idempotent_check(filename: String) -> Result<(), HashMap>> { let sig_comments = read_significant_comments(&filename); let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..])); let args = vec!["rustfmt".to_owned(), filename]; @@ -179,10 +206,11 @@ fn handle_result(result: HashMap) { // TODO: speedup by running through bytes iterator f.read_to_string(&mut text).ok().expect("Failed reading target."); if fmt_text != text { - let diff_str = make_diff(&file_name, &fmt_text, &text); - failures.insert(file_name, diff_str); + let diff = make_diff(&fmt_text, &text, DIFF_CONTEXT_SIZE); + failures.insert(file_name, diff); } } + if !failures.is_empty() { panic!(failures); } @@ -199,36 +227,79 @@ fn get_target(file_name: &str, target: Option<&str>) -> String { } } -// Produces a diff string between the expected output and actual output of -// rustfmt on a given file -fn make_diff(file_name: &str, expected: &str, actual: &str) -> String { +pub enum DiffLine { + Context(String), + Expected(String), + Resulting(String), +} + +pub struct Mismatch { + line_number: u32, + pub lines: Vec, +} + +impl Mismatch { + fn new(line_number: u32) -> Mismatch { + Mismatch { line_number: line_number, lines: Vec::new() } + } +} + +// Produces a diff between the expected output and actual output of rustfmt. +fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { let mut line_number = 1; - let mut prev_both = true; - let mut text = String::new(); + let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size); + let mut lines_since_mismatch = context_size + 1; + let mut results = Vec::new(); + let mut mismatch = Mismatch::new(0); for result in diff::lines(expected, actual) { match result { diff::Result::Left(str) => { - if prev_both { - text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number)); + if lines_since_mismatch >= context_size { + results.push(mismatch); + mismatch = Mismatch::new(line_number - context_queue.len() as u32); } - text.push_str(&format!("-{}⏎\n", str)); - prev_both = false; + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Resulting(str.to_owned())); + lines_since_mismatch = 0; } diff::Result::Right(str) => { - if prev_both { - text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number)); + if lines_since_mismatch >= context_size { + results.push(mismatch); + mismatch = Mismatch::new(line_number - context_queue.len() as u32); } - text.push_str(&format!("+{}⏎\n", str)); - prev_both = false; + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Expected(str.to_owned())); line_number += 1; + lines_since_mismatch = 0; } - diff::Result::Both(..) => { + diff::Result::Both(str, _) => { + if context_queue.len() >= context_size { + let _ = context_queue.pop_front(); + } + + if lines_since_mismatch < context_size { + mismatch.lines.push(DiffLine::Context(str.to_owned())); + } else { + context_queue.push_back(str); + } + line_number += 1; - prev_both = true; + lines_since_mismatch += 1; } } } - text + results.push(mismatch); + results.remove(0); + + results }