about summary refs log tree commit diff
path: root/src/compiletest
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-02-18 05:19:19 -0500
committerNiko Matsakis <niko@alum.mit.edu>2016-02-24 18:40:38 -0500
commit7c2e5e35a61adfee1fec69f811f79930bfd2c3b9 (patch)
tree841b928d86fab527e93392f0e116b0e52d6e367c /src/compiletest
parent0ef8d426050318934d16d962325ec002eaf0c29d (diff)
downloadrust-7c2e5e35a61adfee1fec69f811f79930bfd2c3b9.tar.gz
rust-7c2e5e35a61adfee1fec69f811f79930bfd2c3b9.zip
Generalize from passing around a single path to the test file to passing
around a set of paths called `TestPaths`

This commit is not quite standalone; it basically contains all the
borrowing plumbing bits, the interesting stuff comes in the next commit.
Diffstat (limited to 'src/compiletest')
-rw-r--r--src/compiletest/compiletest.rs51
-rw-r--r--src/compiletest/runtest.rs321
2 files changed, 202 insertions, 170 deletions
diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs
index da8331fc062..e29a55bbadc 100644
--- a/src/compiletest/compiletest.rs
+++ b/src/compiletest/compiletest.rs
@@ -28,10 +28,12 @@ extern crate log;
 
 use std::env;
 use std::fs;
+use std::io;
 use std::path::{Path, PathBuf};
 use getopts::{optopt, optflag, reqopt};
 use common::Config;
 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
+use test::TestPaths;
 use util::logv;
 
 pub mod procsrv;
@@ -267,15 +269,35 @@ pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
     debug!("making tests from {:?}",
            config.src_base.display());
     let mut tests = Vec::new();
-    let dirs = fs::read_dir(&config.src_base).unwrap();
+    collect_tests_from_dir(config,
+                           &config.src_base,
+                           &config.src_base,
+                           &PathBuf::new(),
+                           &mut tests)
+        .unwrap();
+    tests
+}
+
+fn collect_tests_from_dir(config: &Config,
+                          base: &Path,
+                          dir: &Path,
+                          relative_dir_path: &Path,
+                          tests: &mut Vec<test::TestDescAndFn>)
+                          -> io::Result<()> {
     for file in dirs {
-        let file = file.unwrap().path();
-        debug!("inspecting file {:?}", file.display());
-        if is_test(config, &file) {
-            tests.push(make_test(config, &file))
+        let file = try!(file);
+        let file_path = file.path();
+        debug!("inspecting file {:?}", file_path.display());
+        if is_test(config, &file_path) {
+            let paths = TestPaths {
+                file: file_path,
+                base: base.to_path_buf(),
+                relative_dir: relative_dir_path.to_path_buf(),
+            };
+            tests.push(make_test(config, &paths))
         }
     }
-    tests
+    Ok(())
 }
 
 pub fn is_test(config: &Config, testfile: &Path) -> bool {
@@ -305,15 +327,14 @@ pub fn is_test(config: &Config, testfile: &Path) -> bool {
     return valid;
 }
 
-pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
-{
+pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
     test::TestDescAndFn {
         desc: test::TestDesc {
-            name: make_test_name(config, testfile),
-            ignore: header::is_test_ignored(config, testfile),
+            name: make_test_name(config, testpaths),
+            ignore: header::is_test_ignored(config, &testpaths.file),
             should_panic: test::ShouldPanic::No,
         },
-        testfn: make_test_closure(config, &testfile),
+        testfn: make_test_closure(config, testpaths),
     }
 }
 
@@ -330,11 +351,11 @@ pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
 }
 
-pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
-    let config = (*config).clone();
-    let testfile = testfile.to_path_buf();
+pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
+    let config = config.clone();
+    let testpaths = testpaths.clone();
     test::DynTestFn(Box::new(move || {
-        runtest::run(config, &testfile)
+        runtest::run(config, &testpaths)
     }))
 }
 
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 5eef6b7f24c..601033ff5e8 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -15,6 +15,7 @@ use errors;
 use header::TestProps;
 use header;
 use procsrv;
+use test::TestPaths;
 use util::logv;
 
 use std::env;
@@ -27,7 +28,7 @@ use std::net::TcpStream;
 use std::path::{Path, PathBuf, Component};
 use std::process::{Command, Output, ExitStatus};
 
