Files
omarchy/test/shell.d/yt6801-driver-test.sh
T

358 lines
13 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin"
mkdir -p "$stub_bin"
export TEST_STATE="$test_tmp/state"
export TEST_CALLS="$test_tmp/calls"
export TEST_FAULT=""
export TEST_DEVICE="0000:01:00.0"
export TEST_DEVICE_2="0000:02:00.0"
export PATH="$stub_bin:$PATH"
# Model kernel state separately from package files: DKMS removal deletes the
# latter and refreshes depmod, but cannot unload the module already in memory.
cat >"$stub_bin/stub" <<'SH'
#!/bin/bash
set -euo pipefail
command=${0##*/}
printf '%s %s\n' "$command" "$*" >>"$TEST_CALLS"
case "$command" in
lspci)
[[ $* == "-Dn -d 1f0a:6801" ]] || exit 90
[[ $TEST_FAULT != "pci-fail" ]] || exit 1
cat "$TEST_STATE/devices"
[[ $TEST_FAULT != "pci-partial" ]] || exit 1
;;
pacman)
if [[ $* == "-Qq" ]]; then
query_count=$(<"$TEST_STATE/queries")
query_count=$((query_count + 1))
printf '%s\n' "$query_count" >"$TEST_STATE/queries"
printf '%s\n' linux
if [[ -e $TEST_STATE/package ]]; then
printf '%s\n' yt6801-dkms
fi
[[ $TEST_FAULT != "query-fail" ]] || exit 1
if [[ $TEST_FAULT == "helper-query-fail" ]] && (( query_count == 2 )); then
exit 1
fi
if [[ $TEST_FAULT == "final-query-fail" ]] && [[ ! -e $TEST_STATE/package ]]; then
exit 1
fi
elif [[ $* == "-Rns --noconfirm yt6801-dkms" ]]; then
[[ $TEST_FAULT != "remove-fail" ]] || exit 1
if [[ $TEST_FAULT != "remove-noop" ]]; then
rm -f "$TEST_STATE/package" "$TEST_STATE/module-files"
fi
if [[ $TEST_FAULT == "remove-reload" ]]; then
touch "$TEST_STATE/vendor-loaded"
elif [[ $TEST_FAULT == "remove-unbind" ]]; then
rm -f "$TEST_STATE/bindings/$TEST_DEVICE"
fi
[[ $TEST_FAULT != "remove-interrupt" ]] || exit 1
else
exit 90
fi
;;
lsmod)
[[ $TEST_FAULT != "modules-fail" ]] || exit 1
if [[ $TEST_FAULT == "final-modules-fail" && ! -e $TEST_STATE/package ]]; then
exit 1
fi
printf '%s\n' 'Module Size Used by'
if [[ -e $TEST_STATE/vendor-loaded ]]; then
printf '%s\n' 'yt6801 65536 0'
fi
;;
readlink)
[[ $# == 2 && $1 == "-f" && $2 == /sys/bus/pci/devices/*/driver ]] || exit 90
device=${2%/driver}
device=${device##*/}
[[ $TEST_FAULT != "binding-read-fail" ]] || exit 1
if [[ -e $TEST_STATE/bindings/$device ]]; then
printf '/sys/bus/pci/drivers/%s\n' "$(<"$TEST_STATE/bindings/$device")"
else
printf '%s\n' "$2"
fi
;;
modinfo)
[[ $* == "-F alias dwmac-motorcomm" ]] || exit 90
[[ $TEST_FAULT != "alias-fail" ]] || exit 1
if [[ $TEST_FAULT == "alias-wrong" ]]; then
printf '%s\n' 'pci:v00001F0Ad00006802sv*sd*bc*sc*i*'
else
printf '%s\n' 'pci:v00001F0Ad00006801sv*sd*bc*sc*i*'
fi
;;
modprobe)
# Real modprobe cannot resolve a module through its deleted lookup entry.
if [[ $* == "-r yt6801" ]]; then
[[ -e $TEST_STATE/module-files ]] || exit 1
exit 90
fi
[[ $* == "dwmac-motorcomm" ]] || exit 90
[[ $TEST_FAULT != "load-fail" ]] || exit 1
if [[ $TEST_FAULT != "load-noop" ]]; then
touch "$TEST_STATE/upstream-loaded"
fi
[[ $TEST_FAULT != "load-interrupt" ]] || exit 1
;;
rmmod)
[[ $* == "yt6801" ]] || exit 90
[[ $TEST_FAULT != "unload-fail" ]] || exit 1
[[ -e $TEST_STATE/vendor-loaded ]] || exit 1
if [[ $TEST_FAULT != "unload-noop" ]]; then
rm "$TEST_STATE/vendor-loaded"
for binding in "$TEST_STATE/bindings/"*; do
if [[ -f $binding && $(<"$binding") == "yt6801" ]]; then
rm "$binding"
fi
done
fi
[[ $TEST_FAULT != "unload-interrupt" ]] || exit 1
;;
tee)
[[ $* == "/sys/bus/pci/drivers_probe" ]] || exit 90
read -r device
[[ $device == "$TEST_DEVICE" || $device == "$TEST_DEVICE_2" ]] || exit 90
[[ $TEST_FAULT != "probe-fail" ]] || exit 1
if [[ $TEST_FAULT != "probe-noop" && -e $TEST_STATE/upstream-loaded && ! -e $TEST_STATE/bindings/$device ]]; then
if [[ $TEST_FAULT != "second-probe-noop" || $device != "$TEST_DEVICE_2" ]]; then
printf '%s\n' dwmac-motorcomm >"$TEST_STATE/bindings/$device"
fi
fi
[[ $TEST_FAULT != "probe-interrupt" ]] || exit 1
;;
sudo)
[[ $TEST_FAULT != "sudo-fail" ]] || exit 1
case "$*" in
'modprobe dwmac-motorcomm'|'rmmod yt6801'|'tee /sys/bus/pci/drivers_probe'|'pacman -Rns --noconfirm yt6801-dkms') "$@" ;;
*) exit 90 ;;
esac
;;
omarchy-notification-dismiss) ;;
*) exit 90 ;;
esac
SH
chmod +x "$stub_bin/stub"
for command in lspci pacman lsmod readlink modinfo modprobe rmmod tee sudo omarchy-notification-dismiss; do
ln -s stub "$stub_bin/$command"
done
ln -s "$ROOT/bin/omarchy-pkg-drop" "$stub_bin/omarchy-pkg-drop"
reset_state() {
rm -rf "$TEST_STATE"
mkdir -p "$TEST_STATE/bindings"
: >"$TEST_CALLS"
TEST_FAULT=""
printf '%s\n' 0 >"$TEST_STATE/queries"
printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE" >"$TEST_STATE/devices"
printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE"
touch "$TEST_STATE/package" "$TEST_STATE/module-files" "$TEST_STATE/vendor-loaded"
}
run_migration() {
bash -euo pipefail "$ROOT/migrations/1788279117.sh" >"$test_tmp/output" 2>&1
}
assert_complete() {
[[ ! -e $TEST_STATE/package ]] || fail "completed migration left the vendor package installed"
[[ ! -e $TEST_STATE/vendor-loaded ]] || fail "completed migration left the vendor module loaded"
local device rest
while read -r device rest; do
[[ -n $device ]] || continue
[[ -f $TEST_STATE/bindings/$device && $(<"$TEST_STATE/bindings/$device") == "dwmac-motorcomm" ]] ||
fail "completed migration left an adapter without the upstream driver" "$device"
done <"$TEST_STATE/devices"
}
assert_no_privileges() {
if grep -q '^sudo ' "$TEST_CALLS"; then
fail "$1" "$(<"$TEST_CALLS")"
fi
}
reset_state
run_migration
assert_complete
# Enforce the safe ordering across the modeled DKMS package transaction.
[[ $(grep -E '^(modprobe|rmmod|tee|pacman -Rns) ' "$TEST_CALLS") == "$(printf '%s\n' \
'modprobe dwmac-motorcomm' 'rmmod yt6801' 'tee /sys/bus/pci/drivers_probe' 'pacman -Rns --noconfirm yt6801-dkms')" ]] ||
fail "the replacement must bind before DKMS package removal"
pass "migration cuts over before removing the vendor package and module files"
: >"$TEST_CALLS"
TEST_FAULT=sudo-fail
run_migration
assert_complete
assert_no_privileges "a later user's completed migration must need no privileges"
if grep -Eq '^(modinfo|modprobe|rmmod|tee) ' "$TEST_CALLS"; then
fail "a completed migration must not need module metadata or reprobe"
fi
pass "completed machines need no privileges, module metadata, or reprobe"
reset_state
printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE_2" >>"$TEST_STATE/devices"
printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE_2"
run_migration
assert_complete
pass "every target adapter is rebound and verified"
for fault in pci-fail pci-partial query-fail modules-fail alias-fail alias-wrong; do
reset_state
TEST_FAULT=$fault
if run_migration; then
fail "migration must fail on $fault"
fi
assert_no_privileges "$fault must be detected before changing hardware or packages"
[[ -e $TEST_STATE/package && -e $TEST_STATE/vendor-loaded ]] || fail "$fault lost the installed fallback"
pass "$fault fails before cutover"
done
reset_state
printf '%s\n' 'invalid-address 0200: 1f0a:6801' >"$TEST_STATE/devices"
if run_migration; then
fail "malformed PCI enumeration must fail the migration"
fi
assert_no_privileges "invalid PCI data must be rejected before privileged work"
pass "invalid PCI addresses cannot reach the reprobe command"
for fault in sudo-fail load-fail load-noop unload-fail unload-noop probe-fail probe-noop binding-read-fail helper-query-fail remove-fail; do
reset_state
TEST_FAULT=$fault
if run_migration; then
fail "migration must fail on $fault"
fi
[[ -e $TEST_STATE/package ]] || fail "$fault discarded the package before cutover succeeded"
TEST_FAULT=""
run_migration
assert_complete
pass "$fault preserves the package and permits a successful retry"
done
for fault in remove-noop remove-reload remove-unbind final-query-fail final-modules-fail; do
reset_state
TEST_FAULT=$fault
if run_migration; then
fail "postconditions must detect $fault"
fi
TEST_FAULT=""
run_migration
assert_complete
pass "postconditions detect $fault and a retry completes"
done
for fault in load-interrupt unload-interrupt probe-interrupt remove-interrupt; do
reset_state
TEST_FAULT=$fault
if run_migration; then
fail "interrupted $fault must fail the migration"
fi
TEST_FAULT=""
run_migration
assert_complete
pass "retry completes after $fault"
done
reset_state
rm "$TEST_STATE/package" "$TEST_STATE/module-files"
run_migration
assert_complete
pass "retry unloads the vendor module even after an earlier DKMS removal deleted its files"
reset_state
rm "$TEST_STATE/vendor-loaded" "$TEST_STATE/bindings/$TEST_DEVICE"
run_migration
assert_complete
pass "an initially unbound adapter is reprobed without unloading an absent vendor module"
reset_state
rm "$TEST_STATE/vendor-loaded"
printf '%s\n' dwmac-motorcomm >"$TEST_STATE/bindings/$TEST_DEVICE"
TEST_FAULT=alias-fail
run_migration
assert_complete
pass "an already-bound adapter only needs package cleanup even if module metadata is unavailable"
reset_state
printf '%s 0200: 1f0a:6801\n' "$TEST_DEVICE_2" >>"$TEST_STATE/devices"
printf '%s\n' yt6801 >"$TEST_STATE/bindings/$TEST_DEVICE_2"
TEST_FAULT=second-probe-noop
if run_migration; then
fail "one working adapter must not hide another adapter's failed cutover"
fi
[[ -e $TEST_STATE/package ]] || fail "partial cutover removed the vendor package"
TEST_FAULT=""
run_migration
assert_complete
pass "partial multi-adapter cutover remains pending and can be retried"
for driver in dwmac-motorcomm-extra dwmac_motorcomm vfio-pci; do
reset_state
rm "$TEST_STATE/vendor-loaded"
printf '%s\n' "$driver" >"$TEST_STATE/bindings/$TEST_DEVICE"
if run_migration; then
fail "unexpected driver $driver must not count as upstream binding"
fi
[[ -e $TEST_STATE/package && $(<"$TEST_STATE/bindings/$TEST_DEVICE") == "$driver" ]] ||
fail "failed cutover must preserve an unrelated binding and the package"
pass "exact sysfs binding rejects $driver without forcibly unbinding it"
done
reset_state
: >"$TEST_STATE/devices"
rm -f "$TEST_STATE/bindings/$TEST_DEVICE"
run_migration
assert_complete
pass "without target hardware, the migration still unloads and removes the vendor driver"
: >"$TEST_CALLS"
TEST_FAULT=sudo-fail
run_migration
assert_no_privileges "an unaffected machine must need no privileges"
pass "an unaffected machine is a successful no-op"
# Setup leaves are sourced. A manual rerun on an old or partially migrated
# system must leave the fallback in place for the migration's proven cutover.
reset_state
bash -eE -c 'source "$1"' bash "$ROOT/install/hardware/fix-yt6801-ethernet-adapter.sh"
[[ -e $TEST_STATE/package && -e $TEST_STATE/vendor-loaded ]] ||
fail "manual hardware setup retired the YT6801 fallback"
[[ ! -s $TEST_CALLS ]] ||
fail "manual hardware setup performed package, discovery, or module work" "$(cat "$TEST_CALLS")"
pass "manual hardware setup leaves the vendor fallback for the proven migration cutover"
# Exercise the real runner's completion markers and queue, without using the
# current user's state directory or running any other repository migrations.
reset_state
mkdir -p "$test_tmp/omarchy/migrations"
ln -s "$ROOT/migrations/1788279117.sh" "$test_tmp/omarchy/migrations/1788279117.sh"
printf '%s\n' 'touch "$TEST_STATE/later-migration"' >"$test_tmp/omarchy/migrations/1788279118.sh"
export OMARCHY_PATH="$test_tmp/omarchy"
export OMARCHY_MIGRATION_STATE="$test_tmp/migration-state"
TEST_FAULT=probe-noop
if "$ROOT/bin/omarchy-migrate" >"$test_tmp/output" 2>&1; then
fail "migration runner must fail when cutover is incomplete"
fi
[[ ! -e $OMARCHY_MIGRATION_STATE/1788279117.sh && ! -e $TEST_STATE/later-migration ]] ||
fail "failed cutover must not write a completion marker or run later migrations"
TEST_FAULT=""
"$ROOT/bin/omarchy-migrate" >"$test_tmp/output" 2>&1
assert_complete
[[ -f $OMARCHY_MIGRATION_STATE/1788279117.sh && -f $TEST_STATE/later-migration ]] ||
fail "successful retry must mark completion and continue the queue"
pass "the real migration runner keeps failures pending and completes successful retries"
if grep -Fxq 'yt6801-dkms' "$ROOT/install/omarchy-other.packages"; then
fail "default package list still installs yt6801-dkms"
fi
pass "the default package list no longer installs the vendor driver"