Skip to content

Commit 4d4de48

Browse files
bdalrhmadpaco-aws
authored andcommitted
Move cargo tests to compiletest. (rust-lang#198)
* Move cargo tests to compiletest. * Rename `cargo` to `cargo-rmc`. * Add an expected line to simple-main test. Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com>
1 parent 140f2b4 commit 4d4de48

File tree

20 files changed

+101
-93
lines changed

20 files changed

+101
-93
lines changed

cargo-rmc-tests/.gitignore

-7
This file was deleted.

cargo-rmc-tests/empty-main/expected.main

-1
This file was deleted.

cargo-rmc-tests/run.py

-56
This file was deleted.

scripts/cargo-rmc

+9-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import argparse
66
import glob
77
import sys
88
import rmc
9+
import os
910

1011

1112
EXIT_CODE_SUCCESS = 0
@@ -25,6 +26,8 @@ def main():
2526
parser.add_argument("--mangler", default="v0")
2627
parser.add_argument("--visualize", action="store_true")
2728
parser.add_argument("--srcdir", default=".")
29+
parser.add_argument("--target-dir", default="target",
30+
help="Directory for all generated artifacts")
2831
parser.add_argument("--wkdir", default=".")
2932
parser.add_argument("crate", help="crate to verify")
3033
parser.add_argument("cbmc_args", nargs=argparse.REMAINDER,
@@ -42,15 +45,16 @@ def main():
4245
if not rmc.dependencies_in_path():
4346
return 1
4447

45-
rmc.cargo_build(args.crate, args.verbose, args.debug, args.mangler)
48+
rmc.cargo_build(args.crate, args.target_dir,
49+
args.verbose, args.debug, args.mangler)
4650

47-
pattern = f"target/debug/deps/*.json"
51+
pattern = os.path.join(args.target_dir, "debug", "deps", "*.json")
4852
jsons = glob.glob(pattern)
4953
if len(jsons) != 1:
5054
print("ERROR: unexpected number of json outputs")
5155
return 1
52-
cbmc_filename = "cbmc.out"
53-
c_filename = "cbmc.c"
56+
cbmc_filename = os.path.join(args.target_dir, "cbmc.out")
57+
c_filename = os.path.join(args.target_dir, "cbmc.c")
5458
if EXIT_CODE_SUCCESS != rmc.symbol_table_to_gotoc(jsons[0], cbmc_filename, args.verbose, args.keep_temps):
5559
return 1
5660

@@ -64,7 +68,7 @@ def main():
6468
if args.visualize:
6569
return rmc.run_visualize(cbmc_filename, args.cbmc_args, \
6670
args.verbose, args.quiet, args.keep_temps, \
67-
args.function, args.srcdir, args.wkdir)
71+
args.function, args.srcdir, args.wkdir, args.target_dir)
6872
else:
6973
return rmc.run_cbmc(cbmc_filename, args.cbmc_args, args.verbose, args.quiet)
7074

scripts/rmc-regression.sh

+2-9
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,10 @@ check-cbmc-viewer-version.py --major 2 --minor 5
2121
# Formatting check
2222
./x.py fmt --check
2323

24-
# Standalone rmc tests
24+
# Standalone rmc tests and cargo tests
2525
pushd $RUST_DIR
2626
./x.py build -i --stage 1 library/std ${EXTRA_X_PY_BUILD_ARGS}
27-
./x.py test -i --stage 1 cbmc firecracker prusti smack
28-
29-
# Standalone cargo-rmc tests
30-
cd cargo-rmc-tests
31-
for DIR in */; do
32-
./run.py $DIR
33-
done
34-
popd
27+
./x.py test -i --stage 1 cbmc firecracker prusti smack cargo-rmc
3528

3629
# run-make tests
3730
./x.py test -i --stage 1 src/test/run-make --test-args gotoc

scripts/rmc.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ def compile_single_rust_file(input_filename, output_filename, verbose=False, deb
7676
return run_cmd(build_cmd, env=build_env, label="compile", verbose=verbose, debug=debug)
7777

7878

79-
def cargo_build(crate, verbose=False, debug=False, mangler="v0"):
79+
def cargo_build(crate, target_dir="target", verbose=False, debug=False, mangler="v0"):
8080
rustflags = ["-Z", "codegen-backend=gotoc", "-Z", f"symbol-mangling-version={mangler}", f"--cfg={RMC_CFG}"]
8181
rustflags = " ".join(rustflags)
8282
if "RUSTFLAGS" in os.environ:
8383
rustflags = os.environ["RUSTFLAGS"] + " " + rustflags
8484

85-
build_cmd = ["cargo", "build"]
85+
build_cmd = ["cargo", "build", "--target-dir", target_dir]
8686
build_env = {"RUSTFLAGS": rustflags,
8787
"RUSTC": RMC_RUSTC_EXE,
8888
"PATH": os.environ["PATH"],
@@ -102,11 +102,11 @@ def run_cbmc(cbmc_filename, cbmc_args, verbose=False, quiet=False):
102102
cbmc_cmd = ["cbmc"] + cbmc_args + [cbmc_filename]
103103
return run_cmd(cbmc_cmd, label="cbmc", output_to="stdout", verbose=verbose, quiet=quiet)
104104

105-
def run_visualize(cbmc_filename, cbmc_args, verbose=False, quiet=False, keep_temps=False, function="main", srcdir=".", wkdir="."):
106-
results_filename = "results.xml"
107-
coverage_filename = "coverage.xml"
108-
property_filename = "property.xml"
109-
temp_goto_filename = "temp.goto"
105+
def run_visualize(cbmc_filename, cbmc_args, verbose=False, quiet=False, keep_temps=False, function="main", srcdir=".", wkdir=".", outdir="."):
106+
results_filename = os.path.join(outdir, "results.xml")
107+
coverage_filename = os.path.join(outdir, "coverage.xml")
108+
property_filename = os.path.join(outdir, "property.xml")
109+
temp_goto_filename = os.path.join(outdir, "temp.goto")
110110
if not keep_temps:
111111
for filename in [results_filename, coverage_filename, property_filename, temp_goto_filename]:
112112
atexit.register(delete_file, filename)
@@ -115,7 +115,7 @@ def run_visualize(cbmc_filename, cbmc_args, verbose=False, quiet=False, keep_tem
115115
# 2) cbmc --xml-ui --trace ~/rmc/library/rmc/rmc_lib.c temp.goto > results.xml
116116
# 3) cbmc --xml-ui --cover location ~/rmc/library/rmc/rmc_lib.c temp.goto > coverage.xml
117117
# 4) cbmc --xml-ui --show-properties ~/rmc/library/rmc/rmc_lib.c temp.goto > property.xml
118-
# 5) cbmc-viewer --result results.xml --coverage coverage.xml --property property.xml --srcdir . --goto temp.goto
118+
# 5) cbmc-viewer --result results.xml --coverage coverage.xml --property property.xml --srcdir . --goto temp.goto --reportdir report
119119

120120
run_goto_cc(cbmc_filename, temp_goto_filename, verbose, quiet)
121121

@@ -128,22 +128,24 @@ def run_cbmc_local(cbmc_args, output_to):
128128
run_cbmc_local(cbmc_xml_args + ["--cover", "location"], coverage_filename)
129129
run_cbmc_local(cbmc_xml_args + ["--show-properties"], property_filename)
130130

131-
run_cbmc_viewer(temp_goto_filename, results_filename, coverage_filename, property_filename, verbose, quiet, srcdir, wkdir)
131+
run_cbmc_viewer(temp_goto_filename, results_filename, coverage_filename,
132+
property_filename, verbose, quiet, srcdir, wkdir, os.path.join(outdir, "report"))
132133

133134
return retcode
134135

135136
def run_goto_cc(src, dst, verbose=False, quiet=False, function="main"):
136137
cmd = ["goto-cc"] + ["--function", function] + [src] + ["-o", dst]
137138
return run_cmd(cmd, label="goto-cc", verbose=verbose, quiet=quiet)
138139

139-
def run_cbmc_viewer(goto_filename, results_filename, coverage_filename, property_filename, verbose=False, quiet=False, srcdir=".", wkdir="."):
140+
def run_cbmc_viewer(goto_filename, results_filename, coverage_filename, property_filename, verbose=False, quiet=False, srcdir=".", wkdir=".", reportdir="report"):
140141
cmd = ["cbmc-viewer"] + \
141142
["--result", results_filename] + \
142143
["--coverage", coverage_filename] + \
143144
["--property", property_filename] + \
144145
["--srcdir", os.path.realpath(srcdir)] + \
145146
["--wkdir", os.path.realpath(wkdir)] + \
146-
["--goto", goto_filename]
147+
["--goto", goto_filename] + \
148+
["--reportdir", reportdir]
147149
return run_cmd(cmd, label="cbmc-viewer", verbose=verbose, quiet=quiet)
148150

149151

src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ impl<'a> Builder<'a> {
468468
test::Prusti,
469469
test::Serial,
470470
test::SMACK,
471+
test::CargoRMC,
471472
// Run run-make last, since these won't pass without make on Windows
472473
test::RunMake,
473474
),

src/bootstrap/test.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,8 @@ default_test!(Serial { path: "src/test/serial", mode: "rmc", suite: "serial" });
12101210

12111211
default_test!(SMACK { path: "src/test/smack", mode: "rmc", suite: "smack" });
12121212

1213+
default_test!(CargoRMC { path: "src/test/cargo-rmc", mode: "cargo-rmc", suite: "cargo-rmc" });
1214+
12131215
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
12141216
struct Compiletest {
12151217
compiler: Compiler,

src/test/cargo-rmc/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Cargo.lock
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[main.assertion.1] line 4 assertion failed: 1 == 2: FAILURE
2+
VERIFICATION FAILED
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
3-
fn main() {}
3+
fn main() {
4+
assert!(1 == 2);
5+
}

src/tools/compiletest/src/common.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub enum Mode {
2424
MirOpt,
2525
Assembly,
2626
RMC,
27+
CargoRMC,
2728
}
2829

2930
impl Mode {
@@ -55,6 +56,7 @@ impl FromStr for Mode {
5556
"mir-opt" => Ok(MirOpt),
5657
"assembly" => Ok(Assembly),
5758
"rmc" => Ok(RMC),
59+
"cargo-rmc" => Ok(CargoRMC),
5860
_ => Err(()),
5961
}
6062
}
@@ -77,6 +79,7 @@ impl fmt::Display for Mode {
7779
MirOpt => "mir-opt",
7880
Assembly => "assembly",
7981
RMC => "rmc",
82+
CargoRMC => "cargo-rmc",
8083
};
8184
fmt::Display::fmt(s, f)
8285
}

src/tools/compiletest/src/main.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
7676
"mode",
7777
"which sort of compile tests to run",
7878
"run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
79-
| rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly | rmc",
79+
| rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly | rmc | cargo-rmc",
8080
)
8181
.reqopt(
8282
"",
@@ -587,6 +587,24 @@ fn collect_tests_from_dir(
587587
let build_dir = output_relative_path(config, relative_dir_path);
588588
fs::create_dir_all(&build_dir).unwrap();
589589

590+
// If we find a `Cargo.toml` file in the current directory and we're in
591+
// Cargo-rmc mode, we should look for `*.expected` files and create an
592+
// output directory corresponding to each to avoid race conditions during
593+
// the testing phase. We immediately return after adding the tests to avoid
594+
// treating `*.rs` files as tests.
595+
if config.mode == Mode::CargoRMC && dir.join("Cargo.toml").exists() {
596+
for file in fs::read_dir(dir)? {
597+
let file_path = file?.path();
598+
if file_path.to_str().unwrap().ends_with(".expected") {
599+
fs::create_dir_all(&build_dir.join(file_path.file_stem().unwrap())).unwrap();
600+
let paths =
601+
TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
602+
tests.extend(make_test(config, &paths, inputs));
603+
}
604+
}
605+
return Ok(());
606+
}
607+
590608
// Add each `.rs` file as a test, and recurse further on any
591609
// subdirectories we find, except for `aux` directories.
592610
for file in fs::read_dir(dir)? {

src/tools/compiletest/src/runtest.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
44
use crate::common::{output_base_dir, output_base_name, output_testname_unique};
5-
use crate::common::{Assembly, Incremental, JsDocTest, MirOpt, RunMake, RustdocJson, Ui, RMC};
5+
use crate::common::{
6+
Assembly, CargoRMC, Incremental, JsDocTest, MirOpt, RunMake, RustdocJson, Ui, RMC,
7+
};
68
use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc};
79
use crate::common::{CompareMode, FailMode, PassMode};
810
use crate::common::{Config, TestPaths};
@@ -352,6 +354,7 @@ impl<'test> TestCx<'test> {
352354
Assembly => self.run_assembly_test(),
353355
JsDocTest => self.run_js_doc_test(),
354356
RMC => self.run_rmc_test(),
357+
CargoRMC => self.run_cargo_rmc_test(),
355358
}
356359
}
357360

@@ -2023,7 +2026,7 @@ impl<'test> TestCx<'test> {
20232026
rustc.arg(dir_opt);
20242027
}
20252028
RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson | RunMake
2026-
| CodegenUnits | JsDocTest | Assembly | RMC => {
2029+
| CodegenUnits | JsDocTest | Assembly | RMC | CargoRMC => {
20272030
// do not use JSON output
20282031
}
20292032
}
@@ -2426,6 +2429,47 @@ impl<'test> TestCx<'test> {
24262429
}
24272430
}
24282431

2432+
/// Runs cargo-rmc on the function specified by the stem of `self.testpaths.file`.
2433+
/// An error message is printed to stdout if verification result does not
2434+
/// contain the expected output in `self.testpaths.file`.
2435+
fn run_cargo_rmc_test(&self) {
2436+
// We create our own command for the same reasons listed in `run_rmc_test` method.
2437+
let mut cargo = Command::new("cargo");
2438+
// We run `cargo` on the directory where we found the `*.expected` file
2439+
let parent_dir = self.testpaths.file.parent().unwrap();
2440+
// The name of the function to test is the same as the stem of `*.expected` file
2441+
let function_name = self.testpaths.file.file_stem().unwrap().to_str().unwrap();
2442+
cargo
2443+
.arg("rmc")
2444+
.arg("--target")
2445+
.arg(self.output_base_dir().join("target"))
2446+
.arg(&parent_dir)
2447+
.arg("--")
2448+
.args(["--function", function_name]);
2449+
self.add_rmc_dir_to_path(&mut cargo);
2450+
let proc_res = self.compose_and_run_compiler(cargo, None);
2451+
let expected = fs::read_to_string(self.testpaths.file.clone()).unwrap();
2452+
// Print an error if the verification result does not contains the expected lines.
2453+
if let Some(line) = TestCx::contains_lines(&proc_res.stdout, expected.split('\n').collect())
2454+
{
2455+
self.fatal_proc_rec(
2456+
&format!("test failed: expected output to contain the line: {}", line),
2457+
&proc_res,
2458+
);
2459+
}
2460+
}
2461+
2462+
/// Looks for each line in `str`. Returns `None` if all line are in `str`.
2463+
/// Otherwise, it returns the first line not found in `str`.
2464+
fn contains_lines<'a>(str: &str, lines: Vec<&'a str>) -> Option<&'a str> {
2465+
for line in lines {
2466+
if !str.contains(line) {
2467+
return Some(line);
2468+
}
2469+
}
2470+
None
2471+
}
2472+
24292473
fn charset() -> &'static str {
24302474
// FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
24312475
if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }

0 commit comments

Comments
 (0)