1
Fork 0

Refactor diff handling in tests

This splits the generation and display of mismatches. Mismatches now include a few lines of context. Finally, diffs are now coloured.
This commit is contained in:
Marcus Klaas 2015-08-30 20:47:46 +02:00
parent b59ab9c13f
commit adeafb3e45
3 changed files with 124 additions and 23 deletions

29
Cargo.lock generated
View file

@ -6,6 +6,7 @@ dependencies = [
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "toml 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -22,6 +23,15 @@ name = "diff"
version = "0.1.4" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "libc" name = "libc"
version = "0.1.8" version = "0.1.8"
@ -60,6 +70,15 @@ name = "strings"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/nrc/strings.rs.git#6d748148fbe3bf2d9e5ac2ede65ac503d7491a4f" 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]] [[package]]
name = "toml" name = "toml"
version = "0.1.21" version = "0.1.21"
@ -68,3 +87,13 @@ dependencies = [
"rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "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"

View file

@ -19,3 +19,4 @@ rustc-serialize = "0.3.14"
[dev-dependencies] [dev-dependencies]
diff = "0.1.0" diff = "0.1.0"
regex = "0.1" regex = "0.1"
term = "0.2"

View file

@ -13,14 +13,17 @@
extern crate rustfmt; extern crate rustfmt;
extern crate diff; extern crate diff;
extern crate regex; extern crate regex;
extern crate term;
use std::collections::HashMap; use std::collections::{VecDeque, HashMap};
use std::fs; use std::fs;
use std::io::{self, Read, BufRead, BufReader}; use std::io::{self, Read, BufRead, BufReader};
use std::thread; use std::thread;
use rustfmt::*; use rustfmt::*;
use rustfmt::config::Config; use rustfmt::config::Config;
static DIFF_CONTEXT_SIZE: usize = 3;
fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String { fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
let path = dir_entry.ok().expect("Couldn't get DirEntry.").path(); let path = dir_entry.ok().expect("Couldn't get DirEntry.").path();
@ -87,16 +90,40 @@ fn check_files<I>(files: I) -> (u32, u32)
(count, fails) (count, fails)
} }
fn print_mismatches(result: HashMap<String, String>) { fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
for (_, fmt_text) in result { let mut t = term::stdout().unwrap();
println!("{}", fmt_text);
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. // Ick, just needed to get a &'static to handle_result.
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result; static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;
pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> { pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, Vec<Mismatch>>> {
let sig_comments = read_significant_comments(&filename); let sig_comments = read_significant_comments(&filename);
let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..])); let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));
let args = vec!["rustfmt".to_owned(), filename]; let args = vec!["rustfmt".to_owned(), filename];
@ -179,10 +206,11 @@ fn handle_result(result: HashMap<String, String>) {
// TODO: speedup by running through bytes iterator // TODO: speedup by running through bytes iterator
f.read_to_string(&mut text).ok().expect("Failed reading target."); f.read_to_string(&mut text).ok().expect("Failed reading target.");
if fmt_text != text { if fmt_text != text {
let diff_str = make_diff(&file_name, &fmt_text, &text); let diff = make_diff(&fmt_text, &text, DIFF_CONTEXT_SIZE);
failures.insert(file_name, diff_str); failures.insert(file_name, diff);
} }
} }
if !failures.is_empty() { if !failures.is_empty() {
panic!(failures); 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 pub enum DiffLine {
// rustfmt on a given file Context(String),
fn make_diff(file_name: &str, expected: &str, actual: &str) -> String { Expected(String),
Resulting(String),
}
pub struct Mismatch {
line_number: u32,
pub lines: Vec<DiffLine>,
}
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<Mismatch> {
let mut line_number = 1; let mut line_number = 1;
let mut prev_both = true; let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
let mut text = String::new(); 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) { for result in diff::lines(expected, actual) {
match result { match result {
diff::Result::Left(str) => { diff::Result::Left(str) => {
if prev_both { if lines_since_mismatch >= context_size {
text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number)); 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) => { diff::Result::Right(str) => {
if prev_both { if lines_since_mismatch >= context_size {
text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number)); 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; 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; line_number += 1;
prev_both = true; lines_since_mismatch += 1;
} }
} }
} }
text results.push(mismatch);
results.remove(0);
results
} }