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

Allow checking of run-pass execution output in compiletest #63825

Merged
merged 7 commits into from
Sep 4, 2019
84 changes: 2 additions & 82 deletions src/test/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// run-pass
// ignore-cloudabi no processes
// ignore-emscripten no processes
// ignore-sgx no processes
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
// check-run-results

// Tests ensuring that `dbg!(expr)` has the expected run-time behavior.
// as well as some compile time properties we expect.
Expand All @@ -18,7 +16,7 @@ struct Point<T> {
#[derive(Debug, PartialEq)]
struct NoCopy(usize);

fn test() {
fn main() {
let a: Unit = dbg!(Unit);
let _: Unit = dbg!(a);
// We can move `a` because it's Copy.
Expand Down Expand Up @@ -67,81 +65,3 @@ fn test() {
assert_eq!((1u8, 2u32, "Yeah"), dbg!(1u8, 2u32,
"Yeah",));
}

fn validate_stderr(stderr: Vec<String>) {
assert_eq!(stderr, &[
":22] Unit = Unit",

":23] a = Unit",

":29] Point{x: 42, y: 24,} = Point {",
" x: 42,",
" y: 24,",
"}",

":30] b = Point {",
" x: 42,",
" y: 24,",
"}",

":38]",

":42] &a = NoCopy(",
" 1337,",
")",

":42] dbg!(& a) = NoCopy(",
" 1337,",
")",
":47] f(&42) = 42",

"before",
":52] { foo += 1; eprintln!(\"before\"); 7331 } = 7331",

":60] (\"Yeah\",) = (",
" \"Yeah\",",
")",

":63] 1 = 1",
":63] 2 = 2",

":67] 1u8 = 1",
":67] 2u32 = 2",
":67] \"Yeah\" = \"Yeah\"",
]);
}

fn main() {
// The following is a hack to deal with compiletest's inability
// to check the output (to stdout) of run-pass tests.
use std::env;
use std::process::Command;

let mut args = env::args();
let prog = args.next().unwrap();
let child = args.next();
if let Some("child") = child.as_ref().map(|s| &**s) {
// Only run the test if we've been spawned as 'child'
test()
} else {
// This essentially spawns as 'child' to run the tests
// and then it collects output of stderr and checks the output
// against what we expect.
let out = Command::new(&prog).arg("child").output().unwrap();
assert!(out.status.success());
assert!(out.stdout.is_empty());

let stderr = String::from_utf8(out.stderr).unwrap();
let stderr = stderr.lines().map(|mut s| {
if s.starts_with("[") {
// Strip `[` and file path:
s = s.trim_start_matches("[");
assert!(s.starts_with(file!()));
s = s.trim_start_matches(file!());
}
s.to_owned()
}).collect();

validate_stderr(stderr);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[$DIR/dbg-macro-expected-behavior.rs:20] Unit = Unit
[$DIR/dbg-macro-expected-behavior.rs:21] a = Unit
[$DIR/dbg-macro-expected-behavior.rs:27] Point{x: 42, y: 24,} = Point {
x: 42,
y: 24,
}
[$DIR/dbg-macro-expected-behavior.rs:28] b = Point {
x: 42,
y: 24,
}
[$DIR/dbg-macro-expected-behavior.rs:36]
[$DIR/dbg-macro-expected-behavior.rs:40] &a = NoCopy(
1337,
)
[$DIR/dbg-macro-expected-behavior.rs:40] dbg!(& a) = NoCopy(
1337,
)
[$DIR/dbg-macro-expected-behavior.rs:45] f(&42) = 42
before
[$DIR/dbg-macro-expected-behavior.rs:50] { foo += 1; eprintln!("before"); 7331 } = 7331
[$DIR/dbg-macro-expected-behavior.rs:58] ("Yeah",) = (
"Yeah",
)
[$DIR/dbg-macro-expected-behavior.rs:61] 1 = 1
[$DIR/dbg-macro-expected-behavior.rs:61] 2 = 2
[$DIR/dbg-macro-expected-behavior.rs:65] 1u8 = 1
[$DIR/dbg-macro-expected-behavior.rs:65] 2u32 = 2
[$DIR/dbg-macro-expected-behavior.rs:65] "Yeah" = "Yeah"
4 changes: 3 additions & 1 deletion src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,12 @@ pub fn expected_output_path(
testpaths.file.with_extension(extension)
}

pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT];
pub const UI_STDERR: &str = "stderr";
pub const UI_STDOUT: &str = "stdout";
pub const UI_FIXED: &str = "fixed";
pub const UI_RUN_STDERR: &str = "run.stderr";
pub const UI_RUN_STDOUT: &str = "run.stdout";

/// Absolute path to the directory where all output for all tests in the given
/// `relative_dir` group should reside. Example:
Expand Down
16 changes: 16 additions & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ impl EarlyProps {
config.parse_needs_sanitizer_support(ln) {
props.ignore = Ignore::Ignore;
}

if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
props.ignore = Ignore::Ignore;
}

}

if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
Expand Down Expand Up @@ -326,6 +331,8 @@ pub struct TestProps {
pub force_host: bool,
// Check stdout for error-pattern output as well as stderr
pub check_stdout: bool,
// Check stdout & stderr for output of run-pass test
pub check_run_results: bool,
// For UI tests, allows compiler to generate arbitrary output to stdout
pub dont_check_compiler_stdout: bool,
// For UI tests, allows compiler to generate arbitrary output to stderr
Expand Down Expand Up @@ -388,6 +395,7 @@ impl TestProps {
build_aux_docs: false,
force_host: false,
check_stdout: false,
check_run_results: false,
dont_check_compiler_stdout: false,
dont_check_compiler_stderr: false,
no_prefer_dynamic: false,
Expand Down Expand Up @@ -468,6 +476,10 @@ impl TestProps {
self.check_stdout = config.parse_check_stdout(ln);
}

if !self.check_run_results {
self.check_run_results = config.parse_check_run_results(ln);
}

if !self.dont_check_compiler_stdout {
self.dont_check_compiler_stdout = config.parse_dont_check_compiler_stdout(ln);
}
Expand Down Expand Up @@ -712,6 +724,10 @@ impl Config {
self.parse_name_directive(line, "check-stdout")
}

fn parse_check_run_results(&self, line: &str) -> bool {
self.parse_name_directive(line, "check-run-results")
}

fn parse_dont_check_compiler_stdout(&self, line: &str) -> bool {
self.parse_name_directive(line, "dont-check-compiler-stdout")
}
Expand Down
96 changes: 74 additions & 22 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::common::{CompareMode, PassMode};
use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
use crate::common::{output_base_dir, output_base_name, output_testname_unique};
use crate::common::{Codegen, CodegenUnits, Rustdoc};
use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb};
Expand Down Expand Up @@ -288,6 +289,11 @@ enum ReadFrom {
Stdin(String),
}

enum TestOutput {
Compile,
Run,
}

impl<'test> TestCx<'test> {
/// Code executed for each revision in turn (or, if there are no
/// revisions, exactly once, with revision == None).
Expand Down Expand Up @@ -2934,6 +2940,61 @@ impl<'test> TestCx<'test> {
}
}

fn load_compare_outputs(&self, proc_res: &ProcRes,
output_kind: TestOutput, explicit_format: bool) -> usize {

let (stderr_kind, stdout_kind) = match output_kind {
TestOutput::Compile => (UI_STDERR, UI_STDOUT),
TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT)
};

let expected_stderr = self.load_expected_output(stderr_kind);
let expected_stdout = self.load_expected_output(stdout_kind);

let normalized_stdout = match output_kind {
TestOutput::Run if self.config.remote_test_client.is_some() => {
// When tests are run using the remote-test-client, the string
// 'uploaded "$TEST_BUILD_DIR/<test_executable>, waiting for result"'
// is printed to stdout by the client and then captured in the ProcRes,
// so it needs to be removed when comparing the run-pass test execution output
lazy_static! {
static ref REMOTE_TEST_RE: Regex = Regex::new(
"^uploaded \"\\$TEST_BUILD_DIR(/[[:alnum:]_\\-]+)+\", waiting for result\n"
).unwrap();
}
REMOTE_TEST_RE.replace(
&self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout),
""
).to_string()
}
_ => self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout)
};

let stderr = if explicit_format {
proc_res.stderr.clone()
} else {
json::extract_rendered(&proc_res.stderr)
};

let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
let mut errors = 0;
match output_kind {
TestOutput::Compile => {
if !self.props.dont_check_compiler_stdout {
errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
}
if !self.props.dont_check_compiler_stderr {
errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
}
}
TestOutput::Run => {
errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
}
}
errors
}

fn run_ui_test(&self) {
// if the user specified a format in the ui test
// print the output to the stderr file, otherwise extract
Expand All @@ -2946,32 +3007,13 @@ impl<'test> TestCx<'test> {
let proc_res = self.compile_test();
self.check_if_test_should_compile(&proc_res);

let expected_stderr = self.load_expected_output(UI_STDERR);
let expected_stdout = self.load_expected_output(UI_STDOUT);
let expected_fixed = self.load_expected_output(UI_FIXED);

let normalized_stdout =
self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);

let stderr = if explicit {
proc_res.stderr.clone()
} else {
json::extract_rendered(&proc_res.stderr)
};

let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);

let mut errors = 0;
if !self.props.dont_check_compiler_stdout {
errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
}
if !self.props.dont_check_compiler_stderr {
errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
}

let modes_to_prune = vec![CompareMode::Nll];
self.prune_duplicate_outputs(&modes_to_prune);

let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);

if self.config.compare_mode.is_some() {
// don't test rustfix with nll right now
} else if self.config.rustfix_coverage {
Expand Down Expand Up @@ -3049,7 +3091,17 @@ impl<'test> TestCx<'test> {

if self.should_run_successfully() {
let proc_res = self.exec_compiled_test();

let run_output_errors = if self.props.check_run_results {
self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
} else {
0
};
if run_output_errors > 0 {
self.fatal_proc_rec(
&format!("{} errors occured comparing run output.", run_output_errors),
&proc_res,
);
}
if !proc_res.status.success() {
self.fatal_proc_rec("test run failed!", &proc_res);
}
Expand Down