2 # SPDX-License-Identifier: GPL-2.0
4 """Tests the `rust_is_available.sh` script.
6 Some of the tests require the real programs to be available in `$PATH`
7 under their canonical name (and with the expected versions).
18 class TestRustIsAvailable(unittest
.TestCase
):
20 class Expected(enum
.Enum
):
22 SUCCESS_WITH_WARNINGS
= enum
.auto()
23 SUCCESS_WITH_EXTRA_OUTPUT
= enum
.auto()
27 def generate_executable(cls
, content
):
28 path
= pathlib
.Path(cls
.tempdir
.name
)
29 name
= str(len(tuple(path
.iterdir())))
31 with
open(path
, "w") as file_
:
33 os
.chmod(path
, os
.stat(path
).st_mode | stat
.S_IXUSR
)
37 def generate_clang(cls
, stdout
):
38 return cls
.generate_executable(f
"""#!/usr/bin/env python3
40 if "-E" in " ".join(sys.argv):
41 print({repr("Clang " + " ".join(cls.llvm_default_version.split(" ")))})
47 def generate_rustc(cls
, stdout
):
48 return cls
.generate_executable(f
"""#!/usr/bin/env python3
50 if "--print sysroot" in " ".join(sys.argv):
51 print({repr(cls.rust_default_sysroot)})
57 def generate_bindgen(cls
, version_stdout
, libclang_stderr
):
58 return cls
.generate_executable(f
"""#!/usr/bin/env python3
60 if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
61 print({repr(libclang_stderr)}, file=sys.stderr)
63 print({repr(version_stdout)})
67 def generate_bindgen_version(cls
, stdout
):
68 return cls
.generate_bindgen(stdout
, cls
.bindgen_default_bindgen_libclang_stderr
)
71 def generate_bindgen_libclang(cls
, stderr
):
72 return cls
.generate_bindgen(cls
.bindgen_default_bindgen_version_stdout
, stderr
)
76 cls
.tempdir
= tempfile
.TemporaryDirectory()
78 cls
.missing
= pathlib
.Path(cls
.tempdir
.name
) / "missing"
80 cls
.nonexecutable
= pathlib
.Path(cls
.tempdir
.name
) / "nonexecutable"
81 with
open(cls
.nonexecutable
, "w") as file_
:
82 file_
.write("nonexecutable")
84 cls
.unexpected_binary
= "true"
86 cls
.rustc_default_version
= subprocess
.check_output(("scripts/min-tool-version.sh", "rustc")).decode().strip()
87 cls
.bindgen_default_version
= subprocess
.check_output(("scripts/min-tool-version.sh", "bindgen")).decode().strip()
88 cls
.llvm_default_version
= subprocess
.check_output(("scripts/min-tool-version.sh", "llvm")).decode().strip()
89 cls
.rust_default_sysroot
= subprocess
.check_output(("rustc", "--print", "sysroot")).decode().strip()
91 cls
.bindgen_default_bindgen_version_stdout
= f
"bindgen {cls.bindgen_default_version}"
92 cls
.bindgen_default_bindgen_libclang_stderr
= f
"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {cls.llvm_default_version} [-W#pragma-messages], err: false"
94 cls
.default_rustc
= cls
.generate_rustc(f
"rustc {cls.rustc_default_version}")
95 cls
.default_bindgen
= cls
.generate_bindgen(cls
.bindgen_default_bindgen_version_stdout
, cls
.bindgen_default_bindgen_libclang_stderr
)
96 cls
.default_cc
= cls
.generate_clang(f
"clang version {cls.llvm_default_version}")
98 def run_script(self
, expected
, override_env
):
100 "RUSTC": self
.default_rustc
,
101 "BINDGEN": self
.default_bindgen
,
102 "CC": self
.default_cc
,
105 for key
, value
in override_env
.items():
111 result
= subprocess
.run("scripts/rust_is_available.sh", env
=env
, capture_output
=True)
113 # The script should never output anything to `stdout`.
114 self
.assertEqual(result
.stdout
, b
"")
116 if expected
== self
.Expected
.SUCCESS
:
117 # When expecting a success, the script should return 0
118 # and it should not output anything to `stderr`.
119 self
.assertEqual(result
.returncode
, 0)
120 self
.assertEqual(result
.stderr
, b
"")
121 elif expected
== self
.Expected
.SUCCESS_WITH_EXTRA_OUTPUT
:
122 # When expecting a success with extra output (that is not warnings,
123 # which is the common case), the script should return 0 and it
124 # should output at least something to `stderr` (the output should
125 # be checked further by the test).
126 self
.assertEqual(result
.returncode
, 0)
127 self
.assertNotEqual(result
.stderr
, b
"")
128 elif expected
== self
.Expected
.SUCCESS_WITH_WARNINGS
:
129 # When expecting a success with warnings, the script should return 0
130 # and it should output at least the instructions to `stderr`.
131 self
.assertEqual(result
.returncode
, 0)
132 self
.assertIn(b
"Please see Documentation/rust/quick-start.rst for details", result
.stderr
)
134 # When expecting a failure, the script should return non-0
135 # and it should output at least the instructions to `stderr`.
136 self
.assertNotEqual(result
.returncode
, 0)
137 self
.assertIn(b
"Please see Documentation/rust/quick-start.rst for details", result
.stderr
)
139 # The output will generally be UTF-8 (i.e. unless the user has
140 # put strange values in the environment).
141 result
.stderr
= result
.stderr
.decode()
145 def test_rustc_unset(self
):
146 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": None })
147 self
.assertIn("Environment variable 'RUSTC' is not set.", result
.stderr
)
148 self
.assertIn("This script is intended to be called from Kbuild.", result
.stderr
)
150 def test_bindgen_unset(self
):
151 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": None })
152 self
.assertIn("Environment variable 'BINDGEN' is not set.", result
.stderr
)
153 self
.assertIn("This script is intended to be called from Kbuild.", result
.stderr
)
155 def test_cc_unset(self
):
156 result
= self
.run_script(self
.Expected
.FAILURE
, { "CC": None })
157 self
.assertIn("Environment variable 'CC' is not set.", result
.stderr
)
158 self
.assertIn("This script is intended to be called from Kbuild.", result
.stderr
)
160 def test_rustc_missing(self
):
161 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": self
.missing
})
162 self
.assertIn(f
"Rust compiler '{self.missing}' could not be found.", result
.stderr
)
164 def test_bindgen_missing(self
):
165 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": self
.missing
})
166 self
.assertIn(f
"Rust bindings generator '{self.missing}' could not be found.", result
.stderr
)
168 def test_rustc_nonexecutable(self
):
169 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": self
.nonexecutable
})
170 self
.assertIn(f
"Running '{self.nonexecutable}' to check the Rust compiler version failed with", result
.stderr
)
172 def test_rustc_unexpected_binary(self
):
173 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": self
.unexpected_binary
})
174 self
.assertIn(f
"Running '{self.unexpected_binary}' to check the Rust compiler version did not return", result
.stderr
)
176 def test_rustc_unexpected_name(self
):
177 rustc
= self
.generate_rustc(f
"unexpected {self.rustc_default_version} (a8314ef7d 2022-06-27)")
178 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": rustc
})
179 self
.assertIn(f
"Running '{rustc}' to check the Rust compiler version did not return", result
.stderr
)
181 def test_rustc_unexpected_version(self
):
182 rustc
= self
.generate_rustc("rustc unexpected (a8314ef7d 2022-06-27)")
183 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": rustc
})
184 self
.assertIn(f
"Running '{rustc}' to check the Rust compiler version did not return", result
.stderr
)
186 def test_rustc_no_minor(self
):
187 rustc
= self
.generate_rustc(f
"rustc {'.'.join(self.rustc_default_version.split('.')[:2])} (a8314ef7d 2022-06-27)")
188 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": rustc
})
189 self
.assertIn(f
"Running '{rustc}' to check the Rust compiler version did not return", result
.stderr
)
191 def test_rustc_old_version(self
):
192 rustc
= self
.generate_rustc("rustc 1.60.0 (a8314ef7d 2022-06-27)")
193 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUSTC": rustc
})
194 self
.assertIn(f
"Rust compiler '{rustc}' is too old.", result
.stderr
)
196 def test_rustc_new_version(self
):
197 rustc
= self
.generate_rustc("rustc 1.999.0 (a8314ef7d 2099-06-27)")
198 result
= self
.run_script(self
.Expected
.SUCCESS_WITH_WARNINGS
, { "RUSTC": rustc
})
199 self
.assertIn(f
"Rust compiler '{rustc}' is too new. This may or may not work.", result
.stderr
)
201 def test_bindgen_nonexecutable(self
):
202 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": self
.nonexecutable
})
203 self
.assertIn(f
"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result
.stderr
)
205 def test_bindgen_unexpected_binary(self
):
206 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": self
.unexpected_binary
})
207 self
.assertIn(f
"Running '{self.unexpected_binary}' to check the bindings generator version did not return", result
.stderr
)
209 def test_bindgen_unexpected_name(self
):
210 bindgen
= self
.generate_bindgen_version(f
"unexpected {self.bindgen_default_version}")
211 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
212 self
.assertIn(f
"Running '{bindgen}' to check the bindings generator version did not return", result
.stderr
)
214 def test_bindgen_unexpected_version(self
):
215 bindgen
= self
.generate_bindgen_version("bindgen unexpected")
216 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
217 self
.assertIn(f
"Running '{bindgen}' to check the bindings generator version did not return", result
.stderr
)
219 def test_bindgen_no_minor(self
):
220 bindgen
= self
.generate_bindgen_version(f
"bindgen {'.'.join(self.bindgen_default_version.split('.')[:2])}")
221 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
222 self
.assertIn(f
"Running '{bindgen}' to check the bindings generator version did not return", result
.stderr
)
224 def test_bindgen_old_version(self
):
225 bindgen
= self
.generate_bindgen_version("bindgen 0.50.0")
226 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
227 self
.assertIn(f
"Rust bindings generator '{bindgen}' is too old.", result
.stderr
)
229 def test_bindgen_new_version(self
):
230 bindgen
= self
.generate_bindgen_version("bindgen 0.999.0")
231 result
= self
.run_script(self
.Expected
.SUCCESS_WITH_WARNINGS
, { "BINDGEN": bindgen
})
232 self
.assertIn(f
"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result
.stderr
)
234 def test_bindgen_libclang_failure(self
):
236 { "LLVM_CONFIG_PATH": self
.missing
},
237 { "LIBCLANG_PATH": self
.missing
},
238 { "CLANG_PATH": self
.missing
},
240 with self
.subTest(env
=env
):
241 result
= self
.run_script(self
.Expected
.FAILURE
, env |
{ "PATH": os
.environ
["PATH"], "BINDGEN": "bindgen" })
242 self
.assertIn("Running 'bindgen' to check the libclang version (used by the Rust", result
.stderr
)
243 self
.assertIn("bindings generator) failed with code ", result
.stderr
)
245 def test_bindgen_libclang_unexpected_version(self
):
246 bindgen
= self
.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version unexpected [-W#pragma-messages], err: false")
247 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
248 self
.assertIn(f
"Running '{bindgen}' to check the libclang version (used by the Rust", result
.stderr
)
249 self
.assertIn("bindings generator) did not return an expected output. See output", result
.stderr
)
251 def test_bindgen_libclang_old_version(self
):
252 bindgen
= self
.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 10.0.0 [-W#pragma-messages], err: false")
253 result
= self
.run_script(self
.Expected
.FAILURE
, { "BINDGEN": bindgen
})
254 self
.assertIn(f
"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result
.stderr
)
256 def test_clang_matches_bindgen_libclang_different_bindgen(self
):
257 bindgen
= self
.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
258 result
= self
.run_script(self
.Expected
.SUCCESS_WITH_WARNINGS
, { "BINDGEN": bindgen
})
259 self
.assertIn("version does not match Clang's. This may be a problem.", result
.stderr
)
261 def test_clang_matches_bindgen_libclang_different_clang(self
):
262 cc
= self
.generate_clang("clang version 999.0.0")
263 result
= self
.run_script(self
.Expected
.SUCCESS_WITH_WARNINGS
, { "CC": cc
})
264 self
.assertIn("version does not match Clang's. This may be a problem.", result
.stderr
)
266 def test_rustc_src_core_krustflags(self
):
267 result
= self
.run_script(self
.Expected
.FAILURE
, { "PATH": os
.environ
["PATH"], "RUSTC": "rustc", "KRUSTFLAGS": f
"--sysroot={self.missing}" })
268 self
.assertIn("Source code for the 'core' standard library could not be found", result
.stderr
)
270 def test_rustc_src_core_rustlibsrc(self
):
271 result
= self
.run_script(self
.Expected
.FAILURE
, { "RUST_LIB_SRC": self
.missing
})
272 self
.assertIn("Source code for the 'core' standard library could not be found", result
.stderr
)
274 def test_success_cc_unknown(self
):
275 result
= self
.run_script(self
.Expected
.SUCCESS_WITH_EXTRA_OUTPUT
, { "CC": self
.missing
})
276 self
.assertIn("unknown C compiler", result
.stderr
)
278 def test_success_cc_multiple_arguments_ccache(self
):
279 clang
= self
.generate_clang(f
"""Ubuntu clang version {self.llvm_default_version}-1ubuntu1
280 Target: x86_64-pc-linux-gnu
282 InstalledDir: /usr/bin
284 result
= self
.run_script(self
.Expected
.SUCCESS
, { "CC": f
"{clang} clang" })
286 def test_success_rustc_version(self
):
287 for rustc_stdout
in (
288 f
"rustc {self.rustc_default_version} (a8314ef7d 2022-06-27)",
289 f
"rustc {self.rustc_default_version}-dev (a8314ef7d 2022-06-27)",
290 f
"rustc {self.rustc_default_version}-1.60.0 (a8314ef7d 2022-06-27)",
292 with self
.subTest(rustc_stdout
=rustc_stdout
):
293 rustc
= self
.generate_rustc(rustc_stdout
)
294 result
= self
.run_script(self
.Expected
.SUCCESS
, { "RUSTC": rustc
})
296 def test_success_bindgen_version(self
):
297 for bindgen_stdout
in (
298 f
"bindgen {self.bindgen_default_version}",
299 f
"bindgen {self.bindgen_default_version}-dev",
300 f
"bindgen {self.bindgen_default_version}-0.999.0",
302 with self
.subTest(bindgen_stdout
=bindgen_stdout
):
303 bindgen
= self
.generate_bindgen_version(bindgen_stdout
)
304 result
= self
.run_script(self
.Expected
.SUCCESS
, { "BINDGEN": bindgen
})
306 def test_success_bindgen_libclang(self
):
308 f
"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (https://github.com/llvm/llvm-project.git 4a2c05b05ed07f1f620e94f6524a8b4b2760a0b1) [-W#pragma-messages], err: false",
309 f
"/home/jd/Documents/dev/kernel-module-flake/linux-6.1/outputs/dev/lib/modules/6.1.0-development/source/scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} [-W#pragma-messages], err: false",
310 f
"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (Fedora 13.0.0-3.fc35) [-W#pragma-messages], err: false",
312 /nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
313 scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} [-W#pragma-messages], err: false
316 /nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1.0-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
317 /home/jd/Documents/dev/kernel-module-flake/linux-6.1/outputs/dev/lib/modules/6.1.0-development/source/scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version} (Fedora 13.0.0-3.fc35) [-W#pragma-messages], err: false
320 with self
.subTest(stderr
=stderr
):
321 bindgen
= self
.generate_bindgen_libclang(stderr
)
322 result
= self
.run_script(self
.Expected
.SUCCESS
, { "BINDGEN": bindgen
})
324 def test_success_clang_version(self
):
325 for clang_stdout
in (
326 f
"clang version {self.llvm_default_version} (https://github.com/llvm/llvm-project.git 4a2c05b05ed07f1f620e94f6524a8b4b2760a0b1)",
327 f
"clang version {self.llvm_default_version}-dev",
328 f
"clang version {self.llvm_default_version}-2~ubuntu20.04.1",
329 f
"Ubuntu clang version {self.llvm_default_version}-2~ubuntu20.04.1",
331 with self
.subTest(clang_stdout
=clang_stdout
):
332 clang
= self
.generate_clang(clang_stdout
)
333 result
= self
.run_script(self
.Expected
.SUCCESS
, { "CC": clang
})
335 def test_success_real_programs(self
):
336 for cc
in ["gcc", "clang"]:
337 with self
.subTest(cc
=cc
):
338 result
= self
.run_script(self
.Expected
.SUCCESS
, {
339 "PATH": os
.environ
["PATH"],
341 "BINDGEN": "bindgen",
345 if __name__
== "__main__":