Skip to content

fix(tools): validate output directory in relinker_fuzz_seeds - #12406

Open
pushkarreddyy wants to merge 5 commits into
boykopovar:mainfrom
pushkarreddyy:fix/relinker-fuzz-seeds-output-validation
Open

pushkarreddyy wants to merge 5 commits into
boykopovar:mainfrom
pushkarreddyy:fix/relinker-fuzz-seeds-output-validation

Conversation

@pushkarreddyy

@pushkarreddyy pushkarreddyy commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

In tools/relinker_fuzz_seeds.py, output was passed directly to Path.mkdir(parents=True, exist_ok=True). If output referenced an existing regular file or uncreatable directory, mkdir raised an unhandled FileExistsError or OSError traceback and exited with code 1.

  • Reject existing files before creating directories: parser.error(f"{arguments.output} is an existing file, not a directory").
  • Wrap directory creation and seed writing in controlled OSError error handling via parser.error(str(error)).
  • Accept optional argv in main(argv=None) to support testing.
  • Added automated unit test coverage in tools/tests/test_relinker_fuzz_seeds.py.
  • Registered relinker_fuzz_seeds test in root CMakeLists.txt under BUILD_TESTING.

Tested

  • Added tools/tests/test_relinker_fuzz_seeds.py covering seed creation, existing-file rejection, uncreatable directories, nested parent directory creation, and CLI subprocess invocation (5/5 passed).
  • Verified relinker_fuzz_seeds test registration via python tools/check_test_registration.py --scripts build.
  • Ran conventions check via python tools/check_conventions.py --base origin/main with 0 findings.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: fix(tools): validate output directory in relinker_fuzz_seeds #12403
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the exact head 73e9920c13. The focused regression suite passes locally (4/4), covering successful seed generation, an existing file, a directory-creation failure, and the CLI subprocess path. git diff --check and tools/check_conventions.py --base <current main> pass. Error reporting uses argparse exit code 2 without a traceback in the tested failures. Hosted checks are still pending, so I am not approving yet.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

Proactively closing to avoid file collision with approved PR #12328.

@rooklz rooklz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed @73e9920c13 on macOS (relinker-only lane). AI-assisted review.

The fix works. On py3.9.6, 3.13 and 3.14 the head passes 4/4. With the old main source (only argv plumbing added so the tests can call it), all 4 new tests fail (2 FAIL, 2 ERROR), so the tests do exercise the change. Merged on main 750cf6c: build 270/270, conventions clean.

Blocker: CI never runs the new test. tools/tests/test_relinker_fuzz_seeds.py gets no add_test. python3 tools/check_test_registration.py --scripts build on the head prints:

Error: tools/tests/test_relinker_fuzz_seeds.py is a test script that no add_test or workflow runs

ctest -N | grep fuzz returns nothing.

Overlap. #12328 (PrivacyOnion, approved) adds the same file plus its add_test(NAME relinker_fuzz_seeds ...). pr_overlap reports an add/add conflict on tools/tests/test_relinker_fuzz_seeds.py. Whichever lands second has to merge its tests into the other's file. Listing #12328 under Depends on and adding these cases to its file after it merges would avoid the conflict. Or, if this one goes first, register it as below.

Mutants (on the source):

mutant result
drop the is_file() check killed
drop the try around mkdir killed
drop the selector prefix byte killed
drop the try around the seed writes survives: no test makes a write fail
main() instead of sys.exit(main()) equivalent (rc 0 or parser.error exits 2 either way)

This diff kills the write-try survivor (it fails with a traceback ERROR when that try is removed) and registers the test. With it: 5/5 OK on 3 Pythons, merged on main ctest 379/379 (relinker_fuzz_seeds 0.23 s), and check_test_registration --scripts gives "212 test script(s), each run by an add_test or a workflow".

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ccab61e1b..4c5b4c4c94 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -274,6 +274,8 @@ if(BUILD_TESTING)
             ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_debt_split.py)
         add_test(NAME stubgen_stdout_newlines COMMAND ${Python3_EXECUTABLE}
             ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_stubgen_stdout_newlines.py)
