|
3 | 3 | import sys |
4 | 4 | import sysconfig |
5 | 5 | import tarfile |
| 6 | +import unittest.mock |
6 | 7 | import zipfile |
7 | 8 |
|
8 | 9 | import pytest |
9 | 10 |
|
10 | 11 | import elfdeps |
| 12 | +from elfdeps import __main__ as cli |
11 | 13 |
|
12 | 14 | SYMBOLS_SETTINGS = elfdeps.ELFAnalyzeSettings(include_symbols=True) |
13 | 15 |
|
@@ -240,3 +242,73 @@ def test_symbols_libpython() -> None: |
240 | 242 | sym = exported[name] |
241 | 243 | assert sym.binding == elfdeps.SymbolBinding.GLOBAL |
242 | 244 | assert sym.type == elfdeps.SymbolType.OBJECT |
| 245 | + |
| 246 | + |
| 247 | +class TestCLISymbols: |
| 248 | + """Test --symbols and --demangle CLI options.""" |
| 249 | + |
| 250 | + def test_symbols_in_output(self, capsys: pytest.CaptureFixture[str]) -> None: |
| 251 | + """--symbols includes exported/imported symbols in output.""" |
| 252 | + cli.main([str(sys.executable), "--symbols"]) |
| 253 | + out = capsys.readouterr().out |
| 254 | + assert "exported_symbols:" in out |
| 255 | + assert "imported_symbols:" in out |
| 256 | + |
| 257 | + def test_no_symbols_in_output(self, capsys: pytest.CaptureFixture[str]) -> None: |
| 258 | + """Without --symbols, symbol fields are omitted.""" |
| 259 | + cli.main([str(sys.executable)]) |
| 260 | + out = capsys.readouterr().out |
| 261 | + assert "exported_symbols" not in out |
| 262 | + assert "imported_symbols" not in out |
| 263 | + |
| 264 | + def test_demangle_requires_symbols(self) -> None: |
| 265 | + """--demangle without --symbols is an error.""" |
| 266 | + with pytest.raises(SystemExit, match="2"): |
| 267 | + cli.main([str(sys.executable), "--demangle"]) |
| 268 | + |
| 269 | + def test_demangle_requires_pycxxfilt(self) -> None: |
| 270 | + """--demangle errors when pycxxfilt is not installed.""" |
| 271 | + with unittest.mock.patch.object(cli, "pycxxfilt", None): |
| 272 | + with pytest.raises(SystemExit, match="2"): |
| 273 | + cli.main([str(sys.executable), "--symbols", "--demangle"]) |
| 274 | + |
| 275 | + def test_format_symbol_no_demangle(self) -> None: |
| 276 | + """_format_symbol without demangle returns name[@version].""" |
| 277 | + sym = elfdeps.SymbolInfo( |
| 278 | + "_Z3fooi", "V1", elfdeps.SymbolBinding.GLOBAL, elfdeps.SymbolType.FUNC |
| 279 | + ) |
| 280 | + assert cli._format_symbol(sym) == "_Z3fooi@V1" |
| 281 | + |
| 282 | + sym_plain = elfdeps.SymbolInfo( |
| 283 | + "_Z3fooi", None, elfdeps.SymbolBinding.GLOBAL, elfdeps.SymbolType.FUNC |
| 284 | + ) |
| 285 | + assert cli._format_symbol(sym_plain) == "_Z3fooi" |
| 286 | + |
| 287 | + def test_format_symbol_demangle(self) -> None: |
| 288 | + """_format_symbol with demangle demanges C++ names.""" |
| 289 | + pycxxfilt = pytest.importorskip("pycxxfilt") # noqa: F841 |
| 290 | + sym = elfdeps.SymbolInfo( |
| 291 | + "_Z3fooi", "V1", elfdeps.SymbolBinding.GLOBAL, elfdeps.SymbolType.FUNC |
| 292 | + ) |
| 293 | + result = cli._format_symbol(sym, demangle=True) |
| 294 | + assert result == "foo(int)@V1" |
| 295 | + |
| 296 | + def test_format_symbol_demangle_not_mangled(self) -> None: |
| 297 | + """_format_symbol with demangle keeps non-mangled names.""" |
| 298 | + pycxxfilt = pytest.importorskip("pycxxfilt") # noqa: F841 |
| 299 | + sym = elfdeps.SymbolInfo( |
| 300 | + "printf", |
| 301 | + "GLIBC_2.34", |
| 302 | + elfdeps.SymbolBinding.GLOBAL, |
| 303 | + elfdeps.SymbolType.FUNC, |
| 304 | + ) |
| 305 | + result = cli._format_symbol(sym, demangle=True) |
| 306 | + assert result == "printf@GLIBC_2.34" |
| 307 | + |
| 308 | + def test_cli_demangle_output(self, capsys: pytest.CaptureFixture[str]) -> None: |
| 309 | + """--symbols --demangle produces demangled output.""" |
| 310 | + pycxxfilt = pytest.importorskip("pycxxfilt") # noqa: F841 |
| 311 | + cli.main([str(sys.executable), "--symbols", "--demangle"]) |
| 312 | + out = capsys.readouterr().out |
| 313 | + assert "exported_symbols:" in out |
| 314 | + assert "imported_symbols:" in out |
0 commit comments