mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 16:10:08 +00:00
Improve update checks and cleanup UX, add timeout regressions
This commit is contained in:
@@ -122,3 +122,52 @@ EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "batch_uninstall_applications tolerates brew autoremove timeout" {
|
||||
local app_bundle="$HOME/Applications/BrewTimeout.app"
|
||||
mkdir -p "$app_bundle"
|
||||
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/uninstall/batch.sh"
|
||||
|
||||
request_sudo_access() { return 0; }
|
||||
start_inline_spinner() { :; }
|
||||
stop_inline_spinner() { :; }
|
||||
get_file_owner() { whoami; }
|
||||
get_path_size_kb() { echo "100"; }
|
||||
bytes_to_human() { echo "$1"; }
|
||||
drain_pending_input() { :; }
|
||||
print_summary_block() { :; }
|
||||
force_kill_app() { return 0; }
|
||||
remove_apps_from_dock() { :; }
|
||||
refresh_launch_services_after_uninstall() { echo "LS_REFRESH"; }
|
||||
|
||||
get_brew_cask_name() { echo "brew-timeout-cask"; return 0; }
|
||||
brew_uninstall_cask() { return 0; }
|
||||
|
||||
run_with_timeout() {
|
||||
local duration="$1"
|
||||
shift
|
||||
echo "TIMEOUT_CALL:$duration:$*" >> "$HOME/timeout_calls.log"
|
||||
if [[ "$duration" == "30" ]]; then
|
||||
return 124
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
selected_apps=("0|$HOME/Applications/BrewTimeout.app|BrewTimeout|com.example.brewtimeout|0|Never")
|
||||
files_cleaned=0
|
||||
total_items=0
|
||||
total_size_cleaned=0
|
||||
|
||||
printf '\n' | batch_uninstall_applications
|
||||
|
||||
cat "$HOME/timeout_calls.log"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"TIMEOUT_CALL:30:bash -c HOMEBREW_NO_ENV_HINTS=1 brew autoremove 2>/dev/null"* ]]
|
||||
[[ "$output" == *"LS_REFRESH"* ]]
|
||||
}
|
||||
|
||||
@@ -420,6 +420,80 @@ EOF
|
||||
[[ "$output" == *"COUNT=0"* ]]
|
||||
}
|
||||
|
||||
@test "check_homebrew_updates reports counts and exports update variables" {
|
||||
run bash --noprofile --norc << 'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/check/all.sh"
|
||||
|
||||
run_with_timeout() {
|
||||
local timeout="${1:-}"
|
||||
shift
|
||||
"$@"
|
||||
}
|
||||
|
||||
brew() {
|
||||
if [[ "$1" == "outdated" && "$2" == "--formula" && "$3" == "--quiet" ]]; then
|
||||
printf "wget\njq\n"
|
||||
return 0
|
||||
fi
|
||||
if [[ "$1" == "outdated" && "$2" == "--cask" && "$3" == "--quiet" ]]; then
|
||||
printf "iterm2\n"
|
||||
return 0
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
check_homebrew_updates
|
||||
echo "COUNTS=${BREW_OUTDATED_COUNT}:${BREW_FORMULA_OUTDATED_COUNT}:${BREW_CASK_OUTDATED_COUNT}"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Homebrew"* ]]
|
||||
[[ "$output" == *"2 formula, 1 cask available"* ]]
|
||||
[[ "$output" == *"COUNTS=3:2:1"* ]]
|
||||
}
|
||||
|
||||
@test "check_homebrew_updates shows timeout warning when brew query times out" {
|
||||
run bash --noprofile --norc << 'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/check/all.sh"
|
||||
|
||||
run_with_timeout() { return 124; }
|
||||
brew() { return 0; }
|
||||
rm -f "$HOME/.cache/mole/brew_updates"
|
||||
|
||||
check_homebrew_updates
|
||||
echo "COUNTS=${BREW_OUTDATED_COUNT}:${BREW_FORMULA_OUTDATED_COUNT}:${BREW_CASK_OUTDATED_COUNT}"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Homebrew"* ]]
|
||||
[[ "$output" == *"Check timed out"* ]]
|
||||
[[ "$output" == *"COUNTS=0:0:0"* ]]
|
||||
}
|
||||
|
||||
@test "check_homebrew_updates shows failure warning when brew query fails" {
|
||||
run bash --noprofile --norc << 'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/check/all.sh"
|
||||
|
||||
run_with_timeout() { return 1; }
|
||||
brew() { return 0; }
|
||||
rm -f "$HOME/.cache/mole/brew_updates"
|
||||
|
||||
check_homebrew_updates
|
||||
echo "COUNTS=${BREW_OUTDATED_COUNT}:${BREW_FORMULA_OUTDATED_COUNT}:${BREW_CASK_OUTDATED_COUNT}"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Homebrew"* ]]
|
||||
[[ "$output" == *"Check failed"* ]]
|
||||
[[ "$output" == *"COUNTS=0:0:0"* ]]
|
||||
}
|
||||
|
||||
@test "check_macos_update avoids slow softwareupdate scans" {
|
||||
run bash --noprofile --norc << 'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
@@ -38,6 +38,44 @@ EOF
|
||||
[[ "$output" != *"Trash"* ]]
|
||||
}
|
||||
|
||||
@test "clean_user_essentials falls back when Finder trash operations time out" {
|
||||
mkdir -p "$HOME/.Trash"
|
||||
touch "$HOME/.Trash/one.tmp" "$HOME/.Trash/two.tmp"
|
||||
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
DRY_RUN=false
|
||||
start_section_spinner() { :; }
|
||||
stop_section_spinner() { :; }
|
||||
safe_clean() { :; }
|
||||
note_activity() { :; }
|
||||
is_path_whitelisted() { return 1; }
|
||||
debug_log() { :; }
|
||||
run_with_timeout() {
|
||||
local _duration="$1"
|
||||
shift
|
||||
if [[ "$1" == "osascript" ]]; then
|
||||
return 124
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
safe_remove() {
|
||||
local target="$1"
|
||||
/bin/rm -rf "$target"
|
||||
return 0
|
||||
}
|
||||
|
||||
clean_user_essentials
|
||||
[[ ! -e "$HOME/.Trash/one.tmp" ]] || exit 1
|
||||
[[ ! -e "$HOME/.Trash/two.tmp" ]] || exit 1
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Trash · emptied, 2 items"* ]]
|
||||
}
|
||||
|
||||
@test "clean_app_caches includes macOS system caches" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
@@ -58,6 +96,24 @@ EOF
|
||||
[[ "$output" == *"Saved application states"* ]] || [[ "$output" == *"App caches"* ]]
|
||||
}
|
||||
|
||||
@test "clean_app_caches shows spinner during initial app cache scan" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
start_section_spinner() { echo "SPIN_START:$1"; }
|
||||
stop_section_spinner() { echo "SPIN_STOP"; }
|
||||
safe_clean() { :; }
|
||||
clean_support_app_data() { :; }
|
||||
clean_group_container_caches() { :; }
|
||||
|
||||
clean_app_caches
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"SPIN_START:Scanning app caches..."* ]]
|
||||
}
|
||||
|
||||
@test "clean_support_app_data targets crash, wallpaper, and messages preview caches only" {
|
||||
local support_home="$HOME/support-cache-home-1"
|
||||
run env HOME="$support_home" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
|
||||
@@ -14,7 +14,7 @@ setup_file() {
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -f "$PROJECT_ROOT/install_channel"
|
||||
rm -rf "$HOME/.config/mole"
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
@@ -46,9 +46,8 @@ SCRIPT
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$HOME/.config"
|
||||
mkdir -p "$HOME"
|
||||
rm -f "$PROJECT_ROOT/install_channel"
|
||||
rm -rf "$HOME/.config/mole"
|
||||
mkdir -p "$HOME/.config/mole"
|
||||
}
|
||||
|
||||
@test "mole --help prints command overview" {
|
||||
@@ -67,7 +66,8 @@ setup() {
|
||||
|
||||
@test "mole --version shows nightly channel metadata" {
|
||||
expected_version="$(grep '^VERSION=' "$PROJECT_ROOT/mole" | head -1 | sed 's/VERSION=\"\(.*\)\"/\1/')"
|
||||
cat > "$PROJECT_ROOT/install_channel" <<'EOF'
|
||||
mkdir -p "$HOME/.config/mole"
|
||||
cat > "$HOME/.config/mole/install_channel" <<'EOF'
|
||||
CHANNEL=nightly
|
||||
EOF
|
||||
|
||||
@@ -83,6 +83,12 @@ EOF
|
||||
[[ "$output" == *"Unknown command: unknown-command"* ]]
|
||||
}
|
||||
|
||||
@test "mole uninstall --whitelist returns unsupported option error" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" uninstall --whitelist
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"Unknown uninstall option: --whitelist"* ]]
|
||||
}
|
||||
|
||||
@test "touchid status reports current configuration" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" touchid status
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -299,3 +299,27 @@ EOF
|
||||
[[ "$output" == *"$volumes_root/unused-runtime"* ]]
|
||||
[[ "$output" != *"$volumes_root/in-use-runtime"* ]]
|
||||
}
|
||||
|
||||
@test "clean_dev_mobile continues cleanup when simctl is unavailable" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/dev.sh"
|
||||
|
||||
check_android_ndk() { :; }
|
||||
clean_xcode_documentation_cache() { :; }
|
||||
clean_xcode_simulator_runtime_volumes() { :; }
|
||||
clean_xcode_device_support() { echo "DEVICE_SUPPORT:$2"; }
|
||||
safe_clean() { echo "SAFE_CLEAN:$2"; }
|
||||
note_activity() { :; }
|
||||
debug_log() { :; }
|
||||
xcrun() { return 1; }
|
||||
|
||||
clean_dev_mobile
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"simctl not available"* ]]
|
||||
[[ "$output" == *"DEVICE_SUPPORT:iOS DeviceSupport"* ]]
|
||||
[[ "$output" == *"SAFE_CLEAN:Android SDK cache"* ]]
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ setup() {
|
||||
}
|
||||
|
||||
@test "Makefile has build target for Go binaries" {
|
||||
run bash -c "grep -q 'go build' '$PROJECT_ROOT/Makefile'"
|
||||
run bash -c "grep -Eq '(^|[[:space:]])(go|\\$\\(GO\\))[[:space:]]+build' '$PROJECT_ROOT/Makefile'"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
||||
@@ -313,6 +313,49 @@ EOF
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "refresh_launch_services_after_uninstall falls back after timeout" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/uninstall/batch.sh"
|
||||
|
||||
log_file="$HOME/lsregister-timeout.log"
|
||||
: > "$log_file"
|
||||
call_index=0
|
||||
|
||||
get_lsregister_path() { echo "/bin/echo"; }
|
||||
debug_log() { echo "DEBUG:$*" >> "$log_file"; }
|
||||
run_with_timeout() {
|
||||
local duration="$1"
|
||||
shift
|
||||
call_index=$((call_index + 1))
|
||||
echo "CALL${call_index}:$duration:$*" >> "$log_file"
|
||||
|
||||
if [[ "$call_index" -eq 2 ]]; then
|
||||
return 124
|
||||
fi
|
||||
if [[ "$call_index" -eq 3 ]]; then
|
||||
return 124
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
if refresh_launch_services_after_uninstall; then
|
||||
echo "RESULT:ok"
|
||||
else
|
||||
echo "RESULT:fail"
|
||||
fi
|
||||
|
||||
cat "$log_file"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"RESULT:ok"* ]]
|
||||
[[ "$output" == *"CALL2:15:/bin/echo -r -f -domain local -domain user -domain system"* ]]
|
||||
[[ "$output" == *"CALL3:10:/bin/echo -r -f -domain local -domain user"* ]]
|
||||
[[ "$output" == *"DEBUG:LaunchServices rebuild timed out, trying lighter version"* ]]
|
||||
}
|
||||
|
||||
@test "remove_mole deletes manual binaries and caches" {
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
touch "$HOME/.local/bin/mole"
|
||||
|
||||
@@ -78,10 +78,32 @@ ask_for_updates
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 1 ] # ESC cancels
|
||||
[[ "$output" == *"Homebrew, 3 formula, 2 cask"* ]]
|
||||
[[ "$output" == *"App Store, 1 apps"* ]]
|
||||
[[ "$output" == *"macOS system"* ]]
|
||||
[[ "$output" == *"Mole"* ]]
|
||||
[[ "$output" == *"Update Mole now?"* ]]
|
||||
[[ "$output" == *"Run "* ]]
|
||||
[[ "$output" == *"brew upgrade"* ]]
|
||||
[[ "$output" == *"Software Update"* ]]
|
||||
[[ "$output" == *"App Store"* ]]
|
||||
[[ "$output" != *"AVAILABLE UPDATES"* ]]
|
||||
}
|
||||
|
||||
@test "ask_for_updates with only macOS update shows settings hint without brew hint" {
|
||||
run bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/manage/update.sh"
|
||||
BREW_OUTDATED_COUNT=0
|
||||
BREW_FORMULA_OUTDATED_COUNT=0
|
||||
BREW_CASK_OUTDATED_COUNT=0
|
||||
APPSTORE_UPDATE_COUNT=0
|
||||
MACOS_UPDATE_AVAILABLE=true
|
||||
MOLE_UPDATE_AVAILABLE=false
|
||||
ask_for_updates
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"Software Update"* ]]
|
||||
[[ "$output" != *"brew upgrade"* ]]
|
||||
[[ "$output" != *"AVAILABLE UPDATES"* ]]
|
||||
}
|
||||
|
||||
@test "ask_for_updates accepts Enter when updates exist" {
|
||||
@@ -97,10 +119,50 @@ ask_for_updates
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"AVAILABLE UPDATES"* ]]
|
||||
[[ "$output" == *"Update Mole now?"* ]]
|
||||
[[ "$output" == *"yes"* ]]
|
||||
}
|
||||
|
||||
@test "ask_for_updates auto-detects brew updates when counts are unset" {
|
||||
run bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
cat > "$MOCK_BIN_DIR/brew" <<'SCRIPT'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$1" == "outdated" && "$2" == "--formula" && "$3" == "--quiet" ]]; then
|
||||
printf "wget\njq\n"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$1" == "outdated" && "$2" == "--cask" && "$3" == "--quiet" ]]; then
|
||||
printf "iterm2\n"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
SCRIPT
|
||||
chmod +x "$MOCK_BIN_DIR/brew"
|
||||
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/manage/update.sh"
|
||||
unset BREW_OUTDATED_COUNT BREW_FORMULA_OUTDATED_COUNT BREW_CASK_OUTDATED_COUNT
|
||||
APPSTORE_UPDATE_COUNT=0
|
||||
MACOS_UPDATE_AVAILABLE=false
|
||||
MOLE_UPDATE_AVAILABLE=false
|
||||
|
||||
set +e
|
||||
ask_for_updates
|
||||
ask_status=$?
|
||||
set -e
|
||||
|
||||
echo "COUNTS:${BREW_OUTDATED_COUNT}:${BREW_FORMULA_OUTDATED_COUNT}:${BREW_CASK_OUTDATED_COUNT}"
|
||||
exit "$ask_status"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"brew upgrade"* ]]
|
||||
[[ "$output" == *"COUNTS:3:2:1"* ]]
|
||||
[[ "$output" != *"AVAILABLE UPDATES"* ]]
|
||||
}
|
||||
|
||||
@test "format_brew_update_label lists formula and cask counts" {
|
||||
run bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
Reference in New Issue
Block a user