Skip to content

Commit

Permalink
Rollup merge of #89477 - Nicholas-Baron:compute_diff_rs, r=Mark-Simul…
Browse files Browse the repository at this point in the history
…acrum

Move items related to computing diffs to a separate file

Work towards #89475.
  • Loading branch information
GuillaumeGomez authored Oct 7, 2021
2 parents ab276b8 + 3760c91 commit 48548c9
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 142 deletions.
157 changes: 157 additions & 0 deletions src/tools/compiletest/src/compute_diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use std::collections::VecDeque;
use std::fs::{File, FileType};
use std::path::Path;

#[derive(Debug, PartialEq)]
pub enum DiffLine {
Context(String),
Expected(String),
Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
pub line_number: u32,
pub lines: Vec<DiffLine>,
}

impl Mismatch {
fn new(line_number: u32) -> Mismatch {
Mismatch { line_number, lines: Vec::new() }
}
}

// Produces a diff between the expected output and actual output.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
let mut line_number = 1;
let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
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) {
match result {
diff::Result::Left(str) => {
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
results.push(mismatch);
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
}

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;
lines_since_mismatch = 0;
}
diff::Result::Right(str) => {
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
results.push(mismatch);
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
}

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::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 if context_size > 0 {
context_queue.push_back(str);
}

line_number += 1;
lines_since_mismatch += 1;
}
}
}

results.push(mismatch);
results.remove(0);

results
}

pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
use std::fmt::Write;
let mut output = String::new();
let diff_results = make_diff(expected, actual, context_size);
for result in diff_results {
let mut line_number = result.line_number;
for line in result.lines {
match line {
DiffLine::Expected(e) => {
writeln!(output, "-\t{}", e).unwrap();
line_number += 1;
}
DiffLine::Context(c) => {
writeln!(output, "{}\t{}", line_number, c).unwrap();
line_number += 1;
}
DiffLine::Resulting(r) => {
writeln!(output, "+\t{}", r).unwrap();
}
}
}
writeln!(output).unwrap();
}
output
}

/// Filters based on filetype and extension whether to diff a file.
///
/// Returns whether any data was actually written.
pub(crate) fn write_filtered_diff<Filter>(
diff_filename: &str,
out_dir: &Path,
compare_dir: &Path,
verbose: bool,
filter: Filter,
) -> bool
where
Filter: Fn(FileType, Option<&str>) -> bool,
{
use std::io::{Read, Write};
let mut diff_output = File::create(diff_filename).unwrap();
let mut wrote_data = false;
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 filter(entry.file_type(), extension) {
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();
let diff = unified_diff::diff(
&expected,
&expected_path.to_string_lossy(),
&actual,
&actual_path.to_string_lossy(),
3,
);
wrote_data |= !diff.is_empty();
diff_output.write_all(&diff).unwrap();
}
}

if !wrote_data {
println!("note: diff is identical to nightly rustdoc");
assert!(diff_output.metadata().unwrap().len() == 0);
return false;
} else if verbose {
eprintln!("printing diff:");
let mut buf = Vec::new();
diff_output.read_to_end(&mut buf).unwrap();
std::io::stderr().lock().write_all(&mut buf).unwrap();
}
true
}
1 change: 1 addition & 0 deletions src/tools/compiletest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use self::header::{make_test_description, EarlyProps};
mod tests;

pub mod common;
pub mod compute_diff;
pub mod errors;
pub mod header;
mod json;
Expand Down
154 changes: 12 additions & 142 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::common::{CompareMode, FailMode, PassMode};
use crate::common::{Config, TestPaths};
use crate::common::{Pretty, RunPassValgrind};
use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
use crate::compute_diff::{write_diff, write_filtered_diff};
use crate::errors::{self, Error, ErrorKind};
use crate::header::TestProps;
use crate::json;
Expand All @@ -18,7 +19,7 @@ use regex::{Captures, Regex};
use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};

use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet, VecDeque};
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs::{self, create_dir_all, File, OpenOptions};
Expand Down Expand Up @@ -100,111 +101,6 @@ pub fn get_lib_name(lib: &str, dylib: bool) -> String {
}
}

#[derive(Debug, PartialEq)]
pub enum DiffLine {
Context(String),
Expected(String),
Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
pub line_number: u32,
pub lines: Vec<DiffLine>,
}

impl Mismatch {
fn new(line_number: u32) -> Mismatch {
Mismatch { line_number, lines: Vec::new() }
}
}

// Produces a diff between the expected output and actual output.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
let mut line_number = 1;
let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
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) {
match result {
diff::Result::Left(str) => {
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
results.push(mismatch);
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
}

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;
lines_since_mismatch = 0;
}
diff::Result::Right(str) => {
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
results.push(mismatch);
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
}

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::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 if context_size > 0 {
context_queue.push_back(str);
}

line_number += 1;
lines_since_mismatch += 1;
}
}
}

results.push(mismatch);
results.remove(0);

results
}

fn write_diff(expected: &str, actual: &str, context_size: usize) -> String {
use std::fmt::Write;
let mut output = String::new();
let diff_results = make_diff(expected, actual, context_size);
for result in diff_results {
let mut line_number = result.line_number;
for line in result.lines {
match line {
DiffLine::Expected(e) => {
writeln!(output, "-\t{}", e).unwrap();
line_number += 1;
}
DiffLine::Context(c) => {
writeln!(output, "{}\t{}", line_number, c).unwrap();
line_number += 1;
}
DiffLine::Resulting(r) => {
writeln!(output, "+\t{}", r).unwrap();
}
}
}
writeln!(output).unwrap();
}
output
}

pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
match &*config.target {
"arm-linux-androideabi"
Expand Down Expand Up @@ -2507,43 +2403,17 @@ impl<'test> TestCx<'test> {

let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());

{
let mut diff_output = File::create(&diff_filename).unwrap();
let mut wrote_data = false;
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()
if !write_filtered_diff(
&diff_filename,
out_dir,
&compare_dir,
self.config.verbose,
|file_type, extension| {
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();
let diff = unified_diff::diff(
&expected,
&expected_path.to_string_lossy(),
&actual,
&actual_path.to_string_lossy(),
3,
);
wrote_data |= !diff.is_empty();
diff_output.write_all(&diff).unwrap();
}
}

if !wrote_data {
println!("note: diff is identical to nightly rustdoc");
assert!(diff_output.metadata().unwrap().len() == 0);
return;
} else if self.config.verbose {
eprintln!("printing diff:");
let mut buf = Vec::new();
diff_output.read_to_end(&mut buf).unwrap();
std::io::stderr().lock().write_all(&mut buf).unwrap();
}
},
) {
return;
}

match self.config.color {
Expand Down

0 comments on commit 48548c9

Please sign in to comment.