mirror of
https://github.com/stornic56/debianito-post-install.git
synced 2026-10-31 13:08:06 +00:00
afac6c7876
Bookworm's plasma-workspace ships only the X11 session; the Wayland session (plasmawayland.desktop) lives in the separate plasma-workspace-wayland package (a Recommends of plasma-desktop, now listed explicitly for the same determinism as the explicit sddm). The package does not exist on trixie, where plasma-workspace integrates Wayland natively, so naming it off-bookworm would fail the apt transaction: resolve it once per installer with _kde_wayland_pkg() and build the package list in a conditional array so neither the pre-apply summary nor the apt command ever shows an empty argument. Also correct the header comment: bookworm still defaults SDDM to the X11 session, while trixie makes Wayland primary (plasma.desktop in wayland-sessions) with plasmax11.desktop as the X11 fallback. Replace the hard-coded SDDM autologin session cascade with an existence-based ordered probe (_sddm_session_candidates) that prefers Wayland across releases, keeps the LXQt sessions last for when the module starts offering LXQt, and reads its session roots from SESSION_DIR_WAYLAND / SESSION_DIR_X11 for hermetic testing. Extend tests/desktops.bats with 13 offline tests: gate helper output on bookworm/trixie, captured apt commands for both installers, summary content (no empty package line), and the ordered session probe against fixture trees.
430 lines
14 KiB
Bash
430 lines
14 KiB
Bash
#!/usr/bin/env bash
|
|
# desktops.bats — Offline tests for the desktop helpers in
|
|
# modules/desktop_display.sh (SP2) and the desktop module
|
|
# wiring in _load_module (SP2.5).
|
|
#
|
|
# No `set -euo pipefail` here: BATS controls error handling
|
|
# per test (see utils.bats).
|
|
#
|
|
# State-mutating functions are called DIRECTLY (a `run`
|
|
# subshell would lose their globals); pure-output helpers
|
|
# use `run`.
|
|
#
|
|
# 100% offline: dpkg and whiptail come from the PATH mocks
|
|
# in test_helper.bash, systemctl uses the helper mock,
|
|
# /etc/X11/default-display-manager is redirected through the
|
|
# DM_FILE fixture, and the real audio installer is replaced
|
|
# by a call-counter stub.
|
|
|
|
load test_helper.bash
|
|
|
|
# Stub for modules/system/audio.sh (never loaded here):
|
|
# count calls instead of running apt. Counter reset in setup().
|
|
_install_pipewire_standard() {
|
|
PIPEWIRE_INSTALL_CALLS=$((PIPEWIRE_INSTALL_CALLS + 1))
|
|
}
|
|
|
|
setup() {
|
|
mock_reset
|
|
load_utils
|
|
# load_utils() sources utils.sh inside a function, so the
|
|
# top-level `declare -A _LOADED_MODULES` (utils.sh:48) was
|
|
# function-local and vanished when it returned — the same
|
|
# caveat the helper documents for the ETH_* arrays.
|
|
# Re-declare it globally for the _load_module tests.
|
|
declare -gA _LOADED_MODULES=()
|
|
|
|
# Only function definitions at the top level: safe to source.
|
|
# Only function definitions at the top level: safe to source.
|
|
source "${TEST_PROJECT_ROOT}/modules/desktop_display.sh"
|
|
# The GNOME installers are pure function definitions too;
|
|
# desktop_display.sh (their DM/pipewire collaborators) is
|
|
# already sourced above.
|
|
source "${TEST_PROJECT_ROOT}/modules/desktops/desktop_gnome.sh"
|
|
# Fixture redirect for /etc/X11/default-display-manager:
|
|
# the file exists but is empty, so `-s` fails and the
|
|
# systemctl fallback is exercised deterministically.
|
|
DM_FILE="$(mktemp)"
|
|
export DM_FILE
|
|
|
|
PIPEWIRE_INSTALL_CALLS=0
|
|
}
|
|
|
|
teardown() {
|
|
if [ -n "${DM_FILE:-}" ]; then
|
|
rm -f "$DM_FILE"
|
|
fi
|
|
if [ -n "${MOCK_SUDO_LOG:-}" ]; then
|
|
rm -f "$MOCK_SUDO_LOG"
|
|
fi
|
|
if [ -n "${MOCK_APT_ROOT:-}" ]; then
|
|
rm -rf "$MOCK_APT_ROOT"
|
|
fi
|
|
if [ -n "${SESSION_FIXTURE:-}" ]; then
|
|
rm -rf "$SESSION_FIXTURE"
|
|
fi
|
|
unset SESSION_DIR_WAYLAND SESSION_DIR_X11
|
|
}
|
|
|
|
# ── _existing_display_manager ────────────────────────
|
|
@test "_existing_display_manager reads the default DM file" {
|
|
printf '/usr/sbin/gdm3\n' >"$DM_FILE"
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "gdm3" ]
|
|
}
|
|
|
|
@test "_existing_display_manager prefers the DM file over an enabled unit" {
|
|
printf '/usr/sbin/sddm\n' >"$DM_FILE"
|
|
export MOCK_SYSTEMCTL_ENABLED="gdm3"
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "sddm" ]
|
|
}
|
|
|
|
@test "_existing_display_manager falls back to an enabled systemd unit" {
|
|
# DM_FILE exists but is empty: `-s` must fail, not `-e`.
|
|
export MOCK_SYSTEMCTL_ENABLED="lightdm"
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "lightdm" ]
|
|
}
|
|
|
|
@test "_existing_display_manager evaluates units in candidate order" {
|
|
# gdm3 → lightdm → sddm → greetd → gdm, whatever order
|
|
# systemctl reports them in.
|
|
export MOCK_SYSTEMCTL_ENABLED="sddm lightdm"
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "lightdm" ]
|
|
}
|
|
|
|
@test "_existing_display_manager falls back when the DM file is missing" {
|
|
rm -f "$DM_FILE"
|
|
export MOCK_SYSTEMCTL_ENABLED="greetd"
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "greetd" ]
|
|
}
|
|
|
|
@test "_existing_display_manager prints nothing when nothing is configured" {
|
|
run _existing_display_manager
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
# ── _offer_pipewire_after_desktop ────────────────────
|
|
@test "_offer_pipewire_after_desktop skips without prompting when pipewire-audio is installed" {
|
|
MOCK_DPKG_INSTALLED="pipewire-audio"
|
|
WHIPTAIL_CALLS=0
|
|
whiptail() { WHIPTAIL_CALLS=$((WHIPTAIL_CALLS + 1)); }
|
|
_offer_pipewire_after_desktop
|
|
[ "$WHIPTAIL_CALLS" -eq 0 ]
|
|
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
|
|
}
|
|
|
|
@test "_offer_pipewire_after_desktop installs the stack when accepted" {
|
|
export MOCK_WHIPTAIL_RC=0 # user answers "Yes"
|
|
_offer_pipewire_after_desktop
|
|
[ "$PIPEWIRE_INSTALL_CALLS" -eq 1 ]
|
|
}
|
|
|
|
@test "_offer_pipewire_after_desktop returns 0 without installing when declined" {
|
|
export MOCK_WHIPTAIL_RC=1 # user answers "No"
|
|
offer_rc=0
|
|
_offer_pipewire_after_desktop || offer_rc=$?
|
|
[ "$offer_rc" -eq 0 ]
|
|
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
|
|
}
|
|
|
|
@test "_offer_pipewire_after_desktop checks the pipewire package on Bullseye" {
|
|
export DEBIAN_VERSION="11"
|
|
MOCK_DPKG_INSTALLED="pipewire" # Bullseye package name
|
|
_offer_pipewire_after_desktop
|
|
[ "$PIPEWIRE_INSTALL_CALLS" -eq 0 ]
|
|
}
|
|
|
|
@test "_offer_pipewire_after_desktop ignores pipewire-audio on Bullseye" {
|
|
export DEBIAN_VERSION="11"
|
|
MOCK_DPKG_INSTALLED="pipewire-audio" # wrong name on 11
|
|
export MOCK_WHIPTAIL_RC=0 # user accepts
|
|
_offer_pipewire_after_desktop
|
|
[ "$PIPEWIRE_INSTALL_CALLS" -eq 1 ]
|
|
}
|
|
|
|
# ── _load_module: desktop wiring (SP2.5) ─────────────
|
|
# The dependency cascade is not under test here: mark the
|
|
# transitive deps of the desktop modules as loaded so only
|
|
# the module file itself is sourced.
|
|
_load_module_with_deps_marked() {
|
|
local module="$1"
|
|
declare -gA _LOADED_MODULES=(
|
|
[desktop_display]=1
|
|
[audio]=1
|
|
[repos]=1
|
|
[repo_detect]=1
|
|
)
|
|
_load_module "$module"
|
|
}
|
|
|
|
@test "_load_module xfce sources modules/desktops/desktop_xfce.sh" {
|
|
_load_module_with_deps_marked xfce
|
|
type xfce_menu >/dev/null
|
|
type _install_xfce_custom >/dev/null
|
|
type _xfce_polkit_rules >/dev/null
|
|
}
|
|
|
|
@test "_load_module lxde sources modules/desktops/desktop_lxde.sh" {
|
|
_load_module_with_deps_marked lxde
|
|
type lxde_menu >/dev/null
|
|
type _install_lxde_full >/dev/null
|
|
}
|
|
|
|
@test "_load_module gnome sources modules/desktops/desktop_gnome.sh" {
|
|
_load_module_with_deps_marked gnome
|
|
type gnome_menu >/dev/null
|
|
type _install_gnome_full >/dev/null
|
|
}
|
|
|
|
@test "_load_module kde sources modules/desktops/desktop_kde.sh" {
|
|
_load_module_with_deps_marked kde
|
|
type kde_menu >/dev/null
|
|
type _install_kde_minimal >/dev/null
|
|
}
|
|
|
|
@test "_load_module returns 1 for an unknown module" {
|
|
run _load_module no_such_module
|
|
[ "$status" -eq 1 ]
|
|
}
|
|
|
|
# ── terminal selection: ptyxis on trixie ──────────────
|
|
# gnome-core declares the OR-dep "gnome-terminal or
|
|
# gnome-console or ptyxis"; apt resolves an unset OR-dep
|
|
# with the first alternative, so listing the terminal
|
|
# explicitly in the same apt transaction is what steers
|
|
# the resolver. These tests assert the exact package
|
|
# that lands in the apt command.
|
|
#
|
|
# _gnome_capture_apt_cmd runs one installer with every
|
|
# external effect stubbed: _run_cmd is replaced so the
|
|
# apt command is captured instead of executed, and the
|
|
# PATH sudo mock stays in dry-run mode so debconf and
|
|
# systemctl invocations are logged, not executed.
|
|
_gnome_capture_apt_cmd() {
|
|
local installer="$1"
|
|
GNOME_APT_CMD=""
|
|
_confirm_install_list() { return 0; }
|
|
_ensure_apt_updated() { :; }
|
|
_run_cmd() { GNOME_APT_CMD="$2"; }
|
|
"$installer"
|
|
printf '%s\n' "$GNOME_APT_CMD"
|
|
}
|
|
|
|
@test "GNOME Full installs ptyxis on trixie" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _gnome_capture_apt_cmd _install_gnome_full
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y gnome gdm3 ptyxis"* ]]
|
|
[[ "$output" != *gnome-terminal* ]]
|
|
}
|
|
|
|
@test "GNOME Core installs ptyxis on trixie" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _gnome_capture_apt_cmd _install_gnome_core
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y gnome-core gdm3 ptyxis"* ]]
|
|
[[ "$output" != *gnome-terminal* ]]
|
|
}
|
|
|
|
@test "GNOME Full falls back to gnome-terminal off trixie" {
|
|
export DEBIAN_CODENAME="bookworm"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _gnome_capture_apt_cmd _install_gnome_full
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y gnome gdm3 gnome-terminal"* ]]
|
|
[[ "$output" != *ptyxis* ]]
|
|
}
|
|
|
|
@test "GNOME falls back to gnome-terminal when the codename is empty" {
|
|
# /etc/os-release missing or without VERSION_CODENAME
|
|
# leaves DEBIAN_CODENAME unset: the safe default is the
|
|
# historical behaviour.
|
|
export DEBIAN_CODENAME=""
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _gnome_capture_apt_cmd _install_gnome_full
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y gnome gdm3 gnome-terminal"* ]]
|
|
[[ "$output" != *ptyxis* ]]
|
|
}
|
|
|
|
# ── KDE Plasma: bookworm Wayland gate ─────────────
|
|
# plasma-workspace-wayland exists only on bookworm:
|
|
# bookworm's plasma-workspace ships the X11 session
|
|
# only, so the explicit listing keeps the Wayland
|
|
# session in the transaction deterministically; off
|
|
# bookworm the package does not exist and nothing is
|
|
# named in the apt command.
|
|
|
|
# _kde_capture_apt_cmd runs one KDE installer with
|
|
# every external effect stubbed (same shape as the
|
|
# GNOME capture helper): the apt command is captured
|
|
# instead of executed, and the PATH sudo mock stays
|
|
# in dry-run mode so debconf and systemctl invocations
|
|
# are logged, not executed.
|
|
_kde_capture_apt_cmd() {
|
|
local installer="$1"
|
|
_load_module_with_deps_marked kde
|
|
KDE_APT_CMD=""
|
|
_confirm_install_list() { return 0; }
|
|
_ensure_apt_updated() { :; }
|
|
_run_cmd() { KDE_APT_CMD="$2"; }
|
|
"$installer"
|
|
printf '%s\n' "$KDE_APT_CMD"
|
|
}
|
|
|
|
# _kde_capture_confirm runs one KDE installer with the
|
|
# pre-apply summary captured instead of shown: _confirm
|
|
# records the list _confirm_install_list builds, so the
|
|
# summary content (never an empty package line) can be
|
|
# asserted directly. The pipewire follow-up offer is
|
|
# stubbed so the recorded list is the package summary.
|
|
_kde_capture_confirm() {
|
|
local installer="$1"
|
|
_load_module_with_deps_marked kde
|
|
KDE_CONFIRM_LIST=""
|
|
_confirm() { KDE_CONFIRM_LIST="$2"; return 0; }
|
|
_ensure_apt_updated() { :; }
|
|
_run_cmd() { :; }
|
|
_offer_pipewire_after_desktop() { :; }
|
|
"$installer"
|
|
printf '%s\n' "$KDE_CONFIRM_LIST"
|
|
}
|
|
|
|
@test "_kde_wayland_pkg prints plasma-workspace-wayland on bookworm" {
|
|
export DEBIAN_CODENAME="bookworm"
|
|
_load_module_with_deps_marked kde
|
|
run _kde_wayland_pkg
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "plasma-workspace-wayland" ]
|
|
}
|
|
|
|
@test "_kde_wayland_pkg prints nothing off bookworm" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
_load_module_with_deps_marked kde
|
|
run _kde_wayland_pkg
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "KDE Full installs the Wayland session package on bookworm" {
|
|
export DEBIAN_CODENAME="bookworm"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_apt_cmd _install_kde_full
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y kde-standard sddm plasma-workspace-wayland"* ]]
|
|
}
|
|
|
|
@test "KDE Full omits the Wayland session package off bookworm" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_apt_cmd _install_kde_full
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y kde-standard sddm"* ]]
|
|
[[ "$output" != *"plasma-workspace-wayland"* ]]
|
|
}
|
|
|
|
@test "KDE Minimal installs the Wayland session package on bookworm" {
|
|
export DEBIAN_CODENAME="bookworm"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_apt_cmd _install_kde_minimal
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y kde-plasma-desktop sddm plasma-workspace-wayland"* ]]
|
|
}
|
|
|
|
@test "KDE Minimal omits the Wayland session package off bookworm" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_apt_cmd _install_kde_minimal
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"apt install -y kde-plasma-desktop sddm"* ]]
|
|
[[ "$output" != *"plasma-workspace-wayland"* ]]
|
|
}
|
|
|
|
@test "KDE Minimal summary lists the Wayland session package on bookworm" {
|
|
export DEBIAN_CODENAME="bookworm"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_confirm _install_kde_minimal
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"plasma-workspace-wayland"* ]]
|
|
}
|
|
|
|
@test "KDE Minimal summary shows no empty package line off bookworm" {
|
|
export DEBIAN_CODENAME="trixie"
|
|
export MOCK_SUDO_DRY_RUN=1
|
|
run _kde_capture_confirm _install_kde_minimal
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"kde-plasma-desktop"* ]]
|
|
[[ "$output" == *"sddm"* ]]
|
|
[[ "$output" != *"plasma-workspace-wayland"* ]]
|
|
[[ "$output" != *"- "$'\n'* ]]
|
|
}
|
|
|
|
# ── _sddm_session_candidates: ordered session probe ──
|
|
# Existence decides (no codename gates) and Wayland is
|
|
# preferred. The roots are redirected to a per-test
|
|
# fixture tree through SESSION_DIR_WAYLAND /
|
|
# SESSION_DIR_X11 (same spirit as the DM_FILE fixture),
|
|
# so the probe never touches the real /usr/share tree.
|
|
|
|
_sddm_session_fixture() {
|
|
SESSION_FIXTURE="$(mktemp -d)"
|
|
mkdir -p "$SESSION_FIXTURE/wayland" "$SESSION_FIXTURE/x11"
|
|
SESSION_DIR_WAYLAND="$SESSION_FIXTURE/wayland"
|
|
SESSION_DIR_X11="$SESSION_FIXTURE/x11"
|
|
export SESSION_DIR_WAYLAND SESSION_DIR_X11
|
|
}
|
|
|
|
@test "SDDM session probe prefers bookworm Wayland (plasmawayland)" {
|
|
_sddm_session_fixture
|
|
: >"$SESSION_DIR_WAYLAND/plasmawayland.desktop"
|
|
: >"$SESSION_DIR_X11/plasma.desktop"
|
|
run _sddm_session_candidates
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "plasmawayland" ]
|
|
}
|
|
|
|
@test "SDDM session probe picks trixie Wayland (plasma.desktop)" {
|
|
_sddm_session_fixture
|
|
: >"$SESSION_DIR_WAYLAND/plasma.desktop"
|
|
: >"$SESSION_DIR_X11/plasmax11.desktop"
|
|
run _sddm_session_candidates
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "plasma" ]
|
|
}
|
|
|
|
@test "SDDM session probe falls back to trixie X11 (plasmax11)" {
|
|
_sddm_session_fixture
|
|
: >"$SESSION_DIR_X11/plasmax11.desktop"
|
|
run _sddm_session_candidates
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "plasmax11" ]
|
|
}
|
|
|
|
@test "SDDM session probe keeps LXQt last when no KDE session exists" {
|
|
_sddm_session_fixture
|
|
: >"$SESSION_DIR_WAYLAND/lxqt-wayland.desktop"
|
|
: >"$SESSION_DIR_X11/lxqt.desktop"
|
|
run _sddm_session_candidates
|
|
[ "$status" -eq 0 ]
|
|
[ "$output" = "lxqt-wayland" ]
|
|
}
|
|
|
|
@test "SDDM session probe returns empty when no session file exists" {
|
|
_sddm_session_fixture
|
|
run _sddm_session_candidates
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|