about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-03-01 21:10:36 -0500
committerNiko Matsakis <niko@alum.mit.edu>2016-03-01 21:10:36 -0500
commitbb2a425d5861297a9a5d08b8d5826b7703ecea04 (patch)
tree804b482f58173ed38f45754dc509c9aedecafb34
parentd78b4a93509174bed2080b627d74c956d2654366 (diff)
downloadrust-bb2a425d5861297a9a5d08b8d5826b7703ecea04.tar.gz
rust-bb2a425d5861297a9a5d08b8d5826b7703ecea04.zip
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`
-rw-r--r--src/compiletest/errors.rs55
-rw-r--r--src/compiletest/header.rs56
-rw-r--r--src/compiletest/runtest.rs4
3 files changed, 88 insertions, 27 deletions
diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs
index a3ad022ebd5..63bc657baa4 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<ExpectedError> {
+///
+/// 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<String>) -> Vec<ExpectedError> {
     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<ExpectedError> {
     // 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<usize>,
                   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<usize>,
         (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 26567e12ea7..c9dfc0bf1a4 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<String>,
     // Lines that should be expected, in order, on standard out
     pub error_patterns: Vec<String> ,
     // 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<String>,
+    // Revisions to test for incremental compilation.
+    pub revisions: Vec<String>,
 }
 
 // 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<String> {
     parse_name_value_directive(line, "compile-flags")
 }
 
+fn parse_revisions(line: &str) -> Option<Vec<String>> {
+    parse_name_value_directive(line, "revisions")
+        .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+}
+
 fn parse_run_flags(line: &str) -> Option<String> {
     parse_name_value_directive(line, "run-flags")
 }
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 4ebfce86381..ab766eecd9d 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<String> = errors::load_errors(&testpaths.file)
+    let expected: HashSet<String> = errors::load_errors(&testpaths.file, &props.revision)
         .iter()
         .map(|e| e.msg.trim().to_string())
         .collect();