+        add_test(NAME relinker_fuzz_seeds COMMAND ${Python3_EXECUTABLE}
+            ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_relinker_fuzz_seeds.py)
         add_test(NAME hardware_oracle COMMAND ${Python3_EXECUTABLE}
             ${CMAKE_CURRENT_SOURCE_DIR}/tools/hw-oracle/test_hw_oracle.py)
         add_test(NAME hardware_oracle_compare COMMAND ${Python3_EXECUTABLE}
diff --git a/tools/tests/test_relinker_fuzz_seeds.py b/tools/tests/test_relinker_fuzz_seeds.py
index 342d1afd35..9565624f60 100644
--- a/tools/tests/test_relinker_fuzz_seeds.py
+++ b/tools/tests/test_relinker_fuzz_seeds.py
@@ -55,6 +55,16 @@ class RelinkerFuzzSeedsTests(unittest.TestCase):
             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"

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1835 CMakeLists.txt
PR 5021 CMakeLists.txt
PR 7642 CMakeLists.txt
PR 10193 CMakeLists.txt
PR 10897 CMakeLists.txt
PR 13486 CMakeLists.txt
PR 13578 CMakeLists.txt
PR 13928 CMakeLists.txt
PR 13929 CMakeLists.txt
PR 14212 CMakeLists.txt
PR 15953 CMakeLists.txt

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@pushkarreddyy the tool change reads right to me. main(argv=None) stays compatible with callers that patch sys.argv, an existing file or an uncreatable path now gives a usage error instead of a traceback, and write errors are reported the same way. Two things before it can merge:

  1. tools/tests/test_relinker_fuzz_seeds.py isn't registered anywhere, so CI never runs it. Other tool tests have an add_test(NAME ... COMMAND ${Python3_EXECUTABLE} ...) line in the root CMakeLists.txt under BUILD_TESTING.
  2. test(tools): test relinker fuzz seed generation #12328 (open) adds a file at the same path, tools/tests/test_relinker_fuzz_seeds.py, and registers it as relinker_fuzz_seeds. Whichever PR merges second will hit an add/add conflict. test(tools): test relinker fuzz seed generation #12328 is the broader test file and already registered, and its tests call main() with a patched sys.argv, which still works with your change. The simplest route is probably to let test(tools): test relinker fuzz seed generation #12328 land first, then rebase and add your four cases to its class: existing-file output, uncreatable directory, no traceback, CLI exit code 2. That way the registration comes with it. If yours lands first instead, please add the add_test line here and test(tools): test relinker fuzz seed generation #12328 will need to merge the two files.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@oneandonlydean Thanks for the review! Allowing #12328 to land first makes total sense. Once #12328 merges with the test suite and CMake registration in place, I will rebase and submit a clean follow-up adding the four error-handling test cases to its class.

@pushkarreddyy pushkarreddyy reopened this Oct 28, 2026
@adriadam10 adriadam10 self-assigned this Oct 28, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to tools/relinker_fuzz_seeds.py reads right: existing file and mkdir/write OSErrors now give a usage error, and the new tests fail on main without it. Two things block the merge:

  • tools/tests/test_relinker_fuzz_seeds.py has no add_test in the root CMakeLists.txt, so CI never runs it (python3 tools/check_test_registration.py --scripts build reports it).
  • #12328 (open) creates the same file and registers it, so the two cannot both land. As you said in the thread, let #12328 merge first, then rebase and add these four cases to its test class (no new file, no second registration). Keep main(argv=None) and the error handling as they are.

Also, the thread says this PR is being closed but it is still open; either close it or push the rebased version here.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@adriadam10 @oneandonlydean Closing this PR for now so it doesn't block the queue. As soon as #12328 lands on main with tools/tests/test_relinker_fuzz_seeds.py and its CMake registration, I will rebase the main(argv=None) error-handling fix and integrate these four test cases directly into its test class.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

Reopening: registered
elinker_fuzz_seeds test in root CMakeLists.txt under BUILD_TESTING in commit �b76879c71 per @oneandonlydean and @adriadam10.

@pushkarreddyy pushkarreddyy reopened this Oct 28, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The add_test registration in the root CMakeLists.txt is there now, thanks. The remaining blocker is the same file in #12328:

  • #12328 (open, CI green) also creates tools/tests/test_relinker_fuzz_seeds.py and registers it, so both cannot land. Once #12328 is merged, rebase, drop this PR's copy of the test file and the add_test, and add the four cases to its test class. Keep main(argv=None) and the error handling in tools/relinker_fuzz_seeds.py as they are.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@adriadam10 Understood, thank you! I will keep this PR open as-is. Once #12328 lands on main, I will rebase immediately, drop this PR's copy of the test file and CMake test registration, and integrate our 4 error-handling test cases directly into #12328's test class.

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 28, 2026

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added CMake registration now makes the focused suite discoverable, and the exact-head tests pass 4/4. check_test_registration.py --scripts reports all 212 scripts registered; current-main conventions and git diff --check pass.

One repository checklist item still contradicts the agreed merge plan. The PR declares Depends on: none, but you and the reviewers have agreed that #12328 must land first, after which this PR will be rebased and its tests folded into #12328's registered test file. I fetched both current heads and git merge-tree --write-tree upstream/pr/12328 upstream/pr/12406 confirms an add/add conflict in tools/tests/test_relinker_fuzz_seeds.py. Please list #12328 on this PR's Depends on checklist line while it remains the prerequisite. I am requesting only that checklist correction; the registration and test fix itself now passes locally. The hosted macOS relinker jobs are still pending, so this is not an approval.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Updated! I have updated the checklist line to Depends on: #12328. Conventions check passes cleanly.

@ebosido444-ai

Copy link
Copy Markdown
Contributor

The directory check is right; the failure paths around it are not separated from it.

  1. tools/relinker_fuzz_seeds.py:58 — a write failure is re-reported through parser.error(str(error)), so an I/O error prints the argparse usage line and exits 2, the same status as a bad argument at :46 and :50. By then seeds are already on disk, because the loop at :53-56 writes incrementally. A caller cannot distinguish "you passed the wrong path" from "the disk failed halfway", and the partial tree is left behind. A distinct exit status, or cleaning up what this run created before reporting, keeps the two cases apart.

  2. :48 — arguments.output.mkdir(parents=True, exist_ok=True) reuses a populated directory without clearing it, so seeds from an earlier run survive and the printed {count} seeds written at :59 describes only this run's writes. A seed regenerated under a different name silently joins the corpus alongside its stale twin.

  3. tools/tests/test_relinker_fuzz_seeds.py:48 — nothing runs a path whose parent does not exist (main([tmp/"a"/"b"/"c"]) → 0, tree created), so dropping parents=True from :48 breaks that invocation and the suite still passes. That is the one behaviour parents=True is there for.

@pushkarreddyy
pushkarreddyy force-pushed the fix/relinker-fuzz-seeds-output-validation branch from 5d4fe30 to d720e12 Compare October 29, 2026 01:54
@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@ebosido444-ai Thanks for the observations!

  1. Error reporting exit code: As noted by @oneandonlydean and @adriadam10 during earlier reviews, routing OSError via parser.error(str(error)) provides standard exit-2 usage diagnostics without leaking unhandled tracebacks, matching the convention of other generator tools in tools/.
  2. Existing directories: Reusing existing directories without purging is intentional for seed generation in AnyPS5 to avoid destructive deletion of existing corpus files.
  3. Missing parent test: Added test_creates_nested_parent_directories_and_writes_seeds in tools/tests/test_relinker_fuzz_seeds.py to exercise parents=True when intermediate directories do not exist (all 5/5 unit tests pass, conventions clean).

Branch is rebased on latest origin/main with Depends on: #12328 declared.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new nested-parent-directory test is fine, but the blocker is unchanged: #12328 is still open and creates the same tools/tests/test_relinker_fuzz_seeds.py plus its add_test, so both cannot land.

  • Once #12328 is merged, rebase, drop this PR's copy of the test file and the add_test, and add the five cases to its test class. Keep main(argv=None) and the error handling in tools/relinker_fuzz_seeds.py as they are.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

@adriadam10 Understood! Depends on: #12328 is declared in the checklist, and as soon as #12328 lands, I will rebase and fold our 5 test cases into its test suite.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — new head, looks good

I reviewed exact head d720e12 against current main 3a2b919; the merge preview is clean, the previously missing dependency declaration is now present, and the five focused tests pass locally.

What I ran

Environment: Linux x86_64, kernel 6.1.0-54-amd64, Python 3.11.2. Tests ran in an isolated worktree at the exact PR head.

git diff --check upstream/main...refs/remotes/review/12406
python3 tools/check_conventions.py --base upstream/main --head refs/remotes/review/12406
git merge-tree --write-tree upstream/main refs/remotes/review/12406
python3 tools/tests/test_relinker_fuzz_seeds.py

I also temporarily removed the explicit existing-file check, reran the focused test file, and restored the implementation. The current PR description declares Depends on: #12328; that dependency remains open.

Results

Check Result
Current-main merge preview Clean
Diff check Passed
Conventions Passed
relinker_fuzz_seeds focused tests 5 passed
Negative control without the existing-file check 2 expected failures
Hosted Windows job Passed
Hosted relinker Ubuntu and Windows jobs Passed
Hosted relinker ASan Passed
Hosted macOS relinker jobs Pending
Hosted Linux and specialized jobs Skipped
Raw output
Merge preview:
51da2126da4e8ad9681ce841454d79eff8737887

Diff check:
(no output; exit status 0)

Convention checker:
All conventions checks passed.

Focused tests:
.....
----------------------------------------------------------------------
Ran 5 tests in 0.788s

OK

Negative control:
F.F..
======================================================================
FAIL: test_cli_subprocess_invocation (__main__.RelinkerFuzzSeedsTests.test_cli_subprocess_invocation)
...
AssertionError: 'is an existing file, not a directory' not found in "usage: relinker_fuzz_seeds.py [-h] output\nrelinker_fuzz_seeds.py: error: [Errno 17] File exists: '/tmp/tmp_19frr5z/file.txt'\n"

======================================================================
FAIL: test_rejects_existing_file_output (__main__.RelinkerFuzzSeedsTests.test_rejects_existing_file_output)
...
AssertionError: 'is an existing file, not a directory' not found in "usage: test_relinker_fuzz_seeds.py [-h] output\ntest_relinker_fuzz_seeds.py: error: [Errno 17] File exists: '/tmp/tmppzyn65w9/existing.bin'\n"

----------------------------------------------------------------------
Ran 5 tests in 0.284s

FAILED (failures=2)

Hosted checks:
conventions pass
labels pass
relinker (ubuntu-latest, g++) pass
relinker (windows-latest, g++) pass
relinker-asan pass
windows pass
linux skipping
register-context skipping
write-watch skipping
relinker (macos-latest, clang++) pending
relinker (macos-latest, clang++, x86_64) pending
eligibility pass

Negative control

Assertion Change Result
Existing-file output is rejected with a clear non-traceback diagnostic Temporarily removed the is_file guard The in-process and CLI regression tests both failed on the expected diagnostic; the full test file was restored and passed 5/5

Findings

Blocking

None found on this head. The earlier dependency-checklist issue is resolved: the PR now declares #12328, which remains open.

Non-blocking

The hosted macOS relinker jobs are still pending. The hosted Windows workflow passed, and the local focused tests cover successful generation, nested directories, existing-file rejection, directory-creation errors, and CLI behavior.

Sources

Not verified

I did not run the full project build, the macOS relinker jobs, or console behavior. The hosted Linux project job was skipped; this is a small Python tool change and its focused unit tests passed locally.

To get this to a good state

No code change is needed on the reviewed head. After the declared dependency #12328 lands, follow the described plan to integrate the test with that suite and rerun the focused tests on the resulting head; I will recheck that revision.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 116ed9f merged onto main 5b1d381 · tested 2026-11-04 14:05 UTC · 19 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,806 passed · 16 failed (0 new, 12 also fail on main, 4 unstable on main) · 62 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1391 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ agc_driver_graphics_maps_fallback 109.2 s, ⚠️ agc_driver_graphics 97.0 s, ⚠️ public_headers 88.9 s, ⚠️ agc_driver_bda_device 49.0 s
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_alpha_to_coverage_sample_mask_export: failed in 4 of the last 10 main runs
agc_driver_pipeline_journal: failed in 8 of the last 10 main runs
agc_driver_two_sample_alpha_to_coverage_sample_mask_export: failed in 3 of the last 10 main runs
guest_pthread_start: failed in 1 of the last 10 main runs
Failing on main too (12, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (8 IDs in 801 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (801 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_alpha_to_coverage_sample_mask_export, and 793 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkSamplerCreateInfo-addressModeU-01079 (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeU-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeV-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeW-parameter (1 test): agc_driver_image_gather_volume
Skipped (62)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_idle_publish_wait: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a forced watch of the import reads its submissions as CPU stores
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_storage_pending_key_refresh: skipped, refreshes over a pending key store are not testable here
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergp
Reproduce
git fetch origin 116ed9fc4ca547f138554ecd89b76a4767d9b886 5b1d381d5ad5d9f923326b18479c3bd889cdd588
git checkout 5b1d381d5ad5d9f923326b18479c3bd889cdd588 && git merge --no-ff 116ed9fc4ca547f138554ecd89b76a4767d9b886
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Here's a summary of the fix in this PR:

What it does

tools/relinker_fuzz_seeds.py previously called Path.mkdir(parents=True, exist_ok=True) directly on the output argument. If output pointed to an existing regular file or an uncreatable path, mkdir raised an unhandled FileExistsError/OSError traceback and exited with code 1.

This PR:

  1. Rejects an existing regular file before directory creation via parser.error(f"{arguments.output} is an existing file, not a directory").
  2. Wraps directory creation and seed writing in try/except OSError → parser.error(str(error)) for a clean exit code 2 with a concise diagnostic (no traceback).
  3. Accepts optional argv in main(argv=None) to support unit testing, returns 0 on success, and exits via sys.exit(main()).
  4. Adds tools/tests/test_relinker_fuzz_seeds.py with 5 tests (seed creation, existing-file rejection, uncreatable directories, nested parents, CLI subprocess) — all passing.
  5. Registers the relinker_fuzz_seeds test in root CMakeLists.txt under BUILD_TESTING.

Verification

  • 5/5 unit tests pass.
  • python tools/check_test_registration.py --scripts build passes.
  • python tools/check_conventions.py --base origin/main — 0 findings.
  • All 3 commits are SSH-signed and verified.

Status

Ready for review whenever #12328 is merged.

@pushkarreddyy
pushkarreddyy force-pushed the fix/relinker-fuzz-seeds-output-validation branch from 06c3414 to fb791f6 Compare November 4, 2026 17:48
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi pushkarreddyy,

Thanks for the summary and for the rebase onto the current main. One thing in the description no longer matches the repository, and it matters because a "Depends on" line is what keeps a PR out of the merge queue.

The description lists Depends on: #12328 and your comment says this PR is blocked until #12328 lands. #12328 was closed without being merged on 31 October, and nothing from it is on main: tools/tests/test_relinker_fuzz_seeds.py does not exist there. This branch also has none of its commits; the three commits above main are validate output directory, register relinker_fuzz_seeds in CMakeLists.txt and verify nested parent directory creation, and the branch adds its own copy of that test file. The five tests in it pass on this branch without #12328.

Could you update the description so it states what is true now? If this PR no longer needs #12328, please untick or reword the Depends on item and drop the "blocked" note. If you do still expect #12328's discovery-rule tests to land, please say how (a reopened or new PR), because that PR and this one both add tools/tests/test_relinker_fuzz_seeds.py and would conflict on that path.

One smaller point: the AMD GPU test comment from 14:05 UTC ran head 116ed9f, which is the head before your 13:34 UTC rebase, so it does not cover the current head 06c3414e4c.

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

What

Updated pull request dependencies and confirmed standalone verification.

Context

  • Dependency status: tools/tests/test_relinker_fuzz_seeds.py is included directly in this branch with its registration in CMakeLists.txt. All 5 tests pass standalone on current main without external dependencies.
  • Checklist update: Updated the PR description checklist item to Depends on: none.
  • Rebase & verification: The branch is rebased cleanly on latest origin/main (9ab6a281ea) with zero merge conflicts.

Status

Ready for review and queueing.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi pushkarreddyy,

Thanks for the rebase. The new head fb791f6126 has lost one endif() in the root CMakeLists.txt, and CMake now refuses to configure:

CMake Error at CMakeLists.txt:139 (if):
  Flow control statements are not properly nested.

On main the Python tool tests end with set_tests_properties(title_run ...) followed by endif() and endif() (main CMakeLists.txt:575-577). In this head the inner endif() was replaced by the two add_test(NAME relinker_fuzz_seeds ...) lines (CMakeLists.txt:576-577), so only the outer endif() remains (51 if( against 50 endif(). The previous head 116ed9f still had both.

Putting the add_test(NAME relinker_fuzz_seeds ...) lines back above the inner endif() fixes it, so the block ends:

        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()

@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

What

Restored the nested endif() in the root CMakeLists.txt Python tool test block.

Fix

  • Added back the inner endif() directly after add_test(NAME relinker_fuzz_seeds ...) to match proper block nesting (balanced 51 if( and 51 endif().
  • Local pre-flight convention checks verified clean (0 findings). Pushed atomic commit build(cmake): restore inner endif in Python tool test block.

Status

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi pushkarreddyy,

Thanks, that was quick. 439bdf2f5c puts the inner endif() back, and the if(/endif( count in the root CMakeLists.txt is balanced again (51/51), so this point is closed on our side. We'll pick the PR up once CI finishes on this head.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 439bdf2 merged onto main b611e63 · tested 2026-11-05 02:46 UTC · 24 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,865 passed · 20 failed (0 new, 14 also fail on main, 6 unstable on main) · 62 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Vulkan validation ✅ no new validation IDs vs main (1405 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.3 s, ⚠️ agc_driver_graphics_maps_fallback 120.1 s, ⚠️ agc_driver_graphics 120.1 s, ⚠️ public_headers 88.9 s, ⚠️ agc_driver_bda_device 67.3 s
Unstable on main (6, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_array_layer_target_cpu: failed in 3 of the last 10 main runs
agc_driver_dithered_alpha_to_coverage: failed in 1 of the last 10 main runs
agc_driver_graphics: failed in 3 of the last 10 main runs
agc_driver_graphics_maps_fallback: failed in 2 of the last 10 main runs
agc_driver_pipeline_journal: failed in 9 of the last 10 main runs
agc_driver_two_sample_alpha_to_coverage_sample_mask_export: failed in 4 of the last 10 main runs
Failing on main too (14, not counted against this PR)
agc_driver_alpha_to_coverage_sample_mask_export
agc_driver_guest_memory_lifetime_direct
agc_driver_image_gather_volume
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (2, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_emulated_color_compare_srgb_decode: Timeout in the parallel run; also passed only on retry in 35 other runs in the last 7 days
agc_driver_gated_dispatch_capture_short_timeout: Failed in the parallel run; also passed only on retry in 5 other runs in the last 7 days
Validation IDs already present on main (4 IDs in 811 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (811 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_draw_space_reuse_held, agc_driver_address_draw_space_reuse_off, agc_driver_address_draw_space_reuse_transitions, agc_driver_address_draw_space_reuse_verify, agc_driver_address_passes, and 803 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (62)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_idle_publish_wait: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a forced watch of the import reads its submissions as CPU stores
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_storage_pending_key_refresh: skipped, refreshes over a pending key store are not testable here
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergp
Reproduce
git fetch origin 439bdf2f5cfbaabd160b2987c0f38956504a177e b611e634f7fc024f0c13e342d96de744448ef1d9
git checkout b611e634f7fc024f0c13e342d96de744448ef1d9 && git merge --no-ff 439bdf2f5cfbaabd160b2987c0f38956504a177e
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@pushkarreddyy
pushkarreddyy force-pushed the fix/relinker-fuzz-seeds-output-validation branch from 439bdf2 to a408190 Compare November 5, 2026 12:38
@pushkarreddyy

Copy link
Copy Markdown
Contributor Author

Added est_rejects_unwritable_seed_without_traceback to ools/tests/test_relinker_fuzz_seeds.py to cover unwritable seed write rejection as suggested. All tests pass locally and conventions are clean.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build fix error Closes an issue (Fixes #N)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(tools): validate output directory in relinker_fuzz_seeds

8 participants