one more test on real sysiphus packets
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Behavior tests for extract_provided_symbols.py."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT = ROOT / "extract_provided_symbols.py"
|
||||
|
||||
|
||||
def newc_entry(name: str, data: bytes, mode: int = stat.S_IFREG | 0o644) -> bytes:
|
||||
encoded_name = name.encode("utf-8") + b"\0"
|
||||
fields = (
|
||||
1,
|
||||
mode,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
len(data),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
len(encoded_name),
|
||||
0,
|
||||
)
|
||||
header = b"070701" + b"".join(f"{value:08x}".encode() for value in fields)
|
||||
record = header + encoded_name
|
||||
record += b"\0" * (-len(record) % 4)
|
||||
record += data
|
||||
record += b"\0" * (-len(data) % 4)
|
||||
return record
|
||||
|
||||
|
||||
def newc_archive(entries: list[tuple[str, bytes, int]]) -> bytes:
|
||||
payload = b"".join(newc_entry(*entry) for entry in entries)
|
||||
return payload + newc_entry("TRAILER!!!", b"", 0)
|
||||
|
||||
|
||||
class ExtractProvidedSymbolsTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.temporary_directory = tempfile.TemporaryDirectory()
|
||||
self.work = Path(self.temporary_directory.name)
|
||||
self.rpm2cpio = self.work / "rpm2cpio"
|
||||
self.provided_symbols = self.work / "provided_symbols"
|
||||
self.file_command = self.work / "file"
|
||||
|
||||
self.rpm2cpio.write_text(
|
||||
"#!/usr/bin/env python3\n"
|
||||
"import pathlib, sys\n"
|
||||
"sys.stdout.buffer.write(pathlib.Path(sys.argv[1]).read_bytes())\n"
|
||||
)
|
||||
self.provided_symbols.write_text(
|
||||
"#!/usr/bin/env python3\n"
|
||||
"import pathlib, sys\n"
|
||||
"symbols = set()\n"
|
||||
"for name in sys.argv[1:]:\n"
|
||||
" symbols.update(pathlib.Path(name).read_bytes()[4:].decode().splitlines())\n"
|
||||
"print(*sorted(symbols), sep='\\n')\n"
|
||||
)
|
||||
self.file_command.write_text(
|
||||
"#!/usr/bin/env python3\n"
|
||||
"import pathlib, sys\n"
|
||||
"data = pathlib.Path(sys.argv[-1]).read_bytes()\n"
|
||||
"kind = 'pie executable' if b'EXECUTABLE' in data else 'shared object'\n"
|
||||
"print(f'ELF 64-bit LSB {kind}, x86-64, version 1 (SYSV)')\n"
|
||||
)
|
||||
self.rpm2cpio.chmod(0o755)
|
||||
self.provided_symbols.chmod(0o755)
|
||||
self.file_command.chmod(0o755)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.temporary_directory.cleanup()
|
||||
|
||||
def run_script(self, *arguments: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SCRIPT),
|
||||
"--rpm2cpio",
|
||||
str(self.rpm2cpio),
|
||||
"--provided-symbols",
|
||||
str(self.provided_symbols),
|
||||
"--file-command",
|
||||
str(self.file_command),
|
||||
*arguments,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
def test_combines_unique_cpp_symbols_from_multiple_packages(self) -> None:
|
||||
first = self.work / "first.rpm"
|
||||
second = self.work / "second.rpm"
|
||||
output = self.work / "symbols.txt"
|
||||
first.write_bytes(
|
||||
newc_archive(
|
||||
[
|
||||
("./usr/lib64/libfirst.so", b"\x7fELF_ZN3Foo3barEv\nplain_c_symbol\n", stat.S_IFREG | 0o755),
|
||||
("./usr/share/doc/readme", b"not ELF", stat.S_IFREG | 0o644),
|
||||
("./usr/lib64/libalias.so", b"libfirst.so", stat.S_IFLNK | 0o777),
|
||||
]
|
||||
)
|
||||
)
|
||||
second.write_bytes(
|
||||
newc_archive(
|
||||
[
|
||||
(
|
||||
"./usr/lib64/libsecond.so",
|
||||
b"\x7fELF_ZN3Foo3barEv\n_ZN3Foo3bazEv\nanother_c_symbol\n",
|
||||
stat.S_IFREG | 0o755,
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
result = self.run_script(
|
||||
"--cpp-only", "--output", str(output), str(first), str(second)
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(
|
||||
output.read_text().splitlines(),
|
||||
["_ZN3Foo3barEv", "_ZN3Foo3bazEv"],
|
||||
)
|
||||
self.assertIn("package=first.rpm elf_files=1 symbols=2 selected=1", result.stderr)
|
||||
self.assertIn("package=second.rpm elf_files=1 symbols=3 selected=2", result.stderr)
|
||||
self.assertIn("unique_symbols=2", result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
def test_without_cpp_filter_preserves_all_provided_symbols(self) -> None:
|
||||
package = self.work / "all.rpm"
|
||||
package.write_bytes(
|
||||
newc_archive(
|
||||
[
|
||||
(
|
||||
"./usr/lib64/liball.so",
|
||||
b"\x7fELF_ZN3Foo3barEv\nplain_c_symbol\n",
|
||||
stat.S_IFREG | 0o755,
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
result = self.run_script(str(package))
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout.splitlines(), ["_ZN3Foo3barEv", "plain_c_symbol"])
|
||||
|
||||
def test_excludes_elf_executables_that_do_not_generate_library_provides(self) -> None:
|
||||
package = self.work / "mixed.rpm"
|
||||
package.write_bytes(
|
||||
newc_archive(
|
||||
[
|
||||
(
|
||||
"./usr/lib64/libmixed.so",
|
||||
b"\x7fELF_ZN3Lib3runEv\n",
|
||||
stat.S_IFREG | 0o755,
|
||||
),
|
||||
(
|
||||
"./usr/bin/mixed",
|
||||
b"\x7fELFEXECUTABLE\n_ZN4Exec3runEv\n",
|
||||
stat.S_IFREG | 0o755,
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
result = self.run_script("--cpp-only", str(package))
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout.splitlines(), ["_ZN3Lib3runEv"])
|
||||
self.assertIn("elf_files=1", result.stderr)
|
||||
|
||||
def test_rejects_malformed_cpio_stream(self) -> None:
|
||||
package = self.work / "broken.rpm"
|
||||
package.write_bytes(b"not a newc archive")
|
||||
|
||||
result = self.run_script(str(package))
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("newc", result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user