]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/test-cargo-miri/run-test.py
Rollup merge of #104641 - tshepang:grammar, r=Mark-Simulacrum
[rust.git] / src / tools / miri / test-cargo-miri / run-test.py
1 #!/usr/bin/env python3
2 '''
3 Test whether cargo-miri works properly.
4 Assumes the `MIRI_SYSROOT` env var to be set appropriately,
5 and the working directory to contain the cargo-miri-test project.
6 '''
7
8 import sys, subprocess, os, re, difflib
9
10 CGREEN  = '\33[32m'
11 CBOLD   = '\33[1m'
12 CEND    = '\33[0m'
13
14 def fail(msg):
15     print("\nTEST FAIL: {}".format(msg))
16     sys.exit(1)
17
18 def cargo_miri(cmd, quiet = True):
19     args = ["cargo", "miri", cmd]
20     if quiet:
21         args += ["-q"]
22     if 'MIRI_TEST_TARGET' in os.environ:
23         args += ["--target", os.environ['MIRI_TEST_TARGET']]
24     return args
25
26 def normalize_stdout(str):
27     str = str.replace("src\\", "src/") # normalize paths across platforms
28     str = re.sub("finished in \d+\.\d\ds", "finished in $TIME", str) # the time keeps changing, obviously
29     return str
30
31 def normalize_stderr(str):
32     str = re.sub("Preparing a sysroot for Miri \(target: [a-z0-9_-]+\)\.\.\. done\n", "", str) # remove leading cargo-miri setup output
33     return str
34
35 def check_output(actual, path, name):
36     if 'MIRI_BLESS' in os.environ:
37         open(path, mode='w').write(actual)
38         return True
39     expected = open(path).read()
40     if expected == actual:
41         return True
42     print(f"{name} output did not match reference in {path}!")
43     print(f"--- BEGIN diff {name} ---")
44     for text in difflib.unified_diff(expected.split("\n"), actual.split("\n")):
45         print(text)
46     print(f"--- END diff {name} ---")
47     return False
48
49 def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}):
50     print("Testing {}...".format(name))
51     ## Call `cargo miri`, capture all output
52     p_env = os.environ.copy()
53     p_env.update(env)
54     p = subprocess.Popen(
55         cmd,
56         stdin=subprocess.PIPE,
57         stdout=subprocess.PIPE,
58         stderr=subprocess.PIPE,
59         env=p_env,
60     )
61     (stdout, stderr) = p.communicate(input=stdin)
62     stdout = normalize_stdout(stdout.decode("UTF-8"))
63     stderr = normalize_stderr(stderr.decode("UTF-8"))
64
65     stdout_matches = check_output(stdout, stdout_ref, "stdout")
66     stderr_matches = check_output(stderr, stderr_ref, "stderr")
67     
68     if p.returncode == 0 and stdout_matches and stderr_matches:
69         # All good!
70         return
71     fail("exit code was {}".format(p.returncode))
72
73 def test_no_rebuild(name, cmd, env={}):
74     print("Testing {}...".format(name))
75     p_env = os.environ.copy()
76     p_env.update(env)
77     p = subprocess.Popen(
78         cmd,
79         stdout=subprocess.PIPE,
80         stderr=subprocess.PIPE,
81         env=p_env,
82     )
83     (stdout, stderr) = p.communicate()
84     stdout = stdout.decode("UTF-8")
85     stderr = stderr.decode("UTF-8")
86     if p.returncode != 0:
87         fail("rebuild failed");
88     # Also check for 'Running' as a sanity check.
89     if stderr.count(" Compiling ") > 0 or stderr.count(" Running ") == 0:
90         print("--- BEGIN stderr ---")
91         print(stderr, end="")
92         print("--- END stderr ---")
93         fail("Something was being rebuilt when it should not be (or we got no output)");
94
95 def test_cargo_miri_run():
96     test("`cargo miri run` (no isolation)",
97         cargo_miri("run"),
98         "run.default.stdout.ref", "run.default.stderr.ref",
99         stdin=b'12\n21\n',
100         env={
101             'MIRIFLAGS': "-Zmiri-disable-isolation",
102             'MIRITESTVAR': "wrongval", # make sure the build.rs value takes precedence
103         },
104     )
105     # Special test: run it again *without* `-q` to make sure nothing is being rebuilt (Miri issue #1722)
106     test_no_rebuild("`cargo miri run` (no rebuild)",
107         cargo_miri("run", quiet=False) + ["--", ""],
108         env={'MIRITESTVAR': "wrongval"}, # changing the env var causes a rebuild (re-runs build.rs),
109                                          # so keep it set
110     )
111     test("`cargo miri run` (with arguments and target)",
112         cargo_miri("run") + ["--bin", "cargo-miri-test", "--", "hello world", '"hello world"', r'he\\llo\"world'],
113         "run.args.stdout.ref", "run.args.stderr.ref",
114     )
115     test("`cargo miri r` (subcrate, no isolation)",
116         cargo_miri("r") + ["-p", "subcrate"],
117         "run.subcrate.stdout.ref", "run.subcrate.stderr.ref",
118         env={'MIRIFLAGS': "-Zmiri-disable-isolation"},
119     )
120     test("`cargo miri run` (custom target dir)",
121         # Attempt to confuse the argument parser.
122         cargo_miri("run") + ["--target-dir=custom-run", "--", "--target-dir=target/custom-run"],
123         "run.args.stdout.ref", "run.custom-target-dir.stderr.ref",
124     )
125
126 def test_cargo_miri_test():
127     # rustdoc is not run on foreign targets
128     is_foreign = 'MIRI_TEST_TARGET' in os.environ
129     default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref"
130     filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref"
131
132     # macOS needs permissive provenance inside getrandom_1.
133     test("`cargo miri test`",
134         cargo_miri("test"),
135         default_ref, "test.stderr-empty.ref",
136         env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-seed=4242"},
137     )
138     test("`cargo miri test` (no isolation, no doctests)",
139         cargo_miri("test") + ["--bins", "--tests"], # no `--lib`, we disabled that in `Cargo.toml`
140         "test.cross-target.stdout.ref", "test.stderr-empty.ref",
141         env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-disable-isolation"},
142     )
143     test("`cargo miri test` (with filter)",
144         cargo_miri("test") + ["--", "--format=pretty", "pl"],
145         filter_ref, "test.stderr-empty.ref",
146     )
147     test("`cargo miri test` (test target)",
148         cargo_miri("test") + ["--test", "test", "--", "--format=pretty"],
149         "test.test-target.stdout.ref", "test.stderr-empty.ref",
150         env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
151     )
152     test("`cargo miri test` (bin target)",
153         cargo_miri("test") + ["--bin", "cargo-miri-test", "--", "--format=pretty"],
154         "test.bin-target.stdout.ref", "test.stderr-empty.ref",
155     )
156     test("`cargo miri t` (subcrate, no isolation)",
157         cargo_miri("t") + ["-p", "subcrate"],
158         "test.subcrate.stdout.ref", "test.stderr-proc-macro.ref",
159         env={'MIRIFLAGS': "-Zmiri-disable-isolation"},
160     )
161     test("`cargo miri test` (subcrate, doctests)",
162         cargo_miri("test") + ["-p", "subcrate", "--doc"],
163         "test.stdout-empty.ref", "test.stderr-proc-macro-doctest.ref",
164     )
165     test("`cargo miri test` (custom target dir)",
166         cargo_miri("test") + ["--target-dir=custom-test"],
167         default_ref, "test.stderr-empty.ref",
168         env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
169     )
170     del os.environ["CARGO_TARGET_DIR"] # this overrides `build.target-dir` passed by `--config`, so unset it
171     test("`cargo miri test` (config-cli)",
172         cargo_miri("test") + ["--config=build.target-dir=\"config-cli\"", "-Zunstable-options"],
173         default_ref, "test.stderr-empty.ref",
174         env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
175     )
176
177 os.chdir(os.path.dirname(os.path.realpath(__file__)))
178 os.environ["CARGO_TARGET_DIR"] = "target" # this affects the location of the target directory that we need to check
179 os.environ["RUST_TEST_NOCAPTURE"] = "0" # this affects test output, so make sure it is not set
180 os.environ["RUST_TEST_THREADS"] = "1" # avoid non-deterministic output due to concurrent test runs
181
182 target_str = " for target {}".format(os.environ['MIRI_TEST_TARGET']) if 'MIRI_TEST_TARGET' in os.environ else ""
183 print(CGREEN + CBOLD + "## Running `cargo miri` tests{}".format(target_str) + CEND)
184
185 test_cargo_miri_run()
186 test_cargo_miri_test()
187 # Ensure we did not create anything outside the expected target dir.
188 for target_dir in ["target", "custom-run", "custom-test", "config-cli"]:
189     if os.listdir(target_dir) != ["miri"]:
190         fail(f"`{target_dir}` contains unexpected files")
191     # Ensure something exists inside that target dir.
192     os.access(os.path.join(target_dir, "miri", "debug", "deps"), os.F_OK)
193
194 print("\nTEST SUCCESSFUL!")
195 sys.exit(0)