Merge pull request 'feat: XDG Base Directory compliance, --uninstall flag, and xclip advisory' (#13) from feat/xdg-compliance-and-uninstall into main

Reviewed-on: #13
This commit was merged in pull request #13.
This commit is contained in:
2026-05-09 05:02:15 +00:00
3 changed files with 205 additions and 22 deletions
+6 -6
View File
@@ -25,7 +25,7 @@ These are mandatory for the script to run and perform basic color conversions:
These enable specific functionality and can be installed as needed: These enable specific functionality and can be installed as needed:
- **KDE Plasma (Wayland):** Required for screen color picking (`--pick`). - **KDE Plasma (Wayland):** Required for screen color picking (`--pick`).
- **Python 3 PyQt6:** Required for the KDE color picker DBus interface. - **Python 3 PyQt6:** Required for the KDE color picker DBus interface.
- **wl-clipboard** or **xclip:** Enables copying results to the system clipboard (`--copy`). - **wl-clipboard** or **xclip:** Enables copying results to the system clipboard (`--copy`). `wl-clipboard` is strongly preferred on Wayland — `xclip` operates through XWayland and can be unreliable. `--check-deps` and `--install` will warn you if only `xclip` is found.
- **curl:** Required for fetching human-readable color names from the web (`--name`). - **curl:** Required for fetching human-readable color names from the web (`--name`).
- **libnotify:** Enables desktop notifications (`--notify`). - **libnotify:** Enables desktop notifications (`--notify`).
- **ImageMagick:** Generates visual color swatch icons for notifications (`--swatch`). - **ImageMagick:** Generates visual color swatch icons for notifications (`--swatch`).
@@ -45,10 +45,10 @@ curl -sSLO https://git.rootiest.dev/rootiest/color-tool/releases/download/latest
``` ```
This will: This will:
1. Install the script to `~/.local/share/color-tool/`. 1. Install the script to `$XDG_DATA_HOME/color-tool/` (default: `~/.local/share/color-tool/`).
2. Symlink the binary to `~/.local/bin/color-tool`. 2. Symlink the binary to `~/.local/bin/color-tool`.
3. Generate a sample configuration at `~/.config/color-tool/config.toml`. 3. Generate a sample configuration at `$XDG_CONFIG_HOME/color-tool/config.toml` (default: `~/.config/color-tool/config.toml`).
4. Create a `.desktop` entry so you can launch the picker from your application menu. 4. Create a `.desktop` entry in `$XDG_DATA_HOME/applications/` so you can launch the picker from your application menu.
5. Verify and warn about any missing optional or required system dependencies. 5. Verify and warn about any missing optional or required system dependencies.
6. Clean up the downloaded script after installation. 6. Clean up the downloaded script after installation.
@@ -85,7 +85,7 @@ Options:
--set-config Update the configuration (e.g. --set-config desktop --copy) --set-config Update the configuration (e.g. --set-config desktop --copy)
--reset-config Restore the configuration file to its default values --reset-config Restore the configuration file to its default values
--check-deps Check system dependencies and environment support --check-deps Check system dependencies and environment support
--install Install to ~/.local/share/ and symlink to ~/.local/bin/ --install Install to $XDG_DATA_HOME/color-tool/ and symlink to ~/.local/bin/
--help, -h Show this help message --help, -h Show this help message
``` ```
@@ -113,7 +113,7 @@ color-tool --desktop --no-name
## ⚙️ Configuration ## ⚙️ Configuration
You can define your preferred defaults in `~/.config/color-tool/config.toml`. The tool uses a priority hierarchy: **CLI Flags > Desktop Config > Default Config**. You can define your preferred defaults in `$XDG_CONFIG_HOME/color-tool/config.toml` (default: `~/.config/color-tool/config.toml`). The tool uses a priority hierarchy: **CLI Flags > Desktop Config > Default Config**.
```toml ```toml
[defaults] [defaults]
+157 -16
View File
@@ -22,7 +22,12 @@ set -euo pipefail
# Resolve the real directory of this script so we can find bundled helpers # Resolve the real directory of this script so we can find bundled helpers
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
CONFIG_FILE="$HOME/.config/color-tool/config.toml"
# XDG Base Directory Specification — prefer env vars, fall back to conventional defaults
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
CONFIG_FILE="$XDG_CONFIG_HOME/color-tool/config.toml"
REPO_URL="https://git.rootiest.dev/rootiest/color-tool" REPO_URL="https://git.rootiest.dev/rootiest/color-tool"
# Colors and styles # Colors and styles
@@ -43,11 +48,11 @@ C4='\033[38;2;80;200;80m' # Green
C5='\033[38;2;80;160;255m' # Blue C5='\033[38;2;80;160;255m' # Blue
# Path constants # Path constants
DATA_DIR="$HOME/.local/share/color-tool" DATA_DIR="$XDG_DATA_HOME/color-tool"
BIN_DIR="$HOME/.local/bin" BIN_DIR="$HOME/.local/bin"
BIN_PATH="$BIN_DIR/color-tool" BIN_PATH="$BIN_DIR/color-tool"
CONFIG_DIR="$HOME/.config/color-tool" CONFIG_DIR="$XDG_CONFIG_HOME/color-tool"
APP_DIR="$HOME/.local/share/applications" APP_DIR="$XDG_DATA_HOME/applications"
DESKTOP_FILE="$APP_DIR/color-tool.desktop" DESKTOP_FILE="$APP_DIR/color-tool.desktop"
# ── Initial Defaults ────────────────────────────────────────────────────────── # ── Initial Defaults ──────────────────────────────────────────────────────────
@@ -336,6 +341,19 @@ reset_config() {
# ── Help ────────────────────────────────────────────────────────────────────── # ── Help ──────────────────────────────────────────────────────────────────────
# Replace the $HOME prefix with ~/ only when the path actually starts with
# $HOME — avoids false matches on other users' home dirs or custom XDG roots.
tilde_path() {
local path="$1"
if [[ "$path" == "$HOME/"* ]]; then
printf '%s' "~/${path#"$HOME/"}"
elif [[ "$path" == "$HOME" ]]; then
printf '%s' "~"
else
printf '%s' "$path"
fi
}
show_help() { show_help() {
printf "\n" printf "\n"
# Title: "color" in a rainbow gradient, "-tool" in bold # Title: "color" in a rainbow gradient, "-tool" in bold
@@ -357,7 +375,8 @@ show_help() {
printf " ${BOLD}${CYAN}--set-config${RESET} Update the configuration ${DIM}(e.g. --set-config desktop --copy)${RESET}\n" printf " ${BOLD}${CYAN}--set-config${RESET} Update the configuration ${DIM}(e.g. --set-config desktop --copy)${RESET}\n"
printf " ${BOLD}${CYAN}--reset-config${RESET} Restore the configuration file to its default values\n" printf " ${BOLD}${CYAN}--reset-config${RESET} Restore the configuration file to its default values\n"
printf " ${BOLD}${CYAN}--check-deps${RESET} Check system dependencies and environment support\n" printf " ${BOLD}${CYAN}--check-deps${RESET} Check system dependencies and environment support\n"
printf " ${BOLD}${CYAN}--install${RESET} Install to ~/.local/share/color-tool/ and symlink into ~/.local/bin/\n" printf " ${BOLD}${CYAN}--install${RESET} Install to %s/ and symlink into %s/\n" "$(tilde_path "$DATA_DIR")" "$(tilde_path "$BIN_DIR")"
printf " ${BOLD}${CYAN}--uninstall${RESET} Remove installed files and optionally the configuration\n"
printf " ${BOLD}${CYAN}--help${RESET}, ${BOLD}${CYAN}-h${RESET} Show this help message\n\n" printf " ${BOLD}${CYAN}--help${RESET}, ${BOLD}${CYAN}-h${RESET} Show this help message\n\n"
printf " ${BOLD}${YELLOW}Examples${RESET}\n" printf " ${BOLD}${YELLOW}Examples${RESET}\n"
@@ -365,7 +384,7 @@ show_help() {
printf " ${CYAN}${0##*/}${RESET} --pick --output rgb,hsl --json\n" printf " ${CYAN}${0##*/}${RESET} --pick --output rgb,hsl --json\n"
printf " ${CYAN}${0##*/}${RESET} \"rgb(255, 100, 0)\" --output hex,hsla\n\n" printf " ${CYAN}${0##*/}${RESET} \"rgb(255, 100, 0)\" --output hex,hsla\n\n"
printf " ${BOLD}${YELLOW}Config${RESET} ${DIM}~/.config/color-tool/config.toml${RESET}\n" printf " ${BOLD}${YELLOW}Config${RESET} ${DIM}%s${RESET}\n" "$(tilde_path "$CONFIG_FILE")"
printf " ${DIM}[defaults]${RESET} keys: ${CYAN}output json swatch name copy pick notify${RESET}\n" printf " ${DIM}[defaults]${RESET} keys: ${CYAN}output json swatch name copy pick notify${RESET}\n"
printf " ${DIM}[desktop]${RESET} keys: ${CYAN}output json name notify copy swatch${RESET} ${DIM}(pick always on)${RESET}\n\n" printf " ${DIM}[desktop]${RESET} keys: ${CYAN}output json name notify copy swatch${RESET} ${DIM}(pick always on)${RESET}\n\n"
} }
@@ -426,7 +445,7 @@ notify_result() {
local icon_name="color-picker" local icon_name="color-picker"
if [[ $swatch_mode -eq 1 && -n "$hex_color" ]]; then if [[ $swatch_mode -eq 1 && -n "$hex_color" ]]; then
local swatch_path="/tmp/color_swatch_${hex_color//\#/}.png" local swatch_path="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/color_swatch_${hex_color//\#/}.png"
if command -v magick >/dev/null 2>&1; then if command -v magick >/dev/null 2>&1; then
if magick -size 64x64 xc:"$hex_color" "$swatch_path" 2>/dev/null; then if magick -size 64x64 xc:"$hex_color" "$swatch_path" 2>/dev/null; then
icon_name="$swatch_path" icon_name="$swatch_path"
@@ -566,7 +585,16 @@ check_deps() {
_check "python3-pyqt6" "python3 -c 'import PyQt6.QtDBus' 2>/dev/null" "KDE color picker interface" 0 _check "python3-pyqt6" "python3 -c 'import PyQt6.QtDBus' 2>/dev/null" "KDE color picker interface" 0
_check "jq" "command -v jq >/dev/null" "JSON output and data parsing" 1 _check "jq" "command -v jq >/dev/null" "JSON output and data parsing" 1
_check "curl" "command -v curl >/dev/null" "Fetching color names from API" 0 _check "curl" "command -v curl >/dev/null" "Fetching color names from API" 0
_check "wl-clipboard / xclip" "command -v wl-copy >/dev/null || command -v xclip >/dev/null" "System clipboard integration" 0 if command -v wl-copy >/dev/null 2>&1 && command -v xclip >/dev/null 2>&1; then
printf " ${GREEN}✔${RESET} %-25s ${DIM}%s${RESET}\n" "wl-clipboard / xclip" "System clipboard integration"
elif command -v wl-copy >/dev/null 2>&1; then
printf " ${GREEN}✔${RESET} %-25s ${DIM}%s${RESET}\n" "wl-clipboard" "System clipboard integration"
elif command -v xclip >/dev/null 2>&1; then
printf " ${YELLOW}✔${RESET} %-25s ${DIM}%s${RESET}\n" "xclip" "System clipboard integration"
printf " ${YELLOW} └─ xclip found but wl-clipboard is missing — xclip operates through XWayland and may be unreliable on Wayland${RESET}\n"
else
printf " ${YELLOW}⚠${RESET} %-25s ${DIM}%s${RESET}\n" "wl-clipboard / xclip" "System clipboard integration"
fi
_check "libnotify" "command -v notify-send >/dev/null" "Desktop notifications" 0 _check "libnotify" "command -v notify-send >/dev/null" "Desktop notifications" 0
_check "ImageMagick" "command -v magick >/dev/null || command -v convert >/dev/null" "Color swatches in notifications" 0 _check "ImageMagick" "command -v magick >/dev/null || command -v convert >/dev/null" "Color swatches in notifications" 0
_check "KDE Plasma (Wayland)" "[[ -n \"${WAYLAND_DISPLAY:-}\" ]] && { local d=\"${XDG_CURRENT_DESKTOP:-}\"; [[ \"\$d\" == *KDE* || -n \"${KDE_FULL_SESSION:-}\" ]]; }" "Required for screen color picking" 0 _check "KDE Plasma (Wayland)" "[[ -n \"${WAYLAND_DISPLAY:-}\" ]] && { local d=\"${XDG_CURRENT_DESKTOP:-}\"; [[ \"\$d\" == *KDE* || -n \"${KDE_FULL_SESSION:-}\" ]]; }" "Required for screen color picking" 0
@@ -583,7 +611,9 @@ check_dependencies() {
fi fi
if ! command -v wl-copy >/dev/null 2>&1 && ! command -v xclip >/dev/null 2>&1; then if ! command -v wl-copy >/dev/null 2>&1 && ! command -v xclip >/dev/null 2>&1; then
missing+=("wl-clipboard (wl-copy) or xclip — required for clipboard copy support") missing+=("wl-clipboard or xclip — required for clipboard copy support")
elif ! command -v wl-copy >/dev/null 2>&1 && command -v xclip >/dev/null 2>&1; then
missing+=("wl-clipboard — xclip is available but operates through XWayland and may be unreliable on Wayland; wl-clipboard is preferred")
fi fi
if ! command -v python3 >/dev/null 2>&1; then if ! command -v python3 >/dev/null 2>&1; then
@@ -640,7 +670,7 @@ do_install() {
local dirs=("$BIN_DIR" "$DATA_DIR" "$CONFIG_DIR" "$APP_DIR") local dirs=("$BIN_DIR" "$DATA_DIR" "$CONFIG_DIR" "$APP_DIR")
for dir in "${dirs[@]}"; do for dir in "${dirs[@]}"; do
if [[ ! -d "$dir" ]]; then if [[ ! -d "$dir" ]]; then
printf " ${DIM}Creating %s...${RESET}\n" "$dir" printf " ${DIM}Creating %s...${RESET}\n" "$(tilde_path "$dir")"
mkdir -p "$dir" mkdir -p "$dir"
fi fi
done done
@@ -654,9 +684,9 @@ do_install() {
# Config setup # Config setup
if [[ ! -f "$CONFIG_FILE" ]]; then if [[ ! -f "$CONFIG_FILE" ]]; then
write_default_config write_default_config
printf " ${BOLD}${GREEN}config${RESET} %s ${DIM}(sample created)${RESET}\n" "$CONFIG_FILE" printf " ${BOLD}${GREEN}config${RESET} %s ${DIM}(sample created)${RESET}\n" "$(tilde_path "$CONFIG_FILE")"
else else
printf " ${BOLD}${GREEN}config${RESET} %s\n" "$CONFIG_FILE" printf " ${BOLD}${GREEN}config${RESET} %s\n" "$(tilde_path "$CONFIG_FILE")"
fi fi
# Desktop Entry (using consolidated BIN_PATH) # Desktop Entry (using consolidated BIN_PATH)
@@ -675,19 +705,126 @@ NoDisplay=false
EOF EOF
# Summary output # Summary output
printf " ${BOLD}${GREEN}app${RESET} %s\n" "$DESKTOP_FILE" printf " ${BOLD}${GREEN}app${RESET} %s\n" "$(tilde_path "$DESKTOP_FILE")"
printf " ${BOLD}${GREEN}data${RESET} %s/\n" "$DATA_DIR" printf " ${BOLD}${GREEN}data${RESET} %s/\n" "$(tilde_path "$DATA_DIR")"
printf " ${BOLD}${GREEN}bin${RESET} %s -> %s\n" "$BIN_PATH" "$DATA_DIR/color-tool" printf " ${BOLD}${GREEN}bin${RESET} %s -> %s\n" "$(tilde_path "$BIN_PATH")" "$(tilde_path "$DATA_DIR/color-tool")"
# Path verification # Path verification
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
printf "\n${BOLD}${YELLOW}Warning:${RESET} %s is not in your PATH.\n" "$BIN_DIR" printf "\n${BOLD}${YELLOW}Warning:${RESET} %s is not in your PATH.\n" "$(tilde_path "$BIN_DIR")"
suggest_path_add "$BIN_DIR" suggest_path_add "$BIN_DIR"
fi fi
check_dependencies check_dependencies
} }
do_uninstall() {
printf "\n ${BOLD}Uninstalling ${C1}c${C2}o${C3}l${C4}o${C5}r${RESET}${BOLD}-tool...${RESET}\n\n"
local errors=0
local remove_config=0
# 1. Binary symlink
printf " ${DIM}Removing binary symlink...${RESET}\n"
if [[ -L "$BIN_PATH" ]]; then
if rm "$BIN_PATH"; then
printf " ${BOLD}${GREEN}bin${RESET} Removed %s\n" "$(tilde_path "$BIN_PATH")"
else
printf " ${BOLD}${RED}bin${RESET} Failed to remove %s\n" "$(tilde_path "$BIN_PATH")" >&2
errors=$((errors + 1))
fi
elif [[ -e "$BIN_PATH" ]]; then
printf " ${BOLD}${YELLOW}bin${RESET} %s is not a symlink — skipping\n" "$(tilde_path "$BIN_PATH")"
else
printf " ${DIM}bin${RESET} %s not found — skipping\n" "$(tilde_path "$BIN_PATH")"
fi
# 2. Desktop entry
printf " ${DIM}Removing desktop entry...${RESET}\n"
if [[ -f "$DESKTOP_FILE" ]]; then
if rm "$DESKTOP_FILE"; then
printf " ${BOLD}${GREEN}app${RESET} Removed %s\n" "$(tilde_path "$DESKTOP_FILE")"
else
printf " ${BOLD}${RED}app${RESET} Failed to remove %s\n" "$(tilde_path "$DESKTOP_FILE")" >&2
errors=$((errors + 1))
fi
else
printf " ${DIM}app${RESET} %s not found — skipping\n" "$(tilde_path "$DESKTOP_FILE")"
fi
# 3. Data directory
printf " ${DIM}Removing data directory...${RESET}\n"
if [[ -d "$DATA_DIR" ]]; then
if rm -rf "$DATA_DIR"; then
printf " ${BOLD}${GREEN}data${RESET} Removed %s/\n" "$(tilde_path "$DATA_DIR")"
else
printf " ${BOLD}${RED}data${RESET} Failed to remove %s/\n" "$(tilde_path "$DATA_DIR")" >&2
errors=$((errors + 1))
fi
else
printf " ${DIM}data${RESET} %s not found — skipping\n" "$(tilde_path "$DATA_DIR")"
fi
# 4. Config directory — prompt if interactive, skip silently if not
if [[ -d "$CONFIG_DIR" ]]; then
if [[ -t 0 ]]; then
printf "\n ${BOLD}${YELLOW}?${RESET} Remove configuration directory?\n"
printf " ${DIM}%s/${RESET}\n" "$(tilde_path "$CONFIG_DIR")"
printf " ${BOLD}[y/N]${RESET} "
local answer
read -r answer
if [[ "${answer,,}" == "y" || "${answer,,}" == "yes" ]]; then
remove_config=1
else
printf " ${BOLD}${DIM}config${RESET} %s/ kept\n" "$(tilde_path "$CONFIG_DIR")"
fi
else
printf " ${DIM}config${RESET} Non-interactive mode — keeping %s/\n" "$(tilde_path "$CONFIG_DIR")"
fi
fi
if [[ $remove_config -eq 1 ]]; then
printf " ${DIM}Removing configuration directory...${RESET}\n"
if rm -rf "$CONFIG_DIR"; then
printf " ${BOLD}${GREEN}config${RESET} Removed %s/\n" "$(tilde_path "$CONFIG_DIR")"
else
printf " ${BOLD}${RED}config${RESET} Failed to remove %s/\n" "$(tilde_path "$CONFIG_DIR")" >&2
errors=$((errors + 1))
fi
fi
# Verification
printf "\n ${BOLD}Verification${RESET}\n\n"
local verify_errors=0
local labels=("bin" "app" "data")
local paths=("$BIN_PATH" "$DESKTOP_FILE" "$DATA_DIR")
if [[ $remove_config -eq 1 ]]; then
labels+=("config")
paths+=("$CONFIG_DIR")
fi
local i
for i in "${!labels[@]}"; do
local label="${labels[$i]}"
local path="${paths[$i]}"
printf " "
if [[ ! -e "$path" && ! -L "$path" ]]; then
printf "${GREEN}✔${RESET} %-8s %s\n" "$label" "$(tilde_path "$path")"
else
printf "${RED}✘${RESET} %-8s %s ${RED}(still present)${RESET}\n" "$label" "$(tilde_path "$path")"
verify_errors=$((verify_errors + 1))
fi
done
printf "\n"
if [[ $((errors + verify_errors)) -eq 0 ]]; then
printf " ${BOLD}${GREEN}color-tool has been successfully uninstalled.${RESET}\n\n"
else
printf " ${BOLD}${RED}Uninstall completed with errors.${RESET} Some files may need to be removed manually.\n\n" >&2
return 1
fi
}
# ── Color conversion ────────────────────────────────────────────────────────── # ── Color conversion ──────────────────────────────────────────────────────────
get_all_formats() { get_all_formats() {
@@ -875,6 +1012,10 @@ while [[ $# -gt 0 ]]; do
do_install do_install
exit 0 exit 0
;; ;;
--uninstall)
do_uninstall
exit 0
;;
--get-config) --get-config)
get_config get_config
exit 0 exit 0
+42
View File
@@ -59,9 +59,13 @@ MOCK
chmod +x "$BIN_DIR/curl" chmod +x "$BIN_DIR/curl"
ln -s /usr/bin/mv "$BIN_DIR/mv" ln -s /usr/bin/mv "$BIN_DIR/mv"
ln -s /usr/bin/tr "$BIN_DIR/tr" ln -s /usr/bin/tr "$BIN_DIR/tr"
ln -s /usr/bin/cp "$BIN_DIR/cp"
ln -s /usr/bin/ln "$BIN_DIR/ln"
export PATH="$BIN_DIR" export PATH="$BIN_DIR"
export HOME="$TEST_DIR" export HOME="$TEST_DIR"
export XDG_CONFIG_HOME="$TEST_DIR/.config"
export XDG_DATA_HOME="$TEST_DIR/.local/share"
export WAYLAND_DISPLAY=wayland-0 export WAYLAND_DISPLAY=wayland-0
export XDG_CURRENT_DESKTOP=KDE export XDG_CURRENT_DESKTOP=KDE
@@ -232,6 +236,44 @@ else
echo " Actual output: $output" echo " Actual output: $output"
fi fi
# 15. Install — files placed in correct XDG directories
it "installs files to XDG directories"
# Clear any artifacts from earlier tests before verifying a clean install
rm -rf "$XDG_DATA_HOME/color-tool" \
"$XDG_DATA_HOME/applications/color-tool.desktop" \
"$HOME/.local/bin" 2>/dev/null || true
install_out=$("$COLOR_TOOL" --install 2>&1)
if [[ -f "$XDG_DATA_HOME/color-tool/color-tool" ]] &&
[[ -f "$XDG_DATA_HOME/color-tool/wl-colorpicker-plasma.py" ]] &&
[[ -L "$HOME/.local/bin/color-tool" ]] &&
[[ -f "$XDG_DATA_HOME/applications/color-tool.desktop" ]] &&
[[ -f "$XDG_CONFIG_HOME/color-tool/config.toml" ]]; then
echo "PASS"
passed=$((passed + 1))
else
echo "FAIL"
echo " Install output: $install_out"
echo " Files found:"
/usr/bin/find "$TEST_DIR/.local" "$TEST_DIR/.config" \( -type f -o -type l \) 2>/dev/null | /usr/bin/sort || true
fi
# 16. Uninstall — removes installed files, config is kept in non-interactive mode
it "uninstalls installed files (keeps config)"
uninstall_out=$("$COLOR_TOOL" --uninstall </dev/null 2>&1)
if [[ ! -e "$HOME/.local/bin/color-tool" ]] &&
[[ ! -L "$HOME/.local/bin/color-tool" ]] &&
[[ ! -d "$XDG_DATA_HOME/color-tool" ]] &&
[[ ! -f "$XDG_DATA_HOME/applications/color-tool.desktop" ]] &&
[[ -f "$XDG_CONFIG_HOME/color-tool/config.toml" ]]; then
echo "PASS"
passed=$((passed + 1))
else
echo "FAIL"
echo " Uninstall output: $uninstall_out"
echo " Files still present:"
/usr/bin/find "$TEST_DIR/.local" "$TEST_DIR/.config" \( -type f -o -type l \) 2>/dev/null | /usr/bin/sort || true
fi
# ── Cleanup ─────────────────────────────────────────────────────────────────── # ── Cleanup ───────────────────────────────────────────────────────────────────
echo "---------------------------------------" echo "---------------------------------------"
echo "Result: $passed/$total tests passed." echo "Result: $passed/$total tests passed."