Skip to content

Commit e3d8fc5

Browse files
committed
compiletest: Run revisions as independent tests.
- The output of each test is now in its own directory. - "auxiliary" output is now under the respective test directory. - `stage_id` removed from filenames, and instead placed in the stamp file as a hash. This helps keep path lengths down for Windows. In brief, the new layout looks like this: ``` <build_base>/<relative_dir>/<testname>.<revision>.<mode>/ stamp <testname>.err <testname>.out a (binary) auxiliary/lib<auxname>.dylib auxiliary/<auxname>/<auxname>.err auxiliary/<auxname>/<auxname>.out ``` (revision and mode are optional)
1 parent 2438434 commit e3d8fc5

File tree

6 files changed

+234
-168
lines changed

6 files changed

+234
-168
lines changed

src/test/ui-fulldeps/update-references.sh

+11-12
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,17 @@ MYDIR=$(dirname $0)
3131
BUILD_DIR="$1"
3232
shift
3333

34+
shopt -s nullglob
35+
3436
while [[ "$1" != "" ]]; do
35-
STDERR_NAME="${1/%.rs/.stderr}"
36-
STDOUT_NAME="${1/%.rs/.stdout}"
37+
for EXT in "stderr" "stdout"; do
38+
for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
39+
OUT_BASE=`basename "$OUT_NAME"`
40+
if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then
41+
echo updating $MYDIR/$OUT_BASE
42+
cp $OUT_NAME $MYDIR
43+
fi
44+
done
45+
done
3746
shift
38-
if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
39-
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then
40-
echo updating $MYDIR/$STDOUT_NAME
41-
cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
42-
fi
43-
if [ -f $BUILD_DIR/$STDERR_NAME ] && \
44-
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then
45-
echo updating $MYDIR/$STDERR_NAME
46-
cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
47-
fi
4847
done

src/test/ui/update-references.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ shopt -s nullglob
3535

3636
while [[ "$1" != "" ]]; do
3737
for EXT in "stderr" "stdout" "fixed"; do
38-
for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do
38+
for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do
3939
OUT_DIR=`dirname "$1"`
4040
OUT_BASE=`basename "$OUT_NAME"`
4141
if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then

src/tools/compiletest/src/common.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
pub use self::Mode::*;
1111

1212
use std::fmt;
13-
use std::path::PathBuf;
13+
use std::path::{Path, PathBuf};
1414
use std::str::FromStr;
1515

1616
use test::ColorConfig;
17+
use util::PathBufExt;
1718

1819
#[derive(Clone, Copy, PartialEq, Debug)]
1920
pub enum Mode {
@@ -245,10 +246,9 @@ pub struct Config {
245246
pub nodejs: Option<String>,
246247
}
247248

248-
#[derive(Clone)]
249+
#[derive(Debug, Clone)]
249250
pub struct TestPaths {
250251
pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs
251-
pub base: PathBuf, // e.g., compile-test, auxiliary
252252
pub relative_dir: PathBuf, // e.g., foo/bar
253253
}
254254

@@ -278,3 +278,38 @@ pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
278278
pub const UI_STDERR: &str = "stderr";
279279
pub const UI_STDOUT: &str = "stdout";
280280
pub const UI_FIXED: &str = "fixed";
281+
282+
/// Absolute path to the directory where all output for all tests in the given
283+
/// `relative_dir` group should reside. Example:
284+
/// /path/to/build/host-triple/test/ui/relative/
285+
/// This is created early when tests are collected to avoid race conditions.
286+
pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
287+
config.build_base.join(relative_dir)
288+
}
289+
290+
/// Generates a unique name for the test, such as `testname.revision.mode`.
291+
pub fn output_testname_unique(
292+
config: &Config,
293+
testpaths: &TestPaths,
294+
revision: Option<&str>,
295+
) -> PathBuf {
296+
let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
297+
PathBuf::from(&testpaths.file.file_stem().unwrap())
298+
.with_extra_extension(revision.unwrap_or(""))
299+
.with_extra_extension(mode)
300+
}
301+
302+
/// Absolute path to the directory where all output for the given
303+
/// test/revision should reside. Example:
304+
/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
305+
pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
306+
output_relative_path(config, &testpaths.relative_dir)
307+
.join(output_testname_unique(config, testpaths, revision))
308+
}
309+
310+
/// Absolute path to the base filename used as output for the given
311+
/// test/revision. Example:
312+
/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname
313+
pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
314+
output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
315+
}

