test: refresh three picker assertions against the fixed behaviour

1ea6b49 changed what the picker does on purpose. Three assertions here were
still holding the old behaviour and failing for it; none of them were finding
a bug. The product is untouched.

- hostile 3 stacked two faults, no terminal AND no fetchable fzf, and asserted
  the fzf message. The /dev/tty probe was deliberately moved ahead of
  ensure_fzf (a run in a pipe used to print fzf's raw error having already
  overwritten the selection with the defaults preset), so with no tty the fzf
  message is now unreachable. Split in two: the no-tty arm asserts the ORDER,
  and a second arm runs the same missing fzf under a pty, via `script -e`,
  which is the only way to reach ensure_fzf's error at all now. Both arms
  still exit 2, and the fallback it names is still resolved for real.

- hostile 4's hand-edited state file was not being read: seeding the defaults
  preset is now decided by $STATE/picked ("a pick has completed") rather than
  by "$SEL is empty", so the fixture's junk was overwritten with 43 default
  rows before anything drew, and the session was testing the preset. core/tree
  is a default, so the space under test unticked it and the assertion read
  "space stopped working". The fixture now records a completed pick, as every
  machine that has pressed enter does. The CRLF row consequently reports its
  real result — silently ignored — where before it falsely read as tolerated,
  and the pass that followed the failure is no longer printed unconditionally.

- the `perm` session drove a picker that no longer draws. $SEL being readable
  and writable is now a precondition of drawing, because every bind is
  execute-silent and throws its child's status away (that was BUG-7). There is
  no session left to drive: the assertion is that nothing draws, that it exits
  2, and that the refusal names the file and a way out. Through a pty again,
  since the tty probe would otherwise answer first.

Deferred, and still failing: BUG-1/BUG-2 in `promise 3`, three assertions. ^t
over an --exact filter widens along @needs and ticks invasive rows that are
not on screen, and ^t ^t is not its own undo. There is no expected-fail
mechanism in this harness and none was invented, so the scenario exits 3 and
the header says why.

