first commit
This commit is contained in:
commit
1354db6c39
90 files changed
+13123
No files matched your search
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake apt-cache (unit tests): show / rdepends / policy from data/apt/
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
pkgs=()
|
||||
for a in "$@"; do case $a in -*) ;; *) pkgs+=("$a") ;; esac; done
|
||||
verb=${pkgs[0]}
|
||||
pkgs=("${pkgs[@]:1}")
|
||||
case $verb in
|
||||
show)
|
||||
f=$FAKE_DATA/apt/show/${pkgs[0]}
|
||||
if [[ ! -f $f ]] || fake_purged "${pkgs[0]}"; then
|
||||
echo "E: No packages found" >&2
|
||||
exit 100
|
||||
fi
|
||||
cat "$f"
|
||||
;;
|
||||
rdepends)
|
||||
f=$FAKE_DATA/apt/rdepends/${pkgs[0]}
|
||||
if [[ -f $f ]]; then cat "$f"; else printf '%s\nReverse Depends:\n' "${pkgs[0]}"; fi
|
||||
;;
|
||||
policy)
|
||||
# print the stanzas of the requested packages
|
||||
want=" ${pkgs[*]} "
|
||||
show=0
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^([^ ].*):$ ]]; then
|
||||
if [[ $want == *" ${BASH_REMATCH[1]} "* ]]; then show=1; else show=0; fi
|
||||
fi
|
||||
((show)) && printf '%s\n' "$line"
|
||||
done <"$FAKE_DATA/apt/policy.txt"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "fake apt-cache: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake apt-get (unit tests). Scenario: FAKE_APT_SIM=fixture|held|real|none|purge (data/apt/sim-*.txt).
|
||||
# Purge simulation: installed = the "ii" rows of data/apt/dpkg-status.tsv; reverse dependencies from
|
||||
# data/apt/purge-rdeps.txt; pre-existing orphans from data/apt/autoremovable.txt (added by
|
||||
# --auto-remove, which is system-wide like the real one); FAKE_APT_AUTO_ORPHANS="pkg:dep ..." are
|
||||
# the auto-installed dependencies a purge of pkg orphans; FAKE_APT_SIM_EXTRA="pkg:other ..." makes a
|
||||
# simulated purge of pkg also remove other (a dependent apt-cache rdepends did not report).
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
args=" $* "
|
||||
D=$FAKE_DATA/apt
|
||||
|
||||
installed() { grep -q "^.i *"$'\t'"$1"$'\t' "$D/dpkg-status.tsv" && ! fake_purged "$1"; }
|
||||
orphans() { while IFS= read -r o; do installed "$o" && printf '%s\n' "$o"; done <"$D/autoremovable.txt"; }
|
||||
|
||||
# purge_set [--auto-remove] NAME...: the packages a purge removes, sorted
|
||||
purge_set() {
|
||||
local auto=0 n r line
|
||||
if [[ $1 == --auto-remove ]]; then
|
||||
auto=1
|
||||
shift
|
||||
fi
|
||||
{
|
||||
for n in "$@"; do
|
||||
installed "$n" || continue
|
||||
echo "$n"
|
||||
while IFS= read -r line; do
|
||||
[[ $line == "$n: "* ]] || continue
|
||||
for r in ${line#*: }; do installed "$r" && echo "$r"; done
|
||||
done <"$D/purge-rdeps.txt"
|
||||
if ((auto)); then
|
||||
for r in ${FAKE_APT_AUTO_ORPHANS:-}; do [[ ${r%%:*} == "$n" ]] && installed "${r#*:}" && echo "${r#*:}"; done
|
||||
fi
|
||||
for r in ${FAKE_APT_SIM_EXTRA:-}; do [[ ${r%%:*} == "$n" ]] && installed "${r#*:}" && echo "${r#*:}"; done
|
||||
done
|
||||
if ((auto)); then orphans; fi
|
||||
} | sort -u
|
||||
}
|
||||
|
||||
case $args in
|
||||
*' update '*)
|
||||
rc=$(fake_rc FAKE_APT_UPDATE_RC 0)
|
||||
if ((rc)); then
|
||||
echo "E: Failed to fetch http://deb.debian.org/debian/dists/bookworm/InRelease Temporary failure resolving 'deb.debian.org'" >&2
|
||||
echo "E: Some index files failed to download. They have been ignored, or old ones used instead." >&2
|
||||
fi
|
||||
exit "$rc"
|
||||
;;
|
||||
*' -s '*autoremove*)
|
||||
while IFS= read -r o; do [[ -n $o ]] && echo "Remv $o [1.0]"; done < <(orphans)
|
||||
exit 0
|
||||
;;
|
||||
*' -s '*purge*)
|
||||
names=()
|
||||
auto=()
|
||||
for a in "$@"; do
|
||||
case $a in --auto-remove) auto=(--auto-remove) ;; -* | purge) ;; *) names+=("$a") ;; esac
|
||||
done
|
||||
rc=$(fake_rc FAKE_APT_SIM_PURGE_RC 0)
|
||||
if ((rc)); then
|
||||
echo "E: Unable to correct problems, you have held broken packages." >&2
|
||||
exit "$rc"
|
||||
fi
|
||||
for n in "${names[@]}"; do
|
||||
if ! grep -q $'\t'"$n"$'\t' "$D/dpkg-status.tsv" && [[ $n != su-dummy-newdep ]]; then
|
||||
echo "E: Unable to locate package $n" >&2
|
||||
exit 100
|
||||
fi
|
||||
done
|
||||
while IFS= read -r p; do [[ -n $p ]] && echo "Purg $p [1.0]"; done < <(purge_set "${auto[@]}" "${names[@]}")
|
||||
exit 0
|
||||
;;
|
||||
*' -s '*' upgrade '*)
|
||||
f=$D/sim-${FAKE_APT_SIM:-fixture}.txt
|
||||
while IFS= read -r line; do
|
||||
name=
|
||||
case $line in Inst\ * | Conf\ *) name=${line#* }; name=${name%% *} ;; esac
|
||||
if [[ -n $name ]] && fake_purged "$name"; then continue; fi
|
||||
if [[ $line == ' '* && -f $FAKE_DIR/purged ]]; then
|
||||
while IFS= read -r p; do line=${line//$p/}; done <"$FAKE_DIR/purged"
|
||||
fi
|
||||
printf '%s\n' "$line"
|
||||
done <"$f"
|
||||
exit 0
|
||||
;;
|
||||
*' upgrade '*)
|
||||
echo "holds during upgrade: $(sort "$FAKE_DIR/holds" 2>/dev/null | tr '\n' ' ')" >>"$FAKE_LOG"
|
||||
# which of our lock files does the package manager have open (it must have none)?
|
||||
if [[ -d /proc/$$/fd ]]; then
|
||||
n=0
|
||||
for fd in "/proc/$$/fd"/*; do
|
||||
[[ $(readlink "$fd" 2>/dev/null) == *.lock ]] && n=$((n + 1))
|
||||
done
|
||||
echo "lock fds in the upgrade: $n" >>"$FAKE_LOG"
|
||||
fi
|
||||
if [[ -n ${FAKE_APT_UPGRADE_KILL:-} ]]; then
|
||||
kill -s "$FAKE_APT_UPGRADE_KILL" "$PPID"
|
||||
sleep 0.2
|
||||
fi
|
||||
if [[ -n ${FAKE_KILL_READER:-} ]]; then
|
||||
# close the applet's stdout: kill the process reading it, then keep writing
|
||||
pid=$(cat "$FAKE_DIR/reader.pid")
|
||||
kill -9 "$pid"
|
||||
while kill -0 "$pid" 2>/dev/null; do sleep 0.05; done
|
||||
fi
|
||||
echo "Reading package lists..." 2>/dev/null
|
||||
echo "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded." 2>/dev/null
|
||||
exit "$(fake_rc FAKE_APT_UPGRADE_RC 0)"
|
||||
;;
|
||||
*' purge '*)
|
||||
rc=$(fake_rc FAKE_APT_PURGE_RC 0)
|
||||
if ((rc == 0)); then
|
||||
for a in "$@"; do case $a in -* | purge) ;; *) fake_mark_purged "$a" ;; esac; done
|
||||
# FAKE_APT_PURGE_ALSO: packages that disappear although the plan did not name them
|
||||
read -r -a also <<<"${FAKE_APT_PURGE_ALSO:-}"
|
||||
((${#also[@]} == 0)) || fake_mark_purged "${also[@]}"
|
||||
else
|
||||
echo "E: Held packages were changed and -y was used without --allow-change-held-packages." >&2
|
||||
fi
|
||||
exit "$rc"
|
||||
;;
|
||||
esac
|
||||
echo "fake apt-get: unhandled: $*" >&2
|
||||
exit 99
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake apt-mark (unit tests): hold state in $FAKE_DIR/holds (one name per line).
|
||||
# FAKE_APT_MARK_LOCKED=N: the first N hold/unhold calls fail like dpkg's busy frontend lock.
|
||||
# FAKE_APT_MARK_UNHOLD_SIGNAL=SIG: send SIG to the applet before an unhold (a second Ctrl-C).
|
||||
# FAKE_APT_MARK_UNHOLD_RC=N: unhold fails (nothing released). FAKE_APT_MARK_FAIL_MSG: the message a
|
||||
# failing hold prints. Every unhold logs whether $STATE_DIR/holds.lock is held by someone.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
h=$FAKE_DIR/holds
|
||||
touch "$h"
|
||||
verb=$1
|
||||
shift
|
||||
case $verb in hold | unhold)
|
||||
if [[ -n ${FAKE_APT_MARK_LOCKED:-} ]]; then
|
||||
n=$(cat "$FAKE_DIR/locked-calls" 2>/dev/null || echo 0)
|
||||
if ((n < FAKE_APT_MARK_LOCKED)); then
|
||||
echo $((n + 1)) >"$FAKE_DIR/locked-calls"
|
||||
echo "${FAKE_APT_MARK_LOCK_MSG:-dpkg: error: dpkg frontend lock was locked by another process with pid 4242}"
|
||||
echo "E: Sub-process dpkg --set-selections returned an error code (2)"
|
||||
echo "E: Executing dpkg failed. Are you root?"
|
||||
exit 100
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
case $verb in
|
||||
showhold) sort "$h" ;;
|
||||
hold)
|
||||
for p in "$@"; do
|
||||
if grep -qxF -- "$p" "$h"; then echo "$p was already set on hold."; else
|
||||
echo "$p" >>"$h"
|
||||
echo "$p set on hold."
|
||||
fi
|
||||
done
|
||||
[[ -z ${FAKE_APT_MARK_FAIL_MSG:-} ]] || echo "$FAKE_APT_MARK_FAIL_MSG" >&2
|
||||
exit "$(fake_rc FAKE_APT_MARK_HOLD_RC 0)"
|
||||
;;
|
||||
unhold)
|
||||
if [[ -n ${SKEPTICAL_UPDATER_STATE_DIR:-} && -e $SKEPTICAL_UPDATER_STATE_DIR/holds.lock ]] && command -v flock >/dev/null; then
|
||||
if flock -n "$SKEPTICAL_UPDATER_STATE_DIR/holds.lock" true; then l=free; else l=held; fi
|
||||
echo "holds.lock during unhold: $l" >>"$FAKE_LOG"
|
||||
fi
|
||||
if [[ -n ${FAKE_APT_MARK_UNHOLD_RC:-} ]]; then
|
||||
echo "E: Unable to correct problems" >&2
|
||||
exit "$FAKE_APT_MARK_UNHOLD_RC"
|
||||
fi
|
||||
if [[ -n ${FAKE_APT_MARK_UNHOLD_SIGNAL:-} ]]; then
|
||||
kill -s "$FAKE_APT_MARK_UNHOLD_SIGNAL" "$(cat "$FAKE_DIR/applet.pid" 2>/dev/null || echo "$PPID")" 2>/dev/null
|
||||
sleep 0.2
|
||||
fi
|
||||
for p in "$@"; do
|
||||
if grep -qxF -- "$p" "$h"; then
|
||||
grep -vxF -- "$p" "$h" >"$h.new" || true
|
||||
mv "$h.new" "$h"
|
||||
echo "Canceled hold on $p."
|
||||
else echo "$p was already not on hold."; fi
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "fake apt-mark: unhandled: $verb $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake dpkg-query (unit tests): the dpkg database dump used for dependency closures
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
# FAKE_STATE_APPEND / FAKE_STATE_SED: simulate another process writing the statefile meanwhile
|
||||
st=${SKEPTICAL_UPDATER_STATE_DIR:-}/trusted.tsv
|
||||
if [[ -n ${FAKE_STATE_APPEND:-} ]]; then printf '%s\n' "$FAKE_STATE_APPEND" >>"$st"; fi
|
||||
if [[ -n ${FAKE_STATE_SED:-} ]]; then sed -i "$FAKE_STATE_SED" "$st"; fi
|
||||
case " $* " in
|
||||
*' -W '*) cat "$FAKE_DATA/apt/dpkg-status.tsv" ;;
|
||||
*)
|
||||
echo "fake dpkg-query: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake dnf (unit tests). FAKE_DNF_MAJOR=4|5 picks the output dialect; FAKE_DNF_CHECK_RC=0|1 simulates
|
||||
# "no updates" / an error; FAKE_DNF_CHECK=obsoletes replays a check-update with only an
|
||||
# "Obsoleting Packages" section; FAKE_DNF_REMOVE_REFUSE=1 makes `--assumeno remove` fail;
|
||||
# removal tables: see the --assumeno remove branch.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
# shellcheck source=../../data/rpm/db.bash
|
||||
. "$FAKE_DATA/rpm/db.bash"
|
||||
fake_log "$@"
|
||||
D=$FAKE_DATA/dnf
|
||||
major=${FAKE_DNF_MAJOR:-4}
|
||||
pos=()
|
||||
for a in "$@"; do case $a in -*) ;; *) pos+=("$a") ;; esac; done
|
||||
installed() { [[ -n ${EVR[$1]:-} ]] && ! fake_purged "$1"; }
|
||||
case " $* " in
|
||||
*' --version '*) if ((major == 5)); then echo "dnf5 version 5.4.3.0"; else echo "4.14.0"; fi ;;
|
||||
*' makecache '*)
|
||||
rc=$(fake_rc FAKE_DNF_MAKECACHE_RC 0)
|
||||
((rc == 0)) || echo "Error: Failed to download metadata for repo 'su-dummy': Cannot download repomd.xml" >&2
|
||||
exit "$rc"
|
||||
;;
|
||||
*' check-update '*)
|
||||
rc=$(fake_rc FAKE_DNF_CHECK_RC 100)
|
||||
f=$D/check-update-dnf$major.txt
|
||||
[[ ${FAKE_DNF_CHECK:-} != obsoletes ]] || f=$D/check-update-obsoletes.txt
|
||||
case $rc in
|
||||
100)
|
||||
while IFS= read -r line; do
|
||||
read -r na _ <<<"$line"
|
||||
[[ -n $na ]] && fake_purged "${na%.*}" && continue
|
||||
printf '%s\n' "$line"
|
||||
done <"$f"
|
||||
;;
|
||||
1) echo "Error: Failed to download metadata for repo 'baseos'" >&2 ;;
|
||||
esac
|
||||
exit "$rc"
|
||||
;;
|
||||
*' repoquery '*)
|
||||
p=${pos[-1]}
|
||||
case " $* " in
|
||||
*' --upgrades '*) case $p in su-dummy-[abc]) printf 'skeptical-updater dummy %s (v2.0)\n' "$p"; ((major == 5)) || echo ;; esac ;;
|
||||
*) case $p in su-dummy-new) printf 'skeptical-updater dummy su-dummy-new (v1.0)\n' ;; esac ;;
|
||||
esac
|
||||
exit 0
|
||||
;;
|
||||
*' info '*)
|
||||
p=${pos[-1]}
|
||||
if [[ -f $D/info-$p.txt ]]; then cat "$D/info-$p.txt"; else
|
||||
echo "Error: No matching Packages to list" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*' --assumeno '*remove*)
|
||||
# the removal table: the target, its dependents (su-dummy-a for su-dummy-b; FAKE_DNF_DEPENDENT=
|
||||
# "pkg:other"), its unused dependencies (FAKE_DNF_UNUSED="pkg:dep"); FAKE_DNF_WRAP wraps those rows
|
||||
p=${pos[-1]}
|
||||
if [[ -n ${FAKE_DNF_REMOVE_REFUSE:-} ]]; then
|
||||
echo "Error: Problem: The operation would result in removing the following protected packages: $p"
|
||||
exit 1
|
||||
fi
|
||||
if ! installed "$p"; then
|
||||
echo "No match for argument: $p"
|
||||
echo "No packages marked for removal."
|
||||
echo "Dependencies resolved."
|
||||
echo "Nothing to do."
|
||||
echo "Complete!"
|
||||
exit 0
|
||||
fi
|
||||
dependent=() unused=()
|
||||
[[ $p != su-dummy-b ]] || ! installed su-dummy-a || dependent+=(su-dummy-a)
|
||||
for r in ${FAKE_DNF_DEPENDENT:-}; do [[ ${r%%:*} == "$p" ]] && dependent+=("${r#*:}"); done
|
||||
for r in ${FAKE_DNF_UNUSED:-}; do [[ ${r%%:*} == "$p" ]] && unused+=("${r#*:}"); done
|
||||
row() { # row NAME
|
||||
if ((major == 5)); then
|
||||
if [[ -n ${FAKE_DNF_WRAP:-} && $1 != "$p" ]]; then
|
||||
echo " $1"
|
||||
echo " noarch 0:1.0-1 <unknown> 15.0 B"
|
||||
else
|
||||
echo " $1 noarch 0:1.0-1 <unknown> 15.0 B"
|
||||
fi
|
||||
elif [[ -n ${FAKE_DNF_WRAP:-} && $1 != "$p" ]]; then
|
||||
printf ' %s\n' "$1"
|
||||
printf ' noarch 1.0-1 @System 15\n'
|
||||
else
|
||||
printf ' %-20s noarch 1.0-1 @System 15\n' "$1"
|
||||
fi
|
||||
}
|
||||
if ((major == 5)); then
|
||||
echo "Package Arch Version Repository Size"
|
||||
else
|
||||
echo "Dependencies resolved."
|
||||
echo "================================================================================"
|
||||
echo " Package Architecture Version Repository Size"
|
||||
echo "================================================================================"
|
||||
fi
|
||||
echo "Removing:"
|
||||
row "$p"
|
||||
if ((${#dependent[@]})); then
|
||||
echo "Removing dependent packages:"
|
||||
for r in "${dependent[@]}"; do row "$r"; done
|
||||
fi
|
||||
if ((${#unused[@]})); then
|
||||
echo "Removing unused dependencies:"
|
||||
for r in "${unused[@]}"; do row "$r"; done
|
||||
fi
|
||||
echo
|
||||
n=$((1 + ${#dependent[@]} + ${#unused[@]}))
|
||||
if ((major == 5)); then
|
||||
echo "Transaction Summary:"
|
||||
echo " Removing: $n packages"
|
||||
echo "Operation aborted by the user."
|
||||
else
|
||||
echo "Transaction Summary"
|
||||
echo "================================================================================"
|
||||
echo "Remove $n Packages"
|
||||
echo
|
||||
echo "Operation aborted."
|
||||
fi
|
||||
exit 1
|
||||
;;
|
||||
*' upgrade '*) exit "$(fake_rc FAKE_DNF_UPGRADE_RC 0)" ;;
|
||||
*' remove '*)
|
||||
fake_mark_purged "${pos[@]:1}"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "fake dnf: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# tests/unit/fakes/fakelib.bash - shared by the fake package-manager commands.
|
||||
# Every fake logs its invocation ("<name> <args...>") to $FAKE_LOG and keeps its mutable
|
||||
# state (holds, purged packages, ...) under $FAKE_DIR. Replayed outputs live in
|
||||
# tests/unit/data/<backend>/ and are copied from the samples recorded in discovery/*.md.
|
||||
# shellcheck shell=bash
|
||||
|
||||
FAKE_NAME=${FAKE_AS:-${0##*/}}
|
||||
: "${FAKE_LOG:?FAKE_LOG not set}" "${FAKE_DIR:?FAKE_DIR not set}" "${FAKE_DATA:?FAKE_DATA not set}"
|
||||
|
||||
fake_log() { printf '%s %s\n' "$FAKE_NAME" "$*" >>"$FAKE_LOG"; }
|
||||
|
||||
# fake_rc VAR DEFAULT: exit code override from the environment (e.g. FAKE_APT_UPGRADE_RC=100)
|
||||
fake_rc() {
|
||||
local v=${!1:-}
|
||||
printf '%s' "${v:-$2}"
|
||||
}
|
||||
|
||||
# fake_purged NAME: was NAME removed by an earlier fake purge in this test?
|
||||
fake_purged() { [[ -f $FAKE_DIR/purged ]] && grep -qxF -- "$1" "$FAKE_DIR/purged"; }
|
||||
fake_mark_purged() { printf '%s\n' "$@" >>"$FAKE_DIR/purged"; }
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake pacman (unit tests). The "real" database is $FAKE_PACMAN_DBPATH (see pacman-conf);
|
||||
# -Sy --dbpath DIR logs what DIR/sync was seeded with and writes DIR/sync/su-dummy.db
|
||||
# ("reviewed <n>"; plus a .sig with FAKE_PACMAN_SIG=1); -Su logs the real su-dummy.db it applies.
|
||||
# FAKE_PACMAN_SUP_RC=1 makes the -Sup pre-check fail like the ignored-dependency trap;
|
||||
# FAKE_PACMAN_SU_KILL=SIG: -Su signals the applet (Ctrl-C during the upgrade);
|
||||
# FAKE_PACMAN_STDERR=TEXT: -Sl and -Qu also print TEXT on stderr (an unreadable database).
|
||||
# -Rs --print: FAKE_PACMAN_RS_REFUSE=1 makes pacman refuse ("breaks dependency");
|
||||
# FAKE_PACMAN_RS_EXTRA="pkg:other" adds other to the plan of pkg.
|
||||
# FAKE_PACMAN_QU=fixture|real|none selects the -Qu sample,
|
||||
# FAKE_PACMAN_SL=empty makes -Sl print nothing, FAKE_PACMAN_SUP_EXTRA="a b" adds packages that
|
||||
# only -Sup (the real transaction) shows. Every -Q*/-S[il] call records its COLUMNS value, every
|
||||
# call its --dbpath (or "real") in $FAKE_DIR/pacman-dbpath.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
D=$FAKE_DATA/pacman
|
||||
|
||||
# blocks FILE NAME...: print the blank-line separated blocks whose Name is one of NAME
|
||||
# (all blocks when no NAME); exit 1 (after printing the others) if a NAME is missing
|
||||
blocks() {
|
||||
local f=$1 line block='' name='' rc=0 n
|
||||
shift
|
||||
local -A want=() found=()
|
||||
for n in "$@"; do want[$n]=1; done
|
||||
while IFS= read -r line || [[ -n $block ]]; do
|
||||
if [[ -z $line ]]; then
|
||||
if [[ -n $block ]] && { (($# == 0)) || [[ -n ${want[$name]:-} ]]; } && ! fake_purged "$name"; then
|
||||
printf '%s\n' "$block"
|
||||
found[$name]=1
|
||||
fi
|
||||
block='' name=''
|
||||
continue
|
||||
fi
|
||||
[[ $line =~ ^Name\ +:\ (.*)$ ]] && name=${BASH_REMATCH[1]}
|
||||
block+=$line$'\n'
|
||||
done <"$f"
|
||||
for n in "$@"; do
|
||||
if [[ -z ${found[$n]:-} ]]; then
|
||||
echo "error: package '$n' was not found" >&2
|
||||
rc=1
|
||||
fi
|
||||
done
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
op='' dbpath=real print=0
|
||||
targets=()
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--dbpath)
|
||||
dbpath=$2
|
||||
shift
|
||||
;;
|
||||
--logfile | --print-format | --ignore) shift ;;
|
||||
--print) print=1 ;;
|
||||
--*) ;;
|
||||
-[A-Z]*) [[ -n $op ]] || op=$1 ;;
|
||||
-*) ;;
|
||||
*) targets+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
echo "$op $dbpath" >>"$FAKE_DIR/pacman-dbpath"
|
||||
case $op in -Q* | -Si | -Sl) echo "$op ${COLUMNS-unset}" >>"$FAKE_DIR/pacman-columns" ;; esac
|
||||
|
||||
qu() {
|
||||
local f=$D/Qu-${FAKE_PACMAN_QU:-fixture}.txt line
|
||||
[[ -f $f ]] || return 0
|
||||
while IFS= read -r line; do
|
||||
fake_purged "${line%% *}" || printf '%s\n' "$line"
|
||||
done <"$f"
|
||||
}
|
||||
|
||||
case $op in
|
||||
-Sy)
|
||||
if [[ $dbpath != real ]]; then
|
||||
echo "seeded: $(cd "$dbpath/sync" && echo *)" >>"$FAKE_LOG"
|
||||
rc=$(fake_rc FAKE_PACMAN_SY_RC 0)
|
||||
((rc)) && exit "$rc"
|
||||
n=$(($(cat "$FAKE_DIR/sy-count" 2>/dev/null || echo 0) + 1))
|
||||
echo "$n" >"$FAKE_DIR/sy-count"
|
||||
echo "reviewed $n" >"$dbpath/sync/su-dummy.db"
|
||||
[[ -z ${FAKE_PACMAN_SIG:-} ]] || echo "signature $n" >"$dbpath/sync/su-dummy.db.sig"
|
||||
fi
|
||||
exit "$(fake_rc FAKE_PACMAN_SY_RC 0)"
|
||||
;;
|
||||
-Qu)
|
||||
[[ -z ${FAKE_PACMAN_STDERR:-} ]] || echo "$FAKE_PACMAN_STDERR" >&2
|
||||
out=$(qu)
|
||||
[[ -n $out ]] || exit 1
|
||||
printf '%s\n' "$out"
|
||||
;;
|
||||
-Sup)
|
||||
if [[ -n ${FAKE_PACMAN_SUP_RC:-} ]]; then
|
||||
echo ":: unable to satisfy dependency 'su-dummy-b>=2.0' required by su-dummy-a"
|
||||
echo "error: failed to prepare transaction (could not satisfy dependencies)" >&2
|
||||
exit "$FAKE_PACMAN_SUP_RC"
|
||||
fi
|
||||
qu | while read -r n _ _ _ flag; do [[ $flag == '[ignored]' ]] || echo "$n"; done
|
||||
for n in ${FAKE_PACMAN_SUP_EXTRA:-}; do echo "$n"; done
|
||||
;;
|
||||
-Sl)
|
||||
[[ -z ${FAKE_PACMAN_STDERR:-} ]] || echo "$FAKE_PACMAN_STDERR" >&2
|
||||
[[ ${FAKE_PACMAN_SL:-} == empty ]] || cat "$D/Sl.txt"
|
||||
;;
|
||||
-Qi) blocks "$D/Qi.txt" "${targets[@]}" ;;
|
||||
-Si)
|
||||
t=()
|
||||
for a in "${targets[@]}"; do t+=("${a#*/}"); done
|
||||
blocks "$D/Si.txt" "${t[@]}"
|
||||
;;
|
||||
-Su | -Syu)
|
||||
echo "real sync at $op: $(cat "$FAKE_PACMAN_DBPATH/sync/su-dummy.db" 2>/dev/null || echo none) / $(cat "$FAKE_PACMAN_DBPATH/sync/su-dummy.db.sig" 2>/dev/null || echo no-sig)" >>"$FAKE_LOG"
|
||||
if [[ -e $FAKE_PACMAN_DBPATH/db.lck ]]; then echo "db.lck at $op: yes" >>"$FAKE_LOG"; else echo "db.lck at $op: no" >>"$FAKE_LOG"; fi
|
||||
if [[ -n ${FAKE_PACMAN_SU_KILL:-} ]]; then
|
||||
kill -s "$FAKE_PACMAN_SU_KILL" "$PPID"
|
||||
sleep 0.2
|
||||
fi
|
||||
exit "$(fake_rc FAKE_PACMAN_SU_RC 0)"
|
||||
;;
|
||||
-Rs)
|
||||
((print)) || exit 98
|
||||
tg=${targets[0]}
|
||||
if [[ -n ${FAKE_PACMAN_RS_REFUSE:-} || $tg == su-dummy-b ]]; then
|
||||
echo "checking dependencies..."
|
||||
echo ":: removing $tg breaks dependency '$tg' required by su-dummy-a"
|
||||
echo "error: failed to prepare transaction (could not satisfy dependencies)" >&2
|
||||
exit 1
|
||||
fi
|
||||
case $tg in
|
||||
su-dummy-a) printf '%s\n' su-dummy-a su-dummy-b ;; # b was installed as a dependency of a
|
||||
su-dummy-c | glib2 | expat | ca-certificates-mozilla) echo "$tg" ;;
|
||||
*)
|
||||
echo "error: target not found: $tg" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
for r in ${FAKE_PACMAN_RS_EXTRA:-}; do [[ ${r%%:*} == "$tg" ]] && echo "${r#*:}"; done
|
||||
;;
|
||||
-Rns)
|
||||
rc=$(fake_rc FAKE_PACMAN_RNS_RC 0)
|
||||
if ((rc)); then echo "error: failed to prepare transaction (could not satisfy dependencies)" >&2; else fake_mark_purged "${targets[@]}"; fi
|
||||
exit "$rc"
|
||||
;;
|
||||
*)
|
||||
echo "fake pacman: unhandled: $op $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake pacman-conf (unit tests): the "real" pacman database lives in $FAKE_PACMAN_DBPATH
|
||||
case ${1:-} in
|
||||
DBPath) printf '%s/\n' "${FAKE_PACMAN_DBPATH:?}" ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake pman (unit tests): only its presence matters (label "pman")
|
||||
exit 0
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake rpm (unit tests): answers the queries skeptical-updater makes from data/rpm/db.bash
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
# shellcheck source=../../data/rpm/db.bash
|
||||
. "$FAKE_DATA/rpm/db.bash"
|
||||
fake_log "$@"
|
||||
|
||||
installed() { [[ -n ${EVR[$1]:-} ]] && ! fake_purged "$1"; }
|
||||
all=0 whatprov=0 list=0 qf=''
|
||||
names=()
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
-q) ;;
|
||||
-qa) all=1 ;;
|
||||
-ql) list=1 ;;
|
||||
--whatprovides) whatprov=1 ;;
|
||||
--qf | --queryformat)
|
||||
qf=$2
|
||||
shift
|
||||
;;
|
||||
*) names+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
rc=0
|
||||
if ((all)); then
|
||||
for p in "${!EVR[@]}"; do
|
||||
installed "$p" || continue
|
||||
case $qf in
|
||||
*'%{NAME}.%{ARCH}'*)
|
||||
for v in ${EVR[$p]}; do
|
||||
printf '%s.%s\t%s\n' "$p" "${ARCH[$p]}" "$v"
|
||||
done
|
||||
;;
|
||||
*'%{REQUIRENAME}'*'%{=NAME}'*)
|
||||
while IFS= read -r r; do [[ -n $r ]] && printf '%s\t%s\n' "$r" "$p"; done <<<"${REQUIRES[$p]:-}"
|
||||
;;
|
||||
*) printf '%s\n' "$p" ;;
|
||||
esac
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
if ((whatprov)); then
|
||||
for cap in "${names[@]}"; do
|
||||
hit=0
|
||||
for p in "${!EVR[@]}"; do
|
||||
installed "$p" || continue
|
||||
if grep -qxF -- "$cap" <<<"${PROVIDES[$p]:-}"$'\n'"${FILES[$p]:-}"; then
|
||||
printf '%s\n' "$p"
|
||||
hit=1
|
||||
fi
|
||||
done
|
||||
if ((!hit)); then
|
||||
echo "no package provides $cap"
|
||||
rc=1
|
||||
fi
|
||||
done
|
||||
exit "$rc"
|
||||
fi
|
||||
for p in "${names[@]}"; do
|
||||
if ! installed "$p"; then
|
||||
echo "package $p is not installed"
|
||||
rc=1
|
||||
continue
|
||||
fi
|
||||
if ((list)); then
|
||||
if [[ -n ${FILES[$p]:-} ]]; then printf '%s\n' "${FILES[$p]}"; else echo "(contains no files)"; fi
|
||||
continue
|
||||
fi
|
||||
case $qf in
|
||||
*'%{REQUIRENAME}'*) [[ -z ${REQUIRES[$p]:-} ]] || printf '%s\n' "${REQUIRES[$p]}" ;;
|
||||
*'%{PROVIDENAME}'*) [[ -z ${PROVIDES[$p]:-} ]] || printf '%s\n' "${PROVIDES[$p]}" ;;
|
||||
*'%{SUMMARY}'*) printf '%s\n' "${SUMMARY[$p]:-dummy summary of $p}" ;;
|
||||
*) printf '%s-%s.%s\n' "$p" "${EVR[$p]##* }" "${ARCH[$p]}" ;;
|
||||
esac
|
||||
done
|
||||
exit "$rc"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake sudo (unit tests): logs the command it was asked to run, runs nothing
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
exit 0
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake yum (unit tests): yum is the dnf binary on EL9+
|
||||
FAKE_AS=yum exec "${BASH_SOURCE[0]%/*}/../dnf/dnf" "$@"
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake zypper (unit tests). FAKE_ZYPPER_LU=fixture|real|none picks the list-updates XML,
|
||||
# FAKE_ZYPPER_DUP=fixture|empty|removes the `dist-upgrade --dry-run` plan (data/zypper/dup-*.xml).
|
||||
# addlock writes real zypp lock entries into the locksfile.path of $ZYPP_CONF (default
|
||||
# /etc/zypp/locks is never touched: the fake refuses); update/dist-upgrade log that file.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "${ZYPP_CONF:+ZYPP_CONF=$ZYPP_CONF }$*"
|
||||
D=$FAKE_DATA/zypper
|
||||
|
||||
locksfile() {
|
||||
local f=${ZYPP_CONF:-} line l=
|
||||
[[ -n $f && -r $f ]] || return 1
|
||||
while IFS= read -r line; do
|
||||
[[ $line =~ ^[[:space:]]*locksfile\.path[[:space:]]*=[[:space:]]*(.*)$ ]] && l=${BASH_REMATCH[1]}
|
||||
done <"$f"
|
||||
[[ -n $l ]] || return 1
|
||||
printf '%s' "$l"
|
||||
}
|
||||
|
||||
pos=()
|
||||
for a in "$@"; do case $a in -*) ;; *) pos+=("$a") ;; esac; done
|
||||
verb=${pos[0]:-}
|
||||
args=("${pos[@]:1}")
|
||||
xml_remove() { # the install-summary of `remove --dry-run` for the given names
|
||||
local n
|
||||
printf "<?xml version='1.0'?>\n<stream>\n<install-summary download-size=\"0\" space-usage-diff=\"-30\" packages-to-change=\"%s\">\n<to-remove>\n" "$#"
|
||||
for n in "$@"; do printf '<solvable type="package" name="%s" edition="1.0-1" arch="noarch" repository="@System" summary="dummy %s"><description>d</description></solvable>\n' "$n" "$n"; done
|
||||
printf '</to-remove>\n</install-summary>\n</stream>\n'
|
||||
}
|
||||
dry=0
|
||||
case " $* " in *' --dry-run '*) dry=1 ;; esac
|
||||
|
||||
case $verb in
|
||||
refresh)
|
||||
rc=$(fake_rc FAKE_ZYPPER_REFRESH_RC 0)
|
||||
((rc == 0)) || echo "Could not refresh the repositories because of errors." >&2
|
||||
exit "$rc"
|
||||
;;
|
||||
list-updates) cat "$D/lu-${FAKE_ZYPPER_LU:-fixture}.xml" ;;
|
||||
info)
|
||||
p=${args[-1]}
|
||||
if [[ -f $D/info-$p.txt ]]; then cat "$D/info-$p.txt"; else
|
||||
echo "package '$p' not found."
|
||||
exit 104
|
||||
fi
|
||||
;;
|
||||
addlock)
|
||||
l=$(locksfile) || {
|
||||
echo "fake zypper: addlock without a private ZYPP_CONF/locksfile.path" >&2
|
||||
exit 98
|
||||
}
|
||||
for p in "${args[@]}"; do printf '\ntype: package\nmatch_type: glob\ncase_sensitive: on\nsolvable_name: %s\n\n' "$p" >>"$l"; done
|
||||
;;
|
||||
dist-upgrade | dup | update)
|
||||
if ((dry)); then
|
||||
cat "$D/dup-${FAKE_ZYPPER_DUP:-fixture}.xml"
|
||||
exit 0
|
||||
fi
|
||||
if l=$(locksfile); then { echo "--- locks seen by $verb:"; cat "$l"; echo "--- end"; } >>"$FAKE_LOG"; fi
|
||||
if [[ -n ${ZYPP_CONF:-} && -n ${SKEPTICAL_UPDATER_STATE_DIR:-} && -e $SKEPTICAL_UPDATER_STATE_DIR/zypper.lock ]]; then
|
||||
if flock -n "$SKEPTICAL_UPDATER_STATE_DIR/zypper.lock" true; then l=free; else l=held; fi
|
||||
echo "zypper.lock during $verb: $l" >>"$FAKE_LOG"
|
||||
fi
|
||||
if [[ -n ${FAKE_ZYPPER_UPGRADE_KILL:-} ]]; then
|
||||
kill -s "$FAKE_ZYPPER_UPGRADE_KILL" "$PPID"
|
||||
sleep 0.2
|
||||
fi
|
||||
exit "$(fake_rc FAKE_ZYPPER_UPGRADE_RC 0)"
|
||||
;;
|
||||
remove)
|
||||
if ((dry)); then
|
||||
set -- "${args[@]}"
|
||||
# FAKE_ZYPPER_CLEAN="pkg:dep" (unneeded dependency) / FAKE_ZYPPER_EXTRA="pkg:other" join the plan
|
||||
more=()
|
||||
for r in ${FAKE_ZYPPER_CLEAN:-} ${FAKE_ZYPPER_EXTRA:-}; do [[ ${r%%:*} == "${1:-}" ]] && more+=("${r#*:}"); done
|
||||
case ${1:-} in
|
||||
su-dummy-b) xml_remove su-dummy-a su-dummy-b "${more[@]}" ;;
|
||||
su-dummy-*) xml_remove "$1" "${more[@]}" ;;
|
||||
*)
|
||||
echo "'$1' not found in package names. Trying capabilities."
|
||||
echo "No provider of '$1' found."
|
||||
exit 104
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
for p in "${args[@]}"; do
|
||||
case $p in su-dummy-*) ;; *)
|
||||
echo "'$p' not found in package names. Trying capabilities."
|
||||
echo "No provider of '$p' found."
|
||||
exit 104
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fake_mark_purged "${args[@]}"
|
||||
;;
|
||||
*)
|
||||
echo "fake zypper: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Reference in new issue
Block a user