Files
AnyPS5/tools/tests/test_check_compile_preprocessor.py

102 lines
5.9 KiB
Python

import os
import shlex
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import check_compile
class CheckCompilePreprocessorTests(unittest.TestCase):
def test_forwarded_dependencies_preserve_outputs_and_compiler_errors(self):
self.check_forwarded_dependencies(shutil.which("clang") or shutil.which("gcc"), ("-MD", "-MMD"))
def test_gcc_dependency_aliases_preserve_outputs_and_compiler_errors(self):
compiler = shutil.which("gcc")
if compiler is not None:
version = subprocess.run([compiler, "--version"], capture_output=True, text=True)
if "clang" in version.stdout.lower():
self.skipTest("GCC dependency aliases are not supported by Clang")
self.check_forwarded_dependencies(compiler, ("--write-dependencies", "--write-user-dependencies"))
def check_forwarded_dependencies(self, compiler, options):
if compiler is None:
self.skipTest("needs a C compiler")
with tempfile.TemporaryDirectory(prefix="syntax check ") as folder:
root = Path(folder).resolve()
source = root / "unit source.c"
dependency = root / "unit deps.d"
object_file = root / "unit.o"
valid = "#ifndef REQUIRED\n#error lost_preprocessor_definition\n#endif\nint value;\n"
for option in options:
for field in ("arguments", "command"):
with self.subTest(option=option, field=field):
source.write_text(valid, encoding="utf-8")
forwarded = [f"-Wp,{option},{dependency}", "-Wp,-DREQUIRED,-MP,-MT,custom_target,-UUNUSED"]
arguments = [compiler, "-Werror", "-c", str(source), *forwarded, "-o", str(object_file)]
original = subprocess.run(arguments, cwd=root, capture_output=True, text=True)
self.assertEqual(original.returncode, 0, original.stderr)
preserved = {path: path.read_bytes() for path in (dependency, object_file)}
command = (subprocess.list2cmdline(arguments) if os.name == "nt" else
" ".join(shlex.quote(argument) for argument in arguments))
entry = {"directory": str(root), "file": str(source),
field: arguments if field == "arguments" else command}
for text, failures in ((valid, 0), (valid + "#error syntax_check_must_fail\n", 1)):
source.write_text(text, encoding="utf-8")
checked, failed, diagnostics = check_compile.check_source(
(source, source.name, root, [entry]))
self.assertEqual((checked, failed), (1, failures), diagnostics)
if failures:
self.assertIn("syntax_check_must_fail", diagnostics)
for path, content in preserved.items():
self.assertEqual(path.read_bytes(), content, str(path))
self.assertEqual(set(root.iterdir()), {source, dependency, object_file})
dependency.unlink()
object_file.unlink()
source.write_text(valid, encoding="utf-8")
self.assertEqual(check_compile.check_source((source, source.name, root, [entry]))[:2], (1, 0))
self.assertEqual(set(root.iterdir()), {source})
def test_dependency_only_arguments_are_removed(self):
source = Path("unit.c").resolve()
flags = ["-Wp,-MD,unit.d", "-Wp,-MMD,unit.d,-MP", "-Wp,-MF,unit.d,-MT,target,-MQ,quoted",
"-Wp,-MFunit.d,-MTtarget,-MQquoted", "-Wp,-M,-MM,-MG,-MP",
"-Wp,--write-dependencies,unit.d,--dependencies,--user-dependencies,--print-missing-file-dependencies",
"-Wp,--write-user-dependencies,unit.d"]
for flag in flags:
with self.subTest(flag=flag):
self.assertEqual(check_compile.syntax_arguments(["cc", flag, "-c", str(source)], Path.cwd(), source),
["cc", "-fsyntax-only", str(source)])
def test_other_preprocessor_arguments_are_preserved(self):
source = Path("unit.c").resolve()
for flag, expected in (("-Wp,-DNAME=value,-MD,unit.d,-I,include files", "-Wp,-DNAME=value,-I,include files"),
("-Wp,-include,-MD,-MMD,unit.d", "-Wp,-include,-MD"),
("-Wp,--include,-MD,--write-dependencies,unit.d", "-Wp,--include,-MD")):
with self.subTest(flag=flag):
self.assertEqual(check_compile.syntax_arguments(["cc", flag, str(source)], Path.cwd(), source),
["cc", "-fsyntax-only", expected, str(source)])
def test_unsupported_options_and_response_operands_are_still_rejected(self):
source = Path("unit.c").resolve()
for flag in ("-Wp,-unknown-option", "-Wp,-MD,unit.d,-E", "-Wp,-MD,unit.d,-I,@operand.rsp",
"-Wp,-MD,@operand.rsp"):
with self.subTest(flag=flag):
with self.assertRaisesRegex(ValueError, "unsupported forwarded compiler option"):
check_compile.syntax_arguments(["cc", flag, str(source)], Path.cwd(), source)
def test_missing_dependency_values_are_rejected(self):
source = Path("unit.c").resolve()
for option in ("-MD", "-MMD", "-MF", "-MT", "-MQ", "--write-dependencies", "--write-user-dependencies"):
with self.subTest(option=option):
with self.assertRaisesRegex(ValueError, "missing value"):
check_compile.syntax_arguments(["cc", f"-Wp,{option}", str(source)], Path.cwd(), source)
if __name__ == "__main__":
unittest.main()