From 1297859814843ece2fbb0c10de0049ef368d988a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Tue, 28 Jun 2016 13:01:54 +0200 Subject: [PATCH 1/2] Formatting changes --- src/tools/compiletest/src/main.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index cc687b532047e..1bdefcb44f91d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -42,15 +42,16 @@ use util::logv; use self::header::EarlyProps; -pub mod procsrv; -pub mod util; -mod json; -pub mod header; -pub mod runtest; +mod analysisdiff; pub mod common; pub mod errors; +pub mod header; +mod json; mod raise_fd_limit; +pub mod runtest; +pub mod procsrv; mod uidiff; +pub mod util; fn main() { #[cfg(cargobuild)] @@ -86,7 +87,8 @@ pub fn parse_config(args: Vec ) -> Config { reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"), reqopt("", "mode", "which sort of compile tests to run", "(compile-fail|parse-fail|run-fail|run-pass|\ - run-pass-valgrind|pretty|debug-info|incremental)"), + run-pass-valgrind|pretty|debug-info|incremental|\ + save-analysis)"), optflag("", "ignored", "run tests marked as ignored"), optopt("", "runtool", "supervisor program to run tests under \ (eg. emulator, valgrind)", "PROGRAM"), @@ -252,14 +254,11 @@ pub fn run_tests(config: &Config) { env::set_var("RUST_TEST_THREADS","1"); } - match config.mode { - DebugInfoLldb => { - // Some older versions of LLDB seem to have problems with multiple - // instances running in parallel, so only run one test thread at a - // time. - env::set_var("RUST_TEST_THREADS", "1"); - } - _ => { /* proceed */ } + if let DebugInfoLldb = config.mode { + // Some older versions of LLDB seem to have problems with multiple + // instances running in parallel, so only run one test thread at a + // time. + env::set_var("RUST_TEST_THREADS", "1"); } // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests. @@ -303,8 +302,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts { } pub fn make_tests(config: &Config) -> Vec { - debug!("making tests from {:?}", - config.src_base.display()); + debug!("making tests from {:?}", config.src_base.display()); let mut tests = Vec::new(); collect_tests_from_dir(config, &config.src_base, From 510edd29dd7c8cfc84f2fb29c9b866ba0c895848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Tue, 28 Jun 2016 13:02:10 +0200 Subject: [PATCH 2/2] WIP: infrastructure for testing save-analysis --- src/test/save-analysis/functions.json | 166 ++++++++++++++++++++++ src/test/save-analysis/functions.rs | 18 +++ src/tools/compiletest/src/analysisdiff.rs | 90 ++++++++++++ src/tools/compiletest/src/common.rs | 3 + src/tools/compiletest/src/runtest.rs | 53 ++++++- 5 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 src/test/save-analysis/functions.json create mode 100644 src/test/save-analysis/functions.rs create mode 100644 src/tools/compiletest/src/analysisdiff.rs diff --git a/src/test/save-analysis/functions.json b/src/test/save-analysis/functions.json new file mode 100644 index 0000000000000..bcb494b291afc --- /dev/null +++ b/src/test/save-analysis/functions.json @@ -0,0 +1,166 @@ +{ + "prelude": { + "crate_name": "functions", + "span": { + "file_name": "functions.rs", + "byte_start": 0, + "byte_end": 149, + "line_start": 1, + "line_end": 18, + "column_start": 1, + "column_end": 1 + } + }, + "imports": [ + { + "kind": "Use", + "span": { + "file_name": "functions.rs", + "byte_start": 9, + "byte_end": 12, + "line_start": 1, + "line_end": 1, + "column_start": 10, + "column_end": 13 + }, + "name": "bar" + } + ], + "defs": [ + { + "kind": "Mod", + "span": { + "file_name": "functions.rs", + "byte_start": 21, + "byte_end": 24, + "line_start": 3, + "line_end": 3, + "column_start": 5, + "column_end": 8 + }, + "name": "foo", + "qualname": "::foo", + "value": "functions.rs" + }, + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 39, + "byte_end": 42, + "line_start": 4, + "line_end": 4, + "column_start": 12, + "column_end": 15 + }, + "name": "bar", + "qualname": "::foo::bar", + }, + { + "kind": "Mod", + "span": { + "file_name": "functions.rs", + "byte_start": 66, + "byte_end": 69, + "line_start": 9, + "line_end": 9, + "column_start": 5, + "column_end": 8 + }, + "name": "baz", + "qualname": "::baz", + "value": "functions.rs" + }, + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 84, + "byte_end": 87, + "line_start": 10, + "line_end": 10, + "column_start": 12, + "column_end": 15 + }, + "name": "bar", + "qualname": "::baz::bar", + }, + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 110, + "byte_end": 114, + "line_start": 15, + "line_end": 15, + "column_start": 4, + "column_end": 8 + }, + "name": "main", + "qualname": "::main", + "value": "()" + } + ], + "refs": [ + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 9, + "byte_end": 12, + "line_start": 1, + "line_end": 1, + "column_start": 10, + "column_end": 13 + }, + }, + { + "kind": "Mod", + "span": { + "file_name": "functions.rs", + "byte_start": 4, + "byte_end": 7, + "line_start": 1, + "line_end": 1, + "column_start": 5, + "column_end": 8 + }, + }, + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 124, + "byte_end": 127, + "line_start": 16, + "line_end": 16, + "column_start": 5, + "column_end": 8 + }, + }, + { + "kind": "Function", + "span": { + "file_name": "functions.rs", + "byte_start": 141, + "byte_end": 144, + "line_start": 17, + "line_end": 17, + "column_start": 10, + "column_end": 13 + }, + }, + { + "kind": "Mod", + "span": { + "file_name": "functions.rs", + "byte_start": 136, + "byte_end": 139, + "line_start": 17, + "line_end": 17, + "column_start": 5, + "column_end": 8 + }, + } + ] +} diff --git a/src/test/save-analysis/functions.rs b/src/test/save-analysis/functions.rs new file mode 100644 index 0000000000000..98f2a7aaf6694 --- /dev/null +++ b/src/test/save-analysis/functions.rs @@ -0,0 +1,18 @@ +use foo::bar; + +mod foo { + pub fn bar() { + + } +} + +mod baz { + pub fn bar() { + + } +} + +fn main() { + bar(); + baz::bar(); +} diff --git a/src/tools/compiletest/src/analysisdiff.rs b/src/tools/compiletest/src/analysisdiff.rs new file mode 100644 index 0000000000000..1e0ef62a24115 --- /dev/null +++ b/src/tools/compiletest/src/analysisdiff.rs @@ -0,0 +1,90 @@ +use rustc_serialize::json::{self, Json}; +use std::fs::File; +use std::path::Path; + +pub struct Mismatch { + pub path: Vec, + pub expected: Json, + pub found: Option +} + +pub fn compare_analysis(output: &Path, expected: &Path) -> Vec { + let mut output = json::from_reader(&mut File::open(output).unwrap()).expect("Invalid JSON"); + let expected = json::from_reader(&mut File::open(expected).unwrap()).expect("Invalid JSON"); + check_matching(vec![], &mut output, &expected) +} + +// Returns a vector containing mismatches between the keys in `expected` and those +// in `output`. Keys that are present in `output` but not in `expected` are ignored. +fn check_matching(mut path: Vec, output: &mut Json, expected: &Json) -> Vec { + match (output, expected) { + (&mut Json::Object(ref mut o_obj), &Json::Object(ref e_obj)) => { + let mut mismatches = vec![]; + + // Check that all keys in `e_obj` contain the same values as the keys in + // `o_obj` + for (k, e_v) in e_obj { + let mut new_path = path.clone(); + new_path.push(k.clone()); + + // If both keys exist, check recursively. Otherwise, signal a mismatch. + if let Some(o_v) = o_obj.get_mut(k) { + mismatches.extend(check_matching(new_path, o_v, &e_v)); + } else { + mismatches.push(Mismatch { path: new_path, expected: e_v.clone(), found: None }); + } + } + + mismatches + } + (&mut Json::Array(ref mut o_arr), &Json::Array(ref e_arr)) => { + let mut mismatches = vec![]; + let mut o_arr = o_arr.clone(); + path.push("".to_string()); + + // Each element in `e_arr` is compared against each element in `e_obj`, + // until a match has been found. + for e in e_arr { + let mut matches = None; + + // Find a match between `e` and any `o` + for (i, o) in o_arr.iter_mut().enumerate() { + // Note: we can use the empty string as path, since we are not going to use + // the mismatches + if check_matching(Vec::new(), o, &e).len() == 0 { + matches = Some(i); + break; + } + } + + if let Some(i) = matches { + // Remove the element from `o_arr`, to prevent double matches + o_arr.swap_remove(i); + } else { + // No `o` could match `e` + mismatches.push(Mismatch { path: path.clone(), expected: e.clone(), found: None }); + } + } + + mismatches + } + + // Mismatched types or scalar values + (output, expected) => { + if expected == output { + vec![] + } else { + vec![Mismatch { + path: path, + expected: expected.clone(), + found: Some(output.clone()) + }] + } + } + } + + // Enumerate all keys in expected + // Retrieve the current key from output: + // * If present, compare values + // * If absent, +} \ No newline at end of file diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5ec62e06e37ae..be4c568735070 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -28,6 +28,7 @@ pub enum Mode { CodegenUnits, Incremental, RunMake, + SaveAnalysis, Ui, } @@ -48,6 +49,7 @@ impl FromStr for Mode { "codegen-units" => Ok(CodegenUnits), "incremental" => Ok(Incremental), "run-make" => Ok(RunMake), + "save-analysis" => Ok(SaveAnalysis), "ui" => Ok(Ui), _ => Err(()), } @@ -70,6 +72,7 @@ impl fmt::Display for Mode { CodegenUnits => "codegen-units", Incremental => "incremental", RunMake => "run-make", + SaveAnalysis => "save-analysis", Ui => "ui", }, f) } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 953e060465a95..3afad353622d7 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use analysisdiff; use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; -use common::{Incremental, RunMake, Ui}; +use common::{Incremental, RunMake, SaveAnalysis, Ui}; use errors::{self, ErrorKind, Error}; use json; use header::TestProps; @@ -116,6 +117,7 @@ impl<'test> TestCx<'test> { CodegenUnits => self.run_codegen_units_test(), Incremental => self.run_incremental_test(), RunMake => self.run_rmake_test(), + SaveAnalysis => self.run_save_analysis_test(), Ui => self.run_ui_test(), } } @@ -1140,6 +1142,19 @@ actual:\n\ self.compose_and_run_compiler(args, None) } + // Run the compiler in order to produce the save-analysis information + fn analyze(&self) -> ProcRes { + let args = vec!["-Zsave-analysis".to_owned(), "-Zno-trans".to_owned()]; + let args = self.make_compile_args(args, + &self.testpaths.file, + TargetLocation::ThisDirectory( + self.output_base_name().parent() + .unwrap() + .to_path_buf())); + + self.compose_and_run_compiler(args, None) + } + fn exec_compiled_test(&self) -> ProcRes { let env = self.props.exec_env.clone(); @@ -1343,7 +1358,8 @@ actual:\n\ Rustdoc | RunMake | Ui | - CodegenUnits => { + CodegenUnits | + SaveAnalysis => { // do not use JSON output } } @@ -2109,6 +2125,39 @@ actual:\n\ fs::remove_dir(path) } + fn run_save_analysis_test(&self) { + assert!(self.revision.is_none(), "revisions not relevant here"); + + // Generate the save-analysis information in JSON + let proc_res = self.analyze(); + if !proc_res.status.success() { + self.fatal_proc_rec("save-analysis failed!", &proc_res); + } + + let json_expected = self.testpaths.file.with_extension("json"); + let json_output = { + // `save-analysis` dumps the json file to `save-analysis-temp/.json` + let file_name = self.testpaths.file.file_name().unwrap(); + self.output_base_name().join("save-analysis-temp") + .join(file_name) + .with_extension("json") + }; + + // Compare the output and the expected JSON + let mismatches = analysisdiff::compare_analysis(&json_output, &json_expected); + if mismatches.len() != 0 { + println!("Mismatches found:"); + + for m in mismatches { + let found = m.found.map_or("[Nothing]".to_string(), |f| f.to_string()); + println!("Path: {}. Expected: {}. Found: {}", + m.path.join("."), m.expected, found); + } + + self.fatal("save-analysis test failed"); + } + } + fn run_ui_test(&self) { println!("ui: {}", self.testpaths.file.display());