- _local_model_task now tags a local diffusers pipeline that resolves to a video family (LTX / Wan / Hunyuan) as text-to-video, mirroring the cached-repo _cached_repo_task, so supported local video pipelines surface in the Video On-Device picker instead of being routed to the Images picker where the image loader rejects them. Gated on _local_is_diffusers so only a real loadable pipeline dir reaches the video check. - Video validate_load_request now rejects a local pipeline pick whose directory has no model_index.json before the GPU handoff, mirroring the image loader, so a bad local pipeline can no longer evict the resident model and only then fail deep in from_pretrained. - The custom/env-mode uninstall now removes a sibling stable-diffusion.cpp only when it carries the Studio owner marker. install_sd_cpp_prebuilt writes the canonical .unsloth-studio-owned marker on install; uninstall.sh and uninstall.ps1 keep any unowned checkout (a user's own git clone of stable-diffusion.cpp beside a custom Studio root is no longer deleted). A pre-marker Studio build is left behind rather than a user file removed. Adds regression tests: local video pipeline tagged text-to-video (and a video-named non-pipeline dir stays untagged so it can never trigger a doomed pipeline load), the video local-pipeline preflight rejection, the install ownership marker, and the uninstall keeping an unowned sibling while removing an owned one.
100 lines
4.7 KiB
Bash
Executable file
100 lines
4.7 KiB
Bash
Executable file
#!/bin/bash
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
# Unit test for custom/env-mode stable-diffusion.cpp removal in scripts/uninstall.sh.
|
|
#
|
|
# A custom Studio (UNSLOTH_STUDIO_HOME=<root>) installs its native diffusion build beside
|
|
# the root at <parent>/stable-diffusion.cpp -- find_sd_cpp_binary resolves it from
|
|
# UNSLOTH_STUDIO_HOME.parent (sd_cpp_engine.py). Uninstall must remove that sibling too, or
|
|
# a stale build lingers and a fresh install's finder can pick it up. Tested hermetically:
|
|
# the real custom-root removal loop + its helpers are extracted from uninstall.sh and run
|
|
# against per-test fixtures. Follows the extract-via-sed pattern of test_uninstall_shared_icon.sh.
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
UNINSTALL_SH="$SCRIPT_DIR/../../scripts/uninstall.sh"
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
_TMP_ROOT=$(mktemp -d)
|
|
trap 'rm -rf "$_TMP_ROOT"' EXIT
|
|
# Deterministic deny-list checks: keep $HOME clear of the fixture trees.
|
|
HOME="$_TMP_ROOT/home"
|
|
mkdir -p "$HOME"
|
|
|
|
assert_nodir() { _l="$1"; [ -d "$2" ] && { echo " FAIL: $_l (still present: $2)"; FAIL=$((FAIL+1)); } || { echo " PASS: $_l"; PASS=$((PASS+1)); }; }
|
|
assert_dir() { _l="$1"; [ -d "$2" ] && { echo " PASS: $_l"; PASS=$((PASS+1)); } || { echo " FAIL: $_l (missing dir $2)"; FAIL=$((FAIL+1)); }; }
|
|
|
|
# Extract the helpers the loop depends on, plus the real custom-root removal loop.
|
|
HELPERS_FILE=$(mktemp -p "$_TMP_ROOT")
|
|
{
|
|
sed -n '/^_remove_path() {/,/^}/p' "$UNINSTALL_SH"
|
|
sed -n '/^_is_studio_root() {/,/^}/p' "$UNINSTALL_SH"
|
|
sed -n '/^_is_unsafe_root() {/,/^}/p' "$UNINSTALL_SH"
|
|
} > "$HELPERS_FILE"
|
|
LOOP_FILE=$(mktemp -p "$_TMP_ROOT")
|
|
sed -n '/^_custom_studio_roots | while IFS= read -r _custom_root; do/,/^done/p' "$UNINSTALL_SH" > "$LOOP_FILE"
|
|
|
|
# shellcheck disable=SC1090
|
|
. "$HELPERS_FILE"
|
|
|
|
# make_studio <root> : a valid custom Studio root (share/studio.conf owner marker) plus its
|
|
# sibling <parent>/stable-diffusion.cpp build carrying the Studio owner marker, each with a
|
|
# file so removal is observable.
|
|
make_studio() {
|
|
mkdir -p "$1/share"
|
|
: > "$1/share/studio.conf"
|
|
_sib="$(dirname "$1")/stable-diffusion.cpp"
|
|
mkdir -p "$_sib"
|
|
: > "$_sib/sd-cli"
|
|
: > "$_sib/.unsloth-studio-owned" # written by install_sd_cpp_prebuilt on a real install
|
|
}
|
|
run_loop() {
|
|
# shellcheck disable=SC1090
|
|
. "$LOOP_FILE"
|
|
}
|
|
|
|
# 1. Single custom root -> root AND its sibling stable-diffusion.cpp both removed.
|
|
p1="$_TMP_ROOT/inst1"
|
|
make_studio "$p1/studioA"
|
|
: > "$p1/keep.txt" # unrelated sibling file must be untouched
|
|
_custom_studio_roots() { printf '%s\n' "$p1/studioA"; }
|
|
run_loop
|
|
assert_nodir "single custom root removed" "$p1/studioA"
|
|
assert_nodir "custom-root sibling stable-diffusion.cpp removed" "$p1/stable-diffusion.cpp"
|
|
[ -f "$p1/keep.txt" ] && { echo " PASS: unrelated sibling file kept"; PASS=$((PASS+1)); } || { echo " FAIL: unrelated sibling file removed"; FAIL=$((FAIL+1)); }
|
|
|
|
# 2. Two custom roots sharing a parent share one sd.cpp -> all removed, no error on the
|
|
# second (already-gone) removal.
|
|
p2="$_TMP_ROOT/inst2"
|
|
make_studio "$p2/studioB"
|
|
make_studio "$p2/studioC" # same parent -> same sibling sd.cpp
|
|
_custom_studio_roots() { printf '%s\n%s\n' "$p2/studioB" "$p2/studioC"; }
|
|
run_loop
|
|
assert_nodir "shared-parent root B removed" "$p2/studioB"
|
|
assert_nodir "shared-parent root C removed" "$p2/studioC"
|
|
assert_nodir "shared sibling stable-diffusion.cpp removed" "$p2/stable-diffusion.cpp"
|
|
|
|
# 3. A sibling stable-diffusion.cpp WITHOUT the Studio owner marker (a user's own checkout,
|
|
# even a built one, beside a custom root -- or one left when UNSLOTH_SD_CPP_PATH points
|
|
# Studio elsewhere) is KEPT, though the Studio root itself is still removed.
|
|
p3="$_TMP_ROOT/inst3"
|
|
mkdir -p "$p3/studioD/share"; : > "$p3/studioD/share/studio.conf"
|
|
mkdir -p "$p3/stable-diffusion.cpp/build/bin"
|
|
: > "$p3/stable-diffusion.cpp/build/bin/sd-cli" # user's own build, no owner marker
|
|
: > "$p3/stable-diffusion.cpp/main.cpp"
|
|
_custom_studio_roots() { printf '%s\n' "$p3/studioD"; }
|
|
run_loop
|
|
assert_nodir "unowned-sibling: custom root still removed" "$p3/studioD"
|
|
assert_dir "unowned sibling stable-diffusion.cpp kept" "$p3/stable-diffusion.cpp"
|
|
|
|
# 4. Default-mode sd.cpp (a bare ~/.unsloth/stable-diffusion.cpp with no custom root) is NOT
|
|
# touched by the custom-root loop -- it is removed by the separate default-mode line.
|
|
mkdir -p "$HOME/.unsloth/stable-diffusion.cpp"
|
|
_custom_studio_roots() { printf '%s\n' "$p1/studioA"; } # a now-removed root -> guard skips
|
|
run_loop
|
|
assert_dir "default-mode sd.cpp untouched by custom loop" "$HOME/.unsloth/stable-diffusion.cpp"
|
|
|
|
echo ""
|
|
echo "Results: $PASS passed, $FAIL failed"
|
|
[ "$FAIL" = 0 ]
|