#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" output="$(cat "${ROOT_DIR}/install.sh" | bash -s -- --help 2>&1)" || { echo "FAIL: piped install.sh --help should succeed" >&2 exit 1 } if [[ "${output}" != *"install.sh - unified installer for Zenbook Duo Linux"* ]]; then echo "FAIL: help output missing expected banner" >&2 exit 1 fi empty_array_output="$(bash --noprofile --norc -c 'set -euo pipefail; BASH_SOURCE=(); source <(head -n 9 "'"${ROOT_DIR}"'/install.sh"); printf "%s\n" "${SCRIPT_DIR}"' 2>&1)" || { echo "FAIL: init should survive an empty BASH_SOURCE array" >&2 exit 1 } if [[ "${empty_array_output}" != "$(pwd)" ]]; then echo "FAIL: empty-array fallback should use current working directory" >&2 exit 1 fi temp_root="$(mktemp -d)" trap 'rm -rf "${temp_root}"' EXIT fake_bin="${temp_root}/bin" mkdir -p "${fake_bin}" cat > "${fake_bin}/git" <<'EOF' #!/usr/bin/env bash set -euo pipefail if [[ "${1:-}" != "clone" ]]; then echo "unexpected git args: $*" >&2 exit 1 fi target="${@: -1}" mkdir -p "${target}" touch "${target}/setup-common.sh" "${target}/setup-gnome.sh" "${target}/setup-kde.sh" "${target}/setup-niri.sh" "${target}/install-ui.sh" EOF chmod +x "${fake_bin}/git" checkout_output="$(bash --noprofile --norc -c ' set -euo pipefail export PATH="'"${fake_bin}"':/usr/bin:/bin" source <(sed -n "1,128p" "'"${ROOT_DIR}"'/install.sh") SCRIPT_DIR="'"${temp_root}"'/missing-checkout" ensure_repo_checkout ' 2>/dev/null)" || { echo "FAIL: ensure_repo_checkout should succeed with fake git" >&2 exit 1 } if [[ "${checkout_output}" == *"Downloading zenbook-duo-linux..."* ]]; then echo "FAIL: ensure_repo_checkout should not mix status logs into stdout" >&2 exit 1 fi if [[ ! -f "${checkout_output}/setup-niri.sh" ]]; then echo "FAIL: ensure_repo_checkout should print the checkout path" >&2 exit 1 fi desktop_output="$(bash --noprofile --norc -c ' set -euo pipefail source <(sed -n "1,128p" "'"${ROOT_DIR}"'/install.sh") XDG_CURRENT_DESKTOP=GNOME DESKTOP_SESSION= XDG_SESSION_DESKTOP= pick_desktop XDG_CURRENT_DESKTOP="KDE Plasma" DESKTOP_SESSION= XDG_SESSION_DESKTOP= pick_desktop XDG_CURRENT_DESKTOP= DESKTOP_SESSION= XDG_SESSION_DESKTOP=niri pick_desktop ' 2>/dev/null)" || { echo "FAIL: pick_desktop should detect all supported desktops" >&2 exit 1 } if [[ "${desktop_output}" != $'gnome\nkde\nniri' ]]; then echo "FAIL: pick_desktop should map GNOME, KDE, and Niri consistently" >&2 exit 1 fi assert_setup_packages() { local label="$1" local dnf_desktop="$2" local apt_desktop="$3" local pacman_desktop="$4" local manual_hint="$5" local output="" output="$(bash --noprofile --norc -c ' set -euo pipefail source "$1" DNF_DESKTOP_PACKAGES=("$2") APT_DESKTOP_PACKAGES=("$3") PACMAN_DESKTOP_PACKAGES=("$4") MANUAL_DESKTOP_DEPENDENCIES_HINT="$5" build_duo_setup_package_lists printf "dnf:%s\napt:%s\npacman:%s\nhint:%s\n" \ "${DNF_PACKAGES[*]}" \ "${APT_PACKAGES[*]}" \ "${PACMAN_PACKAGES[*]}" \ "${MANUAL_DEPENDENCIES_HINT}" ' _ "${ROOT_DIR}/setup-common.sh" "${dnf_desktop}" "${apt_desktop}" "${pacman_desktop}" "${manual_hint}")" || { echo "FAIL: setup package matrix should build for ${label}" >&2 exit 1 } local expected=$'dnf:usbutils iio-sensor-proxy systemd '"${dnf_desktop}"$'\napt:usbutils iio-sensor-proxy systemd '"${apt_desktop}"$'\npacman:usbutils iio-sensor-proxy systemd '"${pacman_desktop}"$'\nhint:usbutils, iio-sensor-proxy, systemd, '"${manual_hint}" if [[ "${output}" != "${expected}" ]]; then echo "FAIL: setup package matrix mismatch for ${label}" >&2 echo "Got:" >&2 echo "${output}" >&2 exit 1 fi } assert_setup_packages gnome mutter mutter-common-bin mutter mutter/gdctl assert_setup_packages kde kscreen kscreen kscreen kscreen/kscreen-doctor assert_setup_packages niri niri niri niri niri if ! grep -q 'WantedBy=default.target' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should be enabled from default.target" >&2 exit 1 fi if grep -q 'Wants=graphical-session.target' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should not pull in graphical-session.target" >&2 exit 1 fi if ! grep -q 'After=graphical-session.target' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should still order itself after graphical-session.target" >&2 exit 1 fi if ! grep -q 'StartLimitIntervalSec=0' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should keep retrying while the graphical session environment is not ready" >&2 exit 1 fi if ! grep -Fq 'ExecStartPre=/bin/sh -c '\''test -n "\${DISPLAY:-}\${WAYLAND_DISPLAY:-}\${NIRI_SOCKET:-}"'\''' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should wait for graphical session variables before starting" >&2 exit 1 fi if ! grep -q 'RestartSec=2' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: user service should retry startup at a bounded cadence" >&2 exit 1 fi if ! grep -q 'import-environment DISPLAY WAYLAND_DISPLAY NIRI_SOCKET XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP DESKTOP_SESSION XDG_SESSION_TYPE' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: installer should import graphical session environment for the user manager" >&2 exit 1 fi if ! grep -q 'ensure_system_manager_available' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should preflight the system service manager before building" >&2 exit 1 fi if ! grep -q 'run_system_systemctl daemon-reload' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should wrap required system daemon reloads with clear errors" >&2 exit 1 fi if ! grep -q 'ensure_user_manager_available' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should preflight the target user service manager" >&2 exit 1 fi if ! grep -q 'run_user_systemctl_checked daemon-reload' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should hard-fail required user daemon reloads with clear errors" >&2 exit 1 fi if ! grep -Fq 'local bus_address="unix:path=${runtime_dir}/bus"' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should avoid stale user bus environment when calling systemctl --user" >&2 exit 1 fi if ! grep -Fq 'DBUS_SESSION_BUS_ADDRESS="${bus_address}"' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should pass the stable user bus to systemctl --user" >&2 exit 1 fi for service_name in \ 'SYSTEM_SERVICE_NAME="zenbook-duo-rust-daemon.service"' \ 'LIFECYCLE_SERVICE_NAME="zenbook-duo-rust-lifecycle.service"' \ 'USER_SERVICE_NAME="zenbook-duo-session-agent.service"'; do if ! grep -q "${service_name}" "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should keep service name ${service_name}" >&2 exit 1 fi done if ! grep -q 'Installed Rust runtime versions:' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should print installed runtime versions" >&2 exit 1 fi if ! grep -Fq '"${RUNTIME_INSTALL_DIR}/${binary}" --version' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: runtime installer should call installed binaries with --version" >&2 exit 1 fi if ! grep -q 'install_duo_build_dependencies' "${ROOT_DIR}/setup-common.sh"; then echo "FAIL: setup-common.sh should install build dependencies before the Rust runtime" >&2 exit 1 fi if ! grep -q 'DUO_SUDOERS_DROPIN_PATH="/etc/sudoers.d/zenbook-duo"' "${ROOT_DIR}/setup-common.sh"; then echo "FAIL: setup-common.sh should write sudoers rules into /etc/sudoers.d" >&2 exit 1 fi if grep -Fq 'ALL=NOPASSWD:/usr/bin/tee /sys/class/backlight/*/brightness' "${ROOT_DIR}/setup-common.sh"; then echo "FAIL: setup-common.sh should not write wildcard sudoers command arguments" >&2 exit 1 fi if ! grep -A20 'run_duo_setup()' "${ROOT_DIR}/setup-common.sh" | grep -q 'install_duo_build_dependencies'; then echo "FAIL: run_duo_setup should call install_duo_build_dependencies before install_duo_rust_runtime" >&2 exit 1 fi for binary_name in \ zenbook-duo-daemon \ zenbook-duo-session-agent \ zenbook-duo-lifecycle \ zenbook-duo-usb-remap-helper \ usb-media-remap; do binary_path="src/bin/${binary_name}.rs" binary_file="${ROOT_DIR}/ui-tauri-react/src-tauri/${binary_path}" if ! grep -Fq 'print_and_exit_if_requested' "${binary_file}" \ || ! grep -Fq "\"${binary_name}\"" "${binary_file}"; then echo "FAIL: runtime binary ${binary_path} should support --version" >&2 exit 1 fi done if ! grep -q 'SYSTEM_SLEEP_HOOK_PATH="/usr/lib/systemd/system-sleep/zenbook-duo-rust-lifecycle"' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: installer should install the Rust lifecycle sleep hook" >&2 exit 1 fi if ! grep -q 'ln -sfn "${RUNTIME_INSTALL_DIR}/zenbook-duo-lifecycle" "${SYSTEM_SLEEP_HOOK_PATH}"' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: lifecycle binary should be the single system-sleep entrypoint" >&2 exit 1 fi if ! grep -q 'run_user_systemctl_best_effort daemon-reload' "${ROOT_DIR}/uninstall.sh"; then echo "FAIL: uninstaller should not fail cleanup when the user manager daemon reload is unavailable" >&2 exit 1 fi if ! grep -q 'run_user_systemctl_best_effort stop zenbook-duo-session-agent.service' "${ROOT_DIR}/uninstall.sh"; then echo "FAIL: uninstaller should treat user service cleanup as best-effort" >&2 exit 1 fi if grep -q 'ExecStart=.*\(pre\|post\|thaw\|hibernate\)' "${ROOT_DIR}/install-rust-runtime.sh"; then echo "FAIL: lifecycle service should not duplicate suspend/resume handling" >&2 exit 1 fi if ! grep -q 'command -v pacman' "${ROOT_DIR}/setup-common.sh"; then echo "FAIL: shared setup helper should support pacman-based systems" >&2 exit 1 fi for common_packages in \ 'DUO_COMMON_DNF_PACKAGES=(usbutils iio-sensor-proxy systemd)' \ 'DUO_COMMON_APT_PACKAGES=(usbutils iio-sensor-proxy systemd)' \ 'DUO_COMMON_PACMAN_PACKAGES=(usbutils iio-sensor-proxy systemd)'; do if ! grep -q "${common_packages}" "${ROOT_DIR}/setup-common.sh"; then echo "FAIL: shared setup helper should centralize common dependency list ${common_packages}" >&2 exit 1 fi done remap_default_output="$(bash --noprofile --norc -c ' set -euo pipefail source "'"${ROOT_DIR}"'/setup-common.sh" duo_prompt() { printf -v "$2" ""; } DEFAULT_BACKLIGHT=0 DEFAULT_SCALE=1.66 USB_MEDIA_REMAP_ENABLED=false prompt_duo_defaults printf "%s\n" "${USB_MEDIA_REMAP_ENABLED}" DEFAULT_BACKLIGHT=0 DEFAULT_SCALE=1.66 USB_MEDIA_REMAP_ENABLED=true prompt_duo_defaults printf "%s\n" "${USB_MEDIA_REMAP_ENABLED}" ' 2>&1)" || { echo "FAIL: prompt_duo_defaults should be callable with mocked prompts" >&2 exit 1 } if [[ "${remap_default_output}" != $'false\ntrue' ]]; then echo "FAIL: empty USB remap prompt answer should preserve the current default" >&2 printf 'Got:\n%s\n' "${remap_default_output}" >&2 exit 1 fi cat > "${temp_root}/settings-defaults-test.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail source "$1" temp_home="$2/home" mkdir -p "${temp_home}" TARGET_HOME="${temp_home}" TARGET_USER="${USER:-$(id -un)}" DEFAULT_BACKLIGHT="${DUO_DEFAULT_BACKLIGHT}" DEFAULT_SCALE="${DUO_DEFAULT_SCALE}" USB_MEDIA_REMAP_ENABLED="${DUO_DEFAULT_USB_MEDIA_REMAP_ENABLED}" write_duo_settings_defaults python3 - "${temp_home}/.config/zenbook-duo/settings.json" <<'PY' import json import sys from pathlib import Path settings = json.loads(Path(sys.argv[1]).read_text()) expected = { "defaultBacklight": 0, "defaultScale": 1.66, "usbMediaRemapEnabled": True, "startOnBootMinimized": False, "setupCompleted": True, "autoDualScreen": True, "syncBrightness": True, "theme": "system", } for key, value in expected.items(): if settings.get(key) != value: raise SystemExit(f"{key} expected {value!r}, got {settings.get(key)!r}") PY EOF bash "${temp_root}/settings-defaults-test.sh" "${ROOT_DIR}/setup-common.sh" "${temp_root}" || { echo "FAIL: installer-written settings defaults should match the compatibility contract" >&2 exit 1 } if ! grep -q 'pub const DEFAULT_BACKLIGHT_LEVEL: u8 = 0;' "${ROOT_DIR}/ui-tauri-react/src-tauri/src/models/settings.rs"; then echo "FAIL: Rust settings default backlight should match installer defaults" >&2 exit 1 fi if ! grep -q '0o666' "${ROOT_DIR}/ui-tauri-react/src-tauri/src/runtime/daemon.rs"; then echo "FAIL: daemon socket should be accessible across local user sessions" >&2 exit 1 fi if ! grep -q 'defaultBacklight: 0,' "${ROOT_DIR}/ui-tauri-react/src/lib/defaults.ts"; then echo "FAIL: frontend settings default backlight should match installer defaults" >&2 exit 1 fi if ! grep -q 'startOnBootMinimized: false,' "${ROOT_DIR}/ui-tauri-react/src/lib/defaults.ts"; then echo "FAIL: frontend settings default startup preference should match installer defaults" >&2 exit 1 fi for setup_script in setup-gnome.sh setup-kde.sh setup-niri.sh; do expected_name="${setup_script}" if ! grep -q "SETUP_SCRIPT_NAME=\"${expected_name}\"" "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should declare its setup script name for shared errors" >&2 exit 1 fi if ! grep -q 'source "${DUO_SETUP_DIR}/setup-common.sh"' "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should delegate to setup-common.sh" >&2 exit 1 fi if ! grep -q 'DNF_DESKTOP_PACKAGES=' "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should declare only desktop-specific dnf dependencies" >&2 exit 1 fi if ! grep -q 'APT_DESKTOP_PACKAGES=' "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should declare only desktop-specific apt dependencies" >&2 exit 1 fi if ! grep -q 'PACMAN_DESKTOP_PACKAGES=' "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should declare only desktop-specific pacman dependencies" >&2 exit 1 fi if grep -q '^DNF_PACKAGES=' "${ROOT_DIR}/${setup_script}" || grep -q '^APT_PACKAGES=' "${ROOT_DIR}/${setup_script}" || grep -q '^PACMAN_PACKAGES=' "${ROOT_DIR}/${setup_script}"; then echo "FAIL: ${setup_script} should not duplicate common package arrays" >&2 exit 1 fi done if ! grep -q 'PKG_MGR="pacman"' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should detect pacman-based systems" >&2 exit 1 fi if ! grep -q 'install_ui_direct' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should have a direct-install path for pacman systems" >&2 exit 1 fi if ! grep -q 'command -v pkexec' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should prefer pkexec when a graphical auth prompt is available" >&2 exit 1 fi if ! grep -q -- '--root-install-prereqs' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should expose a root-only prereq helper entrypoint" >&2 exit 1 fi if ! grep -q -- '--root-install-package' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should expose a root-only package install helper entrypoint" >&2 exit 1 fi if ! grep -q -- '--root-install-direct' "${ROOT_DIR}/install-ui.sh"; then echo "FAIL: install-ui.sh should expose a root-only direct install helper entrypoint" >&2 exit 1 fi if ! grep -q 'sudo pacman -Rns --noconfirm zenbook-duo-control' "${ROOT_DIR}/uninstall.sh"; then echo "FAIL: uninstall.sh should try pacman removal when available" >&2 exit 1 fi echo "PASS"