test(relinker): align module tests with nonrecursive discovery

This commit is contained in:
boykopovar 2026-10-10 15:31:17 +03:00
parent f756225f09
commit 0bd2e4cb90
5 changed files with 53 additions and 111 deletions

View File

@ -71,7 +71,7 @@ def main():
assert result.returncode == expected, (result.returncode, result.stdout, result.stderr)
for windows in (True, False):
for directory in ('sce_module', '.', 'Media/Modules'):
for directory in ('sce_module', 'prx'):
result, output = convert(f'sprx-request-{windows}-{directory.replace("/", "-")}', windows,
'libGuest.sprx', module_directory=directory, guest_request='libGuest.sprx',
providers={'renamed.sprx': declared_provider(22, ('libGuest',))})
@ -95,22 +95,18 @@ def main():
extra={'unused.prx': declared_provider(77, ('libUnused',)), 'opaque.sprx': b'not an ELF'})
assert result.returncode == 0, result.stderr
artifact = output.parent / 'app0' / (relative.as_posix() + '.guest.prx')
assert artifact.is_file(), list((output.parent / 'app0').rglob('*'))
assert len(list((output.parent / 'app0').rglob('*.guest.prx'))) == 2
if windows:
run(output)
else:
assert needed_libraries(output.read_bytes()) == [
'$ORIGIN/app0/' + directory + '/renamed' + suffix + '.guest.prx',
'$ORIGIN/app0/sce_module/consumer.prx.guest.prx']
assert not artifact.exists(), artifact
assert len(list((output.parent / 'app0').rglob('*.guest.prx'))) == 1
if not windows:
assert 'libGuest.suprx' in needed_libraries(output.read_bytes())
result, output = convert(f'outside-ambiguous-{windows}', windows, 'libGuest.suprx', module_directory='.',
providers={'one.prx': declared_provider(22, ('libGuest',))},
extra={'other/two.prx': declared_provider(33, ('libGuest',))})
assert result.returncode == 2 and 'Ambiguous needed module identity' in result.stderr, result.stderr
assert not output.exists()
assert result.returncode == 0, result.stderr
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
result, output = convert(f'outside-sce-strings-{windows}', windows, 'libGuest.suprx', module_directory='.',
result, output = convert(f'sce-strings-{windows}', windows, 'libGuest.suprx', module_directory='sce_module',
providers={'renamed.prx': sce_strings(declared_provider(22, ('libGuest',)))})
assert result.returncode == 0, result.stderr
if windows:
@ -128,22 +124,22 @@ def main():
malformed = declared_provider(22, ('libGuest',))
struct.pack_into('<Q', malformed, 0x600 + 8 * 16 + 8, (1 << 48) | 0xffffffff)
result, output = convert(f'outside-bad-name-{windows}', windows, 'libGuest.suprx',
module_directory='.', providers={'renamed.prx': malformed})
assert result.returncode == 2 and 'Module metadata string offset out of bounds' in result.stderr, result.stderr
result, output = convert(f'direct-bad-name-{windows}', windows, 'libGuest.suprx',
module_directory='sce_module', providers={'renamed.prx': malformed})
assert result.returncode == 2 and 'String offset out of bounds' in result.stderr, result.stderr
assert not output.exists()
malformed = sce_strings(declared_provider(22, ('libGuest',)))
struct.pack_into('<Q', malformed, 0x618, 0x100)
result, output = convert(f'outside-bad-sce-range-{windows}', windows, 'libGuest.suprx',
module_directory='.', providers={'renamed.prx': malformed})
assert result.returncode == 2 and 'Module metadata strings exceed dynamic data' in result.stderr, result.stderr
result, output = convert(f'direct-bad-sce-range-{windows}', windows, 'libGuest.suprx',
module_directory='sce_module', providers={'renamed.prx': malformed})
assert result.returncode == 2 and 'SCE table exceeds dynamic data segment' in result.stderr, result.stderr
assert not output.exists()
unrelated = declared_provider(77, ('libUnused',))
struct.pack_into('<qQ', unrelated, 0x600 + 5 * 16, 0x70000000, 0)
result, output = convert(f'outside-unrelated-unsupported-body-{windows}', windows, 'libGuest.suprx',
module_directory='.', extra={'unused.prx': unrelated})
result, output = convert(f'ignored-unrelated-unsupported-body-{windows}', windows, 'libGuest.suprx',
module_directory='sce_module', extra={'unused.prx': unrelated})
assert result.returncode == 0, result.stderr
assert len(list((output.parent / 'app0').rglob('*.guest.prx'))) == 1
if windows:

View File

@ -104,29 +104,30 @@ def main():
for windows in (False, True):
case = work / f"{windows}-found"
modules = case / "Media" / "Modules"
modules = case / "sce_module"
modules.mkdir(parents=True)
(modules / "needed.prx").write_bytes(module_with_symbol(True))
(case / "Media").mkdir()
(case / "Media" / "needed.prx").write_text("not ELF")
result, output = convert(case, windows)
assert result.returncode == 0, (result.stdout, result.stderr)
artifact = case / "app0" / "Media" / "Modules" / "needed.prx.guest.prx"
artifact = case / "app0" / "sce_module" / "needed.prx.guest.prx"
assert artifact.read_bytes().startswith(b"MZ" if windows else b"\x7fELF"), artifact
assert list((case / "app0").rglob("*.guest.prx")) == [artifact]
assert " Media/Modules/needed.prx.guest.prx\n" in result.stdout, result.stdout
assert " sce_module/needed.prx.guest.prx\n" in result.stdout, result.stdout
if windows and os.name == "nt":
run = subprocess.run([str(output)], capture_output=True, text=True, timeout=30)
assert run.returncode == 42, (run.returncode, run.stdout, run.stderr)
if not windows:
needed = needed_libraries(output.read_bytes())
assert needed == ["$ORIGIN/app0/Media/Modules/needed.prx.guest.prx"], needed
assert needed == ["$ORIGIN/app0/sce_module/needed.prx.guest.prx"], needed
case = work / f"{windows}-repeated-system"
result, output = convert(case, windows, b"libSceVideoOut.prx", repeats=2)
assert result.returncode == 0, (result.stdout, result.stderr)
assert output.exists(), output
for directory in ("Media/Modules", "sce_module", "sce_module/nested", "sce_modules/nested", "prx/shipping"):
for directory in ("sce_module", "sce_modules", "prx"):
case = work / f"{windows}-debug-name-{directory.replace('/', '-')}"
(case / directory).mkdir(parents=True)
(case / directory / "needed.prx").write_bytes(module_with_symbol(True))
@ -158,16 +159,22 @@ def main():
(case / "first" / "needed.prx").write_bytes(module_with_symbol(True))
(case / "first" / "needed.sprx").write_bytes(module_with_symbol(True))
result, output = convert(case, windows, b"needed.debug_prx")
assert result.returncode == 2 and "Ambiguous needed module" in result.stderr, result.stderr
assert not output.exists(), output
assert result.returncode == 0, result.stderr
assert output.exists(), output
assert not list((case / "app0").rglob("*.guest.prx"))
if not windows:
assert needed_libraries(output.read_bytes()) == ["needed.debug_prx" if "debug-name" in case.name else "needed.prx"]
case = work / f"{windows}-ambiguous"
for name in ("first", "second"):
(case / name).mkdir(parents=True)
(case / name / "needed.prx").write_bytes(module_with_symbol(True))
result, output = convert(case, windows)
assert result.returncode == 2 and "Ambiguous needed module" in result.stderr, result.stderr
assert not output.exists(), output
assert result.returncode == 0, result.stderr
assert output.exists(), output
assert not list((case / "app0").rglob("*.guest.prx"))
if not windows:
assert needed_libraries(output.read_bytes()) == ["needed.debug_prx" if "debug-name" in case.name else "needed.prx"]
case = work / "macos-repeated-system"
(case / "sce_module").mkdir(parents=True)
source = case / "input.elf"

View File

@ -48,97 +48,36 @@ def main():
unsupported = declared_provider(77, ('libUnused',))
struct.pack_into('<qQ', unsupported, 0x600 + 5 * 16, 0x70000000, 0)
for windows in (True, False):
for directory in ('prx/shipping', 'sce_module/nested', 'sce_modules/nested'):
for directory in ('prx/shipping', 'sce_module/nested', 'sce_modules/nested', 'Media/Modules', 'old/games/app0/sce_module'):
label = f'{windows}-{directory.replace("/", "-")}'
for identity in (False, True):
filename = 'renamed.prx' if identity else 'libGuest.suprx'
relative = directory + '/' + filename
files = {relative: declared_provider(22, ('libGuest' if identity else 'other',)),
files = {directory + '/' + filename: declared_provider(22, ('libGuest',)),
directory + '/deeper/' + filename: declared_provider(33, ('libGuest',)),
directory + '/unused.prx': unsupported,
directory + '/opaque.sprx': b'not an ELF',
directory + '/self.prx': bytes.fromhex('4f153d1d') + bytes(128),
directory + '/old.prx.guest.prx': b'\x7fELF'}
result, output = convert(f'{label}-{identity}', windows, 'libGuest.suprx', files)
success(result, output, relative, windows)
other = directory + '/deeper/' + ('duplicate.prx' if identity else filename)
files[other] = declared_provider(33, ('libGuest',))
result, output = convert(f'{label}-{identity}-ambiguous', windows, 'libGuest.suprx', files)
diagnostic = 'Ambiguous needed module identity' if identity else 'Ambiguous needed module:'
assert result.returncode == 2 and diagnostic in result.stderr, result.stderr
assert not output.exists()
for soname in (False, True):
standard = directory.split('/')[0]
relative = standard + ('/physical.prx' if soname else '/libGuest.suprx')
files = {relative: declared_provider(22, ('other',), 'libGuest.suprx' if soname else None),
directory + '/renamed.prx': declared_provider(11, ('libGuest',))}
result, output = convert(f'{label}-precedence-{soname}', windows, 'libGuest.suprx', files)
success(result, output, relative, windows)
files = {directory + '/kept.prx': declared_provider(22, ('libGuest',)),
directory + '/omitted.prx': declared_provider(11, ('libGuest',))}
result, output = convert(f'{label}-excluded-identity', windows, 'libGuest.suprx', files, ('omitted.prx',))
success(result, output, directory + '/kept.prx', windows)
files = {directory + '/libGuest.prx': declared_provider(22, ('libGuest',)),
directory + '/libGuest.sprx': declared_provider(33, ('libGuest',))}
result, output = convert(f'{label}-excluded-stem', windows, 'libGuest.debug_prx', files, ('libGuest.sprx',))
success(result, output, directory + '/libGuest.prx', windows)
result, output = convert(f'{label}-ambiguous-stem', windows, 'libGuest.debug_prx', files)
assert result.returncode == 2 and 'Ambiguous needed module:' in result.stderr, result.stderr
assert not output.exists()
files = {directory + '/libGuest.prx': declared_provider(22, ('libGuest',))}
result, output = convert(f'{label}-excluded-only-stem', windows, 'libGuest.debug_prx', files, ('libGuest.prx',))
assert result.returncode == 0, result.stderr
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
if not windows:
assert 'libGuest.debug_prx' in needed_libraries(output.read_bytes())
files = {directory + '/libGuest.suprx': declared_provider(22, ('libGuest',))}
result, output = convert(f'{label}-excluded-required', windows, 'libGuest.suprx', files, ('libGuest.suprx',))
assert result.returncode == 0, result.stderr
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
files = {directory + '/kept.prx': declared_provider(22, ('libGuest',)),
'unrelated/omitted.prx': declared_provider(11, ('other',))}
for excluded in ('absent.prx', 'omitted.prx'):
result, output = convert(f'{label}-unknown-{excluded}', windows, 'libGuest.suprx', files, (excluded,))
assert result.returncode == 0, (result.stdout, result.stderr)
assert output.is_file()
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
if not windows:
assert needed_libraries(output.read_bytes()) == ['libGuest.suprx']
result, output = convert(f'{label}-{identity}-excluded', windows, 'libGuest.suprx', files, (filename,))
assert result.returncode == 2 and 'Excluded guest module file not found' in result.stderr, result.stderr
assert not output.exists()
generated = 'libGuest.prx.guest.prx'
files = {directory + '/' + generated: declared_provider(22, ('libGuest',))}
result, output = convert(f'{label}-generated-required', windows, generated, files)
standard = directory.split('/')[0] if directory.split('/')[0] in ('prx', 'sce_module', 'sce_modules') else 'sce_module'
relative = standard + '/renamed.prx'
files = {relative: declared_provider(22, ('libGuest',)),
directory + '/duplicate.prx': declared_provider(33, ('libGuest',)),
directory + '/libGuest.suprx': bytes.fromhex('4f153d1d') + bytes(128)}
result, output = convert(f'{label}-direct', windows, 'libGuest.suprx', files)
success(result, output, relative, windows)
result, output = convert(f'{label}-direct-excluded', windows, 'libGuest.suprx', files, ('renamed.prx',))
assert result.returncode == 0, result.stderr
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
if not windows:
assert generated in needed_libraries(output.read_bytes())
result, output = convert(f'{label}-generated-excluded', windows, generated, files, (generated,))
assert result.returncode == 2 and 'Excluded guest module file not found' in result.stderr, result.stderr
assert not output.exists()
if os.name == 'nt':
for windows in (True, False):
for directory in ('PrX/shipping', 'Sce_Module/nested', 'Sce_Modules/nested'):
label = f'host-case-{windows}-{directory.replace("/", "-")}'
files = {directory + '/kept.prx': declared_provider(22, ('libGuest',)),
directory + '/omitted.prx': declared_provider(11, ('libGuest',))}
result, output = convert(label, windows, 'libGuest.suprx', files, ('omitted.prx',))
success(result, output, directory + '/kept.prx', windows)
files = {directory + '/libGuest.suprx': declared_provider(22, ('libGuest',))}
result, output = convert(label + '-required', windows, 'libGuest.suprx', files, ('libGuest.suprx',))
assert result.returncode == 0, result.stderr
assert not list((output.parent / 'app0').rglob('*.guest.prx'))
for filename, identity in (('party.prx', 'Party'), ('libplayfabmultiplayer.prx', 'libPlayFabMultiplayer')):
relative = 'prx/shipping/' + filename
for windows in (True, False):
result, output = convert(f'case-identity-{windows}-{filename}', windows, identity + '.prx',
{relative: declared_provider(22, (identity, 'libGuest'))})
success(result, output, relative, windows)
print('Nested required guest module tests passed')

View File

@ -15,7 +15,7 @@ def main():
work = Path(directory)
failures = []
checked = 0
for index, module_directory in enumerate((".", "sce_module", "Media/Modules", "Media/Modules/runtime")):
for index, module_directory in enumerate(("sce_module", "prx")):
for label, run_path in (("default", None), ("origin", "$ORIGIN"),
("custom", "$ORIGIN/host libs"), ("absolute", str(work / "absolute libs"))):
case = work / f"{index}-{label}"

View File

@ -43,8 +43,8 @@ def main():
source.write_bytes(fixture())
missing = subprocess.run([str(relinker), str(source), str(output)], capture_output=True, text=True, timeout=20)
assert missing.returncode != 0 and not output.exists(), missing
assert "sce_module/sce_modules/prx was not found" in missing.stderr, missing.stderr
assert "--skip-sce-module only if this game" in missing.stderr, missing.stderr
assert "sce_module/*.prx was not found" in missing.stderr, missing.stderr
assert "--skip-sce-module to disable guest module processing" in missing.stderr, missing.stderr
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)], capture_output=True, text=True, timeout=20)
if result.returncode != 0:
raise AssertionError((result.returncode, result.stdout, result.stderr))