feat(info): show the install reason and drop the indentation
N? and `info PKG` now print three lines, none indented:
PKG: <description>
reason: explicitly installed | installed as a dependency | unknown | not installed
required by: <installed dependents, comma separated> | nothing
The reason comes from a new <be>_reason backend function: apt-mark
showauto / showmanual (dpkg decides for a package in neither list), the
Install Reason of pacman -Qi, dnf repoquery --installed %{reason} (dnf4
and dnf5 words) and the Installed line of zypper info (rpm plus libzypp's
AutoInstalled when zypper is locked). A row whose old version is "-" is
not installed and the backend is not asked. The "(installed)" qualifier
moves to --help, README and DESIGN.
The rpm E2E fixture and e2e_reset now also mark su-dummy-a and su-dummy-c
as user-installed on dnf, which records plain rpm installs as "unknown",
so 1? and 2? show both reasons on all 12 distros. The verified commands
are recorded in discovery/{apt,pacman,rpm}.md.
This commit is contained in:
1 parent
1354db6c39
commit
71d7f87aa3
21 files changed
+589
-48
No files matched your search
@@ -0,0 +1,6 @@
|
||||
gcc-12-base
|
||||
libc6
|
||||
libgcc-s1
|
||||
mawk
|
||||
su-dummy-b
|
||||
su-dummy-orphan
|
||||
@@ -51,6 +51,13 @@ declare -A PROVIDES=(
|
||||
[ncurses-libs]=$'ncurses-libs\nlibtinfo.so.6()(64bit)'
|
||||
[redhat-rpm-config]=redhat-rpm-config [rpm]=rpm
|
||||
)
|
||||
# what dnf4 records as the install reason (`repoquery --installed --qf %{reason}`; the fake dnf
|
||||
# speaks dnf5's words with FAKE_DNF_MAJOR=5): plain `rpm -i` leaves "unknown"
|
||||
declare -A REASON=(
|
||||
[su-dummy-a]=user [su-dummy-b]=dependency [su-dummy-c]=unknown [su-dummy-d]=user
|
||||
[bash]=user [glibc]=dependency [glibc-common]=dependency [glibc-gconv-extra]=weak-dependency
|
||||
[basesystem]=group [setup]=dependency [filesystem]=dependency
|
||||
)
|
||||
# FAKE_RPM_NO_D=1: su-dummy-d (which requires every dummy) is not installed
|
||||
declare -A FILES=(
|
||||
[su-dummy-a]=/usr/share/su-dummy/su-dummy-a [su-dummy-b]=/usr/share/su-dummy/su-dummy-b
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
Information for package su-dummy-b:
|
||||
-----------------------------------
|
||||
Repository : su-dummy
|
||||
Name : su-dummy-b
|
||||
Version : 2.0-1
|
||||
Arch : noarch
|
||||
Vendor :
|
||||
Installed Size : 15 B
|
||||
Installed : Yes (automatically)
|
||||
Status : out-of-date (version 1.0-1 installed)
|
||||
Source package : su-dummy-b-2.0-1.src
|
||||
Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
Description :
|
||||
Dummy package su-dummy-b version 2.0 for skeptical-updater tests.
|
||||
|
||||
Second paragraph of su-dummy-b 2.0 (blank line above on purpose).
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
Information for package su-dummy-orphan-parent:
|
||||
-----------------------------------------------
|
||||
Repository : su-dummy
|
||||
Name : su-dummy-orphan-parent
|
||||
Version : 1.0-1
|
||||
Arch : noarch
|
||||
Vendor :
|
||||
Installed Size : 27 B
|
||||
Installed : No
|
||||
Status : not installed
|
||||
Source package : su-dummy-orphan-parent-1.0-1.src
|
||||
Summary : skeptical-updater dummy su-dummy-orphan-parent (v1.0)
|
||||
Description :
|
||||
Dummy package su-dummy-orphan-parent version 1.0 for skeptical-updater tests.
|
||||
|
||||
Second paragraph of su-dummy-orphan-parent 1.0 (blank line above on purpose).
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
# 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.
|
||||
# showauto/showmanual: installed = the "?i" rows of data/apt/dpkg-status.tsv (not purged), auto = those
|
||||
# listed in data/apt/auto.txt, manual = the others; FAKE_APT_MARK_NEITHER="pkg ..." are in neither
|
||||
# list, FAKE_APT_MARK_SHOW_RC=N makes both fail. Like the real ones they print only the named
|
||||
# packages (all of them without a name), native packages without their ":arch" qualifier.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
@@ -27,8 +31,30 @@ case $verb in hold | unhold)
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
# show_marked auto|manual NAME...
|
||||
show_marked() {
|
||||
local want=$1 n st name mark
|
||||
shift
|
||||
local -A pick=()
|
||||
for n in "$@"; do pick[${n%:all}]=1; done
|
||||
if [[ -n ${FAKE_APT_MARK_SHOW_RC:-} ]]; then
|
||||
echo "E: Unable to determine the package states" >&2
|
||||
exit "$FAKE_APT_MARK_SHOW_RC"
|
||||
fi
|
||||
while IFS=$'\t' read -r st name _; do
|
||||
[[ $st == ?i* ]] || continue
|
||||
fake_purged "$name" && continue
|
||||
[[ " ${FAKE_APT_MARK_NEITHER:-} " == *" $name "* ]] && continue
|
||||
if grep -qxF -- "$name" "$FAKE_DATA/apt/auto.txt"; then mark=auto; else mark=manual; fi
|
||||
[[ $mark == "$want" ]] || continue
|
||||
if (($# == 0)) || [[ -n ${pick[$name]:-} || -n ${pick[$name:amd64]:-} ]]; then printf '%s\n' "$name"; fi
|
||||
done <"$FAKE_DATA/apt/dpkg-status.tsv" | sort
|
||||
}
|
||||
|
||||
case $verb in
|
||||
showhold) sort "$h" ;;
|
||||
showauto) show_marked auto "$@" ;;
|
||||
showmanual) show_marked manual "$@" ;;
|
||||
hold)
|
||||
for p in "$@"; do
|
||||
if grep -qxF -- "$p" "$h"; then echo "$p was already set on hold."; else
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# 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
|
||||
# fake dpkg-query (unit tests): the dpkg database dump used for dependency closures, and
|
||||
# `-W -f '${db:Status-Abbrev}' NAME` (the status of one package; purged = unknown to dpkg)
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
@@ -10,7 +11,30 @@ fake_log "$@"
|
||||
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
|
||||
args=" $* "
|
||||
names=()
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
-f) shift ;;
|
||||
-*) ;;
|
||||
*) names+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
if ((${#names[@]})); then
|
||||
rc=0
|
||||
for n in "${names[@]}"; do
|
||||
st=$(awk -F'\t' -v n="${n%:amd64}" '$2 == n { print $1; exit }' "$FAKE_DATA/apt/dpkg-status.tsv")
|
||||
if [[ -z $st ]] || fake_purged "${n%:amd64}"; then
|
||||
echo "dpkg-query: no packages found matching $n" >&2
|
||||
rc=1
|
||||
continue
|
||||
fi
|
||||
printf '%s' "$st"
|
||||
done
|
||||
exit "$rc"
|
||||
fi
|
||||
case $args in
|
||||
*' -W '*) cat "$FAKE_DATA/apt/dpkg-status.tsv" ;;
|
||||
*)
|
||||
echo "fake dpkg-query: unhandled: $*" >&2
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
# 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.
|
||||
# removal tables: see the --assumeno remove branch. `repoquery --installed --qf %{reason}`: REASON of
|
||||
# data/rpm/db.bash (dnf5's words with FAKE_DNF_MAJOR=5), FAKE_DNF_REASON="pkg=raw value" overrides
|
||||
# it, FAKE_DNF_REPOQUERY_RC=N makes repoquery fail.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
# shellcheck source=../../data/rpm/db.bash
|
||||
@@ -41,7 +43,28 @@ case " $* " in
|
||||
;;
|
||||
*' repoquery '*)
|
||||
p=${pos[-1]}
|
||||
rc=$(fake_rc FAKE_DNF_REPOQUERY_RC 0)
|
||||
if ((rc)); then
|
||||
echo "Error: rpmdb open failed" >&2
|
||||
exit "$rc"
|
||||
fi
|
||||
case " $* " in
|
||||
*' --installed '*'%{reason}'*)
|
||||
installed "$p" || exit 0
|
||||
r=${REASON[$p]:-unknown}
|
||||
if ((major == 5)); then
|
||||
case $r in
|
||||
user) r=User ;;
|
||||
dependency) r=Dependency ;;
|
||||
weak-dependency) r='Weak Dependency' ;;
|
||||
group) r=Group ;;
|
||||
unknown) r='External User' ;;
|
||||
esac
|
||||
fi
|
||||
[[ ${FAKE_DNF_REASON:-} != "$p="* ]] || r=${FAKE_DNF_REASON#*=}
|
||||
printf '%s\n' "$r"
|
||||
((major == 5)) || echo
|
||||
;;
|
||||
*' --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
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# 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_REASON="pkg=text" replaces the "Install Reason" of pkg in -Qi (empty text: no such line).
|
||||
# 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
|
||||
@@ -107,7 +108,19 @@ case $op in
|
||||
[[ -z ${FAKE_PACMAN_STDERR:-} ]] || echo "$FAKE_PACMAN_STDERR" >&2
|
||||
[[ ${FAKE_PACMAN_SL:-} == empty ]] || cat "$D/Sl.txt"
|
||||
;;
|
||||
-Qi) blocks "$D/Qi.txt" "${targets[@]}" ;;
|
||||
-Qi)
|
||||
if [[ -z ${FAKE_PACMAN_REASON:-} ]]; then
|
||||
blocks "$D/Qi.txt" "${targets[@]}"
|
||||
exit
|
||||
fi
|
||||
rc=0
|
||||
out=$(blocks "$D/Qi.txt" "${targets[@]}") || rc=$?
|
||||
awk -v n="${FAKE_PACMAN_REASON%%=*}" -v r="${FAKE_PACMAN_REASON#*=}" '
|
||||
/^Name +: / { cur = $0; sub(/^Name +: /, "", cur) }
|
||||
/^Install Reason +: / && cur == n { if (r != "") print "Install Reason : " r; next }
|
||||
{ print }' <<<"$out"
|
||||
exit "$rc"
|
||||
;;
|
||||
-Si)
|
||||
t=()
|
||||
for a in "${targets[@]}"; do t+=("${a#*/}"); done
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
# 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.
|
||||
# info replays data/zypper/info-NAME.txt (exit 104 without one); FAKE_ZYPPER_INFO_RC=7 makes it fail
|
||||
# like a zypp lock held by another process.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "${ZYPP_CONF:+ZYPP_CONF=$ZYPP_CONF }$*"
|
||||
@@ -43,6 +45,11 @@ refresh)
|
||||
list-updates) cat "$D/lu-${FAKE_ZYPPER_LU:-fixture}.xml" ;;
|
||||
info)
|
||||
p=${args[-1]}
|
||||
if [[ -n ${FAKE_ZYPPER_INFO_RC:-} ]]; then
|
||||
echo "System management is locked by the application with pid 4242 (zypper)."
|
||||
echo "Close this application before trying again."
|
||||
exit "$FAKE_ZYPPER_INFO_RC"
|
||||
fi
|
||||
if [[ -f $D/info-$p.txt ]]; then cat "$D/info-$p.txt"; else
|
||||
echo "package '$p' not found."
|
||||
exit 104
|
||||
|
||||
@@ -64,11 +64,30 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc
|
||||
assert_line 'excluded from this run: su-dummy-b'
|
||||
}
|
||||
|
||||
@test "N? prints the description and the installed packages that depend on it" {
|
||||
run skup --no-refresh <<<$'2?\nq'
|
||||
@test "N? prints the description, the install reason and the installed packages that depend on it" {
|
||||
run skup --no-refresh <<<$'1?\n2?\nq'
|
||||
assert_success
|
||||
assert_line 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests'
|
||||
assert_line ' required by (installed): su-dummy-a'
|
||||
assert_output --partial "$(printf '%s\n' '> 1?' \
|
||||
'su-dummy-a: dummy package su-dummy-a for skeptical-updater tests' \
|
||||
'reason: explicitly installed' \
|
||||
'required by: nothing' \
|
||||
'> 2?' \
|
||||
'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests' \
|
||||
'reason: installed as a dependency' \
|
||||
'required by: su-dummy-a')"
|
||||
refute_output --partial 'required by (installed)'
|
||||
}
|
||||
|
||||
@test "N? on a new dependency (old version -) says 'not installed' without asking the package manager" {
|
||||
FAKE_APT_SIM=real run skup --no-refresh <<<$'9?\nq'
|
||||
assert_success
|
||||
assert_line --regexp '^ \[9\] su-dummy-newdep +- +-> +1\.0 +su-fixture:su-repo$'
|
||||
assert_output --partial "$(printf '%s\n' '> 9?' \
|
||||
'su-dummy-newdep: (no description available: unknown package?)' \
|
||||
'reason: not installed' \
|
||||
'required by: nothing')"
|
||||
log_lacks '^apt-mark show'
|
||||
log_lacks '^dpkg-query -W -f \$\{db:Status-Abbrev\} '
|
||||
}
|
||||
|
||||
@test "N?? prints the native full info" {
|
||||
|
||||
+156
-10
@@ -50,13 +50,43 @@ setup() { common_setup; }
|
||||
@test "apt: description = candidate Description; reverse deps deduplicated, '|' stripped" {
|
||||
run skup info su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests' ' required by (installed): su-dummy-a')"
|
||||
assert_output "$(printf '%s\n' 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests' 'reason: installed as a dependency' 'required by: su-dummy-a')"
|
||||
log_has 'apt-cache show --no-all-versions su-dummy-b'
|
||||
log_has 'apt-cache rdepends --installed --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances su-dummy-b'
|
||||
run skup info gpgv
|
||||
assert_success
|
||||
assert_line 'gpgv: GNU privacy guard - signature verification tool'
|
||||
assert_line ' required by (installed): apt'
|
||||
assert_line 'required by: apt'
|
||||
}
|
||||
|
||||
@test "apt: install reason from apt-mark showauto / showmanual, dpkg for a package in neither list" {
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-a: dummy package su-dummy-a for skeptical-updater tests' 'reason: explicitly installed' 'required by: nothing')"
|
||||
log_has 'apt-mark showauto su-dummy-a'
|
||||
log_has 'apt-mark showmanual su-dummy-a'
|
||||
log_lacks '^dpkg-query -W -f \$\{db:Status-Abbrev\} '
|
||||
run skup info su-dummy-b
|
||||
assert_line 'reason: installed as a dependency'
|
||||
# apt-mark prints a native package without the ":arch" it was asked for
|
||||
run skup info libc6:amd64
|
||||
assert_line 'reason: installed as a dependency'
|
||||
# installed but in neither list: manual
|
||||
FAKE_APT_MARK_NEITHER=su-dummy-c run skup info su-dummy-c
|
||||
assert_success
|
||||
assert_line 'reason: explicitly installed'
|
||||
# shellcheck disable=SC2016 # dpkg-query's own ${...} format, literally
|
||||
log_has 'dpkg-query -W -f ${db:Status-Abbrev} su-dummy-c'
|
||||
# removed with its configuration files kept (rc), or unknown to dpkg: not installed
|
||||
run skup info su-dummy-f
|
||||
assert_line 'reason: not installed'
|
||||
run skup info gpgv
|
||||
assert_success
|
||||
assert_line 'reason: not installed'
|
||||
# apt-mark cannot tell
|
||||
FAKE_APT_MARK_SHOW_RC=100 run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_line 'reason: unknown'
|
||||
}
|
||||
|
||||
@test "apt: the description also comes from Description-<lang> fields (Translation files)" {
|
||||
@@ -141,7 +171,7 @@ setup() { common_setup; }
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_line 'su-dummy-a: Skeptical-updater dummy package A'
|
||||
assert_line ' required by (installed): nothing'
|
||||
assert_line 'required by: nothing'
|
||||
run skup info su-dummy-new
|
||||
assert_success
|
||||
assert_line 'su-dummy-new: Skeptical-updater dummy package NEW'
|
||||
@@ -152,9 +182,32 @@ setup() { common_setup; }
|
||||
run skup info glibc
|
||||
assert_success
|
||||
assert_line 'glibc: GNU C Library'
|
||||
assert_line ' required by (installed): bash, gcc-libs, ncurses, readline'
|
||||
assert_line 'required by: bash, gcc-libs, ncurses, readline'
|
||||
run skup info su-dummy-b
|
||||
assert_line ' required by (installed): su-dummy-a'
|
||||
assert_line 'required by: su-dummy-a'
|
||||
}
|
||||
|
||||
@test "pacman: install reason from the 'Install Reason' of -Qi; -Qi failing = not installed" {
|
||||
use_fakes pacman
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-a: Skeptical-updater dummy package A' 'reason: explicitly installed' 'required by: nothing')"
|
||||
run skup info su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-b: Skeptical-updater dummy package B (dependency of A)' 'reason: installed as a dependency' 'required by: su-dummy-a')"
|
||||
run skup info glibc
|
||||
assert_line 'reason: installed as a dependency'
|
||||
# in a sync database only
|
||||
run skup info su-dummy-new
|
||||
assert_success
|
||||
assert_line 'reason: not installed'
|
||||
# a value this tool does not know, or no such field
|
||||
FAKE_PACMAN_REASON='su-dummy-c=Installed for a reason pacman 9 invented' run skup info su-dummy-c
|
||||
assert_success
|
||||
assert_line 'reason: unknown'
|
||||
FAKE_PACMAN_REASON='su-dummy-c=' run skup info su-dummy-c
|
||||
assert_line 'reason: unknown'
|
||||
log_has 'pacman -Qi -- su-dummy-c'
|
||||
}
|
||||
|
||||
@test "pacman: the closure resolves provides (sonames, sh) and version constraints" {
|
||||
@@ -249,7 +302,7 @@ setup() { common_setup; }
|
||||
run skup info su-dummy-d
|
||||
assert_success
|
||||
assert_line 'su-dummy-d: skeptical-updater dummy su-dummy-d (v1.0)'
|
||||
assert_line ' required by (installed): nothing'
|
||||
assert_line 'required by: nothing'
|
||||
FAKE_DNF_MAJOR=5 run skup info su-dummy-a
|
||||
assert_line 'su-dummy-a: skeptical-updater dummy su-dummy-a (v2.0)'
|
||||
}
|
||||
@@ -257,11 +310,55 @@ setup() { common_setup; }
|
||||
@test "rpm reverse deps: provides + owned files joined with every requirement (rich deps too)" {
|
||||
use_fakes dnf rpm
|
||||
run skup info su-dummy-b
|
||||
assert_line ' required by (installed): su-dummy-a, su-dummy-d'
|
||||
assert_line 'required by: su-dummy-a, su-dummy-d'
|
||||
run skup info su-dummy-c
|
||||
assert_line ' required by (installed): su-dummy-d'
|
||||
assert_line 'required by: su-dummy-d'
|
||||
run skup info bash
|
||||
assert_line ' required by (installed): filesystem, glibc-common, redhat-rpm-config, rpm, su-dummy-d'
|
||||
assert_line 'required by: filesystem, glibc-common, redhat-rpm-config, rpm, su-dummy-d'
|
||||
}
|
||||
|
||||
@test "dnf4: install reason from repoquery --installed %{reason}; plain rpm -i = unknown" {
|
||||
use_fakes dnf rpm
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-a: skeptical-updater dummy su-dummy-a (v2.0)' 'reason: explicitly installed' 'required by: su-dummy-d')"
|
||||
log_has 'dnf -q repoquery --installed --queryformat %{reason}'
|
||||
run skup info su-dummy-b
|
||||
assert_line 'reason: installed as a dependency'
|
||||
run skup info su-dummy-c
|
||||
assert_line 'reason: unknown'
|
||||
# weak-dependency counts as a dependency, a group member as asked for
|
||||
run skup info glibc-gconv-extra
|
||||
assert_line 'reason: installed as a dependency'
|
||||
run skup info basesystem
|
||||
assert_line 'reason: explicitly installed'
|
||||
# only in a repository: repoquery --installed prints nothing
|
||||
run skup info su-dummy-new
|
||||
assert_success
|
||||
assert_line 'su-dummy-new: skeptical-updater dummy su-dummy-new (v1.0)'
|
||||
assert_line 'reason: not installed'
|
||||
# repoquery failing: unknown
|
||||
FAKE_DNF_REPOQUERY_RC=1 run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_line 'reason: unknown'
|
||||
}
|
||||
|
||||
@test "dnf5: install reason in dnf5's words (External User = plain rpm -i, counted as user-installed)" {
|
||||
use_fakes dnf rpm
|
||||
export FAKE_DNF_MAJOR=5
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_line 'reason: explicitly installed'
|
||||
run skup info su-dummy-b
|
||||
assert_line 'reason: installed as a dependency'
|
||||
run skup info su-dummy-c
|
||||
assert_line 'reason: explicitly installed'
|
||||
run skup info glibc-gconv-extra
|
||||
assert_line 'reason: installed as a dependency'
|
||||
FAKE_DNF_REASON='su-dummy-c=None' run skup info su-dummy-c
|
||||
assert_line 'reason: unknown'
|
||||
run skup info su-dummy-new
|
||||
assert_line 'reason: not installed'
|
||||
}
|
||||
|
||||
@test "rpm closure: file deps, rich deps without their condition, rpmlib() dropped" {
|
||||
@@ -393,9 +490,58 @@ setup() { common_setup; }
|
||||
@test "zypper: reverse deps and closure use the same pure-rpm code" {
|
||||
use_fakes zypper rpm
|
||||
run skup info su-dummy-c
|
||||
assert_line ' required by (installed): su-dummy-d'
|
||||
assert_line 'required by: su-dummy-d'
|
||||
run skup trust su-dummy-a
|
||||
assert_output 'trusted su-dummy-a (+1 dependency: su-dummy-b)'
|
||||
run cut -f1 "$STATE_FILE"
|
||||
assert_line zypper
|
||||
}
|
||||
|
||||
@test "zypper: install reason from the Installed line of zypper info (not 'Installed Size')" {
|
||||
use_fakes zypper rpm
|
||||
run skup info su-dummy-a
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-a: skeptical-updater dummy su-dummy-a (v2.0)' 'reason: explicitly installed' 'required by: su-dummy-d')"
|
||||
log_has 'zypper --non-interactive --no-refresh --quiet info su-dummy-a'
|
||||
run skup info su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(printf '%s\n' 'su-dummy-b: skeptical-updater dummy su-dummy-b (v2.0)' 'reason: installed as a dependency' 'required by: su-dummy-a, su-dummy-d')"
|
||||
# only in a repository
|
||||
run skup info su-dummy-orphan-parent
|
||||
assert_success
|
||||
assert_line 'su-dummy-orphan-parent: skeptical-updater dummy su-dummy-orphan-parent (v1.0)'
|
||||
assert_line 'reason: not installed'
|
||||
# unknown to zypper (exit 104) and to rpm
|
||||
run skup info nothing-like-this
|
||||
assert_failure 1
|
||||
assert_line 'reason: not installed'
|
||||
}
|
||||
|
||||
@test "zypper: without an answer from zypper (lock held: exit 7) rpm + libzypp's AutoInstalled decide" {
|
||||
use_fakes zypper rpm
|
||||
export SKEPTICAL_UPDATER_ZYPP_ROOT=$BATS_TEST_TMPDIR/zroot FAKE_ZYPPER_INFO_RC=7
|
||||
# no readable AutoInstalled: the tool cannot tell
|
||||
run skup info su-dummy-b
|
||||
assert_success
|
||||
assert_line 'su-dummy-b: skeptical-updater dummy su-dummy-b (v1.0)'
|
||||
assert_line 'reason: unknown'
|
||||
mkdir -p "$SKEPTICAL_UPDATER_ZYPP_ROOT/var/lib/zypp"
|
||||
printf '# AutoInstalled generated Sat Oct 10 11:14:20 2026\n#\nsu-dummy-b\npattern:minimal_base\n#\n' >"$SKEPTICAL_UPDATER_ZYPP_ROOT/var/lib/zypp/AutoInstalled"
|
||||
run skup info su-dummy-b
|
||||
assert_line 'reason: installed as a dependency'
|
||||
run skup info su-dummy-a
|
||||
assert_line 'reason: explicitly installed'
|
||||
run skup info su-dummy-orphan-parent
|
||||
assert_line 'reason: not installed'
|
||||
}
|
||||
|
||||
@test "zypper on Tumbleweed: N? on a new dependency (old -) says 'not installed' without asking" {
|
||||
use_fakes zypper rpm
|
||||
export SKEPTICAL_UPDATER_OS_RELEASE=$FAKE_DATA/os-release-tumbleweed
|
||||
FAKE_ZYPPER_DUP=removes run skup --no-refresh <<<$'2?\nq'
|
||||
assert_success
|
||||
assert_line ' [2] su-dummy-newdep - -> 1.0-1 su-dummy'
|
||||
assert_line 'reason: not installed'
|
||||
assert_line 'required by: nothing'
|
||||
log_lacks 'zypper --non-interactive --no-refresh --quiet info su-dummy-newdep'
|
||||
}
|
||||
@@ -39,7 +39,8 @@ setup() {
|
||||
assert_line "apt${T}su-dummy-a${T}user${T}2026-10-09T09:40:12Z"
|
||||
run skup info su-dummy-b
|
||||
assert_success
|
||||
assert_line ' required by (installed): su-dummy-a'
|
||||
assert_line 'reason: installed as a dependency'
|
||||
assert_line 'required by: su-dummy-a'
|
||||
run skup show su-dummy-a
|
||||
assert_success
|
||||
assert_line 'Package: su-dummy-a'
|
||||
|
||||
Reference in new issue
Block a user