]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #41588 - cengizIO:master, r=nikomatsakis
authorbors <bors@rust-lang.org>
Sat, 29 Apr 2017 12:59:45 +0000 (12:59 +0000)
committerbors <bors@rust-lang.org>
Sat, 29 Apr 2017 12:59:45 +0000 (12:59 +0000)
use diff crate for compile-fail test diagnostics #41474

Hello!

This fixes #41474

We were using a custom implementation to dump the differences between expected and actual outputs of compile-fail tests.

I removed this internal implementation and added `diff` crate as a new dependency to `compile-fail`.

Again, huge thanks to @nikomatsakis for guiding.

1  2 
src/Cargo.lock
src/tools/compiletest/src/main.rs
src/tools/compiletest/src/runtest.rs

diff --combined src/Cargo.lock
index 5d97ccaabbf02ee57252f03363d2a8e844cb39b1,9ff558a8398e392364fda7743567c1c356a3b2ae..21b167f6d42739050e38297991794ab3ef96a6f9
@@@ -78,7 -78,7 +78,7 @@@ dependencies = 
   "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
   "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
   "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
 - "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
 + "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
   "rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
   "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
@@@ -147,6 -147,7 +147,7 @@@ dependencies = 
  name = "compiletest"
  version = "0.0.0"
  dependencies = [
+  "diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
   "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
   "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
   "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  name = "core"
  version = "0.0.0"
  
+ [[package]]
+ name = "diff"
+ version = "0.1.10"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
  [[package]]
  name = "dtoa"
  version = "0.4.1"
@@@ -269,7 -275,7 +275,7 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "mdbook"
 -version = "0.0.19"
 +version = "0.0.21"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -299,7 -305,7 +305,7 @@@ source = "registry+https://github.com/r
  
  [[package]]
  name = "num_cpus"
 -version = "0.2.13"
 +version = "1.4.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  dependencies = [
   "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@@ -366,6 -372,14 +372,6 @@@ dependencies = 
   "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
 -[[package]]
 -name = "qemu-test-client"
 -version = "0.1.0"
 -
 -[[package]]
 -name = "qemu-test-server"
 -version = "0.1.0"
 -
  [[package]]
  name = "quick-error"
  version = "1.1.0"
@@@ -395,14 -409,6 +401,14 @@@ name = "regex-syntax
  version = "0.4.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  
 +[[package]]
 +name = "remote-test-client"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "remote-test-server"
 +version = "0.1.0"
 +
  [[package]]
  name = "rls-data"
  version = "0.1.0"
@@@ -425,7 -431,7 +431,7 @@@ name = "rustbook
  version = "0.1.0"
  dependencies = [
   "clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
 - "mdbook 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
 + "mdbook 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
  ]
  
  [[package]]
@@@ -990,6 -996,7 +996,7 @@@ source = "registry+https://github.com/r
  "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
  "checksum clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e17a4a72ffea176f77d6e2db609c6c919ef221f23862c9915e687fb54d833485"
  "checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac"
+ "checksum diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0a515461b6c8c08419850ced27bc29e86166dcdcde8fbe76f8b1f0589bb49472"
  "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
  "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
  "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
  "checksum lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4732c563b9a21a406565c4747daa7b46742f082911ae4753f390dc9ec7ee1a97"
  "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
  "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
 -"checksum mdbook 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "2598843aeda0c5bb2e8e4d714564f1c3fc40f7844157e34563bf96ae3866b56e"
 +"checksum mdbook 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2e9d848514dcfad4195788d0d42ae5153a477c191d75d5b84fab10f222fbd"
  "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
  "checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
 -"checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3"
 +"checksum num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca313f1862c7ec3e0dfe8ace9fa91b1d9cb5c84ace3d00f5ec4216238e93c167"
  "checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
  "checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8"
  "checksum pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ab1e588ef8efd702c7ed9d2bd774db5e6f4d878bb5a1a9f371828fbdff6973"
index 0e4901ef1aba661b74a5971774f9218b9951a640,1506581a4b40f2b77babfbb6d54b8537c691fa79..4165ea685a5b2b87646e215de6f4ea9a40e00e96
@@@ -25,6 -25,7 +25,7 @@@ extern crate rustc_serialize
  extern crate log;
  extern crate env_logger;
  extern crate filetime;
+ extern crate diff;
  
  use std::env;
  use std::ffi::OsString;
@@@ -49,7 -50,6 +50,6 @@@ pub mod runtest
  pub mod common;
  pub mod errors;
  mod raise_fd_limit;
- mod uidiff;
  
  fn main() {
      env_logger::init().unwrap();
@@@ -106,7 -106,7 +106,7 @@@ pub fn parse_config(args: Vec<String> 
            reqopt("", "llvm-components", "list of LLVM components built in", "LIST"),
            reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS"),
            optopt("", "nodejs", "the name of nodejs", "PATH"),
 -          optopt("", "qemu-test-client", "path to the qemu test client", "PATH"),
 +          optopt("", "remote-test-client", "path to the remote test client", "PATH"),
            optflag("h", "help", "show this message")];
  
      let (argv0, args_) = args.split_first().unwrap();
          llvm_version: matches.opt_str("llvm-version"),
          android_cross_path: opt_path(matches, "android-cross-path"),
          adb_path: opt_str2(matches.opt_str("adb-path")),
 -        adb_test_dir: format!("{}/{}",
 -            opt_str2(matches.opt_str("adb-test-dir")),
 -            opt_str2(matches.opt_str("target"))),
 +        adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
          adb_device_status:
              opt_str2(matches.opt_str("target")).contains("android") &&
              "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
          lldb_python_dir: matches.opt_str("lldb-python-dir"),
          verbose: matches.opt_present("verbose"),
          quiet: matches.opt_present("quiet"),
 -        qemu_test_client: matches.opt_str("qemu-test-client").map(PathBuf::from),
 +        remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
  
          cc: matches.opt_str("cc").unwrap(),
          cxx: matches.opt_str("cxx").unwrap(),
@@@ -250,14 -252,27 +250,14 @@@ pub fn run_tests(config: &Config) 
          if let DebugInfoGdb = config.mode {
              println!("{} debug-info test uses tcp 5039 port.\
                       please reserve it", config.target);
 -        }
 -
 -        // android debug-info test uses remote debugger
 -        // so, we test 1 thread at once.
 -        // also trying to isolate problems with adb_run_wrapper.sh ilooping
 -        match config.mode {
 -            // These tests don't actually run code or don't run for android, so
 -            // we don't need to limit ourselves there
 -            Mode::Ui |
 -            Mode::CompileFail |
 -            Mode::ParseFail |
 -            Mode::RunMake |
 -            Mode::Codegen |
 -            Mode::CodegenUnits |
 -            Mode::Pretty |
 -            Mode::Rustdoc => {}
 -
 -            _ => {
 -                env::set_var("RUST_TEST_THREADS", "1");
 -            }
  
 +            // android debug-info test uses remote debugger so, we test 1 thread
 +            // at once as they're all sharing the same TCP port to communicate
 +            // over.
 +            //
 +            // we should figure out how to lift this restriction! (run them all
 +            // on different ports allocated dynamically).
 +            env::set_var("RUST_TEST_THREADS", "1");
          }
      }
  
          }
  
          DebugInfoGdb => {
 -            if config.qemu_test_client.is_some() {
 +            if config.remote_test_client.is_some() &&
 +               !config.target.contains("android"){
                  println!("WARNING: debuginfo tests are not available when \
 -                          testing with QEMU");
 +                          testing with remote");
                  return
              }
          }
index d38b1d61ddd94d6126c698e3b3e235d7d3c78a87,0bab518fccb8b5bc0cf27965f67a9ec098aba97e..731665ce034582770bb2ddf87967ebe4a9dc616b
@@@ -12,17 -12,19 +12,17 @@@ use common::Config
  use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
  use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
  use common::{Incremental, RunMake, Ui, MirOpt};
+ use diff;
  use errors::{self, ErrorKind, Error};
  use filetime::FileTime;
  use json;
  use header::TestProps;
 -use header;
  use procsrv;
  use test::TestPaths;
- use uidiff;
  use util::logv;
  
  use std::collections::HashSet;
  use std::env;
 -use std::fmt;
  use std::fs::{self, File, create_dir_all};
  use std::io::prelude::*;
  use std::io::{self, BufReader};
@@@ -55,7 -57,7 +55,7 @@@ pub fn run(config: Config, testpaths: &
          print!("\n\n");
      }
      debug!("running {:?}", testpaths.file.display());
 -    let base_props = TestProps::from_file(&testpaths.file);
 +    let base_props = TestProps::from_file(&testpaths.file, &config);
  
      let base_cx = TestCx { config: &config,
                             props: &base_props,
@@@ -68,7 -70,7 +68,7 @@@
      } else {
          for revision in &base_props.revisions {
              let mut revision_props = base_props.clone();
 -            revision_props.load_from(&testpaths.file, Some(&revision));
 +            revision_props.load_from(&testpaths.file, Some(&revision), &config);
              let rev_cx = TestCx {
                  config: &config,
                  props: &revision_props,
@@@ -467,9 -469,7 +467,9 @@@ actual:\n
  
          let debugger_run_result;
          match &*self.config.target {
 -            "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
 +            "arm-linux-androideabi" |
 +            "armv7-linux-androideabi" |
 +            "aarch64-linux-android" => {
  
                  cmds = cmds.replace("run", "continue");
  
                                        exe_file.file_name().unwrap().to_str()
                                        .unwrap());
  
 +                debug!("adb arg: {}", adb_arg);
                  let mut process = procsrv::run_background("",
                                                            &self.config.adb_path
                                                            ,
                  };
  
                  debugger_run_result = ProcRes {
 -                    status: Status::Normal(status),
 +                    status: status,
                      stdout: out,
                      stderr: err,
                      cmdline: cmdline
  
          self.dump_output(&out, &err);
          ProcRes {
 -            status: Status::Normal(status),
 +            status: status,
              stdout: out,
              stderr: err,
              cmdline: format!("{:?}", cmd)
                      }
  
                      for &(ref command_directive, ref check_directive) in &directives {
 -                        header::parse_name_value_directive(
 +                        self.config.parse_name_value_directive(
                              &line,
                              &command_directive).map(|cmd| {
                                  commands.push(cmd)
                              });
  
 -                        header::parse_name_value_directive(
 +                        self.config.parse_name_value_directive(
                              &line,
                              &check_directive).map(|cmd| {
                                  check_lines.push(cmd)
          if self.props.build_aux_docs {
              for rel_ab in &self.props.aux_builds {
                  let aux_testpaths = self.compute_aux_test_paths(rel_ab);
 -                let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
 +                let aux_props = self.props.from_aux_file(&aux_testpaths.file,
 +                                                         self.revision,
 +                                                         self.config);
                  let aux_cx = TestCx {
                      config: self.config,
                      props: &aux_props,
          let env = self.props.exec_env.clone();
  
          match &*self.config.target {
 -
 -            "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
 -                self._arm_exec_compiled_test(env)
 -            }
 -
              // This is pretty similar to below, we're transforming:
              //
              //      program arg1 arg2
              //
              // into
              //
 -            //      qemu-test-client run program:support-lib.so arg1 arg2
 +            //      remote-test-client run program:support-lib.so arg1 arg2
              //
              // The test-client program will upload `program` to the emulator
              // along with all other support libraries listed (in this case
              // `support-lib.so`. It will then execute the program on the
              // emulator with the arguments specified (in the environment we give
              // the process) and then report back the same result.
 -            _ if self.config.qemu_test_client.is_some() => {
 +            _ if self.config.remote_test_client.is_some() => {
                  let aux_dir = self.aux_output_dir_name();
                  let mut args = self.make_run_args();
                  let mut program = args.prog.clone();
                  }
                  args.args.insert(0, program);
                  args.args.insert(0, "run".to_string());
 -                args.prog = self.config.qemu_test_client.clone().unwrap()
 +                args.prog = self.config.remote_test_client.clone().unwrap()
                                  .into_os_string().into_string().unwrap();
                  self.compose_and_run(args,
                                       env,
  
          for rel_ab in &self.props.aux_builds {
              let aux_testpaths = self.compute_aux_test_paths(rel_ab);
 -            let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
 +            let aux_props = self.props.from_aux_file(&aux_testpaths.file,
 +                                                     self.revision,
 +                                                     self.config);
              let mut crate_type = if aux_props.no_prefer_dynamic {
                  Vec::new()
              } else {
                               aux_testpaths.file.display()),
                      &auxres);
              }
 -
 -            match &*self.config.target {
 -                "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
 -                    self._arm_push_aux_shared_library();
 -                }
 -                _ => {}
 -            }
          }
  
          self.compose_and_run(args,
                           input).expect(&format!("failed to exec `{}`", prog));
          self.dump_output(&out, &err);
          return ProcRes {
 -            status: Status::Normal(status),
 +            status: status,
              stdout: out,
              stderr: err,
              cmdline: cmdline,
          println!("---------------------------------------------------");
      }
  
 -    fn _arm_exec_compiled_test(&self, env: Vec<(String, String)>) -> ProcRes {
 -        let args = self.make_run_args();
 -        let cmdline = self.make_cmdline("", &args.prog, &args.args);
 -
 -        // get bare program string
 -        let mut tvec: Vec<String> = args.prog
 -                                        .split('/')
 -                                        .map(str::to_owned)
 -                                        .collect();
 -        let prog_short = tvec.pop().unwrap();
 -
 -        // copy to target
 -        let copy_result = procsrv::run("",
 -                                       &self.config.adb_path,
 -                                       None,
 -                                       &[
 -                                           "push".to_owned(),
 -                                           args.prog.clone(),
 -                                           self.config.adb_test_dir.clone()
 -                                       ],
 -                                       vec![("".to_owned(), "".to_owned())],
 -                                       Some("".to_owned()))
 -            .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -        if self.config.verbose {
 -            println!("push ({}) {} {} {}",
 -                     self.config.target,
 -                     args.prog,
 -                     copy_result.out,
 -                     copy_result.err);
 -        }
 -
 -        logv(self.config, format!("executing ({}) {}", self.config.target, cmdline));
 -
 -        let mut runargs = Vec::new();
 -
 -        // run test via adb_run_wrapper
 -        runargs.push("shell".to_owned());
 -        for (key, val) in env {
 -            runargs.push(format!("{}={}", key, val));
 -        }
 -        runargs.push(format!("{}/../adb_run_wrapper.sh", self.config.adb_test_dir));
 -        runargs.push(format!("{}", self.config.adb_test_dir));
 -        runargs.push(format!("{}", prog_short));
 -
 -        for tv in &args.args {
 -            runargs.push(tv.to_owned());
 -        }
 -        procsrv::run("",
 -                     &self.config.adb_path,
 -                     None,
 -                     &runargs,
 -                     vec![("".to_owned(), "".to_owned())], Some("".to_owned()))
 -            .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -        // get exitcode of result
 -        runargs = Vec::new();
 -        runargs.push("shell".to_owned());
 -        runargs.push("cat".to_owned());
 -        runargs.push(format!("{}/{}.exitcode", self.config.adb_test_dir, prog_short));
 -
 -        let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
 -            procsrv::run("",
 -                         &self.config.adb_path,
 -                         None,
 -                         &runargs,
 -                         vec![("".to_owned(), "".to_owned())],
 -                         Some("".to_owned()))
 -            .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -        let mut exitcode: i32 = 0;
 -        for c in exitcode_out.chars() {
 -            if !c.is_numeric() { break; }
 -            exitcode = exitcode * 10 + match c {
 -                '0' ... '9' => c as i32 - ('0' as i32),
 -                _ => 101,
 -            }
 -        }
 -
 -        // get stdout of result
 -        runargs = Vec::new();
 -        runargs.push("shell".to_owned());
 -        runargs.push("cat".to_owned());
 -        runargs.push(format!("{}/{}.stdout", self.config.adb_test_dir, prog_short));
 -
 -        let procsrv::Result{ out: stdout_out, err: _, status: _ } =
 -            procsrv::run("",
 -                         &self.config.adb_path,
 -                         None,
 -                         &runargs,
 -                         vec![("".to_owned(), "".to_owned())],
 -                         Some("".to_owned()))
 -            .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -        // get stderr of result
 -        runargs = Vec::new();
 -        runargs.push("shell".to_owned());
 -        runargs.push("cat".to_owned());
 -        runargs.push(format!("{}/{}.stderr", self.config.adb_test_dir, prog_short));
 -
 -        let procsrv::Result{ out: stderr_out, err: _, status: _ } =
 -            procsrv::run("",
 -                         &self.config.adb_path,
 -                         None,
 -                         &runargs,
 -                         vec![("".to_owned(), "".to_owned())],
 -                         Some("".to_owned()))
 -            .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -        self.dump_output(&stdout_out, &stderr_out);
 -
 -        ProcRes {
 -            status: Status::Parsed(exitcode),
 -            stdout: stdout_out,
 -            stderr: stderr_out,
 -            cmdline: cmdline
 -        }
 -    }
 -
 -    fn _arm_push_aux_shared_library(&self) {
 -        let tdir = self.aux_output_dir_name();
 -
 -        let dirs = fs::read_dir(&tdir).unwrap();
 -        for file in dirs {
 -            let file = file.unwrap().path();
 -            if file.extension().and_then(|s| s.to_str()) == Some("so") {
 -                // FIXME (#9639): This needs to handle non-utf8 paths
 -                let copy_result = procsrv::run("",
 -                                               &self.config.adb_path,
 -                                               None,
 -                                               &[
 -                                                   "push".to_owned(),
 -                                                   file.to_str()
 -                                                       .unwrap()
 -                                                       .to_owned(),
 -                                                   self.config.adb_test_dir.to_owned(),
 -                                               ],
 -                                               vec![("".to_owned(),
 -                                                     "".to_owned())],
 -                                               Some("".to_owned()))
 -                    .expect(&format!("failed to exec `{}`", self.config.adb_path));
 -
 -                if self.config.verbose {
 -                    println!("push ({}) {:?} {} {}",
 -                             self.config.target, file.display(),
 -                             copy_result.out, copy_result.err);
 -                }
 -            }
 -        }
 -    }
 -
      // codegen tests (using FileCheck)
  
      fn compile_test_and_save_ir(&self) -> ProcRes {
          let output = cmd.output().expect("failed to spawn `make`");
          if !output.status.success() {
              let res = ProcRes {
 -                status: Status::Normal(output.status),
 +                status: output.status,
                  stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
                  stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
                  cmdline: format!("{:?}", cmd),
          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);
+         for diff in diff::lines(actual, expected) {
+             match diff {
+                 diff::Result::Left(l)    => println!("+{}", l),
+                 diff::Result::Both(l, _) => println!(" {}", l),
+                 diff::Result::Right(r)   => println!("-{}", r),
+             }
          }
  
          let output_file = self.output_base_name().with_extension(kind);
@@@ -2436,12 -2599,17 +2441,12 @@@ struct ProcArgs 
  }
  
  pub struct ProcRes {
 -    status: Status,
 +    status: ExitStatus,
      stdout: String,
      stderr: String,
      cmdline: String,
  }
  
 -enum Status {
 -    Parsed(i32),
 -    Normal(ExitStatus),
 -}
 -
  impl ProcRes {
      pub fn fatal(&self, err: Option<&str>) -> ! {
          if let Some(e) = err {
      }
  }
  
 -impl Status {
 -    fn code(&self) -> Option<i32> {
 -        match *self {
 -            Status::Parsed(i) => Some(i),
 -            Status::Normal(ref e) => e.code(),
 -        }
 -    }
 -
 -    fn success(&self) -> bool {
 -        match *self {
 -            Status::Parsed(i) => i == 0,
 -            Status::Normal(ref e) => e.success(),
 -        }
 -    }
 -}
 -
 -impl fmt::Display for Status {
 -    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 -        match *self {
 -            Status::Parsed(i) => write!(f, "exit code: {}", i),
 -            Status::Normal(ref e) => e.fmt(f),
 -        }
 -    }
 -}
 -
  enum TargetLocation {
      ThisFile(PathBuf),
      ThisDirectory(PathBuf),