11# Copyright 2025 Broadcom.
22# SPDX-License-Identifier: Apache-2.0
33#
4+ from __future__ import annotations
5+
46import os
57import pathlib
68import shutil
79import subprocess
10+ from typing import Dict , List
11+
12+ import pytest
813
914from relenv import relocate
1015
1116
12- def test_is_elf_on_text_file (tmp_path ) :
17+ def test_is_elf_on_text_file (tmp_path : pathlib . Path ) -> None :
1318 sample = tmp_path / "sample.txt"
1419 sample .write_text ("not an ELF binary\n " )
1520 assert relocate .is_elf (sample ) is False
1621
1722
18- def test_is_macho_on_text_file (tmp_path ) :
23+ def test_is_macho_on_text_file (tmp_path : pathlib . Path ) -> None :
1924 sample = tmp_path / "sample.txt"
2025 sample .write_text ("plain text\n " )
2126 assert relocate .is_macho (sample ) is False
2227
2328
24- def test_parse_readelf_output ():
29+ def test_parse_readelf_output () -> None :
2530 output = """
2631 0x000000000000000f (NEEDED) Shared library: [libc.so.6]
2732 0x000000000000001d (RUNPATH) Library runpath: [/usr/lib:/opt/lib]
@@ -30,7 +35,7 @@ def test_parse_readelf_output():
3035 assert result == ["/usr/lib" , "/opt/lib" ]
3136
3237
33- def test_parse_otool_output_extracts_rpaths ():
38+ def test_parse_otool_output_extracts_rpaths () -> None :
3439 sample_output = """
3540Load command 0
3641 cmd LC_LOAD_DYLIB
@@ -46,7 +51,9 @@ def test_parse_otool_output_extracts_rpaths():
4651 assert parsed [relocate .LC_RPATH ] == ["@loader_path/../lib" ]
4752
4853
49- def test_patch_rpath_adds_new_entry (monkeypatch , tmp_path ):
54+ def test_patch_rpath_adds_new_entry (
55+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
56+ ) -> None :
5057 binary = tmp_path / "prog"
5158 binary .write_text ("dummy" )
5259
@@ -56,10 +63,12 @@ def test_patch_rpath_adds_new_entry(monkeypatch, tmp_path):
5663 lambda path : ["$ORIGIN/lib" , "/abs/lib" ],
5764 )
5865
59- recorded = {}
66+ recorded : Dict [ str , List [ str ]] = {}
6067
61- def fake_run (cmd , ** kwargs ):
62- recorded ["cmd" ] = cmd
68+ def fake_run (
69+ cmd : List [str ], ** kwargs : object
70+ ) -> subprocess .CompletedProcess [bytes ]:
71+ recorded .setdefault ("cmd" , []).extend (cmd )
6372 return subprocess .CompletedProcess (cmd , 0 , stdout = b"" , stderr = b"" )
6473
6574 monkeypatch .setattr (relocate .subprocess , "run" , fake_run )
@@ -69,13 +78,15 @@ def fake_run(cmd, **kwargs):
6978 assert pathlib .Path (recorded ["cmd" ][- 1 ]) == binary
7079
7180
72- def test_patch_rpath_skips_when_present (monkeypatch , tmp_path ):
81+ def test_patch_rpath_skips_when_present (
82+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
83+ ) -> None :
7384 binary = tmp_path / "prog"
7485 binary .write_text ("dummy" )
7586
7687 monkeypatch .setattr (relocate , "parse_rpath" , lambda path : ["$ORIGIN/lib" ])
7788
78- def fail_run (* _args , ** _kwargs ) :
89+ def fail_run (* _args : object , ** _kwargs : object ) -> None :
7990 raise AssertionError ("patchelf should not be invoked" )
8091
8192 monkeypatch .setattr (relocate .subprocess , "run" , fail_run )
@@ -84,7 +95,9 @@ def fail_run(*_args, **_kwargs):
8495 assert result == "$ORIGIN/lib"
8596
8697
87- def test_handle_elf_sets_rpath (monkeypatch , tmp_path ):
98+ def test_handle_elf_sets_rpath (
99+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
100+ ) -> None :
88101 bin_dir = tmp_path / "bin"
89102 lib_dir = tmp_path / "lib"
90103 bin_dir .mkdir ()
@@ -95,7 +108,9 @@ def test_handle_elf_sets_rpath(monkeypatch, tmp_path):
95108 resident = lib_dir / "libfoo.so"
96109 resident .write_text ("library" )
97110
98- def fake_run (cmd , ** kwargs ):
111+ def fake_run (
112+ cmd : List [str ], ** kwargs : object
113+ ) -> subprocess .CompletedProcess [bytes ]:
99114 if cmd [0 ] == "ldd" :
100115 stdout = f"libfoo.so => { resident } (0x00007)\n libc.so.6 => /lib/libc.so.6 (0x00007)\n "
101116 return subprocess .CompletedProcess (
@@ -105,9 +120,9 @@ def fake_run(cmd, **kwargs):
105120
106121 monkeypatch .setattr (relocate .subprocess , "run" , fake_run )
107122
108- captured = {}
123+ captured : Dict [ str , str ] = {}
109124
110- def fake_patch_rpath (path , relpath ) :
125+ def fake_patch_rpath (path : str , relpath : str ) -> str :
111126 captured ["path" ] = path
112127 captured ["relpath" ] = relpath
113128 return relpath
@@ -125,21 +140,27 @@ def fake_patch_rpath(path, relpath):
125140 assert captured ["relpath" ] == expected_rpath
126141
127142
128- def test_patch_rpath_failure (monkeypatch , tmp_path ):
143+ def test_patch_rpath_failure (
144+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
145+ ) -> None :
129146 binary = tmp_path / "prog"
130147 binary .write_text ("dummy" )
131148
132149 monkeypatch .setattr (relocate , "parse_rpath" , lambda path : [])
133150
134- def fake_run (cmd , ** kwargs ):
151+ def fake_run (
152+ cmd : List [str ], ** kwargs : object
153+ ) -> subprocess .CompletedProcess [bytes ]:
135154 return subprocess .CompletedProcess (cmd , 1 , stdout = b"" , stderr = b"err" )
136155
137156 monkeypatch .setattr (relocate .subprocess , "run" , fake_run )
138157
139158 assert relocate .patch_rpath (binary , "$ORIGIN/lib" ) is False
140159
141160
142- def test_parse_macho_non_object (monkeypatch , tmp_path ):
161+ def test_parse_macho_non_object (
162+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
163+ ) -> None :
143164 output = "foo: is not an object file\n "
144165 monkeypatch .setattr (
145166 relocate .subprocess ,
@@ -151,7 +172,9 @@ def test_parse_macho_non_object(monkeypatch, tmp_path):
151172 assert relocate .parse_macho (tmp_path / "lib.dylib" ) is None
152173
153174
154- def test_handle_macho_copies_when_needed (monkeypatch , tmp_path ):
175+ def test_handle_macho_copies_when_needed (
176+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
177+ ) -> None :
155178 binary = tmp_path / "bin" / "prog"
156179 binary .parent .mkdir ()
157180 binary .write_text ("exe" )
@@ -178,10 +201,12 @@ def test_handle_macho_copies_when_needed(monkeypatch, tmp_path):
178201 shutil , "copymode" , lambda src , dst : copied .setdefault ("copymode" , (src , dst ))
179202 )
180203
181- recorded = {}
204+ recorded : Dict [ str , List [ str ]] = {}
182205
183- def fake_run (cmd , ** kwargs ):
184- recorded ["cmd" ] = cmd
206+ def fake_run (
207+ cmd : List [str ], ** kwargs : object
208+ ) -> subprocess .CompletedProcess [bytes ]:
209+ recorded .setdefault ("cmd" , []).extend (cmd )
185210 return subprocess .CompletedProcess (cmd , 0 , stdout = b"" , stderr = b"" )
186211
187212 monkeypatch .setattr (relocate .subprocess , "run" , fake_run )
@@ -193,7 +218,9 @@ def fake_run(cmd, **kwargs):
193218 assert recorded ["cmd" ][0 ] == "install_name_tool"
194219
195220
196- def test_handle_macho_rpath_only (monkeypatch , tmp_path ):
221+ def test_handle_macho_rpath_only (
222+ monkeypatch : pytest .MonkeyPatch , tmp_path : pathlib .Path
223+ ) -> None :
197224 binary = tmp_path / "bin" / "prog"
198225 binary .parent .mkdir ()
199226 binary .write_text ("exe" )
@@ -218,7 +245,9 @@ def test_handle_macho_rpath_only(monkeypatch, tmp_path):
218245 monkeypatch .setattr (shutil , "copy" , lambda * _args , ** _kw : (_args , _kw ))
219246 monkeypatch .setattr (shutil , "copymode" , lambda * _args , ** _kw : (_args , _kw ))
220247
221- def fake_run (cmd , ** kwargs ):
248+ def fake_run (
249+ cmd : List [str ], ** kwargs : object
250+ ) -> subprocess .CompletedProcess [bytes ]:
222251 if cmd [0 ] == "install_name_tool" :
223252 raise AssertionError ("install_name_tool should not run in rpath_only mode" )
224253 return subprocess .CompletedProcess (cmd , 0 , stdout = b"" , stderr = b"" )
0 commit comments