unsloth/tests
Daniel Han 827d25931b
Stop 19 test files racing on one PowerShell startup cache (#9371)
* Stop 19 test files racing on one PowerShell startup cache

Backend CI run 32341628757 on `1c3dde199` finished `284 failed, 8498 passed`. Every
one of the 284 was a pwsh subprocess ending `died with <Signals.SIGABRT: 6>`, across
19 files that all read as Windows-installer regressions. None of them were. 222 of
the aborts land inside a two-second window, 88 at 07:09:30 and 133 at 07:09:31,
which is a mass kill of every live pwsh rather than independent per-test flakiness.

The cause
------------------------------------------------------------------------
Every `-NonInteractive` startup reads and rewrites an ~83 KB
`$XDG_CACHE_HOME/powershell/StartupProfileData-NonInteractive`, and XDG_CACHE_HOME
defaults to `$HOME/.cache`. Under `-n 4` all four xdist workers share one HOME, so
the whole job's pwsh processes race on one file and a startup that deserialises a
half-written one dies before it reaches our script. `Stack overflow.` is .NET's
failfast, which cannot unwind a blown stack, so it prints one line and calls
abort(); that is the SIGABRT (PowerShell/PowerShell#24461).

Measured twice, independently, 4000 startups per arm:

  run 1  shared cache dir     7/4000 died  {-11: 3, -6: 4}
         private cache dirs   0/4000
  run 2  shared cache dir    11/4000 died  {-11: 10, -6: 1}
         private cache dirs   0/4000

Three distinct crash shapes appeared, and each names the torn file rather than our
scripts: `Stack overflow.`, `System.IO.FileLoadException: The given assembly name`,
and `System.ArgumentException: String cannot have zero length.` 18 deaths in 8000
shared startups, 0 in 8000 private.

CI agrees from the other direction. Of the pwsh-heavy files in that run, exactly one
had zero failures, tests/test_windows_amd_gpu_scan_fallback.py, and it is the only
one that hands its child a private HOME, across roughly 80 startups where the run's
own rate predicts about 16 failures.

What is NOT established
------------------------------------------------------------------------
Neither experiment reproduces CI's rate. Roughly 20% of pwsh startups died there
against 0.2 to 0.3% here, and at CI's actual `-n 4` on this box I measured 0/1200 in
both arms: the race needed 48-way concurrency before it appeared at all. The likely
reason is that four workers on a 4-core runner are in real contention while four
threads on a 192-core box almost never overlap in the critical section, but that is
reasoning and not a measurement, so treat the mechanism as proven and the magnitude
as unexplained. That is also why this does not stop at removing the shared file.

Three layers, in order
------------------------------------------------------------------------
1. Remove the contended resource. One cache directory per xdist worker, fresh per
   session. Workers run their tests one at a time, so within a worker the startups
   stay sequential and the cache still does its job warm; across workers the
   directories are disjoint and there is nothing left to race on. Fresh rather than a
   stable path, because a cache torn by an earlier run would otherwise poison every
   later session on the same box.
2. Retry a run that produced no verdict. Three attempts, unslept, because the trigger
   is process startup rather than a resource that frees up.
3. Attribute what is left. A crash raises PwshInterpreterCrash naming the interpreter.

Layer 1 is the fix; 2 and 3 exist because of the unexplained magnitude above.

Deliberately NOT done: bounding pwsh concurrency with a lock, or giving up `-n 4`.
The workflow records 806.1s to 219.7s from that flag, and the contended resource can
be removed rather than rationed.

The rule that keeps this honest
------------------------------------------------------------------------
A signal is not a verdict, so retrying it papers over nothing: the script never ran
to its end. A normal exit is returned untouched on the first attempt whatever its
code, so a pwsh that runs and gives the WRONG answer still fails with its own
message. Getting that second half wrong would turn this into a way to retry real
regressions into green, which is worse than the bug it fixes, so both directions are
executed in tests/studio/test_pwsh_interpreter_crash_attribution.py against a real
SIGABRT rather than reviewed.

Mutation-tested: relaxing the crash test from `returncode < 0` to `returncode != 0`
fails test_a_clean_run_with_the_wrong_answer_still_fails_with_its_own_message and
test_a_clean_run_is_not_retried, which are exactly the two that guard that direction.

This also generalises `_run_pwsh` from tests/studio/test_install_phase_timing.py,
added earlier today for a second, signal-free shape: pwsh printing its "The
PowerShell process will exit" banner and exiting normally with empty stdout. That one
cannot be seen in the exit status, so it stays a text match.

Verified
------------------------------------------------------------------------
tests/python/test_windows_xformers_installer.py, tests/studio/test_install_phase_timing.py,
tests/studio/install/ and the new guard: 2635 passed, 3 skipped.
Guard alone: 5 passed.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Drop the subprocess import the pwsh conversion left behind

Source lint's import-hoist check is right: every subprocess.run in
test_windows_xformers_installer.py became run_pwsh, so `import subprocess` has no
references left except the one inside a comment explaining why run_pwsh is used
instead. Its wording names the shape exactly -- "was used before, now unused
(references re-pointed)" -- which is what a mechanical call-site rewrite leaves
behind.

Swept the other 18 converted files the same way with an AST pass rather than by
eye. This was the only real one: the remaining hits are `from __future__ import
annotations`, which every such scan reports, and a PropertyMock in
test_rocm_support.py that is present on main unchanged.

42 passed.

* Suppress the core dump on the forged SIGABRT

tests/test_deliberate_crashes_suppress_cores.py caught this: the abort child had no
PR_SET_DUMPABLE=0, so each of these aborts piped a multi-MB core to apport before the
child could be reaped. The guard is right and its message names the fix.

The child still exits -6 and PR_GET_DUMPABLE reads 0, so all five verdicts are
unchanged. Linux-only and non-fatal elsewhere: Windows has no CDLL(None) and pipes no
core, so arming it there would trade a no-op for a lost test.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: danielhanchen <unslothai@gmail.com>
2026-08-20 04:22:41 -07:00
..
_shared Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
fast_inference
kaggle Make a hung kaggle launcher say where it is stuck (#9104) 2026-08-17 18:47:48 -07:00
notebooks Make the Colab oracle tripwire able to fire, and stop blaming start.py for a hung agent CLI (#7838) 2026-08-04 07:42:42 -07:00
python Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
qlora
saving Use Kaggle's large overlay for saves, and refuse a GGUF export that cannot fit (#8439) 2026-08-13 18:56:43 -07:00
security Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
sh Studio: propagate required backend version to repair pipeline (#8610) (#8670) 2026-08-19 19:12:22 +01:00
studio Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
studio_setup_ps1 Studio: route every Windows installer line through the UTF-8 stdout sink (#8148) 2026-08-08 06:47:03 -07:00
utils Keep xFormers attention masks on the GPU running each layer (#8516) 2026-08-19 19:28:34 -03:00
version_compat Hand the LoRA to vLLM on TRL 1.x GRPO rollouts (#8701) 2026-08-14 06:46:48 -07:00
vllm_compat
__init__.py
_grpo_dispatch_source.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
_rl_source.py Stop the GRPO hidden-states wrapper paying for logits it discards (#8576) 2026-08-13 05:15:07 -07:00
_zoo_aggressive_cuda_spoof.py Stop the CUDA test spoofs reporting an exhausted card (#8123) 2026-08-08 04:45:25 -07:00
_zoo_rocm_spoof.py
conftest.py Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
run_all.sh Anchor the host-defaults assertions and take the file off the skip list (#7774) 2026-08-03 02:42:26 -07:00
test_allow_cpu_import_driverless.py Ask whether a device is present before asking what it can do (#8653) 2026-08-13 04:40:07 -07:00
test_attention_implementation.py
test_attn_impl_honor_explicit.py
test_bad_mappings_redirect.py
test_broken_tf_does_not_break_import.py Do not let a broken TensorFlow install break the Unsloth import (#8124) 2026-08-09 05:15:40 -07:00
test_broken_torchvision_probe.py Name a torchvision whose compiled ops do not match torch (#8128) 2026-08-09 04:30:12 -07:00
test_callback_signature_drift.py
test_cli_export_unpacking.py
test_collection_hygiene.py tests: make pytest tests collect cleanly and drop the deleted QVQ registry mirror (#8206) 2026-08-08 20:03:55 -07:00
test_compressed_export_gpu_release.py
test_cuda_spoof_reports_free_memory.py Stop the CUDA test spoofs reporting an exhausted card (#8123) 2026-08-08 04:45:25 -07:00
test_dataclass_default_backfill.py Keep up with transformers 5.x, TRL 0.22.x and peft (#7866) 2026-08-07 06:33:05 -07:00
test_deliberate_crashes_suppress_cores.py Crash guard: read dumpability in order, and stop losing crashes to scope and naming (#8808) 2026-08-16 01:05:31 -07:00
test_device_helpers.py Report the real cause when a save or GGUF export fails (#7861) 2026-08-06 00:12:32 -07:00
test_enforce_kwargs_spacing.py
test_fa2_fast_generate_bypass.py
test_fast_gemv_dispatch.py
test_fast_generate_slow_guard.py
test_finetune_last_n_layers.py
test_flash_attn_4_namespace_shadow.py Keep xFormers working when flash-attn 4 is installed, and guard the varlen int32 overflow (#8957) 2026-08-16 02:39:37 -07:00
test_flex_attention_needs_ampere.py Do not choose flex attention on a card that cannot run its kernel (#8137) 2026-08-08 05:31:23 -07:00
test_float32_generate_autocast.py Do not enter CUDA autocast for a float32 model during generate (#7900) 2026-08-05 04:35:21 -07:00
test_float32_no_fp16_autocast.py Keep an explicitly requested float32 model in float32 without bfloat16 (#7867) 2026-08-09 05:12:02 -07:00
test_fp8_device_context.py
test_fp8_restore_dropped_scale.py
test_fp8_tiny_e8m0.py
test_fused_ce_not_return_dict_logits.py
test_gemma4_chat_template.py
test_gemma_2b_mapper_key.py
test_generate_kwarg_gate.py
test_generation_failure_is_visible.py Restore warnings_issued for trl trainers on transformers 5.1+ (#7854) 2026-08-05 04:34:23 -07:00
test_get_model_name.py
test_gguf_basename_platform_matrix.py Fix GGUF export writing outside the chosen save directory on Windows (#7897) (#7937) 2026-08-05 08:24:48 -07:00
test_gguf_disk_headroom.py Free the intermediate 16-bit merge when the GGUF quants will not fit (#8500) 2026-08-13 04:42:31 -07:00
test_gguf_model_basename.py Fix GGUF export writing outside the chosen save directory on Windows (#7897) (#7937) 2026-08-05 08:24:48 -07:00
test_gguf_windows_export_routing.py Fix GGUF export writing outside the chosen save directory on Windows (#7897) (#7937) 2026-08-05 08:24:48 -07:00
test_gguf_windows_native.py Fix GGUF export writing outside the chosen save directory on Windows (#7897) (#7937) 2026-08-05 08:24:48 -07:00
test_gradient_checkpointing_restore.py
test_grouped_gemm_optional_gather_indices.py fix grouped_gemm crash on the documented gather_indices=None default (#8629) 2026-08-14 05:27:25 -07:00
test_grpo_accumulated_loss_hidden_states_signal.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_grpo_autocast_disabled.py Keep an explicitly requested float32 model in float32 without bfloat16 (#7867) 2026-08-09 05:12:02 -07:00
test_grpo_autocast_per_trainer.py Keep an explicitly requested float32 model in float32 without bfloat16 (#7867) 2026-08-09 05:12:02 -07:00
test_grpo_hidden_states_logits_cost.py Stop the GRPO hidden-states wrapper paying for logits it discards (#8576) 2026-08-13 05:15:07 -07:00
test_grpo_hidden_states_per_call_degradation.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_grpo_hidden_states_signal.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_grpo_hidden_states_wrap_target.py GRPO: wrap the model that owns the head when falling back to hidden states (#8231) 2026-08-09 04:48:46 -07:00
test_grpo_packed_raw_logits_nograd.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_grpo_padded_raw_logits.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_grpo_width_dispatch_sites.py GRPO: dispatch on width at the remaining lm_head matmul call sites (#8204) 2026-08-11 10:44:06 -07:00
test_ignored_tokenizer_casing.py
test_import_fixes_drift.py Fix Apple Silicon M4+ CPU frequency reported as MHz instead of GHz (#8571) 2026-08-12 17:02:52 -03:00
test_installer_interactive_prompts.py Make install.ps1 work with the user's PowerShell profile loaded (#8161) 2026-08-09 04:27:28 -07:00
test_installer_profile_hardening.py Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
test_installer_shortcut_icons.py feat(install): use pre-built Tauri icon for macOS .app bundle (#7628) 2026-07-31 12:05:34 +02:00
test_installer_skip_autostart.py Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
test_installer_system32_guard.py Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
test_installer_unsloth_version.py Stop 19 test files racing on one PowerShell startup cache (#9371) 2026-08-20 04:22:41 -07:00
test_kaggle_gguf_error_message.py Report the real cause when a save or GGUF export fails (#7861) 2026-08-06 00:12:32 -07:00
test_lint_no_parallel_clamp.py Studio: stop MTP forcing llama-server to a single parallel slot (#8172) 2026-08-08 21:09:58 -07:00
test_loader_glob_skip.py
test_map_eos_token.py Honor an explicit map_eos_token=False in get_chat_template (#7475) 2026-07-30 10:09:38 -03:00
test_mapper_no_duplicate_keys.py Add test to guard against duplicate keys in __INT_TO_FLOAT_MAPPER (#7419) 2026-07-29 02:37:10 -07:00
test_missing_optional_dep_skips.py Keep an explicitly requested float32 model in float32 without bfloat16 (#7867) 2026-08-09 05:12:02 -07:00
test_missing_torchvision_vlm.py
test_model_registry.py tests: build the hub-error fixtures without calling __init__ (huggingface_hub 1.x) (#8207) 2026-08-08 21:09:00 -07:00
test_moe_lora_targets.py
test_multi_image_grpo_chunking.py
test_new_mapper_fetched_fp8.py
test_new_mapper_no_global_leak.py
test_nvfp4_quant_load.py
test_offline_loading_helpers.py Report the network error when the offline retry finds nothing cached (#8121) 2026-08-09 05:01:02 -07:00
test_offload_embedding_hooks.py
test_offload_tied_autodisable.py Turn off embedding offload when the model is dispatched across devices (#8272) 2026-08-09 09:26:01 -07:00
test_offload_tied_guard.py
test_offloaded_parameter_hint.py Report the real cause when a save or GGUF export fails (#7861) 2026-08-06 00:12:32 -07:00
test_peft_stale_torchao.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_peft_symbol_backfill.py Keep up with transformers 5.x, TRL 0.22.x and peft (#7866) 2026-08-07 06:33:05 -07:00
test_peft_tensor_parallel_compat.py
test_peft_weight_converter_compat.py
test_prefetch_snapshot_scope.py Fetch a sentence-transformers model's subfolder weights instead of pruning them (#7855) 2026-08-05 04:34:48 -07:00
test_pretrain_compile_reset.py
test_profile_startup_gate.py Make the startup profile a gate, with budgets from its own measurements (#8965) 2026-08-16 02:37:32 -07:00
test_psutil_apple_cpu_freq.py Fix Apple Silicon M4+ CPU frequency reported as MHz instead of GHz (#8571) 2026-08-12 17:02:52 -03:00
test_public_api_surface.py
test_python39_compatibility.py Do not scan vendored packages that refuse to import on our floor (#8252) 2026-08-09 04:06:19 -07:00
test_pythonpath_empty_components.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_raw_text.py tests: make pytest tests collect cleanly and drop the deleted QVQ registry mirror (#8206) 2026-08-08 20:03:55 -07:00
test_raw_text_json_loading.py
test_resolve_model_class.py
test_rl_config_compat.py Read rl.py with an explicit utf-8 encoding in the config compat test (#8333) 2026-08-10 05:47:09 -07:00
test_runtime_text_encoding.py
test_save_entrypoints_reach_converter.py Report the real cause when a save or GGUF export fails (#7861) 2026-08-06 00:12:32 -07:00
test_save_lora_without_vllm.py Attach save_lora without a vLLM engine (#8127) 2026-08-08 06:14:12 -07:00
test_settle_eager_fallbacks_between_steps.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_sft_vision_dataset_gate.py Keep up with transformers 5.x, TRL 0.22.x and peft (#7866) 2026-08-07 06:33:05 -07:00
test_source_read_encoding.py
test_st_save_merged_signature.py Report the real cause when a save or GGUF export fails (#7861) 2026-08-06 00:12:32 -07:00
test_st_subfolder_weights_are_fetched.py Fetch a sentence-transformers model's subfolder weights instead of pruning them (#7855) 2026-08-05 04:34:48 -07:00
test_studio_install_workspace_guard.py Installer: suppress macOS uv developer tools dialog (#8479) 2026-08-11 12:19:30 -07:00
test_studio_root_resilience.py
test_studio_shutdown_thread_wait.py
test_synthetic_chunk_data.py
test_synthetic_vllm_startup_failure.py Cut Repo tests (CPU) roughly in half: stop the synthetic vLLM tests reaping a fake server (#8993) 2026-08-16 07:39:43 -07:00
test_tool_mask_zoo_compat.py
test_torchao_aten_grouped_mm.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_torchao_nf4tensor_move.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_torchao_subprocess_fix.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_torchao_torch_symbol_skew.py Settle deferred compile-mode switches between training steps (#7955) 2026-08-06 02:45:50 -07:00
test_torchaudio_cuda_mismatch.py Do not let a CUDA-mismatched torchaudio take the whole import with it (#8496) 2026-08-13 05:12:36 -07:00
test_transformers5_bare_annotation_live.py Keep up with transformers 5.x, TRL 0.22.x and peft (#7866) 2026-08-07 06:33:05 -07:00
test_transformers_dependency_floor.py Tell users to upgrade the dependency, not transformers, when a --no-deps install leaves a floor unmet (#8212) 2026-08-09 01:45:07 -07:00
test_uma_safetensors_load.py
test_uninitialized_position_ids.py
test_version_single_source.py Report unsloth's own version on the MLX path (#8395) 2026-08-11 01:10:03 -07:00
test_video_path_validation.py Fix formatting_func race in UnslothVisionDataCollator without a lock (#7698) 2026-08-02 22:44:09 -07:00
test_vllm_broken_detection.py
test_vllm_cuda_mismatch_wheel_url.py Stop the vLLM CUDA mismatch hint printing a wheel URL that 404s (#8200) 2026-08-10 06:42:39 -07:00
test_warnings_issued_guard.py Apply the moved trainer kwargs to the config instead of dropping them (#8374) 2026-08-11 04:35:10 -07:00
test_windows_amd_gpu_scan_fallback.py Windows: stop depending on the generated unsloth.exe console script (#8592) 2026-08-13 07:54:51 -07:00
test_windows_rocm_bnb_version.py