10bb1c928SMiguel Ojeda#!/usr/bin/env python3
20bb1c928SMiguel Ojeda# SPDX-License-Identifier: GPL-2.0
30bb1c928SMiguel Ojeda
40bb1c928SMiguel Ojeda"""Tests the `rust_is_available.sh` script.
50bb1c928SMiguel Ojeda
60bb1c928SMiguel OjedaSome of the tests require the real programs to be available in `$PATH`
70bb1c928SMiguel Ojedaunder their canonical name (and with the expected versions).
80bb1c928SMiguel Ojeda"""
90bb1c928SMiguel Ojeda
100bb1c928SMiguel Ojedaimport enum
110bb1c928SMiguel Ojedaimport os
120bb1c928SMiguel Ojedaimport pathlib
130bb1c928SMiguel Ojedaimport stat
140bb1c928SMiguel Ojedaimport subprocess
150bb1c928SMiguel Ojedaimport tempfile
160bb1c928SMiguel Ojedaimport unittest
170bb1c928SMiguel Ojeda
180bb1c928SMiguel Ojedaclass TestRustIsAvailable(unittest.TestCase):
190bb1c928SMiguel Ojeda    @enum.unique
200bb1c928SMiguel Ojeda    class Expected(enum.Enum):
210bb1c928SMiguel Ojeda        SUCCESS = enum.auto()
220bb1c928SMiguel Ojeda        SUCCESS_WITH_WARNINGS = enum.auto()
230bb1c928SMiguel Ojeda        SUCCESS_WITH_EXTRA_OUTPUT = enum.auto()
240bb1c928SMiguel Ojeda        FAILURE = enum.auto()
250bb1c928SMiguel Ojeda
260bb1c928SMiguel Ojeda    @classmethod
270bb1c928SMiguel Ojeda    def generate_executable(cls, content):
280bb1c928SMiguel Ojeda        path = pathlib.Path(cls.tempdir.name)
290bb1c928SMiguel Ojeda        name = str(len(tuple(path.iterdir())))
300bb1c928SMiguel Ojeda        path = path / name
310bb1c928SMiguel Ojeda        with open(path, "w") as file_:
320bb1c928SMiguel Ojeda            file_.write(content)
330bb1c928SMiguel Ojeda        os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
340bb1c928SMiguel Ojeda        return path
350bb1c928SMiguel Ojeda
360bb1c928SMiguel Ojeda    @classmethod
370bb1c928SMiguel Ojeda    def generate_clang(cls, stdout):
380bb1c928SMiguel Ojeda        return cls.generate_executable(f"""#!/usr/bin/env python3
390bb1c928SMiguel Ojedaimport sys
400bb1c928SMiguel Ojedaif "-E" in " ".join(sys.argv):
410bb1c928SMiguel Ojeda    print({repr("Clang " + " ".join(cls.llvm_default_version.split(" ")))})
420bb1c928SMiguel Ojedaelse:
430bb1c928SMiguel Ojeda    print({repr(stdout)})
440bb1c928SMiguel Ojeda""")
450bb1c928SMiguel Ojeda
460bb1c928SMiguel Ojeda    @classmethod
470bb1c928SMiguel Ojeda    def generate_rustc(cls, stdout):
480bb1c928SMiguel Ojeda        return cls.generate_executable(f"""#!/usr/bin/env python3
490bb1c928SMiguel Ojedaimport sys
500bb1c928SMiguel Ojedaif "--print sysroot" in " ".join(sys.argv):
510bb1c928SMiguel Ojeda    print({repr(cls.rust_default_sysroot)})
520bb1c928SMiguel Ojedaelse:
530bb1c928SMiguel Ojeda    print({repr(stdout)})
540bb1c928SMiguel Ojeda""")
550bb1c928SMiguel Ojeda
560bb1c928SMiguel Ojeda    @classmethod
57*b2603f8aSMiguel Ojeda    def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
58d49082faSMiguel Ojeda        if libclang_stderr is None:
59d49082faSMiguel Ojeda            libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
60d49082faSMiguel Ojeda        else:
61d49082faSMiguel Ojeda            libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
62d49082faSMiguel Ojeda
63981ad93cSMiguel Ojeda        if version_0_66_patched:
64981ad93cSMiguel Ojeda            version_0_66_case = "pass"
65981ad93cSMiguel Ojeda        else:
66981ad93cSMiguel Ojeda            version_0_66_case = "raise SystemExit(1)"
67981ad93cSMiguel Ojeda
68*b2603f8aSMiguel Ojeda        if libclang_concat_patched:
69*b2603f8aSMiguel Ojeda            libclang_concat_case = "print('pub static mut foofoo: ::std::os::raw::c_int;')"
70*b2603f8aSMiguel Ojeda        else:
71*b2603f8aSMiguel Ojeda            libclang_concat_case = "pass"
72*b2603f8aSMiguel Ojeda
730bb1c928SMiguel Ojeda        return cls.generate_executable(f"""#!/usr/bin/env python3
740bb1c928SMiguel Ojedaimport sys
750bb1c928SMiguel Ojedaif "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
76d49082faSMiguel Ojeda    {libclang_case}
77981ad93cSMiguel Ojedaelif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
78981ad93cSMiguel Ojeda    {version_0_66_case}
79*b2603f8aSMiguel Ojedaelif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
80*b2603f8aSMiguel Ojeda    {libclang_concat_case}
810bb1c928SMiguel Ojedaelse:
820bb1c928SMiguel Ojeda    print({repr(version_stdout)})
830bb1c928SMiguel Ojeda""")
840bb1c928SMiguel Ojeda
850bb1c928SMiguel Ojeda    @classmethod
86981ad93cSMiguel Ojeda    def generate_bindgen_version(cls, stdout, version_0_66_patched=False):
87981ad93cSMiguel Ojeda        return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr, version_0_66_patched)
880bb1c928SMiguel Ojeda
890bb1c928SMiguel Ojeda    @classmethod
90d49082faSMiguel Ojeda    def generate_bindgen_libclang_failure(cls):
91d49082faSMiguel Ojeda        return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, None)
92d49082faSMiguel Ojeda
93d49082faSMiguel Ojeda    @classmethod
940bb1c928SMiguel Ojeda    def generate_bindgen_libclang(cls, stderr):
950bb1c928SMiguel Ojeda        return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, stderr)
960bb1c928SMiguel Ojeda
970bb1c928SMiguel Ojeda    @classmethod
980bb1c928SMiguel Ojeda    def setUpClass(cls):
990bb1c928SMiguel Ojeda        cls.tempdir = tempfile.TemporaryDirectory()
1000bb1c928SMiguel Ojeda
1010bb1c928SMiguel Ojeda        cls.missing = pathlib.Path(cls.tempdir.name) / "missing"
1020bb1c928SMiguel Ojeda
1030bb1c928SMiguel Ojeda        cls.nonexecutable = pathlib.Path(cls.tempdir.name) / "nonexecutable"
1040bb1c928SMiguel Ojeda        with open(cls.nonexecutable, "w") as file_:
1050bb1c928SMiguel Ojeda            file_.write("nonexecutable")
1060bb1c928SMiguel Ojeda
1070bb1c928SMiguel Ojeda        cls.unexpected_binary = "true"
1080bb1c928SMiguel Ojeda
1090bb1c928SMiguel Ojeda        cls.rustc_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "rustc")).decode().strip()
1100bb1c928SMiguel Ojeda        cls.bindgen_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "bindgen")).decode().strip()
1110bb1c928SMiguel Ojeda        cls.llvm_default_version = subprocess.check_output(("scripts/min-tool-version.sh", "llvm")).decode().strip()
1120bb1c928SMiguel Ojeda        cls.rust_default_sysroot = subprocess.check_output(("rustc", "--print", "sysroot")).decode().strip()
1130bb1c928SMiguel Ojeda
1140bb1c928SMiguel Ojeda        cls.bindgen_default_bindgen_version_stdout = f"bindgen {cls.bindgen_default_version}"
115d49082faSMiguel Ojeda        cls.bindgen_default_bindgen_libclang_failure_exit_code = 42
1160bb1c928SMiguel Ojeda        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"
1170bb1c928SMiguel Ojeda
1180bb1c928SMiguel Ojeda        cls.default_rustc = cls.generate_rustc(f"rustc {cls.rustc_default_version}")
1190bb1c928SMiguel Ojeda        cls.default_bindgen =  cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, cls.bindgen_default_bindgen_libclang_stderr)
1200bb1c928SMiguel Ojeda        cls.default_cc = cls.generate_clang(f"clang version {cls.llvm_default_version}")
1210bb1c928SMiguel Ojeda
1220bb1c928SMiguel Ojeda    def run_script(self, expected, override_env):
1230bb1c928SMiguel Ojeda        env = {
1240bb1c928SMiguel Ojeda            "RUSTC": self.default_rustc,
1250bb1c928SMiguel Ojeda            "BINDGEN": self.default_bindgen,
1260bb1c928SMiguel Ojeda            "CC": self.default_cc,
1270bb1c928SMiguel Ojeda        }
1280bb1c928SMiguel Ojeda
1290bb1c928SMiguel Ojeda        for key, value in override_env.items():
1300bb1c928SMiguel Ojeda            if value is None:
1310bb1c928SMiguel Ojeda                del env[key]
1320bb1c928SMiguel Ojeda                continue
1330bb1c928SMiguel Ojeda            env[key] = value
1340bb1c928SMiguel Ojeda
1350bb1c928SMiguel Ojeda        result = subprocess.run("scripts/rust_is_available.sh", env=env, capture_output=True)
1360bb1c928SMiguel Ojeda
1370bb1c928SMiguel Ojeda        # The script should never output anything to `stdout`.
1380bb1c928SMiguel Ojeda        self.assertEqual(result.stdout, b"")
1390bb1c928SMiguel Ojeda
1400bb1c928SMiguel Ojeda        if expected == self.Expected.SUCCESS:
1410bb1c928SMiguel Ojeda            # When expecting a success, the script should return 0
1420bb1c928SMiguel Ojeda            # and it should not output anything to `stderr`.
1430bb1c928SMiguel Ojeda            self.assertEqual(result.returncode, 0)
1440bb1c928SMiguel Ojeda            self.assertEqual(result.stderr, b"")
1450bb1c928SMiguel Ojeda        elif expected == self.Expected.SUCCESS_WITH_EXTRA_OUTPUT:
1460bb1c928SMiguel Ojeda            # When expecting a success with extra output (that is not warnings,
1470bb1c928SMiguel Ojeda            # which is the common case), the script should return 0 and it
1480bb1c928SMiguel Ojeda            # should output at least something to `stderr` (the output should
1490bb1c928SMiguel Ojeda            # be checked further by the test).
1500bb1c928SMiguel Ojeda            self.assertEqual(result.returncode, 0)
1510bb1c928SMiguel Ojeda            self.assertNotEqual(result.stderr, b"")
1520bb1c928SMiguel Ojeda        elif expected == self.Expected.SUCCESS_WITH_WARNINGS:
1530bb1c928SMiguel Ojeda            # When expecting a success with warnings, the script should return 0
1540bb1c928SMiguel Ojeda            # and it should output at least the instructions to `stderr`.
1550bb1c928SMiguel Ojeda            self.assertEqual(result.returncode, 0)
1560bb1c928SMiguel Ojeda            self.assertIn(b"Please see Documentation/rust/quick-start.rst for details", result.stderr)
1570bb1c928SMiguel Ojeda        else:
1580bb1c928SMiguel Ojeda            # When expecting a failure, the script should return non-0
1590bb1c928SMiguel Ojeda            # and it should output at least the instructions to `stderr`.
1600bb1c928SMiguel Ojeda            self.assertNotEqual(result.returncode, 0)
1610bb1c928SMiguel Ojeda            self.assertIn(b"Please see Documentation/rust/quick-start.rst for details", result.stderr)
1620bb1c928SMiguel Ojeda
1630bb1c928SMiguel Ojeda        # The output will generally be UTF-8 (i.e. unless the user has
1640bb1c928SMiguel Ojeda        # put strange values in the environment).
1650bb1c928SMiguel Ojeda        result.stderr = result.stderr.decode()
1660bb1c928SMiguel Ojeda
1670bb1c928SMiguel Ojeda        return result
1680bb1c928SMiguel Ojeda
1690bb1c928SMiguel Ojeda    def test_rustc_unset(self):
1700bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": None })
1710bb1c928SMiguel Ojeda        self.assertIn("Environment variable 'RUSTC' is not set.", result.stderr)
1720bb1c928SMiguel Ojeda        self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
1730bb1c928SMiguel Ojeda
1740bb1c928SMiguel Ojeda    def test_bindgen_unset(self):
1750bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": None })
1760bb1c928SMiguel Ojeda        self.assertIn("Environment variable 'BINDGEN' is not set.", result.stderr)
1770bb1c928SMiguel Ojeda        self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
1780bb1c928SMiguel Ojeda
1790bb1c928SMiguel Ojeda    def test_cc_unset(self):
1800bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "CC": None })
1810bb1c928SMiguel Ojeda        self.assertIn("Environment variable 'CC' is not set.", result.stderr)
1820bb1c928SMiguel Ojeda        self.assertIn("This script is intended to be called from Kbuild.", result.stderr)
1830bb1c928SMiguel Ojeda
1840bb1c928SMiguel Ojeda    def test_rustc_missing(self):
1850bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.missing })
1860bb1c928SMiguel Ojeda        self.assertIn(f"Rust compiler '{self.missing}' could not be found.", result.stderr)
1870bb1c928SMiguel Ojeda
1880bb1c928SMiguel Ojeda    def test_bindgen_missing(self):
1890bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.missing })
1900bb1c928SMiguel Ojeda        self.assertIn(f"Rust bindings generator '{self.missing}' could not be found.", result.stderr)
1910bb1c928SMiguel Ojeda
1920bb1c928SMiguel Ojeda    def test_rustc_nonexecutable(self):
1930bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.nonexecutable })
1940bb1c928SMiguel Ojeda        self.assertIn(f"Running '{self.nonexecutable}' to check the Rust compiler version failed with", result.stderr)
1950bb1c928SMiguel Ojeda
1960bb1c928SMiguel Ojeda    def test_rustc_unexpected_binary(self):
1970bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.unexpected_binary })
1980bb1c928SMiguel Ojeda        self.assertIn(f"Running '{self.unexpected_binary}' to check the Rust compiler version did not return", result.stderr)
1990bb1c928SMiguel Ojeda
2000bb1c928SMiguel Ojeda    def test_rustc_unexpected_name(self):
2010bb1c928SMiguel Ojeda        rustc = self.generate_rustc(f"unexpected {self.rustc_default_version} (a8314ef7d 2022-06-27)")
2020bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
2030bb1c928SMiguel Ojeda        self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
2040bb1c928SMiguel Ojeda
2050bb1c928SMiguel Ojeda    def test_rustc_unexpected_version(self):
2060bb1c928SMiguel Ojeda        rustc = self.generate_rustc("rustc unexpected (a8314ef7d 2022-06-27)")
2070bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
2080bb1c928SMiguel Ojeda        self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
2090bb1c928SMiguel Ojeda
2100bb1c928SMiguel Ojeda    def test_rustc_no_minor(self):
2110bb1c928SMiguel Ojeda        rustc = self.generate_rustc(f"rustc {'.'.join(self.rustc_default_version.split('.')[:2])} (a8314ef7d 2022-06-27)")
2120bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
2130bb1c928SMiguel Ojeda        self.assertIn(f"Running '{rustc}' to check the Rust compiler version did not return", result.stderr)
2140bb1c928SMiguel Ojeda
2150bb1c928SMiguel Ojeda    def test_rustc_old_version(self):
2160bb1c928SMiguel Ojeda        rustc = self.generate_rustc("rustc 1.60.0 (a8314ef7d 2022-06-27)")
2170bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
2180bb1c928SMiguel Ojeda        self.assertIn(f"Rust compiler '{rustc}' is too old.", result.stderr)
2190bb1c928SMiguel Ojeda
2200bb1c928SMiguel Ojeda    def test_bindgen_nonexecutable(self):
2210bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.nonexecutable })
2220bb1c928SMiguel Ojeda        self.assertIn(f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result.stderr)
2230bb1c928SMiguel Ojeda
2240bb1c928SMiguel Ojeda    def test_bindgen_unexpected_binary(self):
2250bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.unexpected_binary })
2260bb1c928SMiguel Ojeda        self.assertIn(f"Running '{self.unexpected_binary}' to check the bindings generator version did not return", result.stderr)
2270bb1c928SMiguel Ojeda
2280bb1c928SMiguel Ojeda    def test_bindgen_unexpected_name(self):
2290bb1c928SMiguel Ojeda        bindgen = self.generate_bindgen_version(f"unexpected {self.bindgen_default_version}")
2300bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2310bb1c928SMiguel Ojeda        self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
2320bb1c928SMiguel Ojeda
2330bb1c928SMiguel Ojeda    def test_bindgen_unexpected_version(self):
2340bb1c928SMiguel Ojeda        bindgen = self.generate_bindgen_version("bindgen unexpected")
2350bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2360bb1c928SMiguel Ojeda        self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
2370bb1c928SMiguel Ojeda
2380bb1c928SMiguel Ojeda    def test_bindgen_no_minor(self):
2390bb1c928SMiguel Ojeda        bindgen = self.generate_bindgen_version(f"bindgen {'.'.join(self.bindgen_default_version.split('.')[:2])}")
2400bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2410bb1c928SMiguel Ojeda        self.assertIn(f"Running '{bindgen}' to check the bindings generator version did not return", result.stderr)
2420bb1c928SMiguel Ojeda
2430bb1c928SMiguel Ojeda    def test_bindgen_old_version(self):
2440bb1c928SMiguel Ojeda        bindgen = self.generate_bindgen_version("bindgen 0.50.0")
2450bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2460bb1c928SMiguel Ojeda        self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr)
2470bb1c928SMiguel Ojeda
248981ad93cSMiguel Ojeda    def test_bindgen_bad_version_0_66_0_and_0_66_1(self):
249981ad93cSMiguel Ojeda        for version in ("0.66.0", "0.66.1"):
250981ad93cSMiguel Ojeda            with self.subTest(version=version):
251981ad93cSMiguel Ojeda                bindgen = self.generate_bindgen_version(f"bindgen {version}")
252981ad93cSMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
253981ad93cSMiguel Ojeda                self.assertIn(f"Rust bindings generator '{bindgen}' versions 0.66.0 and 0.66.1 may not", result.stderr)
254981ad93cSMiguel Ojeda
255981ad93cSMiguel Ojeda    def test_bindgen_bad_version_0_66_0_and_0_66_1_patched(self):
256981ad93cSMiguel Ojeda        for version in ("0.66.0", "0.66.1"):
257981ad93cSMiguel Ojeda            with self.subTest(version=version):
258981ad93cSMiguel Ojeda                bindgen = self.generate_bindgen_version(f"bindgen {version}", True)
259981ad93cSMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
260981ad93cSMiguel Ojeda
2610bb1c928SMiguel Ojeda    def test_bindgen_libclang_failure(self):
262d49082faSMiguel Ojeda        bindgen = self.generate_bindgen_libclang_failure()
263d49082faSMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
264d49082faSMiguel Ojeda        self.assertIn(f"Running '{bindgen}' to check the libclang version (used by the Rust", result.stderr)
265d49082faSMiguel Ojeda        self.assertIn(f"bindings generator) failed with code {self.bindgen_default_bindgen_libclang_failure_exit_code}. This may be caused by", result.stderr)
2660bb1c928SMiguel Ojeda
2670bb1c928SMiguel Ojeda    def test_bindgen_libclang_unexpected_version(self):
2680bb1c928SMiguel Ojeda        bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version unexpected [-W#pragma-messages], err: false")
2690bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2700bb1c928SMiguel Ojeda        self.assertIn(f"Running '{bindgen}' to check the libclang version (used by the Rust", result.stderr)
2710bb1c928SMiguel Ojeda        self.assertIn("bindings generator) did not return an expected output. See output", result.stderr)
2720bb1c928SMiguel Ojeda
2730bb1c928SMiguel Ojeda    def test_bindgen_libclang_old_version(self):
2740bb1c928SMiguel Ojeda        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")
2750bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
2760bb1c928SMiguel Ojeda        self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
2770bb1c928SMiguel Ojeda
278*b2603f8aSMiguel Ojeda    def test_bindgen_bad_libclang_concat(self):
279*b2603f8aSMiguel Ojeda        for (bindgen_version, libclang_version, expected_not_patched) in (
280*b2603f8aSMiguel Ojeda            ("0.69.4", "18.0.0", self.Expected.SUCCESS),
281*b2603f8aSMiguel Ojeda            ("0.69.4", "19.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
282*b2603f8aSMiguel Ojeda            ("0.69.4", "19.2.0", self.Expected.SUCCESS_WITH_WARNINGS),
283*b2603f8aSMiguel Ojeda
284*b2603f8aSMiguel Ojeda            ("0.69.5", "18.0.0", self.Expected.SUCCESS),
285*b2603f8aSMiguel Ojeda            ("0.69.5", "19.1.0", self.Expected.SUCCESS),
286*b2603f8aSMiguel Ojeda            ("0.69.5", "19.2.0", self.Expected.SUCCESS),
287*b2603f8aSMiguel Ojeda
288*b2603f8aSMiguel Ojeda            ("0.70.0", "18.0.0", self.Expected.SUCCESS),
289*b2603f8aSMiguel Ojeda            ("0.70.0", "19.1.0", self.Expected.SUCCESS),
290*b2603f8aSMiguel Ojeda            ("0.70.0", "19.2.0", self.Expected.SUCCESS),
291*b2603f8aSMiguel Ojeda        ):
292*b2603f8aSMiguel Ojeda            with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
293*b2603f8aSMiguel Ojeda                cc = self.generate_clang(f"clang version {libclang_version}")
294*b2603f8aSMiguel Ojeda                libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
295*b2603f8aSMiguel Ojeda                bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
296*b2603f8aSMiguel Ojeda                result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
297*b2603f8aSMiguel Ojeda                if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
298*b2603f8aSMiguel Ojeda                    self.assertIn(f"Rust bindings generator '{bindgen}' < 0.69.5 together with libclang >= 19.1", result.stderr)
299*b2603f8aSMiguel Ojeda
300*b2603f8aSMiguel Ojeda                bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
301*b2603f8aSMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
302*b2603f8aSMiguel Ojeda
3030bb1c928SMiguel Ojeda    def test_clang_matches_bindgen_libclang_different_bindgen(self):
3040bb1c928SMiguel Ojeda        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")
3050bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
3060bb1c928SMiguel Ojeda        self.assertIn("version does not match Clang's. This may be a problem.", result.stderr)
3070bb1c928SMiguel Ojeda
3080bb1c928SMiguel Ojeda    def test_clang_matches_bindgen_libclang_different_clang(self):
3090bb1c928SMiguel Ojeda        cc = self.generate_clang("clang version 999.0.0")
3100bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "CC": cc })
3110bb1c928SMiguel Ojeda        self.assertIn("version does not match Clang's. This may be a problem.", result.stderr)
3120bb1c928SMiguel Ojeda
3130bb1c928SMiguel Ojeda    def test_rustc_src_core_krustflags(self):
3140bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "PATH": os.environ["PATH"], "RUSTC": "rustc", "KRUSTFLAGS": f"--sysroot={self.missing}" })
3150bb1c928SMiguel Ojeda        self.assertIn("Source code for the 'core' standard library could not be found", result.stderr)
3160bb1c928SMiguel Ojeda
3170bb1c928SMiguel Ojeda    def test_rustc_src_core_rustlibsrc(self):
3180bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.FAILURE, { "RUST_LIB_SRC": self.missing })
3190bb1c928SMiguel Ojeda        self.assertIn("Source code for the 'core' standard library could not be found", result.stderr)
3200bb1c928SMiguel Ojeda
3210bb1c928SMiguel Ojeda    def test_success_cc_unknown(self):
3220bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.SUCCESS_WITH_EXTRA_OUTPUT, { "CC": self.missing })
3230bb1c928SMiguel Ojeda        self.assertIn("unknown C compiler", result.stderr)
3240bb1c928SMiguel Ojeda
3250bb1c928SMiguel Ojeda    def test_success_cc_multiple_arguments_ccache(self):
3260bb1c928SMiguel Ojeda        clang = self.generate_clang(f"""Ubuntu clang version {self.llvm_default_version}-1ubuntu1
3270bb1c928SMiguel OjedaTarget: x86_64-pc-linux-gnu
3280bb1c928SMiguel OjedaThread model: posix
3290bb1c928SMiguel OjedaInstalledDir: /usr/bin
3300bb1c928SMiguel Ojeda""")
3310bb1c928SMiguel Ojeda        result = self.run_script(self.Expected.SUCCESS, { "CC": f"{clang} clang" })
3320bb1c928SMiguel Ojeda
3330bb1c928SMiguel Ojeda    def test_success_rustc_version(self):
3340bb1c928SMiguel Ojeda        for rustc_stdout in (
3350bb1c928SMiguel Ojeda            f"rustc {self.rustc_default_version} (a8314ef7d 2022-06-27)",
3360bb1c928SMiguel Ojeda            f"rustc {self.rustc_default_version}-dev (a8314ef7d 2022-06-27)",
3370bb1c928SMiguel Ojeda            f"rustc {self.rustc_default_version}-1.60.0 (a8314ef7d 2022-06-27)",
3380bb1c928SMiguel Ojeda        ):
3390bb1c928SMiguel Ojeda            with self.subTest(rustc_stdout=rustc_stdout):
3400bb1c928SMiguel Ojeda                rustc = self.generate_rustc(rustc_stdout)
3410bb1c928SMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "RUSTC": rustc })
3420bb1c928SMiguel Ojeda
3430bb1c928SMiguel Ojeda    def test_success_bindgen_version(self):
3440bb1c928SMiguel Ojeda        for bindgen_stdout in (
3450bb1c928SMiguel Ojeda            f"bindgen {self.bindgen_default_version}",
3460bb1c928SMiguel Ojeda            f"bindgen {self.bindgen_default_version}-dev",
3470bb1c928SMiguel Ojeda            f"bindgen {self.bindgen_default_version}-0.999.0",
3480bb1c928SMiguel Ojeda        ):
3490bb1c928SMiguel Ojeda            with self.subTest(bindgen_stdout=bindgen_stdout):
3500bb1c928SMiguel Ojeda                bindgen = self.generate_bindgen_version(bindgen_stdout)
3510bb1c928SMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
3520bb1c928SMiguel Ojeda
3530bb1c928SMiguel Ojeda    def test_success_bindgen_libclang(self):
3540bb1c928SMiguel Ojeda        for stderr in (
3550bb1c928SMiguel Ojeda            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",
3560bb1c928SMiguel Ojeda            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",
3570bb1c928SMiguel Ojeda            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",
3580bb1c928SMiguel Ojeda            f"""
3590bb1c928SMiguel Ojeda/nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
3600bb1c928SMiguel Ojedascripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {self.llvm_default_version}  [-W#pragma-messages], err: false
3610bb1c928SMiguel Ojeda""",
3620bb1c928SMiguel Ojeda            f"""
3630bb1c928SMiguel Ojeda/nix/store/dsd5gz46hdbdk2rfdimqddhq6m8m8fqs-bash-5.1.0-p16/bin/bash: warning: setlocale: LC_ALL: cannot change locale (c)
3640bb1c928SMiguel Ojeda/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
3650bb1c928SMiguel Ojeda"""
3660bb1c928SMiguel Ojeda        ):
3670bb1c928SMiguel Ojeda            with self.subTest(stderr=stderr):
3680bb1c928SMiguel Ojeda                bindgen = self.generate_bindgen_libclang(stderr)
3690bb1c928SMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
3700bb1c928SMiguel Ojeda
3710bb1c928SMiguel Ojeda    def test_success_clang_version(self):
3720bb1c928SMiguel Ojeda        for clang_stdout in (
3730bb1c928SMiguel Ojeda            f"clang version {self.llvm_default_version} (https://github.com/llvm/llvm-project.git 4a2c05b05ed07f1f620e94f6524a8b4b2760a0b1)",
3740bb1c928SMiguel Ojeda            f"clang version {self.llvm_default_version}-dev",
3750bb1c928SMiguel Ojeda            f"clang version {self.llvm_default_version}-2~ubuntu20.04.1",
3760bb1c928SMiguel Ojeda            f"Ubuntu clang version {self.llvm_default_version}-2~ubuntu20.04.1",
3770bb1c928SMiguel Ojeda        ):
3780bb1c928SMiguel Ojeda            with self.subTest(clang_stdout=clang_stdout):
3790bb1c928SMiguel Ojeda                clang = self.generate_clang(clang_stdout)
3800bb1c928SMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, { "CC": clang })
3810bb1c928SMiguel Ojeda
3820bb1c928SMiguel Ojeda    def test_success_real_programs(self):
3830bb1c928SMiguel Ojeda        for cc in ["gcc", "clang"]:
3840bb1c928SMiguel Ojeda            with self.subTest(cc=cc):
3850bb1c928SMiguel Ojeda                result = self.run_script(self.Expected.SUCCESS, {
3860bb1c928SMiguel Ojeda                    "PATH": os.environ["PATH"],
3870bb1c928SMiguel Ojeda                    "RUSTC": "rustc",
3880bb1c928SMiguel Ojeda                    "BINDGEN": "bindgen",
3890bb1c928SMiguel Ojeda                    "CC": cc,
3900bb1c928SMiguel Ojeda                })
3910bb1c928SMiguel Ojeda
3920bb1c928SMiguel Ojedaif __name__ == "__main__":
3930bb1c928SMiguel Ojeda    unittest.main()
394