about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2021-02-23 23:42:42 -0700
committerMichael Howell <michael@notriddle.com>2021-03-01 08:48:43 -0700
commitb29d9d5503ff69caa7885e088e23f5467eb3d10a (patch)
tree07b74f27bcaab49b498f1663d665f2e68fbc7b1b
parent09db05762b283bed62d4f92729cfee4646519833 (diff)
downloadrust-b29d9d5503ff69caa7885e088e23f5467eb3d10a.tar.gz
rust-b29d9d5503ff69caa7885e088e23f5467eb3d10a.zip
Use a crate to produce rustdoc tree comparisons instead of the `diff` command
It doesn't exist on Windows, so we bring our own unified diff implementation.

Fixes #82409
-rw-r--r--Cargo.lock11
-rw-r--r--src/tools/compiletest/Cargo.toml2
-rw-r--r--src/tools/compiletest/src/runtest.rs82
3 files changed, 82 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6e95fd6af27..508d4b3063e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -676,6 +676,7 @@ dependencies = [
 name = "compiletest"
 version = "0.0.0"
 dependencies = [
+ "colored",
  "diff",
  "getopts",
  "glob",
@@ -688,6 +689,7 @@ dependencies = [
  "serde_json",
  "tracing",
  "tracing-subscriber",
+ "unified-diff",
  "walkdir",
  "winapi 0.3.9",
 ]
@@ -5518,6 +5520,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
+name = "unified-diff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "496a3d395ed0c30f411ceace4a91f7d93b148fb5a9b383d5d4cff7850f048d5f"
+dependencies = [
+ "diff",
+]
+
+[[package]]
 name = "unstable-book-gen"
 version = "0.1.0"
 dependencies = [
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index 209254a5d5e..1ab560ac09d 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -5,7 +5,9 @@ version = "0.0.0"
 edition = "2018"
 
 [dependencies]
+colored = "2"
 diff = "0.1.10"
+unified-diff = "0.2.1"
 getopts = "0.2"
 tracing = "0.1"
 tracing-subscriber = { version = "0.2.13", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 723c7f86832..2e7b42b6c7c 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -13,6 +13,7 @@ use crate::header::TestProps;
 use crate::json;
 use crate::util::get_pointer_width;
 use crate::util::{logv, PathBufExt};
+use crate::ColorConfig;
 use regex::{Captures, Regex};
 use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
 
@@ -2440,12 +2441,43 @@ impl<'test> TestCx<'test> {
                 }
             })
         };
-        let mut diff = Command::new("diff");
-        // diff recursively, showing context, and excluding .css files
-        diff.args(&["-u", "-r", "-x", "*.css"]).args(&[&compare_dir, out_dir]);
 
-        let output = if let Some(pager) = pager {
-            let diff_pid = diff.stdout(Stdio::piped()).spawn().expect("failed to run `diff`");
+        let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());
+
+        {
+            let mut diff_output = File::create(&diff_filename).unwrap();
+            for entry in walkdir::WalkDir::new(out_dir) {
+                let entry = entry.expect("failed to read file");
+                let extension = entry.path().extension().and_then(|p| p.to_str());
+                if entry.file_type().is_file()
+                    && (extension == Some("html".into()) || extension == Some("js".into()))
+                {
+                    let expected_path =
+                        compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap());
+                    let expected =
+                        if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
+                    let actual_path = entry.path();
+                    let actual = std::fs::read(&actual_path).unwrap();
+                    diff_output
+                        .write_all(&unified_diff::diff(
+                            &expected,
+                            &expected_path.to_string_lossy(),
+                            &actual,
+                            &actual_path.to_string_lossy(),
+                            3,
+                        ))
+                        .unwrap();
+                }
+            }
+        }
+
+        match self.config.color {
+            ColorConfig::AlwaysColor => colored::control::set_override(true),
+            ColorConfig::NeverColor => colored::control::set_override(false),
+            _ => {}
+        }
+
+        if let Some(pager) = pager {
             let pager = pager.trim();
             if self.config.verbose {
                 eprintln!("using pager {}", pager);
@@ -2453,24 +2485,48 @@ impl<'test> TestCx<'test> {
             let output = Command::new(pager)
                 // disable paging; we want this to be non-interactive
                 .env("PAGER", "")
-                .stdin(diff_pid.stdout.unwrap())
+                .stdin(File::open(&diff_filename).unwrap())
                 // Capture output and print it explicitly so it will in turn be
                 // captured by libtest.
                 .output()
                 .unwrap();
             assert!(output.status.success());
-            output
+            println!("{}", String::from_utf8_lossy(&output.stdout));
+            eprintln!("{}", String::from_utf8_lossy(&output.stderr));
         } else {
-            eprintln!("warning: no pager configured, falling back to `diff --color`");
+            use colored::Colorize;
+            eprintln!("warning: no pager configured, falling back to unified diff");
             eprintln!(
                 "help: try configuring a git pager (e.g. `delta`) with `git config --global core.pager delta`"
             );
-            let output = diff.arg("--color").output().unwrap();
-            assert!(output.status.success() || output.status.code() == Some(1));
-            output
+            let mut out = io::stdout();
+            let mut diff = BufReader::new(File::open(&diff_filename).unwrap());
+            let mut line = Vec::new();
+            loop {
+                line.truncate(0);
+                match diff.read_until(b'\n', &mut line) {
+                    Ok(0) => break,
+                    Ok(_) => {}
+                    Err(e) => eprintln!("ERROR: {:?}", e),
+                }
+                match String::from_utf8(line.clone()) {
+                    Ok(line) => {
+                        if line.starts_with("+") {
+                            write!(&mut out, "{}", line.green()).unwrap();
+                        } else if line.starts_with("-") {
+                            write!(&mut out, "{}", line.red()).unwrap();
+                        } else if line.starts_with("@") {
+                            write!(&mut out, "{}", line.blue()).unwrap();
+                        } else {
+                            out.write_all(line.as_bytes()).unwrap();
+                        }
+                    }
+                    Err(_) => {
+                        write!(&mut out, "{}", String::from_utf8_lossy(&line).reversed()).unwrap();
+                    }
+                }
+            }
         };
-        println!("{}", String::from_utf8_lossy(&output.stdout));
-        eprintln!("{}", String::from_utf8_lossy(&output.stderr));
     }
 
     fn run_rustdoc_json_test(&self) {