Skip to content

test: reset the image decode cache between tests - #3962

Closed
tc3oliver wants to merge 2 commits into
jundot:mainfrom
tc3oliver:test/reset-image-decode-cache
Closed

tc3oliver wants to merge 2 commits into
jundot:mainfrom
tc3oliver:test/reset-image-decode-cache

Conversation

@tc3oliver

@tc3oliver tc3oliver commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

TestSingleModelMemoryPressure::test_single_idle_model_unloads_at_hard_pressure in tests/test_process_memory_enforcer.py passes on its own, but fails if an image-decoding test ran earlier in the same process and none of the enforcer file's earlier tests ran in between:

pytest tests/test_mlx_embeddings_compat.py tests/test_process_memory_enforcer.py::TestSingleModelMemoryPressure
# on main: 1 failed, 10 passed
# Expected abort_all_requests to have been awaited once. Awaited 0 times.

Running the whole enforcer file after the embeddings file passes, and so does CI's default order, so CI doesn't hit this today. It shows up when you select that class or test directly.

The image decode cache in omlx/utils/image.py is process-global and nothing clears it between tests. The embeddings test leaves a decoded 1x1 PNG in it. Under pressure, ProcessMemoryEnforcer._check_and_enforce drops that cache and, if the drop freed anything, reads usage again. The test scripts mx.get_active_memory as a fixed three-read sequence, so the extra read eats the loop-check value and the eviction loop never runs. The enforcer is doing the right thing; the test state leaked.

The fix is an autouse fixture in tests/conftest.py that clears the cache before and after each test, next to the existing decode-activity reset. Test-only change. With it, the command above gives 11 passed.

I merged main to resolve the conflict with #3933's _hermetic_metal_release_accounting fixture; both fixtures are kept as they are.

The image decode cache is process-global and nothing clears it between
tests. test_mlx_embeddings_compat.py decodes a 1x1 PNG through
fetch_images() and leaves it cached.

ProcessMemoryEnforcer drops that cache under pressure and re-reads usage
when the drop freed anything. The single-model hard-pressure test scripts
usage as a fixed sequence of reads, so one leftover entry shifts it by a
read: the post-unload value arrives before the eviction loop, the loop
never runs, and abort_all_requests is never awaited. Run alone the test
passes; after the embeddings test in the same process it fails.

The enforcer is right to re-read after freeing memory. Clear the cache
around every test, as conftest already does for the decode-activity
registry.
@tc3oliver

Copy link
Copy Markdown
Contributor Author

CI failures match the current upstream main baseline after 4c8f634f. The same three tests fail on main and on this PR, on Python 3.11, 3.12 and 3.13:

  • tests/test_mlx_vlm_qwen4_exp_compat.py::test_qwen4_small_hyper_connection_fusion_fails_closed[False]
  • tests/test_mlx_vlm_qwen4_exp_compat.py::test_qwen4_small_hyper_connection_fusion_fails_closed[True]
  • tests/test_server_main.py::test_module_entry_api_key_setup_end_to_end

This PR introduces no additional test failures.

@github-actions

Copy link
Copy Markdown
Contributor

Closing this PR because it has merge conflicts and no new commits for over 30 days. This isn't a judgment on the change. If you'd like to continue, rebase on current main and open a new PR. See #4256 for the policy.

@github-actions github-actions Bot closed this Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant