shift-shell/tests/check-shift-icon-theme.sh

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

258 lines
8.9 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2026 Marco Allegretti <mightymarco4@gmail.com>
# SPDX-License-Identifier: EUPL-1.2
set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_dir"
theme_dir="icons/org.shift.icons"
required_icons="$(mktemp)"
missing_icons="$(mktemp)"
broken_icons="$(mktemp)"
plasma_files="$(mktemp)"
trap 'rm -f "$required_icons" "$missing_icons" "$broken_icons" "$plasma_files"' EXIT
icon_name_prefix_re='^(accessories|activities|applications?|application-|appointment|archive|arrow-|audio|battery|bluetooth|bookmarks|call|camera-|chronometer|color|configure|computer|device-|dialog-|document|drive|edit-|emblem-|entry|favorites|flashlight|folder|format|games|games-|get-hot-new-stuff|go-|help|high-brightness|input-|internet|kcm|keyboard|klipper|krunner|layer|list-|low-brightness|mail|media-|mobile-|network-|notifications|object|office|org\.kde\.|osd-|phone|plasma|preferences-|printer|process|redshift|rotation-|search|security|settings-|software|speedometer|starred|start-here|system-|tablet|tools|touchpad|trash-|unknown|user|utilities|video|view|view-|virtual-|weather|widget-|window|zoom)'
fail() {
printf '%s\n' "$1" >&2
exit 1
}
require_line() {
local file="$1"
local pattern="$2"
local message="$3"
grep -Eq "$pattern" "$file" || fail "$message"
}
icon_exists() {
local icon_name="$1"
[[ -e "$theme_dir/preferences/scalable/$icon_name.svg" ]] && return 0
[[ -e "$theme_dir/actions/symbolic/$icon_name.svg" ]] && return 0
[[ -e "$theme_dir/places/symbolic/$icon_name.svg" ]] && return 0
[[ -e "$theme_dir/apps/scalable/$icon_name.svg" ]] && return 0
return 1
}
require_directory_order() {
local directories
directories="$(awk -F= '$1 == "Directories" { print $2 }' "$theme_dir/index.theme")"
[[ "$directories" == preferences/scalable,*actions/symbolic* ]] \
|| fail "org.shift.icons must list preferences/scalable before actions/symbolic so System Settings category icons do not resolve to symbolic action aliases"
}
check_systemsettings_category_icons() {
[[ -d /usr/share/systemsettings/categories ]] || return 0
local missing_preferences
missing_preferences="$(grep -Rh '^Icon=' /usr/share/systemsettings/categories 2>/dev/null \
| cut -d= -f2- \
| sort -u \
| while IFS= read -r icon_name; do
case "$icon_name" in
preferences-*)
[[ -e "$theme_dir/preferences/scalable/$icon_name.svg" ]] || printf '%s\n' "$icon_name"
;;
esac
done)"
[[ -z "$missing_preferences" ]] || {
printf 'System Settings category icons must exist in preferences/scalable so they are not shadowed by symbolic action aliases:\n' >&2
printf '%s\n' "$missing_preferences" | sed 's/^/ - /' >&2
exit 1
}
}
check_systemsettings_module_icons() {
compgen -G '/usr/share/applications/kcm_*.desktop' >/dev/null || return 0
local missing_preferences
missing_preferences="$({ grep -Rh '^Icon=' /usr/share/applications/kcm_*.desktop 2>/dev/null || true; } \
| cut -d= -f2- \
| sort -u \
| while IFS= read -r icon_name; do
[[ -e "$theme_dir/preferences/scalable/$icon_name.svg" ]] || printf '%s\n' "$icon_name"
done)"
[[ -z "$missing_preferences" ]] || {
printf 'System Settings module icons must exist in preferences/scalable so KCM list entries do not resolve to dark symbolic glyphs:\n' >&2
printf '%s\n' "$missing_preferences" | sed 's/^/ - /' >&2
exit 1
}
}
hicolor_fallback_exists() {
local icon_name="$1"
case "$icon_name" in
start-here-shift)
[[ -e icons/sc-places-start-here-shift.svg ]]
;;
*)
return 1
;;
esac
}
filter_icon_names() {
awk 'length > 0 && $0 !~ /[[:space:]]/ && $0 !~ /\// && $0 !~ /[][{}();:%>]/ && $0 !~ /^#/ && $0 !~ /\.(qml|png|jpg|jpeg|svg|desktop|service|json|xml|wav|ogg)$/ && $0 !~ /-$/ { print }' \
| grep -E "$icon_name_prefix_re"
}
collect_installed_plasma_icon_names() {
command -v rpm >/dev/null || return 0
local package
for package in \
plasma6-desktop \
plasma6-workspace \
plasma6-mobile \
plasma6-nm \
plasma6-pa \
powerdevil6 \
kscreen6 \
bluedevil6 \
systemsettings6 \
kde-cli-tools6 \
kwin6 \
kactivitymanagerd6; do
rpm -ql "$package" 2>/dev/null || true
done \
| awk '/\.(qml|js|json|desktop|actions)$/ || /metadata\.json$/ { print }' \
| { grep -v '^/usr/share/icons/' || true; } \
| sort -u > "$plasma_files"
[[ -s "$plasma_files" ]] || return 0
xargs -r perl -ne 'if (/^(?:Icon|IconName|X-KDE-PluginInfo-Icon)=(.+)$/) { print "$1\n"; } while (/"Icon"\s*:\s*"([^"]+)"/g) { print "$1\n"; } while (/(?:\bicon\b|icon\.name|iconName|iconSource|fallbackIconName|compactRepresentationIcon|categoryIcon|source)\s*:\s*["\x27]([^"\x27]+)["\x27]/g) { print "$1\n"; } while (/(?:setIconName|iconName)\s*\(\s*["\x27]([^"\x27]+)["\x27]/g) { print "$1\n"; }' < "$plasma_files" \
| filter_icon_names || true
}
collect_repo_icon_names() {
find \
components/mobileshell/qml \
containments/homescreens/folio/qml \
containments/homescreens/halcyon/qml \
containments/taskpanel/qml \
initialstart \
kcms \
kwin \
lookandfeel/contents/logout \
quicksettings \
-type f \( -name '*.qml' -o -name '*.js' -o -name '*.json' -o -name '*.desktop' -o -name '*.actions' -o -name '*.notifyrc' \) -print0 \
| xargs -0 perl -ne 'if (/^(?:Icon|IconName|X-KDE-PluginInfo-Icon)=(.+)$/) { print "$1\n"; } while (/"Icon"\s*:\s*"([^"]+)"/g) { print "$1\n"; } while (/(?:\bicon\b|icon\.name|iconName|iconSource|fallbackIconName|compactRepresentationIcon|categoryIcon|source)\s*:\s*["\x27]([^"\x27]+)["\x27]/g) { print "$1\n"; } while (/(?:setIconName|iconName)\s*\(\s*["\x27]([^"\x27]+)["\x27]/g) { print "$1\n"; }' \
| filter_icon_names || true
}
[[ -d "$theme_dir" ]] || fail "Missing Shift icon theme directory: $theme_dir"
while IFS= read -r -d '' icon_path; do
[[ -e "$icon_path" ]] || printf '%s\n' "$icon_path"
done < <(find "$theme_dir" -type l -print0) > "$broken_icons"
if [[ -s "$broken_icons" ]]; then
printf 'Broken Shift icon aliases:\n' >&2
sed 's/^/ - /' "$broken_icons" >&2
exit 1
fi
require_line lookandfeel/contents/defaults '^Theme=org\.shift\.icons$' \
"look-and-feel defaults must select org.shift.icons"
require_line "$theme_dir/index.theme" '^Inherits=hicolor$' \
"org.shift.icons must inherit only hicolor"
require_line "$theme_dir/index.theme" '^Directories=.*actions/symbolic.*places/symbolic.*apps/scalable' \
"org.shift.icons index.theme must list actions, places, and apps directories"
require_directory_order
require_line "$theme_dir/index.theme" '^\[actions/symbolic\]$' \
"org.shift.icons index.theme is missing [actions/symbolic]"
require_line "$theme_dir/index.theme" '^\[places/symbolic\]$' \
"org.shift.icons index.theme is missing [places/symbolic]"
require_line "$theme_dir/index.theme" '^\[apps/scalable\]$' \
"org.shift.icons index.theme is missing [apps/scalable]"
require_line "$theme_dir/index.theme" '^\[preferences/scalable\]$' \
"org.shift.icons index.theme is missing [preferences/scalable]"
check_systemsettings_category_icons
check_systemsettings_module_icons
{
collect_repo_icon_names
collect_installed_plasma_icon_names
if [[ -d /usr/share/icons ]]; then
find /usr/share/icons -name 'network-*.svg' -o -name 'network-*.png' 2>/dev/null \
| sed 's#.*/##; s/\.[^.]*$//' \
| awk 'length > 0 { print }'
fi
cat <<'ICONS'
applications-all
applications-development
applications-education
applications-games
applications-graphics
applications-internet
applications-multimedia
applications-office
applications-other
applications-science
applications-system
applications-utilities
battery-000
battery-000-charging
battery-010
battery-010-charging
battery-020
battery-020-charging
battery-030
battery-030-charging
battery-040
battery-040-charging
battery-050
battery-050-charging
battery-060
battery-060-charging
battery-070
battery-070-charging
battery-080
battery-080-charging
battery-090
battery-090-charging
battery-100
battery-100-charging
battery-missing
network-bluetooth
network-bluetooth-activated
network-mobile-0
network-mobile-20
network-mobile-40
network-mobile-60
network-mobile-80
network-mobile-100
preferences-system
systemsettings
ICONS
} | sort -u > "$required_icons"
while IFS= read -r icon_name; do
[[ -n "$icon_name" ]] || continue
if icon_exists "$icon_name" || hicolor_fallback_exists "$icon_name"; then
continue
fi
printf '%s\n' "$icon_name" >> "$missing_icons"
done < "$required_icons"
if [[ -s "$missing_icons" ]]; then
printf 'Missing required Shift icons:\n' >&2
sed 's/^/ - /' "$missing_icons" >&2
exit 1
fi