about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/compiletest/src/main.rs1
-rw-r--r--src/tools/compiletest/src/runtest.rs35
-rw-r--r--src/tools/compiletest/src/uidiff.rs76
3 files changed, 98 insertions, 14 deletions
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index a9e6c454ffa..cc687b53204 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -50,6 +50,7 @@ pub mod runtest;
 pub mod common;
 pub mod errors;
 mod raise_fd_limit;
+mod uidiff;
 
 fn main() {
     #[cfg(cargobuild)]
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 9e4f331d16e..d97e96da16d 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -18,6 +18,7 @@ use header::TestProps;
 use header;
 use procsrv;
 use test::TestPaths;
+use uidiff;
 use util::logv;
 
 use std::env;
@@ -2115,8 +2116,8 @@ actual:\n\
         let normalized_stderr = self.normalize_output(&proc_res.stderr);
 
         let mut errors = 0;
-        errors += self.compare_output("stdout", normalized_stdout.as_bytes(), &expected_stdout);
-        errors += self.compare_output("stderr", normalized_stderr.as_bytes(), &expected_stderr);
+        errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
+        errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
 
         if errors > 0 {
             println!("To update references, run this command from build directory:");
@@ -2127,7 +2128,8 @@ actual:\n\
                      self.config.src_base.display(),
                      self.config.build_base.display(),
                      relative_path_to_file.display());
-            self.fatal(&format!("{} errors occurred comparing output.", errors));
+            self.fatal_proc_rec(&format!("{} errors occurred comparing output.", errors),
+                                &proc_res);
         }
     }
 
@@ -2135,7 +2137,9 @@ actual:\n\
         let parent_dir = self.testpaths.file.parent().unwrap();
         let parent_dir_str = parent_dir.display().to_string();
         output.replace(&parent_dir_str, "$DIR")
-              .replace("\\", "/") // windows, you know.
+              .replace("\\", "/") // normalize for paths on windows
+              .replace("\r\n", "\n") // normalize for linebreaks on windows
+              .replace("\t", "\\t") // makes tabs visible
     }
 
     fn expected_output_path(&self, kind: &str) -> PathBuf {
@@ -2146,13 +2150,13 @@ actual:\n\
         self.testpaths.file.with_extension(extension)
     }
 
-    fn load_expected_output(&self, path: &Path) -> Vec<u8> {
+    fn load_expected_output(&self, path: &Path) -> String {
         if !path.exists() {
-            return vec![];
+            return String::new();
         }
 
-        let mut result = Vec::new();
-        match File::open(path).and_then(|mut f| f.read_to_end(&mut result)) {
+        let mut result = String::new();
+        match File::open(path).and_then(|mut f| f.read_to_string(&mut result)) {
             Ok(_) => result,
             Err(e) => {
                 self.fatal(&format!("failed to load expected output from `{}`: {}", path.display(), e))
@@ -2160,17 +2164,20 @@ actual:\n\
         }
     }
 
-    fn compare_output(&self, kind: &str, actual: &[u8], expected: &[u8]) -> usize {
-        if self.config.verbose {
-            println!("normalized {}:\n{}\n", kind, str::from_utf8(actual).unwrap_or("not utf8"));
-            println!("expected {}:\n{}\n", kind, str::from_utf8(expected).unwrap_or("not utf8"));
-        }
+    fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
         if actual == expected {
             return 0;
         }
 
+        println!("normalized {}:\n{}\n", kind, actual);
+        println!("expected {}:\n{}\n", kind, expected);
+        println!("diff of {}:\n", kind);
+        for line in uidiff::diff_lines(actual, expected) {
+            println!("{}", line);
+        }
+
         let output_file = self.output_base_name().with_extension(kind);
-        match File::create(&output_file).and_then(|mut f| f.write_all(actual)) {
+        match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
             Ok(()) => { }
             Err(e) => {
                 self.fatal(&format!("failed to write {} to `{}`: {}",
diff --git a/src/tools/compiletest/src/uidiff.rs b/src/tools/compiletest/src/uidiff.rs
new file mode 100644
index 00000000000..66573393971
--- /dev/null
+++ b/src/tools/compiletest/src/uidiff.rs
@@ -0,0 +1,76 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code for checking whether the output of the compiler matches what is
+//! expected.
+
+pub fn diff_lines(actual: &str, expected: &str) -> Vec<String> {
+    // mega simplistic diff algorithm that just prints the things added/removed
+    zip_all(actual.lines(), expected.lines()).enumerate().filter_map(|(i, (a,e))| {
+        match (a, e) {
+            (Some(a), Some(e)) => {
+                if lines_match(e, a) {
+                    None
+                } else {
+                    Some(format!("{:3} - |{}|\n    + |{}|\n", i, e, a))
+                }
+            },
+            (Some(a), None) => {
+                Some(format!("{:3} -\n    + |{}|\n", i, a))
+            },
+            (None, Some(e)) => {
+                Some(format!("{:3} - |{}|\n    +\n", i, e))
+            },
+            (None, None) => panic!("Cannot get here")
+        }
+    }).collect()
+}
+
+fn lines_match(expected: &str, mut actual: &str) -> bool {
+    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("[..]")
+}
+
+struct ZipAll<I1: Iterator, I2: Iterator> {
+    first: I1,
+    second: I2,
+}
+
+impl<T, I1: Iterator<Item=T>, I2: Iterator<Item=T>> Iterator for ZipAll<I1, I2> {
+    type Item = (Option<T>, Option<T>);
+    fn next(&mut self) -> Option<(Option<T>, Option<T>)> {
+        let first = self.first.next();
+        let second = self.second.next();
+
+        match (first, second) {
+            (None, None) => None,
+            (a, b) => Some((a, b))
+        }
+    }
+}
+
+fn zip_all<T, I1: Iterator<Item=T>, I2: Iterator<Item=T>>(a: I1, b: I2) -> ZipAll<I1, I2> {
+    ZipAll {
+        first: a,
+        second: b,
+    }
+}