extern crate itertools;
use self::itertools::Itertools;
use super::super::*;
const CR_LF_ENDING: &'static str = "\r\n";
const CARRIAGE_RETURN_SYMBOL: &'static str = "\u{23ce}";
#[derive(Debug, Default, Clone, Copy)]
pub struct CheckWhitespace;
impl CheckWhitespace {
pub fn new() -> Self {
CheckWhitespace {}
}
}
impl Check for CheckWhitespace {
fn name(&self) -> &str {
"check-whitespace"
}
fn check(&self, ctx: &CheckGitContext, commit: &Commit) -> Result<CheckResult> {
let mut result = CheckResult::new();
let diff_tree = try!(ctx.git()
.arg("diff-tree")
.arg("--no-commit-id")
.arg("--root")
.arg("-c")
.arg("--check")
.arg(commit.sha1.as_str())
.output()
.chain_err(|| "failed to construct diff-tree command"));
if !diff_tree.status.success() {
let output = String::from_utf8_lossy(&diff_tree.stdout);
let crlf_msg = if output.contains(CR_LF_ENDING) {
" including CR/LF line endings"
} else {
""
};
let formatted_output = output.split('\n')
.dropping_back(1)
.map(|line| format!(" {}\n", line))
.join("")
.replace('\r', CARRIAGE_RETURN_SYMBOL);
result.add_error(format!("commit {} adds bad whitespace{}:\n\n{}",
commit.sha1_short,
crlf_msg,
formatted_output));
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::CheckWhitespace;
use super::super::test::*;
static DEFAULT_TOPIC: &'static str = "829cdf8cb069b8f8a634a034d3f85089271601cf";
static ALL_IGNORED_TOPIC: &'static str = "3a87e0f3f7430bbb81ebbd8ae8764b7f26384f1c";
static ALL_IGNORED_BLANKET_TOPIC: &'static str = "92cac7579a26f7d8449512476bd64b3000688fd5";
#[test]
fn test_check_whitespace_defaults() {
let check = CheckWhitespace::new();
let mut conf = GitCheckConfiguration::new();
conf.add_check(&check);
let result = test_check("test_check_whitespace_defaults", DEFAULT_TOPIC, &conf);
assert_eq!(result.warnings().len(), 0);
assert_eq!(result.alerts().len(), 0);
assert_eq!(result.errors().len(), 1);
assert_eq!(&result.errors()[0],
"commit 829cdf8 adds bad whitespace including CR/LF line endings:\n\
\n \
crlf-file:1: trailing whitespace.\n \
+This file contains CRLF lines.\u{23ce}\n \
crlf-file:2: trailing whitespace.\n \
+\u{23ce}\n \
crlf-file:3: trailing whitespace.\n \
+line1\u{23ce}\n \
crlf-file:4: trailing whitespace.\n \
+line2\u{23ce}\n \
crlf-mixed-file:3: trailing whitespace.\n \
+crlf\u{23ce}\n \
extra-newlines:2: new blank line at EOF.\n \
mixed-tabs-spaces:3: space before tab in indent.\n \
+ \tmixed indent\n \
trailing-spaces:3: trailing whitespace.\n \
+trailing \n \
trailing-tab:3: trailing whitespace.\n \
+trailing\t\n");
assert_eq!(result.allowed(), false);
assert_eq!(result.pass(), false);
}
#[test]
fn test_check_whitespace_all_ignored() {
let check = CheckWhitespace::new();
let mut conf = GitCheckConfiguration::new();
conf.add_check(&check);
let result = test_check("test_check_whitespace_all_ignored",
ALL_IGNORED_TOPIC,
&conf);
assert_eq!(result.warnings().len(), 0);
assert_eq!(result.alerts().len(), 0);
assert_eq!(result.errors().len(), 0);
assert_eq!(result.allowed(), false);
assert_eq!(result.pass(), true);
}
#[test]
fn test_check_whitespace_all_ignored_blanket() {
let check = CheckWhitespace::new();
let mut conf = GitCheckConfiguration::new();
conf.add_check(&check);
let result = test_check("test_check_whitespace_all_ignored_blanket",
ALL_IGNORED_BLANKET_TOPIC,
&conf);
assert_eq!(result.warnings().len(), 0);
assert_eq!(result.alerts().len(), 0);
assert_eq!(result.errors().len(), 0);
assert_eq!(result.allowed(), false);
assert_eq!(result.pass(), true);
}
}