unsloth/tests
Leo Borcherding c3d3680e7c
install.sh: do not assume sudo consent when there is no terminal (#7435)
* install.sh: do not assume sudo consent when there is no terminal (#7307 P7)

_smart_apt_install printed an "Accept? [Y/n]" prompt, and when /dev/tty was
unreadable it set REPLY=y and escalated anyway. Every sudo call in that branch
redirects stdin from /dev/null, so on any host where sudo needs a password the
install died on sudo's own error rather than the actionable message the no-sudo
path already prints. Containers, CI and locked-down corporate machines hit this.

Probe with `sudo -n true` first. If there is no terminal to prompt on and sudo
would need a password, exit with the missing packages and the exact command to
run, matching the no-sudo path. Passwordless sudo still escalates unattended,
which is the one case where that is legitimate, and says so in the log.

With a readable /dev/tty the behaviour is unchanged, and the prompt now only
prints when something can actually answer it.

Extend tests/sh/test_apt_distro_prompt.sh to drive the real function across all
four TTY/sudo combinations, rewriting /dev/tty to a fixture path the same way
the existing cases rewrite /etc/os-release. Against the old install.sh five of
these assertions fail. Register the file in studio-backend-ci.yml's shell suite,
which did not run it before.

* install.sh: probe the real tty and the real sudo commands (#7307)

Codex review follow-ups on the no-TTY sudo escalation guard.

`test -r /dev/tty` only reads the device node's permission bits. Inside
containers and systemd units those bits look fine while open() fails with
ENXIO, so the guard still fell through to a prompt nobody could answer.
_can_read_tty() does a real open. The subshell is load-bearing: in dash a
failed redirection on the special builtin `:` exits the script.

`sudo -n true` proves only that `true` is allowed. Under a command-specific
rule like `NOPASSWD: /usr/bin/apt-get` it is the wrong question in both
directions. _sudo_runs_unattended() asks the sudoers policy about the exact
argument vectors we are about to elevate, via `sudo -n -l --`, which checks
without running and fails instead of prompting.

Tests cover both: a NOPASSWD-on-trivia-but-not-apt-get sudoers stub, and a
readable-but-unopenable /dev/tty faked with a unix socket (skipped where the
platform cannot produce that shape).

* install.sh: test sudo by running it with -n, not by asking sudo -l

Codex follow-up. `sudo -n -l -- apt-get ...` answers authorization, not
authentication: on a host where apt-get is permitted but still carries the
PASSWD tag, list mode exits 0 while the actual run needs a password, so the
guard reported unattended and the escalation died exactly as #7307 described.

Inferring the answer from list output means parsing for `!authenticate`, which
is human-readable text that varies by sudo version. Drop the inference. In the
no-terminal branch, run the real commands with `sudo -n`: -n never prompts, so
it cannot block on a closed stdin, and its exit status is the question we were
trying to answer. If it is refused, print the actionable manual command as
before. The terminal branch is unchanged: prompt, then plain sudo, which may
ask for a password because someone is there to type it.

The test stub now models sudo properly (-n refuses and runs nothing when a
password is needed) instead of special-casing the probe's argv.

* install.sh: require a real NOPASSWD rule, and stop blaming the password for apt failures

Two review findings on the headless escalation branch.

A cached authentication timestamp from an earlier, unrelated elevation made
`-n` succeed for a PASSWD-tagged apt-get, so packages installed with nobody
having answered the prompt. Add `-k` so the probe ignores the timestamp and
only a real NOPASSWD rule counts as passwordless. Per sudo(8), `-k` alongside
a command ignores the cached credentials for that invocation and "will not
update the user's cached credentials", so an interactive session elsewhere
does not have to re-authenticate afterwards.

A nonzero status from the elevated apt-get was reported as "likely needs a
password" even when sudo had authenticated fine and apt itself failed on a bad
repository, a dpkg lock or a network outage. sudo returns the command's own
exit status when the command runs, so the two cases are not distinguishable
from the status alone. Report both possibilities and point at the real error.

tests/sh/test_apt_distro_prompt.sh: teach the sudo stub about -k, add a cached
mode, and assert both behaviours. The three new assertions fail against the
previous commit.

* install.sh: an unreadable answer at the consent prompt declines

_can_read_tty proves the device opens, not that anyone is there to answer. A
read that hits EOF still fell back to REPLY=y and escalated, so the branch that
does have a terminal kept the behaviour this change removes from the branch
that does not. A drained or half-closed terminal reached it.

Default to n instead, which is what the post-install autostart prompt at the
bottom of this file already does on the same condition. Enter still means yes:
that is a successful read of an empty line, not a failed read.

tests/sh/test_apt_distro_prompt.sh: add an eof tty fixture, which opens
normally and returns EOF immediately. Both new assertions fail against the
previous commit.

* install.sh: tighten the escalation comments, and correct the exit-status claim

Comment-only. The earlier note said a nonzero status from the elevated apt-get
was not distinguishable from the status alone; sudo(8) is more specific than
that. sudo exits 1 on an authentication or configuration failure and passes the
command's own status through when the command runs, while apt-get(8) returns
100 on error, so the two usually are distinguishable. sudo also exits 1 when
the command cannot be executed, which is why the message still states both
causes rather than naming one.

* install.sh, tests: tighten the comments added by this branch

Comment-only pass over the branch's own comments in both files. Same intent,
fewer lines: drop restatement, keep the parts a reader cannot derive from the
code (why test -r is the wrong probe, why the subshell around the redirection
is load-bearing under dash, what -k buys over -n, and why a nonzero status
does not by itself name the cause).

Verified to touch nothing but comments and blank lines.

---------

Co-authored-by: danielhanchen <unslothai@gmail.com>
2026-07-26 04:27:15 -07:00
..
fast_inference test: fast end-to-end GRPO fast_inference vLLM rollout test (#7136) 2026-07-26 00:22:48 -07:00
notebooks Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
python AMD: CI coverage for recent fixes, plus three wrong gfx ids (#7431) 2026-07-25 18:58:02 -05:00
qlora Formatting: ruff line-length 100, kwarg-spacing passes, drop blank after short local imports (#6079) 2026-06-08 04:24:13 -07:00
saving Replace standalone Studio wording with Unsloth (#7221) 2026-07-19 00:47:04 -07:00
security Harden desktop release token permissions (#7172) 2026-07-16 05:01:48 -07:00
sh install.sh: do not assume sudo consent when there is no terminal (#7435) 2026-07-26 04:27:15 -07:00
studio Studio: reset the reasoning open state when a new stream starts (#7444) 2026-07-26 00:31:00 -07:00
studio_setup_ps1 Make Visual Studio + CMake optional on Windows (prebuilt llama.cpp needs no build tools) (#6499) 2026-06-22 01:11:09 -07:00
utils Experimental: correct varlen sample packing for hybrid linear-attention models (#7249) 2026-07-20 00:57:02 -07:00
version_compat test(version-compat): keep GRPO fake-run logits finite on CPU (#7247) 2026-07-19 04:54:17 -07:00
vllm_compat Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
__init__.py Qwen 3, Bug Fixes (#2445) 2025-04-30 22:38:39 -07:00
_zoo_aggressive_cuda_spoof.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
_zoo_rocm_spoof.py AMD: CI coverage for recent fixes, plus three wrong gfx ids (#7431) 2026-07-25 18:58:02 -05:00
conftest.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
run_all.sh AMD: CI coverage for recent fixes, plus three wrong gfx ids (#7431) 2026-07-25 18:58:02 -05:00
test_attention_implementation.py fix(gpt-oss): prefer flex attention over sdpa (#5701) 2026-05-22 08:38:38 -07:00
test_attn_impl_honor_explicit.py Honor an explicit sdpa or flex_attention request when flash is disabled (#6847) 2026-07-06 05:45:45 -07:00
test_bad_mappings_redirect.py Fix BAD_MAPPINGS not redirecting the -unsloth-bnb-4bit dynamic quants (#6949) 2026-07-08 17:37:44 -03:00
test_callback_signature_drift.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_cli_export_unpacking.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_compressed_export_gpu_release.py studio: shard export checkpoint loads across all visible GPUs (#7215) 2026-07-26 04:16:36 -07:00
test_enforce_kwargs_spacing.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_fast_gemv_dispatch.py Fix fast inference crash on compressed-tensors FP8 models (#7025) 2026-07-09 04:10:59 -07:00
test_fast_generate_slow_guard.py fast_generate: clear error for vLLM-style inputs when fast_inference=False (#6786) 2026-07-03 08:16:32 -07:00
test_finetune_last_n_layers.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_fp8_device_context.py Guard FP8 Triton launches with tensor device context (#6888) 2026-07-08 18:32:51 -03:00
test_fp8_restore_dropped_scale.py Restore dropped FP8 weight_scale_inv tensors on load (#6978) 2026-07-09 06:44:44 -07:00
test_fp8_tiny_e8m0.py Handle odd shapes and non-float scales in FP8BlockQuantLinear (#6848) 2026-07-06 05:44:55 -07:00
test_fused_ce_not_return_dict_logits.py fix: use EMPTY_LOGITS on the fused-CE not-return_dict path (#2068) (#6482) 2026-06-23 01:26:55 -07:00
test_gemma4_chat_template.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_gemma_2b_mapper_key.py Fix duplicate unsloth/gemma-2b-bnb-4bit mapper key routing the base 4bit repo to the instruct model (#6891) 2026-07-08 17:40:39 -03:00
test_generate_kwarg_gate.py Fix gpt-oss offload_embedding and generate() kwargs, and guard offload_embedding on tied/vLLM models (#6774) 2026-07-01 22:39:00 -07:00
test_get_model_name.py Fix DeepScaleR-1.5B mapper entry pointing its 16bit repo at DeepHermes-3-8B (#7088) 2026-07-14 17:56:12 -03:00
test_gradient_checkpointing_restore.py Fix TrainingArguments silently disabling unsloth gradient checkpointing (#6829) 2026-07-03 16:35:02 +01:00
test_ignored_tokenizer_casing.py Match IGNORED_TOKENIZER_NAMES case-insensitively (#6620) 2026-06-23 19:40:50 -03:00
test_import_fixes_drift.py fix: keep LoRA reloads working with PEFT 0.19 (#6748) 2026-06-30 20:26:57 +01:00
test_installer_skip_autostart.py fix(install): fail non-tauri installer errors (#7123) 2026-07-15 04:37:03 -07:00
test_installer_unsloth_version.py Installer: report the installed Unsloth version (#7265) 2026-07-21 03:48:49 -07:00
test_loader_glob_skip.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_missing_torchvision_vlm.py Fix misleading 'only for image models' error for Qwen3-VL when torchvision is missing (#6525) 2026-06-23 01:28:09 -07:00
test_model_registry.py fix(registry): don't register deepseek models at import time (#7227) 2026-07-19 03:37:23 -07:00
test_moe_lora_targets.py Scope MoE expert LoRA detection to actual MLP projection targets (#6849) 2026-07-06 05:45:06 -07:00
test_multi_image_grpo_chunking.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_nvfp4_quant_load.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_offline_loading_helpers.py Fix offline checkpoint load/export: "tokenizer is weirdly not loaded" (#6554) 2026-06-25 23:16:53 -07:00
test_offload_embedding_hooks.py Fix gpt-oss offload_embedding and generate() kwargs, and guard offload_embedding on tied/vLLM models (#6774) 2026-07-01 22:39:00 -07:00
test_offload_tied_guard.py Fix gpt-oss offload_embedding and generate() kwargs, and guard offload_embedding on tied/vLLM models (#6774) 2026-07-01 22:39:00 -07:00
test_peft_tensor_parallel_compat.py fix: keep LoRA reloads working with PEFT 0.19 (#6748) 2026-06-30 20:26:57 +01:00
test_peft_weight_converter_compat.py Formatting: ruff line-length 100, kwarg-spacing passes, drop blank after short local imports (#6079) 2026-06-08 04:24:13 -07:00
test_prefetch_snapshot_scope.py Auto Xet to HTTP download fallback in from_pretrained; share Studio's fallback via unsloth_zoo (#6638) 2026-07-06 05:13:25 -07:00
test_pretrain_compile_reset.py Add regression tests for the stray-forward compile-cache reset (#6569) 2026-06-22 07:22:47 -07:00
test_public_api_surface.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_raw_text.py fix(dataprep): don't emit a degenerate chunk for empty text (#7183) 2026-07-23 00:56:51 -07:00
test_resolve_model_class.py Formatting: ruff line-length 100, kwarg-spacing passes, drop blank after short local imports (#6079) 2026-06-08 04:24:13 -07:00
test_studio_install_workspace_guard.py Studio: add local speech-to-text dictation engine (#7095) 2026-07-23 01:39:03 -07:00
test_studio_root_resilience.py Replace standalone Studio wording with Unsloth (#7221) 2026-07-19 00:47:04 -07:00
test_studio_shutdown_thread_wait.py Replace standalone Studio wording with Unsloth (#7221) 2026-07-19 00:47:04 -07:00
test_synthetic_chunk_data.py Fix SyntheticDataKit.chunk_data emitting chunks over max_tokens (#7073) 2026-07-12 05:06:11 -07:00
test_tool_mask_zoo_compat.py Formatting: ruff line-length 100, kwarg-spacing passes, drop blank after short local imports (#6079) 2026-06-08 04:24:13 -07:00
test_uma_safetensors_load.py Faster safetensors weight loading on unified-memory (integrated) GPUs (#5988) 2026-07-22 05:20:59 -07:00
test_uninitialized_position_ids.py Load DeepSeek-OCR and other VLMs that register AutoModel in auto_map (#6421) 2026-06-18 07:03:43 -07:00
test_video_path_validation.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00
test_vllm_broken_detection.py Fix fast_inference crash on ABI-broken vLLM: probe compiled extensions, not just import vllm (#6621) 2026-06-26 22:43:36 -07:00
test_windows_rocm_bnb_version.py Reduce and tighten comments and docstrings across the test suite (#6429) 2026-06-18 01:07:09 -07:00