Verified: two consecutive runs of `run.sh scenarios/20-picker.sh` at exit 3
with exactly those three failures, and `scenarios/00-smoke.sh` still at 0.
This commit is contained in:
bcherb2
2026-08-21 23:13:36 -04:00
parent 805c407764
commit 7b28415cc6
+72 -33
View File
@@ -20,11 +20,18 @@
# the exit status is the number of failures. # the exit status is the number of failures.
# #
# It exits non-zero today, and every failure it reports is the product's, not # It exits non-zero today, and every failure it reports is the product's, not
# the harness's: BUG-1/2 (^t over a filter ticks invasive rows that are not on # the harness's. BUG-3 (a deliberate empty selection is not restored), BUG-4/6
# screen, and is not its own undo), BUG-3 (a deliberate empty selection is not # (no tty: fzf's raw error, and the defaults preset written by a picker that
# restored), BUG-4/6 (no tty: fzf's raw error, and the defaults preset written # never drew) and BUG-7 (an unreadable state file makes every keystroke a
# by a picker that never drew), BUG-7 (an unreadable state file makes every # silent no-op) were fixed, and the assertions below now hold the FIXED
# keystroke a silent no-op). Fixing those turns this green. # behaviour -- they fail again if it regresses.
#
# What is left is DEFERRED, not unknown, and there is no expected-fail
# mechanism here to hide it behind: BUG-1/2, in `promise 3`, three assertions.
# ^t over an --exact filter widens along @needs and ticks invasive rows that
# are not on screen, and ^t ^t is not its own undo because the reverse edges
# do not retract what the forward ones pulled in. Until those are fixed this
# scenario exits 3.
set -u set -u
FAILS=0 FAILS=0
@@ -909,14 +916,36 @@ export XDG_CACHE_HOME=/tmp/nocache; rm -rf /tmp/nocache
# is modified BY THE TEST here; dotup is still the thing that must not modify it. # is modified BY THE TEST here; dotup is still the thing that must not modify it.
mkdir -p /tmp/nonet mkdir -p /tmp/nonet
printf '#!/bin/sh\nexit 6\n' > /tmp/nonet/curl; chmod +x /tmp/nonet/curl printf '#!/bin/sh\nexit 6\n' > /tmp/nonet/curl; chmod +x /tmp/nonet/curl
# Two things are wrong at once here -- there is no terminal AND no fzf can be
# fetched -- and cmd_pick now answers the terminal first. The /dev/tty probe
# was moved AHEAD of ensure_fzf on purpose (a run in a pipe used to print
# fzf's raw error having already overwritten the selection with the defaults
# preset), so with no tty the fzf message is unreachable by definition. This
# arm asserts the ORDER; the pty arm below asserts the fzf message.
out=$(PATH=/tmp/nonet:$PATH "$D" pick 2>&1); rc=$? out=$(PATH=/tmp/nonet:$PATH "$D" pick 2>&1); rc=$?
[ "$rc" -eq 2 ] || fail "an unfetchable fzf exits $rc, not 2" [ "$rc" -eq 2 ] || fail "with neither a terminal nor a fetchable fzf, pick exits $rc, not 2"
ok "dotup pick exits 2 when fzf cannot be had" ok "dotup pick exits 2 when fzf cannot be had"
printf '%s\n' "$out" | sed 's/^/ | /' printf '%s\n' "$out" | sed 's/^/ | /'
case $out in case $out in
*"no terminal"*) ok "the /dev/tty probe answers before ensure_fzf is reached" ;;
*) fail "with no terminal the tty probe must answer first: got '$(printf '%s' "$out" | head -1)'" ;;
esac
[ -s "$ST/selected" ] && fail "a pick that failed the tty probe still wrote a selection"
# The same missing fzf, with a terminal attached. That is now the only way to
# reach ensure_fzf's error at all, so the message is checked through a pty:
# `script -e` hands the child a real /dev/tty and returns the child's status.
rm -rf /tmp/nocache
: > /tmp/nofzf.log
PATH=/tmp/nonet:$PATH script -q -e -c "$D pick" /tmp/nofzf.log >/dev/null 2>&1; rc=$?
nofzf=$(tr -d '\r' < /tmp/nofzf.log)
[ "$rc" -eq 2 ] || fail "with a terminal but no fetchable fzf, pick exits $rc, not 2"
ok "with a terminal attached and no fzf to be had, dotup pick exits 2"
printf '%s\n' "$nofzf" | sed 's/^/ | /'
case $nofzf in
*"numbered prompt"*) fail "the error still promises a numbered prompt that does not exist" ;; *"numbered prompt"*) fail "the error still promises a numbered prompt that does not exist" ;;
esac esac
case $out in case $nofzf in
*"dotup preset defaults && dotup install"*) ok "it names a fallback that exists" ;; *"dotup preset defaults && dotup install"*) ok "it names a fallback that exists" ;;
*) fail "the error names no usable way forward" ;; *) fail "the error names no usable way forward" ;;
esac esac
@@ -933,6 +962,15 @@ head_ "hostile 4 — a hand-edited state file"
fresh fresh
printf 'core/eza\r\ncore/ripgrep\n\n core/htop\ncore/bat\ncore/bat\ng:core\nnot/a-package\n' > "$ST/selected" printf 'core/eza\r\ncore/ripgrep\n\n core/htop\ncore/bat\ncore/bat\ng:core\nnot/a-package\n' > "$ST/selected"
printf 'core\nnosuchgroup\n' > "$ST/expanded" printf 'core\nnosuchgroup\n' > "$ST/expanded"
# The hand-edited file is only under test if the picker actually reads it.
# Seeding the defaults preset is now decided by "has a pick ever completed"
# ($STATE/picked), not by "$SEL is empty" -- emptiness could not tell a
# deliberate `^x`-then-enter apart from a brand new machine. So a state
# directory with no `picked` in it gets the 43 default rows written over the
# top of the junk BEFORE anything draws, and the session below would then be
# testing the preset rather than the hand edit. Every machine that has ever
# pressed enter has this marker; the fixture needs it too.
: > "$ST/picked"
cat > /tmp/steps.corrupt <<'EOS' cat > /tmp/steps.corrupt <<'EOS'
ready ready
snap c1 snap c1
@@ -956,8 +994,12 @@ if drive corrupt 44 220 /tmp/steps.corrupt "$D pick"; then
[ "$(markof c1 p:core/htop)" = "." ] || note "a leading-space line was honoured" [ "$(markof c1 p:core/htop)" = "." ] || note "a leading-space line was honoured"
[ "$(markof c1 p:core/bat)" = "x" ] || fail "the duplicated clean key was not honoured" [ "$(markof c1 p:core/bat)" = "x" ] || fail "the duplicated clean key was not honoured"
ok "a duplicated key is read once, not twice" ok "a duplicated key is read once, not twice"
selhas c2 core/tree || fail "space stopped working after junk in the state file" if selhas c2 core/tree; then
ok "space still toggles with junk in the file (core/tree went on)" ok "space still toggles with junk in the file (core/tree went on)"
else
fail "space stopped working after junk in the state file: core/tree is not in
$(seln c2) selected line(s) after the keystroke"
fi
[ "$(seln c1)" -eq "$(seln c2)" ] && note "the first toggle silently rewrote the whole [ "$(seln c1)" -eq "$(seln c2)" ] && note "the first toggle silently rewrote the whole
file — sort -u collapsed the duplicate, so the line count did not move file — sort -u collapsed the duplicate, so the line count did not move
even though a package was added" even though a package was added"
@@ -974,30 +1016,27 @@ fi
fresh fresh
"$D" preset defaults "$D" preset defaults
chmod 000 "$ST/selected" chmod 000 "$ST/selected"
cat > /tmp/steps.perm <<'EOS' # There is no session to drive here any more. BUG-7 was that the picker DREW
ready # on an unreadable $SEL: every bind is execute-silent, which throws its child's
snap perm1 # status away, so each tick was discarded in silence and the picker looked
at g:media # alive while being completely inert. Readable-and-writable is now a
key space \x20 # precondition of drawing, so the assertion is that nothing draws at all --
sleep 2500 # and it has to be made through a pty, because the /dev/tty probe runs first
snap perm2 # and would otherwise be the thing that answers.
abort : > /tmp/permrefuse.log
end script -q -e -c "$D pick" /tmp/permrefuse.log >/dev/null 2>&1; permrc=$?
EOS permout=$(tr -d '\r' < /tmp/permrefuse.log)
if drive perm 44 220 /tmp/steps.perm "$D pick"; then [ "$permrc" -eq 2 ] || fail "with an unreadable state file, dotup pick exits $permrc, not 2"
if [ "$(markof perm1 g:core)" = "." ]; then ok "dotup pick refuses to draw on an unreadable state file (rc=$permrc)"
note "an unreadable selected file draws as 'nothing is ticked' rather than as printf '%s\n' "$permout" | sed 's/^/ | /'
an error — the awk that reads it cannot tell 'empty' from 'refused'" case $permout in
fi *"not readable and writable"*) ok "the refusal names the file, and says the ticks would be lost" ;;
if diff -q /tmp/snap.perm1 /tmp/snap.perm2 >/dev/null; then *) fail "the refusal does not explain itself: '$(printf '%s' "$permout" | head -1)'" ;;
fail "with an unreadable state file, space does nothing at all and says nothing: esac
cmd_toggle's final \`sort -u \$tmp > \$SEL\` fails, execute-silent throws case $permout in
the status away, and the row never moves. The picker looks alive and is *"delete it, and re-run"*) ok "and it names a way out (fix the ownership, or delete it)" ;;
inert. (BUG-7)" *) fail "the refusal names no way out" ;;
else esac
ok "a toggle still lands with an unreadable state file"
fi
fi
chmod 644 "$ST/selected" 2>/dev/null || : chmod 644 "$ST/selected" 2>/dev/null || :
head_ "hostile 5 — ^c in the middle of the picker" head_ "hostile 5 — ^c in the middle of the picker"