From 24f0851e670530b48ba0c098044a7fb235847501 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 10:43:09 -0500 Subject: [PATCH 01/14] require that header lines begin with `//` --- src/compiletest/header.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 6efe6e608e8ad..d8d0c383d8e0c 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -252,11 +252,12 @@ fn iter_header(testfile: &Path, it: &mut FnMut(&str) -> bool) -> bool { // module or function. This doesn't seem to be an optimization // with a warm page cache. Maybe with a cold one. let ln = ln.unwrap(); + let ln = ln.trim(); if ln.starts_with("fn") || ln.starts_with("mod") { return true; - } else { - if !(it(ln.trim())) { + } else if ln.starts_with("//") { + if !it(&ln[2..]) { return false; } } From c76125742a3d7ab9f6ad9fbaba8174f959adc18b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 12:26:24 -0500 Subject: [PATCH 02/14] refactor header parsing so it can work "in-place" --- src/compiletest/header.rs | 126 +++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index d8d0c383d8e0c..d5745ab8b34cf 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -54,80 +54,101 @@ pub struct TestProps { // Load any test directives embedded in the file pub fn load_props(testfile: &Path) -> TestProps { - let mut error_patterns = Vec::new(); - let mut aux_builds = Vec::new(); - let mut exec_env = Vec::new(); - let mut compile_flags = None; - let mut run_flags = None; - let mut pp_exact = None; - let mut check_lines = Vec::new(); - let mut build_aux_docs = false; - let mut force_host = false; - let mut check_stdout = false; - let mut no_prefer_dynamic = false; - let mut pretty_expanded = false; - let mut pretty_mode = None; - let mut pretty_compare_only = false; - let mut forbid_output = Vec::new(); + let error_patterns = Vec::new(); + let aux_builds = Vec::new(); + let exec_env = Vec::new(); + let compile_flags = None; + let run_flags = None; + let pp_exact = None; + let check_lines = Vec::new(); + let build_aux_docs = false; + let force_host = false; + let check_stdout = false; + let no_prefer_dynamic = false; + let pretty_expanded = false; + let pretty_compare_only = false; + let forbid_output = Vec::new(); + let mut props = TestProps { + error_patterns: error_patterns, + compile_flags: compile_flags, + run_flags: run_flags, + pp_exact: pp_exact, + aux_builds: aux_builds, + exec_env: exec_env, + check_lines: check_lines, + build_aux_docs: build_aux_docs, + force_host: force_host, + check_stdout: check_stdout, + no_prefer_dynamic: no_prefer_dynamic, + pretty_expanded: pretty_expanded, + pretty_mode: format!("normal"), + pretty_compare_only: pretty_compare_only, + forbid_output: forbid_output, + }; + load_props_into(&mut props, testfile); + props +} + +pub fn load_props_into(props: &mut TestProps, testfile: &Path) { iter_header(testfile, &mut |ln| { if let Some(ep) = parse_error_pattern(ln) { - error_patterns.push(ep); + props.error_patterns.push(ep); } - if compile_flags.is_none() { - compile_flags = parse_compile_flags(ln); + if props.compile_flags.is_none() { + props.compile_flags = parse_compile_flags(ln); } - if run_flags.is_none() { - run_flags = parse_run_flags(ln); + if props.run_flags.is_none() { + props.run_flags = parse_run_flags(ln); } - if pp_exact.is_none() { - pp_exact = parse_pp_exact(ln, testfile); + if props.pp_exact.is_none() { + props.pp_exact = parse_pp_exact(ln, testfile); } - if !build_aux_docs { - build_aux_docs = parse_build_aux_docs(ln); + if !props.build_aux_docs { + props.build_aux_docs = parse_build_aux_docs(ln); } - if !force_host { - force_host = parse_force_host(ln); + if !props.force_host { + props.force_host = parse_force_host(ln); } - if !check_stdout { - check_stdout = parse_check_stdout(ln); + if !props.check_stdout { + props.check_stdout = parse_check_stdout(ln); } - if !no_prefer_dynamic { - no_prefer_dynamic = parse_no_prefer_dynamic(ln); + if !props.no_prefer_dynamic { + props.no_prefer_dynamic = parse_no_prefer_dynamic(ln); } - if !pretty_expanded { - pretty_expanded = parse_pretty_expanded(ln); + if !props.pretty_expanded { + props.pretty_expanded = parse_pretty_expanded(ln); } - if pretty_mode.is_none() { - pretty_mode = parse_pretty_mode(ln); + if let Some(m) = parse_pretty_mode(ln) { + props.pretty_mode = m; } - if !pretty_compare_only { - pretty_compare_only = parse_pretty_compare_only(ln); + if !props.pretty_compare_only { + props.pretty_compare_only = parse_pretty_compare_only(ln); } if let Some(ab) = parse_aux_build(ln) { - aux_builds.push(ab); + props.aux_builds.push(ab); } if let Some(ee) = parse_exec_env(ln) { - exec_env.push(ee); + props.exec_env.push(ee); } if let Some(cl) = parse_check_line(ln) { - check_lines.push(cl); + props.check_lines.push(cl); } if let Some(of) = parse_forbid_output(ln) { - forbid_output.push(of); + props.forbid_output.push(of); } true @@ -136,30 +157,12 @@ pub fn load_props(testfile: &Path) -> TestProps { for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { match env::var(key) { Ok(val) => - if exec_env.iter().find(|&&(ref x, _)| *x == key).is_none() { - exec_env.push((key.to_owned(), val)) + if props.exec_env.iter().find(|&&(ref x, _)| *x == key).is_none() { + props.exec_env.push((key.to_owned(), val)) }, Err(..) => {} } } - - TestProps { - error_patterns: error_patterns, - compile_flags: compile_flags, - run_flags: run_flags, - pp_exact: pp_exact, - aux_builds: aux_builds, - exec_env: exec_env, - check_lines: check_lines, - build_aux_docs: build_aux_docs, - force_host: force_host, - check_stdout: check_stdout, - no_prefer_dynamic: no_prefer_dynamic, - pretty_expanded: pretty_expanded, - pretty_mode: pretty_mode.unwrap_or("normal".to_owned()), - pretty_compare_only: pretty_compare_only, - forbid_output: forbid_output, - } } pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { @@ -253,8 +256,7 @@ fn iter_header(testfile: &Path, it: &mut FnMut(&str) -> bool) -> bool { // with a warm page cache. Maybe with a cold one. let ln = ln.unwrap(); let ln = ln.trim(); - if ln.starts_with("fn") || - ln.starts_with("mod") { + if ln.starts_with("fn") || ln.starts_with("mod") { return true; } else if ln.starts_with("//") { if !it(&ln[2..]) { From d78b4a93509174bed2080b627d74c956d2654366 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 21:09:36 -0500 Subject: [PATCH 03/14] change compile_flags to eagerly split into a vector on whitespace --- src/compiletest/header.rs | 12 +++++++----- src/compiletest/runtest.rs | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index d5745ab8b34cf..26567e12ea778 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -22,7 +22,7 @@ pub struct TestProps { // Lines that should be expected, in order, on standard out pub error_patterns: Vec , // Extra flags to pass to the compiler - pub compile_flags: Option, + pub compile_flags: Vec, // Extra flags to pass when the compiled code is run (such as --bench) pub run_flags: Option, // If present, the name of a file that this test should match when @@ -57,7 +57,6 @@ pub fn load_props(testfile: &Path) -> TestProps { let error_patterns = Vec::new(); let aux_builds = Vec::new(); let exec_env = Vec::new(); - let compile_flags = None; let run_flags = None; let pp_exact = None; let check_lines = Vec::new(); @@ -70,7 +69,7 @@ pub fn load_props(testfile: &Path) -> TestProps { let forbid_output = Vec::new(); let mut props = TestProps { error_patterns: error_patterns, - compile_flags: compile_flags, + compile_flags: vec![], run_flags: run_flags, pp_exact: pp_exact, aux_builds: aux_builds, @@ -95,8 +94,11 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path) { props.error_patterns.push(ep); } - if props.compile_flags.is_none() { - props.compile_flags = parse_compile_flags(ln); + if let Some(flags) = parse_compile_flags(ln) { + props.compile_flags.extend( + flags + .split_whitespace() + .map(|s| s.to_owned())); } if props.run_flags.is_none() { diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 7cad5a4391c01..4ebfce8638121 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -275,7 +275,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { "-L".to_owned(), aux_dir.to_str().unwrap().to_owned()); args.extend(split_maybe_args(&config.target_rustcflags)); - args.extend(split_maybe_args(&props.compile_flags)); + args.extend(props.compile_flags.iter().cloned()); return ProcArgs { prog: config.rustc_path.to_str().unwrap().to_owned(), args: args, @@ -322,7 +322,7 @@ actual:\n\ "-L".to_owned(), aux_dir.to_str().unwrap().to_owned()); args.extend(split_maybe_args(&config.target_rustcflags)); - args.extend(split_maybe_args(&props.compile_flags)); + args.extend(props.compile_flags.iter().cloned()); // FIXME (#9639): This needs to handle non-utf8 paths return ProcArgs { prog: config.rustc_path.to_str().unwrap().to_owned(), @@ -1184,7 +1184,7 @@ fn document(config: &Config, "-o".to_owned(), out_dir.to_str().unwrap().to_owned(), testpaths.file.to_str().unwrap().to_owned()]; - args.extend(split_maybe_args(&props.compile_flags)); + args.extend(props.compile_flags.iter().cloned()); let args = ProcArgs { prog: config.rustdoc_path.to_str().unwrap().to_owned(), args: args, @@ -1369,7 +1369,7 @@ fn make_compile_args(config: &Config, } else { args.extend(split_maybe_args(&config.target_rustcflags)); } - args.extend(split_maybe_args(&props.compile_flags)); + args.extend(props.compile_flags.iter().cloned()); return ProcArgs { prog: config.rustc_path.to_str().unwrap().to_owned(), args: args, From bb2a425d5861297a9a5d08b8d5826b7703ecea04 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 21:10:36 -0500 Subject: [PATCH 04/14] introduce the notion of revisions, currently unused a test file may specify `// revisions: foo bar baz` headers and expected errors may be made specific to a revision by writing `//[foo] header` or `//[foo]~ ERROR` --- src/compiletest/errors.rs | 55 +++++++++++++++++++++++-------------- src/compiletest/header.rs | 56 ++++++++++++++++++++++++++++++++++---- src/compiletest/runtest.rs | 4 +-- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index a3ad022ebd52f..63bc657baa428 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -30,8 +30,10 @@ enum WhichLine { ThisLine, FollowPrevious(usize), AdjustBackward(usize) } /// Goal is to enable tests both like: //~^^^ ERROR go up three /// and also //~^ ERROR message one for the preceding line, and /// //~| ERROR message two for that same line. -// Load any test directives embedded in the file -pub fn load_errors(testfile: &Path) -> Vec { +/// +/// If cfg is not None (i.e., in an incremental test), then we look +/// for `//[X]~` instead, where `X` is the current `cfg`. +pub fn load_errors(testfile: &Path, cfg: &Option) -> Vec { let rdr = BufReader::new(File::open(testfile).unwrap()); // `last_nonfollow_error` tracks the most recently seen @@ -44,30 +46,41 @@ pub fn load_errors(testfile: &Path) -> Vec { // updating it in the map callback below.) let mut last_nonfollow_error = None; - rdr.lines().enumerate().filter_map(|(line_no, ln)| { - parse_expected(last_nonfollow_error, - line_no + 1, - &ln.unwrap()) - .map(|(which, error)| { - match which { - FollowPrevious(_) => {} - _ => last_nonfollow_error = Some(error.line), - } - error - }) - }).collect() + let tag = match *cfg { + Some(ref rev) => format!("//[{}]~", rev), + None => format!("//~") + }; + + rdr.lines() + .enumerate() + .filter_map(|(line_no, ln)| { + parse_expected(last_nonfollow_error, + line_no + 1, + &ln.unwrap(), + &tag) + .map(|(which, error)| { + match which { + FollowPrevious(_) => {} + _ => last_nonfollow_error = Some(error.line), + } + error + }) + }) + .collect() } fn parse_expected(last_nonfollow_error: Option, line_num: usize, - line: &str) -> Option<(WhichLine, ExpectedError)> { - let start = match line.find("//~") { Some(i) => i, None => return None }; - let (follow, adjusts) = if line.char_at(start + 3) == '|' { + line: &str, + tag: &str) + -> Option<(WhichLine, ExpectedError)> { + let start = match line.find(tag) { Some(i) => i, None => return None }; + let (follow, adjusts) = if line.char_at(start + tag.len()) == '|' { (true, 0) } else { - (false, line[start + 3..].chars().take_while(|c| *c == '^').count()) + (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count()) }; - let kind_start = start + 3 + adjusts + (follow as usize); + let kind_start = start + tag.len() + adjusts + (follow as usize); let letters = line[kind_start..].chars(); let kind = letters.skip_while(|c| c.is_whitespace()) .take_while(|c| !c.is_whitespace()) @@ -91,7 +104,9 @@ fn parse_expected(last_nonfollow_error: Option, (which, line) }; - debug!("line={} which={:?} kind={:?} msg={:?}", line_num, which, kind, msg); + debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}", + line_num, tag, which, kind, msg); + Some((which, ExpectedError { line: line, kind: kind, msg: msg, })) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 26567e12ea778..c9dfc0bf1a497 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -18,7 +18,18 @@ use common::Config; use common; use util; +#[derive(Clone, Debug)] pub struct TestProps { + // For the main test file, this is initialized to `None`. But + // when running tests that test multiple revisions, such as + // incremental tests, we will set this to `Some(foo)` where `foo` + // is the current revision identifier. + // + // Note that, unlike the other options here, this value is never + // loaded from the input file (though it is always set to one of + // the values listed in the vec `self.revisions`, which is loaded + // from the file). + pub revision: Option, // Lines that should be expected, in order, on standard out pub error_patterns: Vec , // Extra flags to pass to the compiler @@ -50,6 +61,8 @@ pub struct TestProps { pub pretty_compare_only: bool, // Patterns which must not appear in the output of a cfail test. pub forbid_output: Vec, + // Revisions to test for incremental compilation. + pub revisions: Vec, } // Load any test directives embedded in the file @@ -68,11 +81,13 @@ pub fn load_props(testfile: &Path) -> TestProps { let pretty_compare_only = false; let forbid_output = Vec::new(); let mut props = TestProps { + revision: None, error_patterns: error_patterns, compile_flags: vec![], run_flags: run_flags, pp_exact: pp_exact, aux_builds: aux_builds, + revisions: vec![], exec_env: exec_env, check_lines: check_lines, build_aux_docs: build_aux_docs, @@ -84,12 +99,16 @@ pub fn load_props(testfile: &Path) -> TestProps { pretty_compare_only: pretty_compare_only, forbid_output: forbid_output, }; - load_props_into(&mut props, testfile); + load_props_into(&mut props, testfile, None); props } -pub fn load_props_into(props: &mut TestProps, testfile: &Path) { - iter_header(testfile, &mut |ln| { +/// Load properties from `testfile` into `props`. If a property is +/// tied to a particular revision `foo` (indicated by writing +/// `//[foo]`), then the property is ignored unless `cfg` is +/// `Some("foo")`. +pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str>) { + iter_header(testfile, cfg, &mut |ln| { if let Some(ep) = parse_error_pattern(ln) { props.error_patterns.push(ep); } @@ -101,6 +120,10 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path) { .map(|s| s.to_owned())); } + if let Some(r) = parse_revisions(ln) { + props.revisions.extend(r); + } + if props.run_flags.is_none() { props.run_flags = parse_run_flags(ln); } @@ -235,7 +258,7 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { } } - let val = iter_header(testfile, &mut |ln| { + let val = iter_header(testfile, None, &mut |ln| { !parse_name_directive(ln, "ignore-test") && !parse_name_directive(ln, &ignore_target(config)) && !parse_name_directive(ln, &ignore_architecture(config)) && @@ -250,7 +273,10 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { !val } -fn iter_header(testfile: &Path, it: &mut FnMut(&str) -> bool) -> bool { +fn iter_header(testfile: &Path, + cfg: Option<&str>, + it: &mut FnMut(&str) -> bool) + -> bool { let rdr = BufReader::new(File::open(testfile).unwrap()); for ln in rdr.lines() { // Assume that any directives will be found before the first @@ -260,6 +286,21 @@ fn iter_header(testfile: &Path, it: &mut FnMut(&str) -> bool) -> bool { let ln = ln.trim(); if ln.starts_with("fn") || ln.starts_with("mod") { return true; + } else if ln.starts_with("//[") { + // A comment like `//[foo]` is specific to revision `foo` + if let Some(close_brace) = ln.find("]") { + let lncfg = &ln[3..close_brace]; + let matches = match cfg { + Some(s) => s == &lncfg[..], + None => false, + }; + if matches && !it(&ln[close_brace+1..]) { + return false; + } + } else { + panic!("malformed condition directive: expected `//[foo]`, found `{}`", + ln) + } } else if ln.starts_with("//") { if !it(&ln[2..]) { return false; @@ -285,6 +326,11 @@ fn parse_compile_flags(line: &str) -> Option { parse_name_value_directive(line, "compile-flags") } +fn parse_revisions(line: &str) -> Option> { + parse_name_value_directive(line, "revisions") + .map(|r| r.split_whitespace().map(|t| t.to_string()).collect()) +} + fn parse_run_flags(line: &str) -> Option { parse_name_value_directive(line, "run-flags") } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 4ebfce8638121..ab766eecd9dfe 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -85,7 +85,7 @@ fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { } let output_to_check = get_output(props, &proc_res); - let expected_errors = errors::load_errors(&testpaths.file); + let expected_errors = errors::load_errors(&testpaths.file, &props.revision); if !expected_errors.is_empty() { if !props.error_patterns.is_empty() { fatal("both error pattern and expected errors specified"); @@ -1821,7 +1821,7 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPa .map(|s| (&s[prefix.len()..]).to_string()) .collect(); - let expected: HashSet = errors::load_errors(&testpaths.file) + let expected: HashSet = errors::load_errors(&testpaths.file, &props.revision) .iter() .map(|e| e.msg.trim().to_string()) .collect(); From c5bdc35b606505940333fc605daae2532ca5710c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 22:05:39 -0500 Subject: [PATCH 05/14] refactor and generalize revisions --- src/compiletest/errors.rs | 6 ++--- src/compiletest/header.rs | 11 -------- src/compiletest/runtest.rs | 51 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index 63bc657baa428..44634e4d565ff 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -33,7 +33,7 @@ enum WhichLine { ThisLine, FollowPrevious(usize), AdjustBackward(usize) } /// /// If cfg is not None (i.e., in an incremental test), then we look /// for `//[X]~` instead, where `X` is the current `cfg`. -pub fn load_errors(testfile: &Path, cfg: &Option) -> Vec { +pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec { let rdr = BufReader::new(File::open(testfile).unwrap()); // `last_nonfollow_error` tracks the most recently seen @@ -46,8 +46,8 @@ pub fn load_errors(testfile: &Path, cfg: &Option) -> Vec // updating it in the map callback below.) let mut last_nonfollow_error = None; - let tag = match *cfg { - Some(ref rev) => format!("//[{}]~", rev), + let tag = match cfg { + Some(rev) => format!("//[{}]~", rev), None => format!("//~") }; diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index c9dfc0bf1a497..75d7ada8719b7 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -20,16 +20,6 @@ use util; #[derive(Clone, Debug)] pub struct TestProps { - // For the main test file, this is initialized to `None`. But - // when running tests that test multiple revisions, such as - // incremental tests, we will set this to `Some(foo)` where `foo` - // is the current revision identifier. - // - // Note that, unlike the other options here, this value is never - // loaded from the input file (though it is always set to one of - // the values listed in the vec `self.revisions`, which is loaded - // from the file). - pub revision: Option, // Lines that should be expected, in order, on standard out pub error_patterns: Vec , // Extra flags to pass to the compiler @@ -81,7 +71,6 @@ pub fn load_props(testfile: &Path) -> TestProps { let pretty_compare_only = false; let forbid_output = Vec::new(); let mut props = TestProps { - revision: None, error_patterns: error_patterns, compile_flags: vec![], run_flags: run_flags, diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index ab766eecd9dfe..880f9742b1066 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -70,7 +70,36 @@ fn get_output(props: &TestProps, proc_res: &ProcRes) -> String { } } + +fn for_each_revision(config: &Config, props: &TestProps, testpaths: &TestPaths, + mut op: OP) + where OP: FnMut(&Config, &TestProps, &TestPaths, Option<&str>) +{ + if props.revisions.is_empty() { + op(config, props, testpaths, None) + } else { + for revision in &props.revisions { + let mut revision_props = props.clone(); + header::load_props_into(&mut revision_props, + &testpaths.file, + Some(&revision)); + revision_props.compile_flags.extend(vec![ + format!("--cfg"), + format!("{}", revision), + ]); + op(config, &revision_props, testpaths, Some(revision)); + } + } +} + fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + for_each_revision(config, props, testpaths, run_cfail_test_revision); +} + +fn run_cfail_test_revision(config: &Config, + props: &TestProps, + testpaths: &TestPaths, + revision: Option<&str>) { let proc_res = compile_test(config, props, testpaths); if proc_res.status.success() { @@ -85,7 +114,7 @@ fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { } let output_to_check = get_output(props, &proc_res); - let expected_errors = errors::load_errors(&testpaths.file, &props.revision); + let expected_errors = errors::load_errors(&testpaths.file, revision); if !expected_errors.is_empty() { if !props.error_patterns.is_empty() { fatal("both error pattern and expected errors specified"); @@ -99,6 +128,13 @@ fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { } fn run_rfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + for_each_revision(config, props, testpaths, run_rfail_test_revision); +} + +fn run_rfail_test_revision(config: &Config, + props: &TestProps, + testpaths: &TestPaths, + _revision: Option<&str>) { let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { @@ -130,6 +166,13 @@ fn check_correct_failure_status(proc_res: &ProcRes) { } fn run_rpass_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + for_each_revision(config, props, testpaths, run_rpass_test_revision); +} + +fn run_rpass_test_revision(config: &Config, + props: &TestProps, + testpaths: &TestPaths, + _revision: Option<&str>) { let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { @@ -144,6 +187,8 @@ fn run_rpass_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { } fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant to rpass tests"); + if config.valgrind_path.is_none() { assert!(!config.force_valgrind); return run_rpass_test(config, props, testpaths); @@ -1804,6 +1849,8 @@ fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { } fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant to codegen units"); + let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { @@ -1821,7 +1868,7 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPa .map(|s| (&s[prefix.len()..]).to_string()) .collect(); - let expected: HashSet = errors::load_errors(&testpaths.file, &props.revision) + let expected: HashSet = errors::load_errors(&testpaths.file, None) .iter() .map(|e| e.msg.trim().to_string()) .collect(); From 4cffa9b4e3f8a733cf77de844489afa2ccaef5d0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 1 Mar 2016 22:05:54 -0500 Subject: [PATCH 06/14] convert coherence-cow to use revisions --- src/test/compile-fail/coherence-cow-2.rs | 27 ------------------- .../compile-fail/coherence-cow-no-cover.rs | 23 ---------------- .../{coherence-cow-1.rs => coherence-cow.rs} | 13 +++++++-- 3 files changed, 11 insertions(+), 52 deletions(-) delete mode 100644 src/test/compile-fail/coherence-cow-2.rs delete mode 100644 src/test/compile-fail/coherence-cow-no-cover.rs rename src/test/compile-fail/{coherence-cow-1.rs => coherence-cow.rs} (74%) diff --git a/src/test/compile-fail/coherence-cow-2.rs b/src/test/compile-fail/coherence-cow-2.rs deleted file mode 100644 index 52abceab98b69..0000000000000 --- a/src/test/compile-fail/coherence-cow-2.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// aux-build:coherence_lib.rs - -// Test that the `Pair` type reports an error if it contains type -// parameters, even when they are covered by local types. This test -// was originally intended to test the opposite, but the rules changed -// with RFC 1023 and this became illegal. - -// pretty-expanded FIXME #23616 - -extern crate coherence_lib as lib; -use lib::{Remote,Pair}; - -pub struct Cover(T); - -impl Remote for Pair,T> { } //~ ERROR E0210 - -fn main() { } diff --git a/src/test/compile-fail/coherence-cow-no-cover.rs b/src/test/compile-fail/coherence-cow-no-cover.rs deleted file mode 100644 index cd32e797ae9bf..0000000000000 --- a/src/test/compile-fail/coherence-cow-no-cover.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// aux-build:coherence_lib.rs - -// Test that it's not ok for T to appear uncovered - -extern crate coherence_lib as lib; -use lib::{Remote,Pair}; - -pub struct Cover(T); - -impl Remote for Pair,U> { } -//~^ ERROR type parameter `T` must be used as the type parameter for some local type - -fn main() { } diff --git a/src/test/compile-fail/coherence-cow-1.rs b/src/test/compile-fail/coherence-cow.rs similarity index 74% rename from src/test/compile-fail/coherence-cow-1.rs rename to src/test/compile-fail/coherence-cow.rs index 530bbf57d9104..6a2d1bac49381 100644 --- a/src/test/compile-fail/coherence-cow-1.rs +++ b/src/test/compile-fail/coherence-cow.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: a b c + // aux-build:coherence_lib.rs // pretty-expanded FIXME #23616 @@ -22,7 +24,14 @@ use lib::{Remote,Pair}; pub struct Cover(T); -impl Remote for Pair> { } -//~^ ERROR E0210 +#[cfg(a)] +impl Remote for Pair> { } //[a]~ ERROR E0210 + +#[cfg(b)] +impl Remote for Pair,T> { } //[b]~ ERROR E0210 + +#[cfg(c)] +impl Remote for Pair,U> { } +//[c]~^ ERROR type parameter `T` must be used as the type parameter for some local type fn main() { } From 3fb80e33a89709e3457ad0943f5847eb5800d620 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Mar 2016 04:16:30 -0500 Subject: [PATCH 07/14] thread revision identifier around in test output --- src/compiletest/runtest.rs | 225 ++++++++++++++++++++++--------------- 1 file changed, 134 insertions(+), 91 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 880f9742b1066..0987e6a20dbff 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -103,28 +103,30 @@ fn run_cfail_test_revision(config: &Config, let proc_res = compile_test(config, props, testpaths); if proc_res.status.success() { - fatal_proc_rec(&format!("{} test compiled successfully!", config.mode)[..], - &proc_res); + fatal_proc_rec( + revision, + &format!("{} test compiled successfully!", config.mode)[..], + &proc_res); } - check_correct_failure_status(&proc_res); + check_correct_failure_status(revision, &proc_res); if proc_res.status.success() { - fatal("process did not return an error status"); + fatal(revision, "process did not return an error status"); } let output_to_check = get_output(props, &proc_res); let expected_errors = errors::load_errors(&testpaths.file, revision); if !expected_errors.is_empty() { if !props.error_patterns.is_empty() { - fatal("both error pattern and expected errors specified"); + fatal(revision, "both error pattern and expected errors specified"); } - check_expected_errors(expected_errors, testpaths, &proc_res); + check_expected_errors(revision, expected_errors, testpaths, &proc_res); } else { - check_error_patterns(props, testpaths, &output_to_check, &proc_res); + check_error_patterns(revision, props, testpaths, &output_to_check, &proc_res); } - check_no_compiler_crash(&proc_res); - check_forbid_output(props, &output_to_check, &proc_res); + check_no_compiler_crash(revision, &proc_res); + check_forbid_output(revision, props, &output_to_check, &proc_res); } fn run_rfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { @@ -134,11 +136,11 @@ fn run_rfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { fn run_rfail_test_revision(config: &Config, props: &TestProps, testpaths: &TestPaths, - _revision: Option<&str>) { + revision: Option<&str>) { let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("compilation failed!", &proc_res); + fatal_proc_rec(revision, "compilation failed!", &proc_res); } let proc_res = exec_compiled_test(config, props, testpaths); @@ -146,19 +148,20 @@ fn run_rfail_test_revision(config: &Config, // The value our Makefile configures valgrind to return on failure const VALGRIND_ERR: i32 = 100; if proc_res.status.code() == Some(VALGRIND_ERR) { - fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res); + fatal_proc_rec(revision, "run-fail test isn't valgrind-clean!", &proc_res); } let output_to_check = get_output(props, &proc_res); - check_correct_failure_status(&proc_res); - check_error_patterns(props, testpaths, &output_to_check, &proc_res); + check_correct_failure_status(revision, &proc_res); + check_error_patterns(revision, props, testpaths, &output_to_check, &proc_res); } -fn check_correct_failure_status(proc_res: &ProcRes) { +fn check_correct_failure_status(revision: Option<&str>, proc_res: &ProcRes) { // The value the rust runtime returns on failure const RUST_ERR: i32 = 101; if proc_res.status.code() != Some(RUST_ERR) { fatal_proc_rec( + revision, &format!("failure produced the wrong error: {}", proc_res.status), proc_res); @@ -172,22 +175,22 @@ fn run_rpass_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { fn run_rpass_test_revision(config: &Config, props: &TestProps, testpaths: &TestPaths, - _revision: Option<&str>) { + revision: Option<&str>) { let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("compilation failed!", &proc_res); + fatal_proc_rec(revision, "compilation failed!", &proc_res); } let proc_res = exec_compiled_test(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("test run failed!", &proc_res); + fatal_proc_rec(revision, "test run failed!", &proc_res); } } fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { - assert!(props.revisions.is_empty(), "revisions not relevant to rpass tests"); + assert!(props.revisions.is_empty(), "revisions not relevant here"); if config.valgrind_path.is_none() { assert!(!config.force_valgrind); @@ -197,7 +200,7 @@ fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) let mut proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("compilation failed!", &proc_res); + fatal_proc_rec(None, "compilation failed!", &proc_res); } let mut new_config = config.clone(); @@ -205,11 +208,13 @@ fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) proc_res = exec_compiled_test(&new_config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("test run failed!", &proc_res); + fatal_proc_rec(None, "test run failed!", &proc_res); } } fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant here"); + if props.pp_exact.is_some() { logv(config, "testing for exact pretty-printing".to_owned()); } else { @@ -233,8 +238,9 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { &props.pretty_mode); if !proc_res.status.success() { - fatal_proc_rec(&format!("pretty-printing failed in round {}", round), - &proc_res); + fatal_proc_rec(None, + &format!("pretty-printing failed in round {}", round), + &proc_res); } let ProcRes{ stdout, .. } = proc_res; @@ -269,21 +275,23 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { let proc_res = typecheck_source(config, props, testpaths, actual); if !proc_res.status.success() { - fatal_proc_rec("pretty-printed source does not typecheck", &proc_res); + fatal_proc_rec(None, "pretty-printed source does not typecheck", &proc_res); } if !props.pretty_expanded { return } // additionally, run `--pretty expanded` and try to build it. let proc_res = print_source(config, props, testpaths, srcs[round].clone(), "expanded"); if !proc_res.status.success() { - fatal_proc_rec("pretty-printing (expanded) failed", &proc_res); + fatal_proc_rec(None, "pretty-printing (expanded) failed", &proc_res); } let ProcRes{ stdout: expanded_src, .. } = proc_res; let proc_res = typecheck_source(config, props, testpaths, expanded_src); if !proc_res.status.success() { - fatal_proc_rec("pretty-printed source (expanded) does not typecheck", - &proc_res); + fatal_proc_rec( + None, + "pretty-printed source (expanded) does not typecheck", + &proc_res); } return; @@ -329,7 +337,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { fn compare_source(expected: &str, actual: &str) { if expected != actual { - error("pretty-printed source does not match expected source"); + error(None, "pretty-printed source does not match expected source"); println!("\n\ expected:\n\ ------------------------------------------\n\ @@ -377,6 +385,8 @@ actual:\n\ } fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant here"); + let mut config = Config { target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags), host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags), @@ -394,7 +404,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPa // compile test file (it should have 'compile-flags:-g' in the header) let compiler_run_result = compile_test(config, props, testpaths); if !compiler_run_result.status.success() { - fatal_proc_rec("compilation failed!", &compiler_run_result); + fatal_proc_rec(None, "compilation failed!", &compiler_run_result); } let exe_file = make_exe_name(config, testpaths); @@ -486,7 +496,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPa let tool_path = match config.android_cross_path.to_str() { Some(x) => x.to_owned(), - None => fatal("cannot find android cross path") + None => fatal(None, "cannot find android cross path") }; let debugger_script = make_out_name(config, testpaths, "debugger.script"); @@ -625,7 +635,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPa } if !debugger_run_result.status.success() { - fatal("gdb failed to execute"); + fatal(None, "gdb failed to execute"); } check_debugger_output(&debugger_run_result, &check_lines); @@ -645,8 +655,10 @@ fn find_rust_src_root(config: &Config) -> Option { } fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant here"); + if config.lldb_python_dir.is_none() { - fatal("Can't run LLDB test because LLDB's python path is not set."); + fatal(None, "Can't run LLDB test because LLDB's python path is not set."); } let mut config = Config { @@ -660,7 +672,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testpaths: &TestP // compile test file (it should have 'compile-flags:-g' in the header) let compile_result = compile_test(config, props, testpaths); if !compile_result.status.success() { - fatal_proc_rec("compilation failed!", &compile_result); + fatal_proc_rec(None, "compilation failed!", &compile_result); } let exe_file = make_exe_name(config, testpaths); @@ -737,7 +749,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testpaths: &TestP &rust_src_root); if !debugger_run_result.status.success() { - fatal_proc_rec("Error while running LLDB", &debugger_run_result); + fatal_proc_rec(None, "Error while running LLDB", &debugger_run_result); } check_debugger_output(&debugger_run_result, &check_lines); @@ -770,7 +782,7 @@ fn cmd2procres(config: &Config, testpaths: &TestPaths, cmd: &mut Command) String::from_utf8(stderr).unwrap()) }, Err(e) => { - fatal(&format!("Failed to setup Python process for \ + fatal(None, &format!("Failed to setup Python process for \ LLDB script: {}", e)) } }; @@ -820,7 +832,7 @@ fn parse_debugger_commands(testpaths: &TestPaths, debugger_prefix: &str) }); } Err(e) => { - fatal(&format!("Error while parsing debugger commands: {}", e)) + fatal(None, &format!("Error while parsing debugger commands: {}", e)) } } counter += 1; @@ -902,19 +914,21 @@ fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String]) } } if i != num_check_lines { - fatal_proc_rec(&format!("line not found in debugger output: {}", + fatal_proc_rec(None, &format!("line not found in debugger output: {}", check_lines.get(i).unwrap()), debugger_run_result); } } } -fn check_error_patterns(props: &TestProps, +fn check_error_patterns(revision: Option<&str>, + props: &TestProps, testpaths: &TestPaths, output_to_check: &str, proc_res: &ProcRes) { if props.error_patterns.is_empty() { - fatal(&format!("no error pattern specified in {:?}", + fatal(revision, + &format!("no error pattern specified in {:?}", testpaths.file.display())); } let mut next_err_idx = 0; @@ -936,44 +950,50 @@ fn check_error_patterns(props: &TestProps, let missing_patterns = &props.error_patterns[next_err_idx..]; if missing_patterns.len() == 1 { - fatal_proc_rec(&format!("error pattern '{}' not found!", missing_patterns[0]), - proc_res); + fatal_proc_rec( + revision, + &format!("error pattern '{}' not found!", missing_patterns[0]), + proc_res); } else { for pattern in missing_patterns { - error(&format!("error pattern '{}' not found!", *pattern)); + error(revision, &format!("error pattern '{}' not found!", *pattern)); } - fatal_proc_rec("multiple error patterns not found", proc_res); + fatal_proc_rec(revision, "multiple error patterns not found", proc_res); } } -fn check_no_compiler_crash(proc_res: &ProcRes) { +fn check_no_compiler_crash(revision: Option<&str>, proc_res: &ProcRes) { for line in proc_res.stderr.lines() { if line.starts_with("error: internal compiler error:") { - fatal_proc_rec("compiler encountered internal error", - proc_res); + fatal_proc_rec(revision, + "compiler encountered internal error", + proc_res); } } } -fn check_forbid_output(props: &TestProps, +fn check_forbid_output(revision: Option<&str>, + props: &TestProps, output_to_check: &str, proc_res: &ProcRes) { for pat in &props.forbid_output { if output_to_check.contains(pat) { - fatal_proc_rec("forbidden pattern found in compiler output", proc_res); + fatal_proc_rec(revision, + "forbidden pattern found in compiler output", + proc_res); } } } -fn check_expected_errors(expected_errors: Vec, +fn check_expected_errors(revision: Option<&str>, + expected_errors: Vec, testpaths: &TestPaths, proc_res: &ProcRes) { - // true if we found the error in question let mut found_flags = vec![false; expected_errors.len()]; if proc_res.status.success() { - fatal("process did not return an error status"); + fatal_proc_rec(revision, "process did not return an error status", proc_res); } let prefixes = expected_errors.iter().map(|ee| { @@ -989,23 +1009,6 @@ fn check_expected_errors(expected_errors: Vec, (acc_help || ee.kind == "help:", acc_note || ee.kind == "note:")); - fn prefix_matches(line: &str, prefix: &str) -> bool { - use std::ascii::AsciiExt; - // On windows just translate all '\' path separators to '/' - let line = line.replace(r"\", "/"); - if cfg!(windows) { - line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase()) - } else { - line.starts_with(prefix) - } - } - - // A multi-line error will have followup lines which start with a space - // or open paren. - fn continuation( line: &str) -> bool { - line.starts_with(" ") || line.starts_with("(") - } - // Scan and extract our error/warning messages, // which look like: // filename:line1:col1: line2:col2: *error:* msg @@ -1015,6 +1018,8 @@ fn check_expected_errors(expected_errors: Vec, // // This pattern is ambiguous on windows, because filename may contain // a colon, so any path prefix must be detected and removed first. + let mut unexpected = 0; + let mut not_found = 0; for line in proc_res.stderr.lines() { let mut was_expected = false; let mut prev = 0; @@ -1036,9 +1041,11 @@ fn check_expected_errors(expected_errors: Vec, break; } } - if (prefix_matches(line, &prefixes[i]) || continuation(line)) && + if + (prefix_matches(line, &prefixes[i]) || continuation(line)) && line.contains(&ee.kind) && - line.contains(&ee.msg) { + line.contains(&ee.msg) + { found_flags[i] = true; was_expected = true; break; @@ -1053,20 +1060,44 @@ fn check_expected_errors(expected_errors: Vec, } if !was_expected && is_unexpected_compiler_message(line, expect_help, expect_note) { - fatal_proc_rec(&format!("unexpected compiler message: '{}'", - line), - proc_res); + error(revision, &format!("unexpected compiler message: '{}'", line)); + unexpected += 1; } } for (i, &flag) in found_flags.iter().enumerate() { if !flag { let ee = &expected_errors[i]; - fatal_proc_rec(&format!("expected {} on line {} not found: {}", - ee.kind, ee.line, ee.msg), - proc_res); + error(revision, &format!("expected {} on line {} not found: {}", + ee.kind, ee.line, ee.msg)); + not_found += 1; + } + } + + if unexpected > 0 || not_found > 0 { + fatal_proc_rec( + revision, + &format!("{} unexpected errors found, {} expected errors not found", + unexpected, not_found), + proc_res); + } + + fn prefix_matches(line: &str, prefix: &str) -> bool { + use std::ascii::AsciiExt; + // On windows just translate all '\' path separators to '/' + let line = line.replace(r"\", "/"); + if cfg!(windows) { + line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase()) + } else { + line.starts_with(prefix) } } + + // A multi-line error will have followup lines which start with a space + // or open paren. + fn continuation( line: &str) -> bool { + line.starts_with(" ") || line.starts_with("(") + } } fn is_unexpected_compiler_message(line: &str, expect_help: bool, expect_note: bool) -> bool { @@ -1331,6 +1362,7 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps, None); if !auxres.status.success() { fatal_proc_rec( + None, &format!("auxiliary build of {:?} failed to compile: ", aux_testpaths.file.display()), &auxres); @@ -1582,13 +1614,20 @@ fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) { } } -fn error(err: &str) { println!("\nerror: {}", err); } +fn error(revision: Option<&str>, err: &str) { + match revision { + Some(rev) => println!("\nerror in revision `{}`: {}", rev, err), + None => println!("\nerror: {}", err) + } +} -fn fatal(err: &str) -> ! { error(err); panic!(); } +fn fatal(revision: Option<&str>, err: &str) -> ! { + error(revision, err); panic!(); +} -fn fatal_proc_rec(err: &str, proc_res: &ProcRes) -> ! { - print!("\n\ -error: {}\n\ +fn fatal_proc_rec(revision: Option<&str>, err: &str, proc_res: &ProcRes) -> ! { + error(revision, err); + print!("\ status: {}\n\ command: {}\n\ stdout:\n\ @@ -1600,7 +1639,7 @@ stderr:\n\ {}\n\ ------------------------------------------\n\ \n", - err, proc_res.status, proc_res.cmdline, proc_res.stdout, + proc_res.status, proc_res.cmdline, proc_res.stdout, proc_res.stderr); panic!(); } @@ -1798,20 +1837,22 @@ fn check_ir_with_filecheck(config: &Config, testpaths: &TestPaths) -> ProcRes { } fn run_codegen_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant here"); if config.llvm_bin_path.is_none() { - fatal("missing --llvm-bin-path"); + fatal(None, "missing --llvm-bin-path"); } let mut proc_res = compile_test_and_save_ir(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("compilation failed!", &proc_res); + fatal_proc_rec(None, "compilation failed!", &proc_res); } proc_res = check_ir_with_filecheck(config, testpaths); if !proc_res.status.success() { - fatal_proc_rec("verification with 'FileCheck' failed", - &proc_res); + fatal_proc_rec(None, + "verification with 'FileCheck' failed", + &proc_res); } } @@ -1827,13 +1868,15 @@ fn charset() -> &'static str { } fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + assert!(props.revisions.is_empty(), "revisions not relevant here"); + let out_dir = output_base_name(config, testpaths); let _ = fs::remove_dir_all(&out_dir); ensure_dir(&out_dir); let proc_res = document(config, props, testpaths, &out_dir); if !proc_res.status.success() { - fatal_proc_rec("rustdoc failed!", &proc_res); + fatal_proc_rec(None, "rustdoc failed!", &proc_res); } let root = find_rust_src_root(config).unwrap(); @@ -1844,20 +1887,20 @@ fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { .arg(out_dir) .arg(&testpaths.file)); if !res.status.success() { - fatal_proc_rec("htmldocck failed!", &res); + fatal_proc_rec(None, "htmldocck failed!", &res); } } fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { - assert!(props.revisions.is_empty(), "revisions not relevant to codegen units"); + assert!(props.revisions.is_empty(), "revisions not relevant here"); let proc_res = compile_test(config, props, testpaths); if !proc_res.status.success() { - fatal_proc_rec("compilation failed!", &proc_res); + fatal_proc_rec(None, "compilation failed!", &proc_res); } - check_no_compiler_crash(&proc_res); + check_no_compiler_crash(None, &proc_res); let prefix = "TRANS_ITEM "; From 1d76ccd9d45e818e0554c8ce491d5ed20130e9c8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Mar 2016 10:59:47 -0500 Subject: [PATCH 08/14] Update `COMPILER_TESTS.md` documentation to discuss revisions --- COMPILER_TESTS.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/COMPILER_TESTS.md b/COMPILER_TESTS.md index e2a957e396191..08afd2137ba0c 100644 --- a/COMPILER_TESTS.md +++ b/COMPILER_TESTS.md @@ -42,3 +42,34 @@ whole, instead of just a few lines inside the test. * `ignore-test` always ignores the test * `ignore-lldb` and `ignore-gdb` will skip the debuginfo tests * `min-{gdb,lldb}-version` + +## Revisions + +Certain classes of tests support "revisions" (as of the time of this +writing, this includes run-pass, compile-fail, run-fail, and +incremental, though incremental tests are somewhat +different). Revisions allow a single test file to be used for multiple +tests. This is done by adding a special header at the top of the file: + +``` +// revisions: foo bar baz +``` + +This will result in the test being compiled (and tested) three times, +once with `--cfg foo`, once with `--cfg bar`, and once with `--cfg +baz`. You can therefore use `#[cfg(foo)]` etc within the test to tweak +each of these results. + +You can also customize headers and expected error messages to a particular +revision. To do this, add `[foo]` (or `bar`, `baz`, etc) after the `//` +comment, like so: + +``` +// A flag to pass in only for cfg `foo`: +//[foo]compile-flags: -Z verbose + +#[cfg(foo)] +fn test_foo() { + let x: usize = 32_u32; //[foo]~ ERROR mismatched types +} +``` From 0d6b4e03862065d0b1296e93c9bdfed25e0f60a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Mar 2016 11:34:46 -0500 Subject: [PATCH 09/14] make it possible for a test to declare `should-panic` and write a really basic "meta test" of the compilertest framework --- COMPILER_TESTS.md | 9 +++ src/compiletest/compiletest.rs | 9 ++- src/compiletest/header.rs | 68 ++++++++++++------- .../meta-expected-error-correct-rev.rs | 21 ++++++ .../meta-expected-error-wrong-rev.rs | 25 +++++++ 5 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 src/test/compile-fail/meta-expected-error-correct-rev.rs create mode 100644 src/test/compile-fail/meta-expected-error-wrong-rev.rs diff --git a/COMPILER_TESTS.md b/COMPILER_TESTS.md index 08afd2137ba0c..9b63fe3fba322 100644 --- a/COMPILER_TESTS.md +++ b/COMPILER_TESTS.md @@ -42,6 +42,9 @@ whole, instead of just a few lines inside the test. * `ignore-test` always ignores the test * `ignore-lldb` and `ignore-gdb` will skip the debuginfo tests * `min-{gdb,lldb}-version` +* `should-panic` indicates that the test should fail; used for "meta testing", + where we test the compiletest program itself to check that it will generate + errors in appropriate scenarios ## Revisions @@ -73,3 +76,9 @@ fn test_foo() { let x: usize = 32_u32; //[foo]~ ERROR mismatched types } ``` + +Note that not all headers have meaning when customized too a revision. +For example, the `ignore-test` header (and all "ignore" headers) +currently only apply to the test as a whole, not to particular +revisions. The only headers that are intended to really work when +customized to a revision are error patterns and compiler flags. diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index bbace16f05928..755ec2f31dcd6 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -354,11 +354,16 @@ pub fn is_test(config: &Config, testfile: &Path) -> bool { } pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn { + let early_props = header::early_props(config, &testpaths.file); test::TestDescAndFn { desc: test::TestDesc { name: make_test_name(config, testpaths), - ignore: header::is_test_ignored(config, &testpaths.file), - should_panic: test::ShouldPanic::No, + ignore: early_props.ignore, + should_panic: if early_props.should_panic { + test::ShouldPanic::Yes + } else { + test::ShouldPanic::No + }, }, testfn: make_test_closure(config, testpaths), } diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 75d7ada8719b7..6882be44cbc0d 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -164,8 +164,6 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str> if let Some(of) = parse_forbid_output(ln) { props.forbid_output.push(of); } - - true }); for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { @@ -179,7 +177,42 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str> } } -pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { +pub struct EarlyProps { + pub ignore: bool, + pub should_panic: bool, +} + +// scan the file to detect whether the test should be ignored and +// whether it should panic; these are two things the test runner needs +// to know early, before actually running the test +pub fn early_props(config: &Config, testfile: &Path) -> EarlyProps { + let mut props = EarlyProps { + ignore: false, + should_panic: false, + }; + + iter_header(testfile, None, &mut |ln| { + props.ignore = + props.ignore || + parse_name_directive(ln, "ignore-test") || + parse_name_directive(ln, &ignore_target(config)) || + parse_name_directive(ln, &ignore_architecture(config)) || + parse_name_directive(ln, &ignore_stage(config)) || + parse_name_directive(ln, &ignore_env(config)) || + (config.mode == common::Pretty && + parse_name_directive(ln, "ignore-pretty")) || + (config.target != config.host && + parse_name_directive(ln, "ignore-cross-compile")) || + ignore_gdb(config, ln) || + ignore_lldb(config, ln); + + props.should_panic = + props.should_panic || + parse_name_directive(ln, "should-panic"); + }); + + return props; + fn ignore_target(config: &Config) -> String { format!("ignore-{}", util::get_os(&config.target)) } @@ -246,26 +279,11 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { false } } - - let val = iter_header(testfile, None, &mut |ln| { - !parse_name_directive(ln, "ignore-test") && - !parse_name_directive(ln, &ignore_target(config)) && - !parse_name_directive(ln, &ignore_architecture(config)) && - !parse_name_directive(ln, &ignore_stage(config)) && - !parse_name_directive(ln, &ignore_env(config)) && - !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) && - !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) && - !ignore_gdb(config, ln) && - !ignore_lldb(config, ln) - }); - - !val } fn iter_header(testfile: &Path, cfg: Option<&str>, - it: &mut FnMut(&str) -> bool) - -> bool { + it: &mut FnMut(&str)) { let rdr = BufReader::new(File::open(testfile).unwrap()); for ln in rdr.lines() { // Assume that any directives will be found before the first @@ -274,7 +292,7 @@ fn iter_header(testfile: &Path, let ln = ln.unwrap(); let ln = ln.trim(); if ln.starts_with("fn") || ln.starts_with("mod") { - return true; + return; } else if ln.starts_with("//[") { // A comment like `//[foo]` is specific to revision `foo` if let Some(close_brace) = ln.find("]") { @@ -283,20 +301,18 @@ fn iter_header(testfile: &Path, Some(s) => s == &lncfg[..], None => false, }; - if matches && !it(&ln[close_brace+1..]) { - return false; + if matches { + it(&ln[close_brace+1..]); } } else { panic!("malformed condition directive: expected `//[foo]`, found `{}`", ln) } } else if ln.starts_with("//") { - if !it(&ln[2..]) { - return false; - } + it(&ln[2..]); } } - return true; + return; } fn parse_error_pattern(line: &str) -> Option { diff --git a/src/test/compile-fail/meta-expected-error-correct-rev.rs b/src/test/compile-fail/meta-expected-error-correct-rev.rs new file mode 100644 index 0000000000000..95b4e1a33cccd --- /dev/null +++ b/src/test/compile-fail/meta-expected-error-correct-rev.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: a +// pretty-expanded FIXME #23616 + +// Counterpart to `meta-expected-error-wrong-rev.rs` + +#[cfg(a)] +fn foo() { + let x: u32 = 22_usize; //[a]~ ERROR mismatched types +} + +fn main() { } diff --git a/src/test/compile-fail/meta-expected-error-wrong-rev.rs b/src/test/compile-fail/meta-expected-error-wrong-rev.rs new file mode 100644 index 0000000000000..83e0702af62cf --- /dev/null +++ b/src/test/compile-fail/meta-expected-error-wrong-rev.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: a +// should-panic +// pretty-expanded FIXME #23616 + +// This is a "meta-test" of the compilertest framework itself. In +// particular, it includes the right error message, but the message +// targets the wrong revision, so we expect the execution to fail. +// See also `meta-expected-error-correct-rev.rs`. + +#[cfg(a)] +fn foo() { + let x: u32 = 22_usize; //[b]~ ERROR mismatched types +} + +fn main() { } From ad3ed805030a128d83349cbf3c2dfab15c8a61a7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Mar 2016 14:20:55 -0500 Subject: [PATCH 10/14] rename should-panic to should-fail, per acrichto's suggestion --- COMPILER_TESTS.md | 2 +- src/compiletest/compiletest.rs | 2 +- src/compiletest/header.rs | 10 +++++----- src/test/compile-fail/meta-expected-error-wrong-rev.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/COMPILER_TESTS.md b/COMPILER_TESTS.md index 9b63fe3fba322..3f91c3eb94998 100644 --- a/COMPILER_TESTS.md +++ b/COMPILER_TESTS.md @@ -42,7 +42,7 @@ whole, instead of just a few lines inside the test. * `ignore-test` always ignores the test * `ignore-lldb` and `ignore-gdb` will skip the debuginfo tests * `min-{gdb,lldb}-version` -* `should-panic` indicates that the test should fail; used for "meta testing", +* `should-fail` indicates that the test should fail; used for "meta testing", where we test the compiletest program itself to check that it will generate errors in appropriate scenarios diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 755ec2f31dcd6..1042359d05b0d 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -359,7 +359,7 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn desc: test::TestDesc { name: make_test_name(config, testpaths), ignore: early_props.ignore, - should_panic: if early_props.should_panic { + should_panic: if early_props.should_fail { test::ShouldPanic::Yes } else { test::ShouldPanic::No diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 6882be44cbc0d..cf4d545a827c1 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -179,7 +179,7 @@ pub fn load_props_into(props: &mut TestProps, testfile: &Path, cfg: Option<&str> pub struct EarlyProps { pub ignore: bool, - pub should_panic: bool, + pub should_fail: bool, } // scan the file to detect whether the test should be ignored and @@ -188,7 +188,7 @@ pub struct EarlyProps { pub fn early_props(config: &Config, testfile: &Path) -> EarlyProps { let mut props = EarlyProps { ignore: false, - should_panic: false, + should_fail: false, }; iter_header(testfile, None, &mut |ln| { @@ -206,9 +206,9 @@ pub fn early_props(config: &Config, testfile: &Path) -> EarlyProps { ignore_gdb(config, ln) || ignore_lldb(config, ln); - props.should_panic = - props.should_panic || - parse_name_directive(ln, "should-panic"); + props.should_fail = + props.should_fail || + parse_name_directive(ln, "should-fail"); }); return props; diff --git a/src/test/compile-fail/meta-expected-error-wrong-rev.rs b/src/test/compile-fail/meta-expected-error-wrong-rev.rs index 83e0702af62cf..084c6ed4f4b41 100644 --- a/src/test/compile-fail/meta-expected-error-wrong-rev.rs +++ b/src/test/compile-fail/meta-expected-error-wrong-rev.rs @@ -9,7 +9,7 @@ // except according to those terms. // revisions: a -// should-panic +// should-fail // pretty-expanded FIXME #23616 // This is a "meta-test" of the compilertest framework itself. In From fff9c2b6db70e0bb895fc0d1e207566345f098c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 2 Mar 2016 14:25:25 -0500 Subject: [PATCH 11/14] add a run-fail meta test --- src/test/run-fail/meta-revision-bad.rs | 22 ++++++++++++++++++++++ src/test/run-fail/meta-revision-ok.rs | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/run-fail/meta-revision-bad.rs create mode 100644 src/test/run-fail/meta-revision-ok.rs diff --git a/src/test/run-fail/meta-revision-bad.rs b/src/test/run-fail/meta-revision-bad.rs new file mode 100644 index 0000000000000..bf521d4b4e5e4 --- /dev/null +++ b/src/test/run-fail/meta-revision-bad.rs @@ -0,0 +1,22 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Meta test for compiletest: check that when we give the wrong error +// patterns, the test fails. + +// revisions: foo bar +// should-fail +//[foo] error-pattern:bar +//[bar] error-pattern:foo + +#[cfg(foo)] fn die() {panic!("foo");} +#[cfg(bar)] fn die() {panic!("bar");} + +fn main() { die(); } diff --git a/src/test/run-fail/meta-revision-ok.rs b/src/test/run-fail/meta-revision-ok.rs new file mode 100644 index 0000000000000..f74ec39fdf27a --- /dev/null +++ b/src/test/run-fail/meta-revision-ok.rs @@ -0,0 +1,21 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Meta test for compiletest: check that when we give the right error +// patterns, the test passes. See all `meta-revision-bad.rs`. + +// revisions: foo bar +//[foo] error-pattern:foo +//[bar] error-pattern:bar + +#[cfg(foo)] fn die() {panic!("foo");} +#[cfg(bar)] fn die() {panic!("bar");} + +fn main() { die(); } From 9601e6f252f725f1aa90b15cab0e22732b0076ee Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 Mar 2016 04:17:39 -0500 Subject: [PATCH 12/14] Do not assert revisions list is empty for pretty printing tests --- src/compiletest/runtest.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 0987e6a20dbff..c3a7d9c053a2b 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -213,7 +213,11 @@ fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) } fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { - assert!(props.revisions.is_empty(), "revisions not relevant here"); + // Note: because we run the --pretty tests on the code in run-pass etc, + // we may see a list of revisions -- but we can just ignore them. + // We cannot assert that the list is empty as we do elsewhere. + // + // assert!(props.revisions.is_empty(), "revisions not relevant here"); if props.pp_exact.is_some() { logv(config, "testing for exact pretty-printing".to_owned()); From aa19b41f2549b8456c3f99200d819a726b2c5aab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 Mar 2016 11:17:32 -0500 Subject: [PATCH 13/14] make pretty printer tests understand revisions, and make them ignore the should-fail annotation --- src/compiletest/compiletest.rs | 19 ++++++++++++++----- src/compiletest/runtest.rs | 33 ++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 1042359d05b0d..99745d840f767 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -355,15 +355,24 @@ pub fn is_test(config: &Config, testfile: &Path) -> bool { pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn { let early_props = header::early_props(config, &testpaths.file); + + // The `should-fail` annotation doesn't apply to pretty tests, + // since we run the pretty printer across all tests by default. + // If desired, we could add a `should-fail-pretty` annotation. + let should_panic = match config.mode { + Pretty => test::ShouldPanic::No, + _ => if early_props.should_fail { + test::ShouldPanic::Yes + } else { + test::ShouldPanic::No + } + }; + test::TestDescAndFn { desc: test::TestDesc { name: make_test_name(config, testpaths), ignore: early_props.ignore, - should_panic: if early_props.should_fail { - test::ShouldPanic::Yes - } else { - test::ShouldPanic::No - }, + should_panic: should_panic, }, testfn: make_test_closure(config, testpaths), } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index c3a7d9c053a2b..1d2f560f5f65b 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -213,12 +213,13 @@ fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) } fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { - // Note: because we run the --pretty tests on the code in run-pass etc, - // we may see a list of revisions -- but we can just ignore them. - // We cannot assert that the list is empty as we do elsewhere. - // - // assert!(props.revisions.is_empty(), "revisions not relevant here"); + for_each_revision(config, props, testpaths, run_pretty_test_revision); +} +fn run_pretty_test_revision(config: &Config, + props: &TestProps, + testpaths: &TestPaths, + revision: Option<&str>) { if props.pp_exact.is_some() { logv(config, "testing for exact pretty-printing".to_owned()); } else { @@ -234,7 +235,8 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { let mut round = 0; while round < rounds { - logv(config, format!("pretty-printing round {}", round)); + logv(config, format!("pretty-printing round {} revision {:?}", + round, revision)); let proc_res = print_source(config, props, testpaths, @@ -242,8 +244,9 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { &props.pretty_mode); if !proc_res.status.success() { - fatal_proc_rec(None, - &format!("pretty-printing failed in round {}", round), + fatal_proc_rec(revision, + &format!("pretty-printing failed in round {} revision {:?}", + round, revision), &proc_res); } @@ -270,30 +273,30 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { expected = expected.replace(&cr, "").to_owned(); } - compare_source(&expected, &actual); + compare_source(revision, &expected, &actual); // If we're only making sure that the output matches then just stop here if props.pretty_compare_only { return; } // Finally, let's make sure it actually appears to remain valid code let proc_res = typecheck_source(config, props, testpaths, actual); - if !proc_res.status.success() { - fatal_proc_rec(None, "pretty-printed source does not typecheck", &proc_res); + fatal_proc_rec(revision, "pretty-printed source does not typecheck", &proc_res); } + if !props.pretty_expanded { return } // additionally, run `--pretty expanded` and try to build it. let proc_res = print_source(config, props, testpaths, srcs[round].clone(), "expanded"); if !proc_res.status.success() { - fatal_proc_rec(None, "pretty-printing (expanded) failed", &proc_res); + fatal_proc_rec(revision, "pretty-printing (expanded) failed", &proc_res); } let ProcRes{ stdout: expanded_src, .. } = proc_res; let proc_res = typecheck_source(config, props, testpaths, expanded_src); if !proc_res.status.success() { fatal_proc_rec( - None, + revision, "pretty-printed source (expanded) does not typecheck", &proc_res); } @@ -339,9 +342,9 @@ fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { }; } - fn compare_source(expected: &str, actual: &str) { + fn compare_source(revision: Option<&str>, expected: &str, actual: &str) { if expected != actual { - error(None, "pretty-printed source does not match expected source"); + error(revision, "pretty-printed source does not match expected source"); println!("\n\ expected:\n\ ------------------------------------------\n\ From fc4d0ecf20103e6f394e8771d11373e15ce70501 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 Mar 2016 11:18:42 -0500 Subject: [PATCH 14/14] mention caveat about should-fail in docs --- COMPILER_TESTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COMPILER_TESTS.md b/COMPILER_TESTS.md index 3f91c3eb94998..1cae4ef090fe0 100644 --- a/COMPILER_TESTS.md +++ b/COMPILER_TESTS.md @@ -44,7 +44,7 @@ whole, instead of just a few lines inside the test. * `min-{gdb,lldb}-version` * `should-fail` indicates that the test should fail; used for "meta testing", where we test the compiletest program itself to check that it will generate - errors in appropriate scenarios + errors in appropriate scenarios. This header is ignored for pretty-printer tests. ## Revisions