-pub fn run(config: Config, testfile: &Path) {
+pub fn run(config: Config, testpaths: &TestPaths) {
     match &*config.target {
 
         "arm-linux-androideabi" | "aarch64-linux-android" => {
@@ -43,21 +44,21 @@ pub fn run(config: Config, testfile: &Path) {
         // We're going to be dumping a lot of info. Start on a new line.
         print!("\n\n");
     }
-    debug!("running {:?}", testfile.display());
-    let props = header::load_props(&testfile);
+    debug!("running {:?}", testpaths.file.display());
+    let props = header::load_props(&testpaths.file);
     debug!("loaded props");
     match config.mode {
-        CompileFail => run_cfail_test(&config, &props, &testfile),
-        ParseFail => run_cfail_test(&config, &props, &testfile),
-        RunFail => run_rfail_test(&config, &props, &testfile),
-        RunPass => run_rpass_test(&config, &props, &testfile),
-        RunPassValgrind => run_valgrind_test(&config, &props, &testfile),
-        Pretty => run_pretty_test(&config, &props, &testfile),
-        DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile),
-        DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile),
-        Codegen => run_codegen_test(&config, &props, &testfile),
-        Rustdoc => run_rustdoc_test(&config, &props, &testfile),
-        CodegenUnits => run_codegen_units_test(&config, &props, &testfile),
+        CompileFail => run_cfail_test(&config, &props, &testpaths),
+        ParseFail => run_cfail_test(&config, &props, &testpaths),
+        RunFail => run_rfail_test(&config, &props, &testpaths),
+        RunPass => run_rpass_test(&config, &props, &testpaths),
+        RunPassValgrind => run_valgrind_test(&config, &props, &testpaths),
+        Pretty => run_pretty_test(&config, &props, &testpaths),
+        DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testpaths),
+        DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testpaths),
+        Codegen => run_codegen_test(&config, &props, &testpaths),
+        Rustdoc => run_rustdoc_test(&config, &props, &testpaths),
+        CodegenUnits => run_codegen_units_test(&config, &props, &testpaths),
     }
 }
 
@@ -69,8 +70,8 @@ fn get_output(props: &TestProps, proc_res: &ProcRes) -> String {
     }
 }
 
-fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
-    let proc_res = compile_test(config, props, testfile);
+fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
+    let proc_res = compile_test(config, props, testpaths);
 
     if proc_res.status.success() {
         fatal_proc_rec(&format!("{} test compiled successfully!", config.mode)[..],
@@ -84,27 +85,27 @@ fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
     }
 
     let output_to_check = get_output(props, &proc_res);
-    let expected_errors = errors::load_errors(testfile);
+    let expected_errors = errors::load_errors(&testpaths.file);
     if !expected_errors.is_empty() {
         if !props.error_patterns.is_empty() {
             fatal("both error pattern and expected errors specified");
         }
-        check_expected_errors(expected_errors, testfile, &proc_res);
+        check_expected_errors(expected_errors, testpaths, &proc_res);
     } else {
-        check_error_patterns(props, testfile, &output_to_check, &proc_res);
+        check_error_patterns(props, testpaths, &output_to_check, &proc_res);
     }
     check_no_compiler_crash(&proc_res);
     check_forbid_output(props, &output_to_check, &proc_res);
 }
 
