Skip to content

Commit cb112dc

Browse files
committed
add UI testing framework
1 parent 33a5c9d commit cb112dc

File tree

9 files changed

+236
-3
lines changed

9 files changed

+236
-3
lines changed

mk/tests.mk

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \
274274
check-stage$(1)-T-$(2)-H-$(3)-debuginfo-gdb-exec \
275275
check-stage$(1)-T-$(2)-H-$(3)-debuginfo-lldb-exec \
276276
check-stage$(1)-T-$(2)-H-$(3)-incremental-exec \
277+
check-stage$(1)-T-$(2)-H-$(3)-ui-exec \
277278
check-stage$(1)-T-$(2)-H-$(3)-doc-exec \
278279
check-stage$(1)-T-$(2)-H-$(3)-pretty-exec
279280

@@ -452,6 +453,9 @@ CODEGEN_CC := $(call rwildcard,$(S)src/test/codegen/,*.cc)
452453
CODEGEN_UNITS_RS := $(call rwildcard,$(S)src/test/codegen-units/,*.rs)
453454
INCREMENTAL_RS := $(call rwildcard,$(S)src/test/incremental/,*.rs)
454455
RMAKE_RS := $(wildcard $(S)src/test/run-make/*/Makefile)
456+
UI_RS := $(call rwildcard,$(S)src/test/ui/,*.rs) \
457+
$(call rwildcard,$(S)src/test/ui/,*.stdout) \
458+
$(call rwildcard,$(S)src/test/ui/,*.stderr)
455459
RUSTDOCCK_RS := $(call rwildcard,$(S)src/test/rustdoc/,*.rs)
456460

457461
RPASS_TESTS := $(RPASS_RS)
@@ -469,6 +473,7 @@ CODEGEN_TESTS := $(CODEGEN_RS) $(CODEGEN_CC)
469473
CODEGEN_UNITS_TESTS := $(CODEGEN_UNITS_RS)
470474
INCREMENTAL_TESTS := $(INCREMENTAL_RS)
471475
RMAKE_TESTS := $(RMAKE_RS)
476+
UI_TESTS := $(UI_RS)
472477
RUSTDOCCK_TESTS := $(RUSTDOCCK_RS)
473478

474479
CTEST_SRC_BASE_rpass = run-pass
@@ -541,6 +546,11 @@ CTEST_BUILD_BASE_rmake = run-make
541546
CTEST_MODE_rmake = run-make
542547
CTEST_RUNTOOL_rmake = $(CTEST_RUNTOOL)
543548

549+
CTEST_SRC_BASE_ui = ui
550+
CTEST_BUILD_BASE_ui = ui
551+
CTEST_MODE_ui = ui
552+
CTEST_RUNTOOL_ui = $(CTEST_RUNTOOL)
553+
544554
CTEST_SRC_BASE_rustdocck = rustdoc
545555
CTEST_BUILD_BASE_rustdocck = rustdoc
546556
CTEST_MODE_rustdocck = rustdoc
@@ -672,7 +682,7 @@ CTEST_DEPS_codegen-units_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_UNITS_TESTS)
672682
CTEST_DEPS_incremental_$(1)-T-$(2)-H-$(3) = $$(INCREMENTAL_TESTS)
673683
CTEST_DEPS_rmake_$(1)-T-$(2)-H-$(3) = $$(RMAKE_TESTS) \
674684
$$(CSREQ$(1)_T_$(3)_H_$(3)) $$(SREQ$(1)_T_$(2)_H_$(3))
675-
685+
CTEST_DEPS_ui_$(1)-T-$(2)-H-$(3) = $$(UI_TESTS)
676686
CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \
677687
$$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \
678688
$(S)src/etc/htmldocck.py
@@ -744,7 +754,7 @@ endef
744754

745755
CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \
746756
debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck incremental \
747-
rmake
757+
rmake ui
748758

749759
$(foreach host,$(CFG_HOST), \
750760
$(eval $(foreach target,$(CFG_TARGET), \
@@ -943,6 +953,7 @@ TEST_GROUPS = \
943953
codegen \
944954
codegen-units \
945955
incremental \
956+
ui \
946957
doc \
947958
$(foreach docname,$(DOC_NAMES),doc-$(docname)) \
948959
pretty \

src/test/ui/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Guide to the UI Tests
2+
3+
The UI tests are intended to capture the compiler's complete output,
4+
so that we can test all aspects of the presentation. They work by
5+
compiling a file (e.g., `hello_world/main.rs`), capturing the output,
6+
and then applying some normalization (see below). This normalized
7+
result is then compared against reference files named
8+
`hello_world/main.stderr` and `hello_world/main.stdout`. If either of
9+
those files doesn't exist, the output must be empty. If the test run
10+
fails, we will print out the current output, but it is also saved in
11+
`build/<target-triple>/test/ui/hello_world/main.stdout` (this path is
12+
printed as part of the test failure mesage), so you can run `diff` and
13+
so forth.
14+
15+
# Editing and updating the reference files
16+
17+
If you have changed the compiler's output intentionally, or you are
18+
making a new test, you can use the script `update-references.sh` to
19+
update the references. When you run the test framework, it will report
20+
various errors: in those errors is a command you can use to run the
21+
`update-references.sh` script, which will then copy over the files
22+
from the build directory and use them as the new reference. You can
23+
also just run `update-all-references.sh`. In both cases, you can run
24+
the script with `--help` to get a help message.
25+
26+
# Normalization
27+
28+
The normalization applied is aimed at filenames:
29+
30+
- the test directory is replaced with `$DIR`
31+
- all backslashes (\) are converted to forward slashes (/) (for windows)

src/test/ui/hello_world/main.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that compiling hello world succeeds with no output of any kind.
12+
13+
fn main() {
14+
println!("Hello, world!");
15+
}

src/test/ui/mismatched_types/main.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// rustc-env:RUST_NEW_ERROR_FORMAT
12+
13+
fn main() {
14+
let x: u32 = (
15+
);
16+
}
17+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: mismatched types [--explain E0308]
2+
--> $DIR/main.rs:14:18
3+
14 |> let x: u32 = (
4+
|> ^ expected u32, found ()
5+
note: expected type `u32`
6+
note: found type `()`
7+
8+
error: aborting due to previous error

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
3+
# A script to update the references for all tests. The idea is that
4+
# you do a run, which will generate files in the build directory
5+
# containing the (normalized) actual output of the compiler. You then
6+
# run this script, which will copy those files over. If you find
7+
# yourself manually editing a foo.stderr file, you're doing it wrong.
8+
#
9+
# See all `update-references.sh`, if you just want to update a single test.
10+
11+
if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" != "" ]]; then
12+
echo "usage: $0 <build-directory>"
13+
echo ""
14+
echo "For example:"
15+
echo " $0 ../../../build/x86_64-apple-darwin/test/ui"
16+
fi
17+
18+
BUILD_DIR=$PWD/$1
19+
MY_DIR=$(dirname $0)
20+
cd $MY_DIR
21+
find . -name '*.rs' | xargs ./update-references.sh $BUILD_DIR

src/test/ui/update-references.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
3+
# A script to update the references for particular tests. The idea is
4+
# that you do a run, which will generate files in the build directory
5+
# containing the (normalized) actual output of the compiler. This
6+
# script will then copy that output and replace the "expected output"
7+
# files. You can then commit the changes.
8+
#
9+
# If you find yourself manually editing a foo.stderr file, you're
10+
# doing it wrong.
11+
12+
if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
13+
echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
14+
echo ""
15+
echo "For example:"
16+
echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
17+
fi
18+
19+
MYDIR=$(dirname $0)
20+
21+
BUILD_DIR="$1"
22+
shift
23+
24+
while [[ "$1" != "" ]]; do
25+
STDERR_NAME="${1/%.rs/.stderr}"
26+
STDOUT_NAME="${1/%.rs/.stdout}"
27+
shift
28+
if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
29+
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME > /dev/null); then
30+
echo updating $MYDIR/$STDOUT_NAME
31+
cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
32+
fi
33+
34+
if [ -f $BUILD_DIR/$STDERR_NAME ] && \
35+
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME > /dev/null); then
36+
echo updating $MYDIR/$STDERR_NAME
37+
cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
38+
fi
39+
done
40+
41+

src/tools/compiletest/src/common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub enum Mode {
2828
CodegenUnits,
2929
Incremental,
3030
RunMake,
31+
Ui,
3132
}
3233

3334
impl FromStr for Mode {
@@ -47,6 +48,7 @@ impl FromStr for Mode {
4748
"codegen-units" => Ok(CodegenUnits),
4849
"incremental" => Ok(Incremental),
4950
"run-make" => Ok(RunMake),
51+
"ui" => Ok(Ui),
5052
_ => Err(()),
5153
}
5254
}
@@ -68,6 +70,7 @@ impl fmt::Display for Mode {
6870
CodegenUnits => "codegen-units",
6971
Incremental => "incremental",
7072
RunMake => "run-make",
73+
Ui => "ui",
7174
}, f)
7275
}
7376
}

src/tools/compiletest/src/runtest.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use common::Config;
1212
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
1313
use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
14-
use common::{Incremental, RunMake};
14+
use common::{Incremental, RunMake, Ui};
1515
use errors::{self, ErrorKind, Error};
1616
use json;
1717
use header::TestProps;
@@ -29,6 +29,7 @@ use std::io::prelude::*;
2929
use std::net::TcpStream;
3030
use std::path::{Path, PathBuf};
3131
use std::process::{Command, Output, ExitStatus};
32+
use std::str;
3233

3334
pub fn run(config: Config, testpaths: &TestPaths) {
3435
match &*config.target {
@@ -118,6 +119,7 @@ impl<'test> TestCx<'test> {
118119
CodegenUnits => self.run_codegen_units_test(),
119120
Incremental => self.run_incremental_test(),
120121
RunMake => self.run_rmake_test(),
122+
Ui => self.run_ui_test(),
121123
}
122124
}
123125

@@ -1314,6 +1316,7 @@ actual:\n\
13141316
Codegen |
13151317
Rustdoc |
13161318
RunMake |
1319+
Ui |
13171320
CodegenUnits => {
13181321
// do not use JSON output
13191322
}
@@ -2096,6 +2099,89 @@ actual:\n\
20962099
}
20972100
fs::remove_dir(path)
20982101
}
2102+
2103+
fn run_ui_test(&self) {
2104+
println!("ui: {}", self.testpaths.file.display());
2105+
2106+
let proc_res = self.compile_test();
2107+
2108+
let expected_stderr_path = self.expected_output_path("stderr");
2109+
let expected_stderr = self.load_expected_output(&expected_stderr_path);
2110+
2111+
let expected_stdout_path = self.expected_output_path("stdout");
2112+
let expected_stdout = self.load_expected_output(&expected_stdout_path);
2113+
2114+
let normalized_stdout = self.normalize_output(&proc_res.stdout);
2115+
let normalized_stderr = self.normalize_output(&proc_res.stderr);
2116+
2117+
let mut errors = 0;
2118+
errors += self.compare_output("stdout", normalized_stdout.as_bytes(), &expected_stdout);
2119+
errors += self.compare_output("stderr", normalized_stderr.as_bytes(), &expected_stderr);
2120+
2121+
if errors > 0 {
2122+
println!("To update references, run this command from build directory:");
2123+
let relative_path_to_file =
2124+
self.testpaths.relative_dir
2125+
.join(self.testpaths.file.file_name().unwrap());
2126+
println!("{}/update-references.sh '{}' '{}'",
2127+
self.config.src_base.display(),
2128+
self.config.build_base.display(),
2129+
relative_path_to_file.display());
2130+
self.fatal(&format!("{} errors occurred comparing output.", errors));
2131+
}
2132+
}
2133+
2134+
fn normalize_output(&self, output: &str) -> String {
2135+
let parent_dir = self.testpaths.file.parent().unwrap();
2136+
let parent_dir_str = parent_dir.display().to_string();
2137+
output.replace(&parent_dir_str, "$DIR")
2138+
.replace("\\", "/") // windows, you know.
2139+
}
2140+
2141+
fn expected_output_path(&self, kind: &str) -> PathBuf {
2142+
let extension = match self.revision {
2143+
Some(r) => format!("{}.{}", r, kind),
2144+
None => kind.to_string(),
2145+
};
2146+
self.testpaths.file.with_extension(extension)
2147+
}
2148+
2149+
fn load_expected_output(&self, path: &Path) -> Vec<u8> {
2150+
if !path.exists() {
2151+
return vec![];
2152+
}
2153+
2154+
let mut result = Vec::new();
2155+
match File::open(path).and_then(|mut f| f.read_to_end(&mut result)) {
2156+
Ok(_) => result,
2157+
Err(e) => {
2158+
self.fatal(&format!("failed to load expected output from `{}`: {}", path.display(), e))
2159+
}
2160+
}
2161+
}
2162+
2163+
fn compare_output(&self, kind: &str, actual: &[u8], expected: &[u8]) -> usize {
2164+
if self.config.verbose {
2165+
println!("normalized {}:\n{}\n", kind, str::from_utf8(actual).unwrap_or("not utf8"));
2166+
println!("expected {}:\n{}\n", kind, str::from_utf8(expected).unwrap_or("not utf8"));
2167+
}
2168+
if actual == expected {
2169+
return 0;
2170+
}
2171+
2172+
let output_file = self.output_base_name().with_extension(kind);
2173+
match File::create(&output_file).and_then(|mut f| f.write_all(actual)) {
2174+
Ok(()) => { }
2175+
Err(e) => {
2176+
self.fatal(&format!("failed to write {} to `{}`: {}",
2177+
kind, output_file.display(), e))
2178+
}
2179+
}
2180+
2181+
println!("\nThe actual {0} differed from the expected {0}.", kind);
2182+
println!("Actual {} saved to {}", kind, output_file.display());
2183+
1
2184+
}
20992185
}
21002186

21012187
struct ProcArgs {

0 commit comments

Comments
 (0)