From 199931db801501bd3fcc348f4073773b3d8cbc01 Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:00:17 +0530 Subject: [PATCH 1/5] fix(tools): validate output directory in relinker_fuzz_seeds --- tools/relinker_fuzz_seeds.py | 25 ++++++--- tools/tests/test_relinker_fuzz_seeds.py | 71 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tools/tests/test_relinker_fuzz_seeds.py diff --git a/tools/relinker_fuzz_seeds.py b/tools/relinker_fuzz_seeds.py index 1bd32a3a4365..390b319533ed 100644 --- a/tools/relinker_fuzz_seeds.py +++ b/tools/relinker_fuzz_seeds.py @@ -40,18 +40,27 @@ def images(): yield f"{path.stem}-{name}", bytes(data) -def main(): +def main(argv=None): parser = argparse.ArgumentParser(description="Write relinker test fixtures as relinker_conversion_fuzzer seeds") parser.add_argument("output", type=Path) - arguments = parser.parse_args() - arguments.output.mkdir(parents=True, exist_ok=True) + arguments = parser.parse_args(argv) + if arguments.output.is_file(): + parser.error(f"{arguments.output} is an existing file, not a directory") + try: + arguments.output.mkdir(parents=True, exist_ok=True) + except OSError as error: + parser.error(str(error)) count = 0 - for name, data in images(): - for selector in SELECTORS: - (arguments.output / f"{name}-{selector}").write_bytes(bytes([selector]) + data) - count += 1 + try: + for name, data in images(): + for selector in SELECTORS: + (arguments.output / f"{name}-{selector}").write_bytes(bytes([selector]) + data) + count += 1 + except OSError as error: + parser.error(str(error)) print(f"{count} seeds written to {arguments.output}") + return 0 if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/tools/tests/test_relinker_fuzz_seeds.py b/tools/tests/test_relinker_fuzz_seeds.py new file mode 100644 index 000000000000..342d1afd35e9 --- /dev/null +++ b/tools/tests/test_relinker_fuzz_seeds.py @@ -0,0 +1,71 @@ +import contextlib +import io +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +import relinker_fuzz_seeds + + +class RelinkerFuzzSeedsTests(unittest.TestCase): + def run_main(self, *arguments): + stdout, stderr = io.StringIO(), io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + try: + code = relinker_fuzz_seeds.main([str(arg) for arg in arguments]) + except SystemExit as error: + code = error.code + return code, stdout.getvalue(), stderr.getvalue() + + def test_seeds_written_to_directory(self): + with tempfile.TemporaryDirectory() as directory: + output = Path(directory) / "seeds" + code, stdout, stderr = self.run_main(output) + self.assertEqual(code, 0) + self.assertEqual(stderr, "") + self.assertIn("seeds written to", stdout) + files = list(output.iterdir()) + self.assertGreater(len(files), 0) + for path in files: + data = path.read_bytes() + self.assertIn(data[0], relinker_fuzz_seeds.SELECTORS) + self.assertGreaterEqual(len(data), 65) + + def test_rejects_existing_file_output(self): + with tempfile.TemporaryDirectory() as directory: + existing = Path(directory) / "existing.bin" + existing.write_bytes(b"data") + code, stdout, stderr = self.run_main(existing) + self.assertEqual(code, 2) + self.assertIn("is an existing file, not a directory", stderr) + self.assertNotIn("Traceback", stderr) + self.assertEqual(stdout, "") + self.assertEqual(existing.read_bytes(), b"data") + + def test_rejects_uncreatable_directory_without_traceback(self): + with tempfile.TemporaryDirectory() as directory: + file_barrier = Path(directory) / "file.txt" + file_barrier.write_text("blocked", encoding="utf-8") + invalid_path = file_barrier / "child_dir" + code, stdout, stderr = self.run_main(invalid_path) + self.assertEqual(code, 2) + self.assertNotIn("Traceback", stderr) + self.assertEqual(stdout, "") + + def test_cli_subprocess_invocation(self): + with tempfile.TemporaryDirectory() as directory: + existing = Path(directory) / "file.txt" + existing.write_bytes(b"content") + result = subprocess.run([ + sys.executable, relinker_fuzz_seeds.__file__, str(existing) + ], capture_output=True, text=True) + self.assertEqual(result.returncode, 2) + self.assertIn("is an existing file, not a directory", result.stderr) + self.assertNotIn("Traceback", result.stderr) + + +if __name__ == "__main__": + unittest.main() From 935f0090cec0181a1a252a053fcf3e2a5999dfac Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:59:13 +0530 Subject: [PATCH 2/5] test(tools): register relinker_fuzz_seeds in CMakeLists.txt --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25abb6896ab2..671e0658089f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -613,7 +613,8 @@ if(BUILD_TESTING) add_test(NAME title_run COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_title_run.py) set_tests_properties(title_run PROPERTIES TIMEOUT 60) - endif() + add_test(NAME relinker_fuzz_seeds COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_relinker_fuzz_seeds.py) endif() if(ANYPS5_RELINKER_ONLY) From 2f20290ee75aa56648049787e2ca0180eb3c4d46 Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Thu, 29 Oct 2026 07:23:39 +0530 Subject: [PATCH 3/5] test(tools): verify nested parent directory creation in relinker_fuzz_seeds --- tools/tests/test_relinker_fuzz_seeds.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/tests/test_relinker_fuzz_seeds.py b/tools/tests/test_relinker_fuzz_seeds.py index 342d1afd35e9..e2f52df18919 100644 --- a/tools/tests/test_relinker_fuzz_seeds.py +++ b/tools/tests/test_relinker_fuzz_seeds.py @@ -34,6 +34,15 @@ def test_seeds_written_to_directory(self): self.assertIn(data[0], relinker_fuzz_seeds.SELECTORS) self.assertGreaterEqual(len(data), 65) + def test_creates_nested_parent_directories_and_writes_seeds(self): + with tempfile.TemporaryDirectory() as directory: + output = Path(directory) / "nested" / "deep" / "seeds" + code, stdout, stderr = self.run_main(output) + self.assertEqual(code, 0) + self.assertEqual(stderr, "") + self.assertTrue(output.is_dir()) + self.assertGreater(len(list(output.iterdir())), 0) + def test_rejects_existing_file_output(self): with tempfile.TemporaryDirectory() as directory: existing = Path(directory) / "existing.bin" From a4081905ea74ff7bb98bade845ec7e4506cf1b4e Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Thu, 5 Nov 2026 00:10:28 +0530 Subject: [PATCH 4/5] build(cmake): restore inner endif in Python tool test block --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 671e0658089f..8e926ee5aaa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -615,6 +615,7 @@ if(BUILD_TESTING) set_tests_properties(title_run PROPERTIES TIMEOUT 60) add_test(NAME relinker_fuzz_seeds COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_relinker_fuzz_seeds.py) + endif() endif() if(ANYPS5_RELINKER_ONLY) From 7d6d6f5a00ec21cefd43c0be0a74f3a7a3191831 Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:54:12 +0530 Subject: [PATCH 5/5] test(tools): verify unwritable seed write rejection in relinker_fuzz_seeds --- tools/tests/test_relinker_fuzz_seeds.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/tests/test_relinker_fuzz_seeds.py b/tools/tests/test_relinker_fuzz_seeds.py index e2f52df18919..a876dc0528df 100644 --- a/tools/tests/test_relinker_fuzz_seeds.py +++ b/tools/tests/test_relinker_fuzz_seeds.py @@ -64,6 +64,16 @@ def test_rejects_uncreatable_directory_without_traceback(self): self.assertNotIn("Traceback", stderr) self.assertEqual(stdout, "") + def test_rejects_unwritable_seed_without_traceback(self): + with tempfile.TemporaryDirectory() as directory: + output = Path(directory) / "seeds" + name, _ = next(relinker_fuzz_seeds.images()) + (output / f"{name}-{relinker_fuzz_seeds.SELECTORS[0]}").mkdir(parents=True) + code, stdout, stderr = self.run_main(output) + self.assertEqual(code, 2) + self.assertNotIn("Traceback", stderr) + self.assertEqual(stdout, "") + def test_cli_subprocess_invocation(self): with tempfile.TemporaryDirectory() as directory: existing = Path(directory) / "file.txt"