src/tools/compiletest/src/main.rs

+81-65
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ extern crate test;
3232
extern crate rustfix;
3333

3434
use common::CompareMode;
35-
use common::{expected_output_path, UI_EXTENSIONS};
35+
use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
3636
use common::{Config, TestPaths};
3737
use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
3838
use filetime::FileTime;
3939
use getopts::Options;
4040
use std::env;
4141
use std::ffi::OsString;
4242
use std::fs;
43-
use std::io;
43+
use std::io::{self, Read};
4444
use std::path::{Path, PathBuf};
4545
use std::process::Command;
4646
use test::ColorConfig;
@@ -552,10 +552,9 @@ fn collect_tests_from_dir(
552552
if name == *"Makefile" && config.mode == Mode::RunMake {
553553
let paths = TestPaths {
554554
file: dir.to_path_buf(),
555-
base: base.to_path_buf(),
556555
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
557556
};
558-
tests.push(make_test(config, &paths));
557+
tests.extend(make_test(config, &paths));
559558
return Ok(());
560559
}
561560
}
@@ -566,7 +565,7 @@ fn collect_tests_from_dir(
566565
// sequential loop because otherwise, if we do it in the
567566
// tests themselves, they race for the privilege of
568567
// creating the directories and sometimes fail randomly.
569-
let build_dir = config.build_base.join(&relative_dir_path);
568+
let build_dir = output_relative_path(config, relative_dir_path);
570569
fs::create_dir_all(&build_dir).unwrap();
571570

572571
// Add each `.rs` file as a test, and recurse further on any
@@ -580,21 +579,12 @@ fn collect_tests_from_dir(
580579
debug!("found test file: {:?}", file_path.display());
581580
let paths = TestPaths {
582581
file: file_path,
583-
base: base.to_path_buf(),
584582
relative_dir: relative_dir_path.to_path_buf(),
585583
};
586-
tests.push(make_test(config, &paths))
584+
tests.extend(make_test(config, &paths))
587585
} else if file_path.is_dir() {
588586
let relative_file_path = relative_dir_path.join(file.file_name());
589-
if &file_name == "auxiliary" {
590-
// `aux` directories contain other crates used for
591-
// cross-crate tests. Don't search them for tests, but
592-
// do create a directory in the build dir for them,
593-
// since we will dump intermediate output in there
594-
// sometimes.
595-
let build_dir = config.build_base.join(&relative_file_path);
596-
fs::create_dir_all(&build_dir).unwrap();
597-
} else {
587+
if &file_name != "auxiliary" {
598588
debug!("found directory: {:?}", file_path.display());
599589
collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
600590
}
@@ -617,7 +607,7 @@ pub fn is_test(file_name: &OsString) -> bool {
617607
!invalid_prefixes.iter().any(|p| file_name.starts_with(p))
618608
}
619609

620-
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
610+
pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
621611
let early_props = if config.mode == Mode::RunMake {
622612
// Allow `ignore` directives to be in the Makefile.
623613
EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
@@ -637,46 +627,68 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn
637627
},
638628
};
639629

640-
// Debugging emscripten code doesn't make sense today
641-
let ignore = early_props.ignore || !up_to_date(config, testpaths, &early_props)
642-
|| (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
643-
&& config.target.contains("emscripten");
644-
645-
test::TestDescAndFn {
646-
desc: test::TestDesc {
647-
name: make_test_name(config, testpaths),
648-
ignore,
649-
should_panic,
650-
allow_fail: false,
651-
},
652-
testfn: make_test_closure(config, testpaths),
653-
}
630+
// Incremental tests are special, they inherently cannot be run in parallel.
631+
// `runtest::run` will be responsible for iterating over revisions.
632+
let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
633+
vec![None]
634+
} else {
635+
early_props.revisions.iter().map(|r| Some(r)).collect()
636+
};
637+
revisions
638+
.into_iter()
639+
.map(|revision| {
640+
// Debugging emscripten code doesn't make sense today
641+
let ignore = early_props.ignore
642+
|| !up_to_date(
643+
config,
644+
testpaths,
645+
&early_props,
646+
revision.map(|s| s.as_str()),
647+
)
648+
|| (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
649+
&& config.target.contains("emscripten");
650+
test::TestDescAndFn {
651+
desc: test::TestDesc {
652+
name: make_test_name(config, testpaths, revision),
653+
ignore,
654+
should_panic,
655+
allow_fail: false,
656+
},
657+
testfn: make_test_closure(config, testpaths, revision),
658+
}
659+
})
660+
.collect()
654661
}
655662

656-
fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
657-
let mode_suffix = match config.compare_mode {
658-
Some(ref mode) => format!("-{}", mode.to_str()),
659-
None => format!(""),
660-
};
661-
let stamp_name = format!(
662-
"{}-{}{}.stamp",
663-
testpaths.file.file_name().unwrap().to_str().unwrap(),
664-
config.stage_id,
665-
mode_suffix
666-
);
667-
config
668-
.build_base
669-
.canonicalize()
670-
.unwrap_or_else(|_| config.build_base.clone())
671-
.join(&testpaths.relative_dir)
672-
.join(stamp_name)
663+
fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
664+
output_base_dir(config, testpaths, revision).join("stamp")
673665
}
674666

675-
fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool {
667+
fn up_to_date(
668+
config: &Config,
669+
testpaths: &TestPaths,
670+
props: &EarlyProps,
671+
revision: Option<&str>,
672+
) -> bool {
673+
let stamp_name = stamp(config, testpaths, revision);
674+
// Check hash.
675+
let mut f = match fs::File::open(&stamp_name) {
676+
Ok(f) => f,
677+
Err(_) => return true,
678+
};
679+
let mut contents = String::new();
680+
f.read_to_string(&mut contents)
681+
.expect("Can't read stamp contents");
682+
let expected_hash = runtest::compute_stamp_hash(config);
683+
if contents != expected_hash {
684+
return true;
685+
}
686+
687+
// Check timestamps.
676688
let rust_src_dir = config
677689
.find_rust_src_root()
678690
.expect("Could not find Rust source root");
679-
let stamp = mtime(&stamp(config, testpaths));
691+
let stamp = mtime(&stamp_name);
680692
let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
681693
for aux in props.aux.iter() {
682694
inputs.push(mtime(&testpaths
@@ -714,16 +726,8 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
714726

715727
// UI test files.
716728
for extension in UI_EXTENSIONS {
717-
for revision in &props.revisions {
718-
let path =
719-
&expected_output_path(testpaths, Some(revision), &config.compare_mode, extension);
720-
inputs.push(mtime(path));
721-
}
722-
723-
if props.revisions.is_empty() {
724-
let path = &expected_output_path(testpaths, None, &config.compare_mode, extension);
725-
inputs.push(mtime(path));
726-
}
729+
let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
730+
inputs.push(mtime(path));
727731
}
728732

729733
inputs.iter().any(|input| *input > stamp)
@@ -735,7 +739,11 @@ fn mtime(path: &Path) -> FileTime {
735739
.unwrap_or_else(|_| FileTime::zero())
736740
}
737741

738-
pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
742+
fn make_test_name(
743+
config: &Config,
744+
testpaths: &TestPaths,
745+
revision: Option<&String>,
746+
) -> test::TestName {
739747
// Convert a complete path to something like
740748
//
741749
// run-pass/foo/bar/baz.rs
@@ -747,17 +755,25 @@ pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName
747755
None => format!(""),
748756
};
749757
test::DynTestName(format!(
750-
"[{}{}] {}",
758+
"[{}{}] {}{}",
751759
config.mode,
752760
mode_suffix,
753-
path.display()
761+
path.display(),
762+
revision.map_or("".to_string(), |rev| format!("#{}", rev))
754763
))
755764
}
756765

757-
pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
766+
fn make_test_closure(
767+
config: &Config,
768+
testpaths: &TestPaths,
769+
revision: Option<&String>,
770+
) -> test::TestFn {
758771
let config = config.clone();
759772
let testpaths = testpaths.clone();
760-
test::DynTestFn(Box::new(move || runtest::run(config, &testpaths)))
773+
let revision = revision.cloned();
774+
test::DynTestFn(Box::new(move || {
775+
runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
776+
}))
761777
}
762778

763779
/// Returns (Path to GDB, GDB Version, GDB has Rust Support)

0 commit comments

Comments
 (0)