-fn run_rfail_test(config: &Config, props: &TestProps, testfile: &Path) {
-    let proc_res = compile_test(config, props, testfile);
+fn run_rfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
+    let proc_res = compile_test(config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("compilation failed!", &proc_res);
     }
 
-    let proc_res = exec_compiled_test(config, props, testfile);
+    let proc_res = exec_compiled_test(config, props, testpaths);
 
     // The value our Makefile configures valgrind to return on failure
     const VALGRIND_ERR: i32 = 100;
@@ -114,7 +115,7 @@ fn run_rfail_test(config: &Config, props: &TestProps, testfile: &Path) {
 
     let output_to_check = get_output(props, &proc_res);
     check_correct_failure_status(&proc_res);
-    check_error_patterns(props, testfile, &output_to_check, &proc_res);
+    check_error_patterns(props, testpaths, &output_to_check, &proc_res);
 }
 
 fn check_correct_failure_status(proc_res: &ProcRes) {
@@ -128,27 +129,27 @@ fn check_correct_failure_status(proc_res: &ProcRes) {
     }
 }
 
-fn run_rpass_test(config: &Config, props: &TestProps, testfile: &Path) {
-    let proc_res = compile_test(config, props, testfile);
+fn run_rpass_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
+    let proc_res = compile_test(config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("compilation failed!", &proc_res);
     }
 
-    let proc_res = exec_compiled_test(config, props, testfile);
+    let proc_res = exec_compiled_test(config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("test run failed!", &proc_res);
     }
 }
 
-fn run_valgrind_test(config: &Config, props: &TestProps, testfile: &Path) {
+fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
     if config.valgrind_path.is_none() {
         assert!(!config.force_valgrind);
-        return run_rpass_test(config, props, testfile);
+        return run_rpass_test(config, props, testpaths);
     }
 
-    let mut proc_res = compile_test(config, props, testfile);
+    let mut proc_res = compile_test(config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("compilation failed!", &proc_res);
@@ -156,14 +157,14 @@ fn run_valgrind_test(config: &Config, props: &TestProps, testfile: &Path) {
 
     let mut new_config = config.clone();
     new_config.runtool = new_config.valgrind_path.clone();
-    proc_res = exec_compiled_test(&new_config, props, testfile);
+    proc_res = exec_compiled_test(&new_config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("test run failed!", &proc_res);
     }
 }
 
-fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
+fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
     if props.pp_exact.is_some() {
         logv(config, "testing for exact pretty-printing".to_owned());
     } else {
@@ -174,7 +175,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
         match props.pp_exact { Some(_) => 1, None => 2 };
 
     let mut src = String::new();
-    File::open(testfile).unwrap().read_to_string(&mut src).unwrap();
+    File::open(&testpaths.file).unwrap().read_to_string(&mut src).unwrap();
     let mut srcs = vec!(src);
 
     let mut round = 0;
@@ -182,7 +183,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
         logv(config, format!("pretty-printing round {}", round));
         let proc_res = print_source(config,
                                     props,
-                                    testfile,
+                                    testpaths,
                                     srcs[round].to_owned(),
                                     &props.pretty_mode);
 
@@ -198,7 +199,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
 
     let mut expected = match props.pp_exact {
         Some(ref file) => {
-            let filepath = testfile.parent().unwrap().join(file);
+            let filepath = testpaths.file.parent().unwrap().join(file);
             let mut s = String::new();
             File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
             s
@@ -220,7 +221,7 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
     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, testfile, actual);
+    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);
@@ -228,13 +229,13 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
     if !props.pretty_expanded { return }
 
     // additionally, run `--pretty expanded` and try to build it.
-    let proc_res = print_source(config, props, testfile, srcs[round].clone(), "expanded");
+    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);
     }
 
     let ProcRes{ stdout: expanded_src, .. } = proc_res;
-    let proc_res = typecheck_source(config, props, testfile, expanded_src);
+    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);
@@ -244,15 +245,15 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
 
     fn print_source(config: &Config,
                     props: &TestProps,
-                    testfile: &Path,
+                    testpaths: &TestPaths,
                     src: String,
                     pretty_type: &str) -> ProcRes {
-        let aux_dir = aux_output_dir_name(config, testfile);
+        let aux_dir = aux_output_dir_name(config, testpaths);
         compose_and_run(config,
-                        testfile,
+                        testpaths,
                         make_pp_args(config,
                                      props,
-                                     testfile,
+                                     testpaths,
                                      pretty_type.to_owned()),
                         props.exec_env.clone(),
                         &config.compile_lib_path,
@@ -262,9 +263,9 @@ fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
 
     fn make_pp_args(config: &Config,
                     props: &TestProps,
-                    testfile: &Path,
+                    testpaths: &TestPaths,
                     pretty_type: String) -> ProcArgs {
-        let aux_dir = aux_output_dir_name(config, testfile);
+        let aux_dir = aux_output_dir_name(config, testpaths);
         // FIXME (#9639): This needs to handle non-utf8 paths
         let mut args = vec!("-".to_owned(),
                             "-Zunstable-options".to_owned(),
@@ -300,13 +301,13 @@ actual:\n\
     }
 
     fn typecheck_source(config: &Config, props: &TestProps,
-                        testfile: &Path, src: String) -> ProcRes {
-        let args = make_typecheck_args(config, props, testfile);
-        compose_and_run_compiler(config, props, testfile, args, Some(src))
+                        testpaths: &TestPaths, src: String) -> ProcRes {
+        let args = make_typecheck_args(config, props, testpaths);
+        compose_and_run_compiler(config, props, testpaths, args, Some(src))
     }
 
-    fn make_typecheck_args(config: &Config, props: &TestProps, testfile: &Path) -> ProcArgs {
-        let aux_dir = aux_output_dir_name(config, testfile);
+    fn make_typecheck_args(config: &Config, props: &TestProps, testpaths: &TestPaths) -> ProcArgs {
+        let aux_dir = aux_output_dir_name(config, testpaths);
         let target = if props.force_host {
             &*config.host
         } else {
@@ -330,7 +331,7 @@ actual:\n\
     }
 }
 
-fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
+fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
     let mut config = Config {
         target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
         host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
@@ -342,16 +343,16 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
         commands,
         check_lines,
         breakpoint_lines
-    } = parse_debugger_commands(testfile, "gdb");
+    } = parse_debugger_commands(testpaths, "gdb");
     let mut cmds = commands.join("\n");
 
     // compile test file (it should have 'compile-flags:-g' in the header)
-    let compiler_run_result = compile_test(config, props, testfile);
+    let compiler_run_result = compile_test(config, props, testpaths);
     if !compiler_run_result.status.success() {
         fatal_proc_rec("compilation failed!", &compiler_run_result);
     }
 
-    let exe_file = make_exe_name(config, testfile);
+    let exe_file = make_exe_name(config, testpaths);
 
     let debugger_run_result;
     match &*config.target {
@@ -369,8 +370,10 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
                                          config.host, config.target));
             for line in &breakpoint_lines {
                 script_str.push_str(&format!("break {:?}:{}\n",
-                                             testfile.file_name().unwrap()
-                                                     .to_string_lossy(),
+                                             testpaths.file
+                                                      .file_name()
+                                                      .unwrap()
+                                                      .to_string_lossy(),
                                              *line)[..]);
             }
             script_str.push_str(&cmds);
@@ -378,7 +381,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
 
             debug!("script_str = {}", script_str);
             dump_output_file(config,
-                             testfile,
+                             testpaths,
                              &script_str,
                              "debugger.script");
 
@@ -441,7 +444,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
                 None => fatal("cannot find android cross path")
             };
 
-            let debugger_script = make_out_name(config, testfile, "debugger.script");
+            let debugger_script = make_out_name(config, testpaths, "debugger.script");
             // FIXME (#9639): This needs to handle non-utf8 paths
             let debugger_opts =
                 vec!("-quiet".to_owned(),
@@ -531,7 +534,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
             // Add line breakpoints
             for line in &breakpoint_lines {
                 script_str.push_str(&format!("break '{}':{}\n",
-                                             testfile.file_name().unwrap()
+                                             testpaths.file.file_name().unwrap()
                                                      .to_string_lossy(),
                                              *line));
             }
@@ -541,7 +544,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
 
             debug!("script_str = {}", script_str);
             dump_output_file(config,
-                             testfile,
+                             testpaths,
                              &script_str,
                              "debugger.script");
 
@@ -550,7 +553,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
                 if cfg!(windows) {"gdb.exe"} else {"gdb"}
             }
 
-            let debugger_script = make_out_name(config, testfile, "debugger.script");
+            let debugger_script = make_out_name(config, testpaths, "debugger.script");
 
             // FIXME (#9639): This needs to handle non-utf8 paths
             let debugger_opts =
@@ -567,7 +570,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
             let environment = vec![("PYTHONPATH".to_owned(), rust_pp_module_abs_path)];
 
             debugger_run_result = compose_and_run(config,
-                                                  testfile,
+                                                  testpaths,
                                                   proc_args,
                                                   environment,
                                                   &config.run_lib_path,
@@ -596,7 +599,7 @@ fn find_rust_src_root(config: &Config) -> Option<PathBuf> {
     return None;
 }
 
-fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
+fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
     if config.lldb_python_dir.is_none() {
         fatal("Can't run LLDB test because LLDB's python path is not set.");
     }
@@ -610,12 +613,12 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
     let config = &mut config;
 
     // compile test file (it should have 'compile-flags:-g' in the header)
-    let compile_result = compile_test(config, props, testfile);
+    let compile_result = compile_test(config, props, testpaths);
     if !compile_result.status.success() {
         fatal_proc_rec("compilation failed!", &compile_result);
     }
 
-    let exe_file = make_exe_name(config, testfile);
+    let exe_file = make_exe_name(config, testpaths);
 
     match config.lldb_version {
         Some(ref version) => {
@@ -634,7 +637,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
         check_lines,
         breakpoint_lines,
         ..
-    } = parse_debugger_commands(testfile, "lldb");
+    } = parse_debugger_commands(testpaths, "lldb");
 
     // Write debugger script:
     // We don't want to hang when calling `quit` while the process is still running
@@ -676,13 +679,14 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
     // Write the script into a file
     debug!("script_str = {}", script_str);
     dump_output_file(config,
-                     testfile,
+                     testpaths,
                      &script_str,
                      "debugger.script");
-    let debugger_script = make_out_name(config, testfile, "debugger.script");
+    let debugger_script = make_out_name(config, testpaths, "debugger.script");
 
     // Let LLDB execute the script via lldb_batchmode.py
     let debugger_run_result = run_lldb(config,
+                                       testpaths,
                                        &exe_file,
                                        &debugger_script,
                                        &rust_src_root);
@@ -694,6 +698,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
     check_debugger_output(&debugger_run_result, &check_lines);
 
     fn run_lldb(config: &Config,
+                testpaths: &TestPaths,
                 test_executable: &Path,
                 debugger_script: &Path,
                 rust_src_root: &Path)
@@ -701,7 +706,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
         // Prepare the lldb_batchmode which executes the debugger script
         let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
         cmd2procres(config,
-                    test_executable,
+                    testpaths,
                     Command::new(&config.python)
                             .arg(&lldb_script_path)
                             .arg(test_executable)
@@ -711,7 +716,7 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
     }
 }
 
-fn cmd2procres(config: &Config, test_executable: &Path, cmd: &mut Command)
+fn cmd2procres(config: &Config, testpaths: &TestPaths, cmd: &mut Command)
               -> ProcRes {
     let (status, out, err) = match cmd.output() {
         Ok(Output { status, stdout, stderr }) => {
@@ -725,7 +730,7 @@ fn cmd2procres(config: &Config, test_executable: &Path, cmd: &mut Command)
         }
     };
 
-    dump_output(config, test_executable, &out, &err);
+    dump_output(config, testpaths, &out, &err);
     ProcRes {
         status: Status::Normal(status),
         stdout: out,
@@ -740,7 +745,7 @@ struct DebuggerCommands {
     breakpoint_lines: Vec<usize>,
 }
 
-fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
+fn parse_debugger_commands(testpaths: &TestPaths, debugger_prefix: &str)
                            -> DebuggerCommands {
     let command_directive = format!("{}-command", debugger_prefix);
     let check_directive = format!("{}-check", debugger_prefix);
@@ -749,7 +754,7 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
     let mut commands = vec!();
     let mut check_lines = vec!();
     let mut counter = 1;
-    let reader = BufReader::new(File::open(file_path).unwrap());
+    let reader = BufReader::new(File::open(&testpaths.file).unwrap());
     for line in reader.lines() {
         match line {
             Ok(line) => {
@@ -860,11 +865,12 @@ fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String])
 }
 
 fn check_error_patterns(props: &TestProps,
-                        testfile: &Path,
+                        testpaths: &TestPaths,
                         output_to_check: &str,
                         proc_res: &ProcRes) {
     if props.error_patterns.is_empty() {
-        fatal(&format!("no error pattern specified in {:?}", testfile.display()));
+        fatal(&format!("no error pattern specified in {:?}",
+                       testpaths.file.display()));
     }
     let mut next_err_idx = 0;
     let mut next_err_pat = &props.error_patterns[next_err_idx];
@@ -915,7 +921,7 @@ fn check_forbid_output(props: &TestProps,
 }
 
 fn check_expected_errors(expected_errors: Vec<errors::ExpectedError>,
-                         testfile: &Path,
+                         testpaths: &TestPaths,
                          proc_res: &ProcRes) {
 
     // true if we found the error in question
@@ -926,7 +932,7 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError>,
     }
 
     let prefixes = expected_errors.iter().map(|ee| {
-        format!("{}:{}:", testfile.display(), ee.line)
+        format!("{}:{}:", testpaths.file.display(), ee.line)
     }).collect::<Vec<String>>();
 
     let (expect_help, expect_note) =
@@ -1142,62 +1148,64 @@ impl fmt::Display for Status {
 }
 
 fn compile_test(config: &Config, props: &TestProps,
-                testfile: &Path) -> ProcRes {
-    let aux_dir = aux_output_dir_name(config, testfile);
+                testpaths: &TestPaths) -> ProcRes {
+    let aux_dir = aux_output_dir_name(config, testpaths);
     // FIXME (#9639): This needs to handle non-utf8 paths
     let link_args = vec!("-L".to_owned(),
                          aux_dir.to_str().unwrap().to_owned());
     let args = make_compile_args(config,
                                  props,
                                  link_args,
-                                 |a, b| TargetLocation::ThisFile(make_exe_name(a, b)), testfile);
-    compose_and_run_compiler(config, props, testfile, args, None)
+                                 |a, b| TargetLocation::ThisFile(make_exe_name(a, b)), testpaths);
+    compose_and_run_compiler(config, props, testpaths, args, None)
 }
 
-fn document(config: &Config, props: &TestProps,
-            testfile: &Path, out_dir: &Path) -> ProcRes {
+fn document(config: &Config,
+            props: &TestProps,
+            testpaths: &TestPaths,
+            out_dir: &Path)
+            -> ProcRes {
     if props.build_aux_docs {
         for rel_ab in &props.aux_builds {
-            let abs_ab = config.aux_base.join(rel_ab);
-            let aux_props = header::load_props(&abs_ab);
-
-            let auxres = document(config, &aux_props, &abs_ab, out_dir);
+            let aux_testpaths = compute_aux_test_paths(config, testpaths, rel_ab);
+            let aux_props = header::load_props(&aux_testpaths.file);
+            let auxres = document(config, &aux_props, &aux_testpaths, out_dir);
             if !auxres.status.success() {
                 return auxres;
             }
         }
     }
 
-    let aux_dir = aux_output_dir_name(config, testfile);
+    let aux_dir = aux_output_dir_name(config, testpaths);
     let mut args = vec!["-L".to_owned(),
                         aux_dir.to_str().unwrap().to_owned(),
                         "-o".to_owned(),
                         out_dir.to_str().unwrap().to_owned(),
-                        testfile.to_str().unwrap().to_owned()];
+                        testpaths.file.to_str().unwrap().to_owned()];
     args.extend(split_maybe_args(&props.compile_flags));
     let args = ProcArgs {
         prog: config.rustdoc_path.to_str().unwrap().to_owned(),
         args: args,
     };
-    compose_and_run_compiler(config, props, testfile, args, None)
+    compose_and_run_compiler(config, props, testpaths, args, None)
 }
 
 fn exec_compiled_test(config: &Config, props: &TestProps,
-                      testfile: &Path) -> ProcRes {
+                      testpaths: &TestPaths) -> ProcRes {
 
     let env = props.exec_env.clone();
 
     match &*config.target {
 
         "arm-linux-androideabi" | "aarch64-linux-android" => {
-            _arm_exec_compiled_test(config, props, testfile, env)
+            _arm_exec_compiled_test(config, props, testpaths, env)
         }
 
         _=> {
-            let aux_dir = aux_output_dir_name(config, testfile);
+            let aux_dir = aux_output_dir_name(config, testpaths);
             compose_and_run(config,
-                            testfile,
-                            make_run_args(config, props, testfile),
+                            testpaths,
+                            make_run_args(config, props, testpaths),
                             env,
                             &config.run_lib_path,
                             Some(aux_dir.to_str().unwrap()),
@@ -1207,20 +1215,20 @@ fn exec_compiled_test(config: &Config, props: &TestProps,
 }
 
 fn compose_and_run_compiler(config: &Config, props: &TestProps,
-                            testfile: &Path, args: ProcArgs,
+                            testpaths: &TestPaths, args: ProcArgs,
                             input: Option<String>) -> ProcRes {
     if !props.aux_builds.is_empty() {
-        ensure_dir(&aux_output_dir_name(config, testfile));
+        ensure_dir(&aux_output_dir_name(config, testpaths));
     }
 
-    let aux_dir = aux_output_dir_name(config, testfile);
+    let aux_dir = aux_output_dir_name(config, testpaths);
     // FIXME (#9639): This needs to handle non-utf8 paths
     let extra_link_args = vec!["-L".to_owned(),
                                aux_dir.to_str().unwrap().to_owned()];
 
     for rel_ab in &props.aux_builds {
-        let abs_ab = config.aux_base.join(rel_ab);
-        let aux_props = header::load_props(&abs_ab);
+        let aux_testpaths = compute_aux_test_paths(config, testpaths, rel_ab);
+        let aux_props = header::load_props(&aux_testpaths.file);
         let mut crate_type = if aux_props.no_prefer_dynamic {
             Vec::new()
         } else {
@@ -1247,13 +1255,13 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
                               &aux_props,
                               crate_type,
                               |a,b| {
-                                  let f = make_lib_name(a, b, testfile);
+                                  let f = make_lib_name(a, &b.file, testpaths);
                                   let parent = f.parent().unwrap();
                                   TargetLocation::ThisDirectory(parent.to_path_buf())
                               },
-                              &abs_ab);
+                              &aux_testpaths);
         let auxres = compose_and_run(config,
-                                     &abs_ab,
+                                     &aux_testpaths,
                                      aux_args,
                                      Vec::new(),
                                      &config.compile_lib_path,
@@ -1262,20 +1270,20 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
         if !auxres.status.success() {
             fatal_proc_rec(
                 &format!("auxiliary build of {:?} failed to compile: ",
-                        abs_ab.display()),
+                        aux_testpaths.file.display()),
                 &auxres);
         }
 
         match &*config.target {
             "arm-linux-androideabi"  | "aarch64-linux-android" => {
-                _arm_push_aux_shared_library(config, testfile);
+                _arm_push_aux_shared_library(config, testpaths);
             }
             _ => {}
         }
     }
 
     compose_and_run(config,
-                    testfile,
+                    testpaths,
                     args,
                     Vec::new(),
                     &config.compile_lib_path,
@@ -1285,16 +1293,17 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps,
 
 fn ensure_dir(path: &Path) {
     if path.is_dir() { return; }
-    fs::create_dir(path).unwrap();
+    fs::create_dir_all(path).unwrap();
 }
 
-fn compose_and_run(config: &Config, testfile: &Path,
+fn compose_and_run(config: &Config,
+                   testpaths: &TestPaths,
                    ProcArgs{ args, prog }: ProcArgs,
                    procenv: Vec<(String, String)> ,
                    lib_path: &str,
                    aux_path: Option<&str>,
                    input: Option<String>) -> ProcRes {
-    return program_output(config, testfile, lib_path,
+    return program_output(config, testpaths, lib_path,
                           prog, aux_path, args, procenv, input);
 }
 
@@ -1307,18 +1316,18 @@ fn make_compile_args<F>(config: &Config,
                         props: &TestProps,
                         extras: Vec<String> ,
                         xform: F,
-                        testfile: &Path)
+                        testpaths: &TestPaths)
                         -> ProcArgs where
-    F: FnOnce(&Config, &Path) -> TargetLocation,
+    F: FnOnce(&Config, &TestPaths) -> TargetLocation,
 {
-    let xform_file = xform(config, testfile);
+    let xform_file = xform(config, testpaths);
     let target = if props.force_host {
         &*config.host
     } else {
         &*config.target
     };
     // FIXME (#9639): This needs to handle non-utf8 paths
-    let mut args = vec!(testfile.to_str().unwrap().to_owned(),
+    let mut args = vec!(testpaths.file.to_str().unwrap().to_owned(),
                         "-L".to_owned(),
                         config.build_base.to_str().unwrap().to_owned(),
                         format!("--target={}", target));
@@ -1350,15 +1359,15 @@ fn make_compile_args<F>(config: &Config,
     };
 }
 
-fn make_lib_name(config: &Config, auxfile: &Path, testfile: &Path) -> PathBuf {
+fn make_lib_name(config: &Config, auxfile: &Path, testpaths: &TestPaths) -> PathBuf {
     // what we return here is not particularly important, as it
     // happens; rustc ignores everything except for the directory.
     let auxname = output_testname(auxfile);
-    aux_output_dir_name(config, testfile).join(&auxname)
+    aux_output_dir_name(config, testpaths).join(&auxname)
 }
 
-fn make_exe_name(config: &Config, testfile: &Path) -> PathBuf {
-    let mut f = output_base_name(config, testfile);
+fn make_exe_name(config: &Config, testpaths: &TestPaths) -> PathBuf {
+    let mut f = output_base_name(config, testpaths);
     // FIXME: This is using the host architecture exe suffix, not target!
     if config.target == "asmjs-unknown-emscripten" {
         let mut fname = f.file_name().unwrap().to_os_string();
@@ -1372,7 +1381,7 @@ fn make_exe_name(config: &Config, testfile: &Path) -> PathBuf {
     f
 }
 
-fn make_run_args(config: &Config, props: &TestProps, testfile: &Path)
+fn make_run_args(config: &Config, props: &TestProps, testpaths: &TestPaths)
                  -> ProcArgs {
     // If we've got another tool to run under (valgrind),
     // then split apart its command
@@ -1383,7 +1392,7 @@ fn make_run_args(config: &Config, props: &TestProps, testfile: &Path)
         args.push("nodejs".to_owned());
     }
 
-    let exe_file = make_exe_name(config, testfile);
+    let exe_file = make_exe_name(config, testpaths);
 
     // FIXME (#9639): This needs to handle non-utf8 paths
     args.push(exe_file.to_str().unwrap().to_owned());
@@ -1415,7 +1424,7 @@ fn split_maybe_args(argstr: &Option<String>) -> Vec<String> {
     }
 }
 
-fn program_output(config: &Config, testfile: &Path, lib_path: &str, prog: String,
+fn program_output(config: &Config, testpaths: &TestPaths, lib_path: &str, prog: String,
                   aux_path: Option<&str>, args: Vec<String>,
                   env: Vec<(String, String)>,
                   input: Option<String>) -> ProcRes {
@@ -1437,7 +1446,7 @@ fn program_output(config: &Config, testfile: &Path, lib_path: &str, prog: String
                      &args,
                      env,
                      input).expect(&format!("failed to exec `{}`", prog));
-    dump_output(config, testfile, &out, &err);
+    dump_output(config, testpaths, &out, &err);
     return ProcRes {
         status: Status::Normal(status),
         stdout: out,
@@ -1463,31 +1472,33 @@ fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
     }
 }
 
-fn dump_output(config: &Config, testfile: &Path, out: &str, err: &str) {
-    dump_output_file(config, testfile, out, "out");
-    dump_output_file(config, testfile, err, "err");
+fn dump_output(config: &Config, testpaths: &TestPaths, out: &str, err: &str) {
+    dump_output_file(config, testpaths, out, "out");
+    dump_output_file(config, testpaths, err, "err");
     maybe_dump_to_stdout(config, out, err);
 }
 
-fn dump_output_file(config: &Config, testfile: &Path,
-                    out: &str, extension: &str) {
-    let outfile = make_out_name(config, testfile, extension);
+fn dump_output_file(config: &Config,
+                    testpaths: &TestPaths,
+                    out: &str,
+                    extension: &str) {
+    let outfile = make_out_name(config, testpaths, extension);
     File::create(&outfile).unwrap().write_all(out.as_bytes()).unwrap();
 }
 
-fn make_out_name(config: &Config, testfile: &Path, extension: &str) -> PathBuf {
-    output_base_name(config, testfile).with_extension(extension)
+fn make_out_name(config: &Config, testpaths: &TestPaths, extension: &str) -> PathBuf {
+    output_base_name(config, testpaths).with_extension(extension)
 }
 
-fn aux_output_dir_name(config: &Config, testfile: &Path) -> PathBuf {
-    let f = output_base_name(config, testfile);
+fn aux_output_dir_name(config: &Config, testpaths: &TestPaths) -> PathBuf {
+    let f = output_base_name(config, testpaths);
     let mut fname = f.file_name().unwrap().to_os_string();
     fname.push(&format!(".{}.libaux", config.mode));
     f.with_file_name(&fname)
 }
 
-fn output_testname(testfile: &Path) -> PathBuf {
-    PathBuf::from(testfile.file_stem().unwrap())
+fn output_testname(filepath: &Path) -> PathBuf {
+    PathBuf::from(filepath.file_stem().unwrap())
 }
 
 fn output_base_name(config: &Config, testfile: &Path) -> PathBuf {
@@ -1531,10 +1542,10 @@ stderr:\n\
 
 fn _arm_exec_compiled_test(config: &Config,
                            props: &TestProps,
-                           testfile: &Path,
+                           testpaths: &TestPaths,
                            env: Vec<(String, String)>)
                            -> ProcRes {
-    let args = make_run_args(config, props, testfile);
+    let args = make_run_args(config, props, testpaths);
     let cmdline = make_cmdline("",
                                &args.prog,
                                &args.args);
@@ -1645,7 +1656,7 @@ fn _arm_exec_compiled_test(config: &Config,
         .expect(&format!("failed to exec `{}`", config.adb_path));
 
     dump_output(config,
-                testfile,
+                testpaths,
                 &stdout_out,
                 &stderr_out);
 
@@ -1657,8 +1668,8 @@ fn _arm_exec_compiled_test(config: &Config,
     }
 }
 
-fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
-    let tdir = aux_output_dir_name(config, testfile);
+fn _arm_push_aux_shared_library(config: &Config, testpaths: &TestPaths) {
+    let tdir = aux_output_dir_name(config, testpaths);
 
     let dirs = fs::read_dir(&tdir).unwrap();
     for file in dirs {
@@ -1692,8 +1703,8 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
 // codegen tests (using FileCheck)
 
 fn compile_test_and_save_ir(config: &Config, props: &TestProps,
-                                 testfile: &Path) -> ProcRes {
-    let aux_dir = aux_output_dir_name(config, testfile);
+                                 testpaths: &TestPaths) -> ProcRes {
+    let aux_dir = aux_output_dir_name(config, testpaths);
     // FIXME (#9639): This needs to handle non-utf8 paths
     let mut link_args = vec!("-L".to_owned(),
                              aux_dir.to_str().unwrap().to_owned());
@@ -1705,34 +1716,34 @@ fn compile_test_and_save_ir(config: &Config, props: &TestProps,
                                  |a, b| TargetLocation::ThisDirectory(
                                      output_base_name(a, b).parent()
                                         .unwrap().to_path_buf()),
-                                 testfile);
-    compose_and_run_compiler(config, props, testfile, args, None)
+                                 testpaths);
+    compose_and_run_compiler(config, props, testpaths, args, None)
 }
 
-fn check_ir_with_filecheck(config: &Config, testfile: &Path) -> ProcRes {
-    let irfile = output_base_name(config, testfile).with_extension("ll");
+fn check_ir_with_filecheck(config: &Config, testpaths: &TestPaths) -> ProcRes {
+    let irfile = output_base_name(config, testpaths).with_extension("ll");
     let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck");
     let proc_args = ProcArgs {
         // FIXME (#9639): This needs to handle non-utf8 paths
         prog: prog.to_str().unwrap().to_owned(),
         args: vec!(format!("-input-file={}", irfile.to_str().unwrap()),
-                   testfile.to_str().unwrap().to_owned())
+                   testpaths.file.to_str().unwrap().to_owned())
     };
-    compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
+    compose_and_run(config, testpaths, proc_args, Vec::new(), "", None, None)
 }
 
-fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
+fn run_codegen_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
 
     if config.llvm_bin_path.is_none() {
         fatal("missing --llvm-bin-path");
     }
 
-    let mut proc_res = compile_test_and_save_ir(config, props, testfile);
+    let mut proc_res = compile_test_and_save_ir(config, props, testpaths);
     if !proc_res.status.success() {
         fatal_proc_rec("compilation failed!", &proc_res);
     }
 
-    proc_res = check_ir_with_filecheck(config, testfile);
+    proc_res = check_ir_with_filecheck(config, testpaths);
     if !proc_res.status.success() {
         fatal_proc_rec("verification with 'FileCheck' failed",
                       &proc_res);
@@ -1750,30 +1761,30 @@ fn charset() -> &'static str {
     }
 }
 
-fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) {
-    let out_dir = output_base_name(config, testfile);
+fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
+    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, testfile, &out_dir);
+    let proc_res = document(config, props, testpaths, &out_dir);
     if !proc_res.status.success() {
         fatal_proc_rec("rustdoc failed!", &proc_res);
     }
     let root = find_rust_src_root(config).unwrap();
 
     let res = cmd2procres(config,
-                          testfile,
+                          testpaths,
                           Command::new(&config.python)
                                   .arg(root.join("src/etc/htmldocck.py"))
                                   .arg(out_dir)
-                                  .arg(testfile));
+                                  .arg(&testpaths.file));
     if !res.status.success() {
         fatal_proc_rec("htmldocck failed!", &res);
     }
 }
 
-fn run_codegen_units_test(config: &Config, props: &TestProps, testfile: &Path) {
-    let proc_res = compile_test(config, props, testfile);
+fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
+    let proc_res = compile_test(config, props, testpaths);
 
     if !proc_res.status.success() {
         fatal_proc_rec("compilation failed!", &proc_res);
@@ -1790,7 +1801,7 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testfile: &Path) {
         .map(|s| (&s[prefix.len()..]).to_string())
         .collect();
 
-    let expected: HashSet<String> = errors::load_errors(testfile)
+    let expected: HashSet<String> = errors::load_errors(&testpaths.file)
         .iter()
         .map(|e| e.msg.trim().to_string())
         .collect();