Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give a better error when rustdoc tests fail #78752

Merged
merged 8 commits into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/tools/compiletest/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,14 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
if serde_json::from_str::<FutureIncompatReport>(line).is_ok() {
vec![]
} else {
proc_res.fatal(Some(&format!(
"failed to decode compiler output as json: \
proc_res.fatal(
Some(&format!(
"failed to decode compiler output as json: \
`{}`\nline: {}\noutput: {}",
error, line, output
)));
error, line, output
)),
|| (),
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/compiletest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ pub fn run_tests(config: Config) {
}
Err(e) => {
// We don't know if tests passed or not, but if there was an error
// during testing we don't want to just suceeed (we may not have
// during testing we don't want to just succeed (we may not have
// tested something), so fail.
//
// This should realistically "never" happen, so don't try to make
Expand Down
143 changes: 138 additions & 5 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ pub fn compute_stamp_hash(config: &Config) -> String {
format!("{:x}", hash.finish())
}

#[derive(Copy, Clone)]
struct TestCx<'test> {
config: &'test Config,
props: &'test TestProps,
Expand Down Expand Up @@ -1728,7 +1729,7 @@ impl<'test> TestCx<'test> {
self.config.target.contains("vxworks") && !self.is_vxworks_pure_static()
}

fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
fn build_all_auxiliary(&self, rustc: &mut Command) -> PathBuf {
let aux_dir = self.aux_output_dir_name();

if !self.props.aux_builds.is_empty() {
Expand All @@ -1747,6 +1748,11 @@ impl<'test> TestCx<'test> {
rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir.display(), lib_name));
}

aux_dir
}

fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
let aux_dir = self.build_all_auxiliary(&mut rustc);
self.props.unset_rustc_env.clone().iter().fold(&mut rustc, |rustc, v| rustc.env_remove(v));
rustc.envs(self.props.rustc_env.clone());
self.compose_and_run(
Expand Down Expand Up @@ -2208,7 +2214,17 @@ impl<'test> TestCx<'test> {

fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
self.error(err);
proc_res.fatal(None);
proc_res.fatal(None, || ());
}

fn fatal_proc_rec_with_ctx(
jyn514 marked this conversation as resolved.
Show resolved Hide resolved
&self,
err: &str,
proc_res: &ProcRes,
on_failure: impl FnOnce(Self),
) -> ! {
self.error(err);
proc_res.fatal(None, || on_failure(*self));
}

// codegen tests (using FileCheck)
Expand Down Expand Up @@ -2325,15 +2341,131 @@ impl<'test> TestCx<'test> {
let res = self.cmd2procres(
Command::new(&self.config.docck_python)
.arg(root.join("src/etc/htmldocck.py"))
.arg(out_dir)
.arg(&out_dir)
.arg(&self.testpaths.file),
);
if !res.status.success() {
self.fatal_proc_rec("htmldocck failed!", &res);
self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| {
this.compare_to_default_rustdoc(&out_dir)
});
}
}
}

fn compare_to_default_rustdoc(&mut self, out_dir: &Path) {
println!("info: generating a diff against nightly rustdoc");

let suffix =
self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix));
// Don't give an error if the directory didn't already exist
let _ = fs::remove_dir_all(&compare_dir);
jyn514 marked this conversation as resolved.
Show resolved Hide resolved
create_dir_all(&compare_dir).unwrap();

// We need to create a new struct for the lifetimes on `config` to work.
let new_rustdoc = TestCx {
config: &Config {
// FIXME: use beta or a user-specified rustdoc instead of
// hardcoding the default toolchain
rustdoc_path: Some("rustdoc".into()),
// Needed for building auxiliary docs below
rustc_path: "rustc".into(),
..self.config.clone()
},
..*self
};

let output_file = TargetLocation::ThisDirectory(new_rustdoc.aux_output_dir_name());
let mut rustc = new_rustdoc.make_compile_args(
&new_rustdoc.testpaths.file,
output_file,
EmitMetadata::No,
AllowUnused::Yes,
);
rustc.arg("-L").arg(&new_rustdoc.aux_output_dir_name());
new_rustdoc.build_all_auxiliary(&mut rustc);

let proc_res = new_rustdoc.document(&compare_dir);
if !proc_res.status.success() {
proc_res.fatal(Some("failed to run nightly rustdoc"), || ());
}

#[rustfmt::skip]
let tidy_args = [
"--indent", "yes",
"--indent-spaces", "2",
"--wrap", "0",
"--show-warnings", "no",
"--markup", "yes",
"--quiet", "yes",
"-modify",
];
let tidy_dir = |dir| {
let tidy = |file: &_| {
Command::new("tidy")
.args(&tidy_args)
.arg(file)
.spawn()
.unwrap_or_else(|err| {
self.fatal(&format!("failed to run tidy - is it installed? - {}", err))
})
.wait()
.unwrap()
};
for entry in walkdir::WalkDir::new(dir) {
let entry = entry.expect("failed to read file");
if entry.file_type().is_file()
&& entry.path().extension().and_then(|p| p.to_str()) == Some("html".into())
{
tidy(entry.path());
}
}
};
tidy_dir(out_dir);
tidy_dir(&compare_dir);

let pager = {
let output = Command::new("git").args(&["config", "--get", "core.pager"]).output().ok();
output.and_then(|out| {
if out.status.success() {
Some(String::from_utf8(out.stdout).expect("invalid UTF8 in git pager"))
} else {
None
}
})
};
let mut diff = Command::new("diff");
diff.args(&["-u", "-r"]).args(&[out_dir, &compare_dir]);

let output = if let Some(pager) = pager {
let diff_pid = diff.stdout(Stdio::piped()).spawn().expect("failed to run `diff`");
let pager = pager.trim();
if self.config.verbose {
eprintln!("using pager {}", pager);
}
let output = Command::new(pager)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can pager be like diff -u (contain arguments)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, Command::new runs it as if it were in quotes.

// disable paging; we want this to be non-interactive
.env("PAGER", "")
.stdin(diff_pid.stdout.unwrap())
// Capture output and print it explicitly so it will in turn be
// captured by libtest.
.output()
.unwrap();
assert!(output.status.success());
output
} else {
eprintln!("warning: no pager configured, falling back to `diff --color`");
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
};
println!("{}", String::from_utf8_lossy(&output.stdout));
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
}

fn get_lines<P: AsRef<Path>>(
&self,
path: &P,
Expand Down Expand Up @@ -3590,7 +3722,7 @@ pub struct ProcRes {
}

impl ProcRes {
pub fn fatal(&self, err: Option<&str>) -> ! {
pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
if let Some(e) = err {
println!("\nerror: {}", e);
}
Expand All @@ -3612,6 +3744,7 @@ impl ProcRes {
json::extract_rendered(&self.stdout),
json::extract_rendered(&self.stderr),
);
on_failure();
// Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
// compiletest, which is unnecessary noise.
std::panic::resume_unwind(Box::new(()));
Expand Down