fix: close the four known installer bugs (DU-H1, DU-H2, BUG-1, BUG-2)
- DU-H1: flags are parsed wherever they sit, so `install --unattended` and `--unattended install` are the same run; any unknown flag, word, or subcommand exits 2 to stderr before a package manager is touched. - DU-H2: every download lands in one private mktemp -d (mode 700) workdir per run, is checked non-empty before sudo tar sees it, and an EXIT/INT/TERM trap cleans up. No fixed /tmp paths remain. - BUG-1: ^t is now toggle-shown — it ticks only the rows the active filter is showing, and @needs expansion stops at the first invasive row, so an invasive package can never be ticked off-screen. - BUG-2: ^t journals what it added, so a second ^t over the same shown set unticks exactly that set; the bind no longer clears the query. - lab: the type verb polls fzf's reported query to a deadline instead of a fixed sleep; marks_settled retries within its deadline. Suite 256/0 host, 214/0 docker (ubuntu:24.04), mutations 24/24 killed (six new mutants re-introduce each bug and all die), lab 6/6 green.
This commit is contained in:
@@ -26,12 +26,13 @@
|
||||
# silent no-op) were fixed, and the assertions below now hold the FIXED
|
||||
# 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.
|
||||
# BUG-1/2, in `promise 3`, were the last three left: ^t over an --exact filter
|
||||
# widened along @needs and ticked invasive rows that were not on screen, and
|
||||
# ^t ^t was not its own undo because the reverse edges do not retract what the
|
||||
# forward ones pulled in. Both are fixed -- ^t is `dotup toggle-shown`, whose
|
||||
# forward walk stops at an invasive dependency and whose second press replays
|
||||
# a journal of the first -- and those three assertions now hold the FIXED
|
||||
# behaviour too. The scenario exits 0, and every remaining NOTE is a NOTE.
|
||||
set -u
|
||||
|
||||
FAILS=0
|
||||
@@ -72,6 +73,7 @@ cat > /tmp/api.sh <<'SH'
|
||||
# api.sh <port> total totalCount
|
||||
# api.sh <port> pos cursor position, 0-based
|
||||
# api.sh <port> cur the key under the cursor
|
||||
# api.sh <port> query the query fzf has actually READ off the keyboard
|
||||
p=$1; a=$2
|
||||
j=$(curl -s --max-time 5 "localhost:$p/?limit=500") || exit 1
|
||||
[ -n "$j" ] || exit 1
|
||||
@@ -92,6 +94,9 @@ count) printf '%s' "$j" | grep -o '"matchCount":[0-9]*' | cut -d: -f2 ;;
|
||||
total) printf '%s' "$j" | grep -o '"totalCount":[0-9]*' | cut -d: -f2 ;;
|
||||
pos) printf '%s' "$j" | grep -o '"position":[0-9]*' | cut -d: -f2 ;;
|
||||
cur) printf '%s' "$j" | sed 's/.*"current":{//; s/},"matches".*//' | grep -o '\\t[pg]:[^"]*' | cut -c3- ;;
|
||||
# Not `sed 's/.*"query":"//'`: .* is greedy and would anchor on a later
|
||||
# occurrence of the word inside a row's own text.
|
||||
query) printf '%s' "$j" | grep -o '"query":"[^"]*"' | head -1 | cut -d'"' -f4 ;;
|
||||
raw) printf '%s\n' "$j" ;;
|
||||
esac
|
||||
SH
|
||||
@@ -166,7 +171,8 @@ proc api {what} {
|
||||
# it sees is off by one keystroke for the rest of the session. Two reads that
|
||||
# agree are a settled screen. This cost two false failures to learn.
|
||||
proc marks_settled {} {
|
||||
set deadline [expr {[clock milliseconds] + 8000}]
|
||||
set deadline [expr {[clock milliseconds] + 15000}]
|
||||
set last "the picker never held still"
|
||||
while {[clock milliseconds] < $deadline} {
|
||||
pump
|
||||
set a [api marks]
|
||||
@@ -177,15 +183,21 @@ proc marks_settled {} {
|
||||
# Cross-check the parse against fzf's own matchCount. A reader that
|
||||
# quietly returns nothing becomes a fifteen-minute hang somewhere
|
||||
# else; this turns it into one named failure, here.
|
||||
#
|
||||
# RETRY rather than bail: the two reads above and this count are
|
||||
# three separate HTTP round trips, so a reload landing between them
|
||||
# disagrees for one sample and agrees on the next. Bailing on the
|
||||
# first disagreement made a redraw look like a broken reader. It is
|
||||
# still a named failure -- just at the deadline, with the last
|
||||
# disagreement as the reason.
|
||||
set n [llength [split $a "\n"]]
|
||||
set c [api count]
|
||||
if {[string is integer -strict $c] && $n != $c} {
|
||||
bail "marks_settled: parsed $n rows but fzf reports $c matches -- the --listen reader is out of step with fzf's JSON"
|
||||
}
|
||||
return $a
|
||||
if {![string is integer -strict $c] || $n == $c} { return $a }
|
||||
set last "parsed $n rows but fzf reports $c matches -- the --listen reader is out of step with fzf's JSON"
|
||||
}
|
||||
after 100
|
||||
}
|
||||
bail "marks_settled: the picker never held still for 8s"
|
||||
bail "marks_settled: $last (15s)"
|
||||
}
|
||||
|
||||
# The same settling, but tolerant: used BEFORE a keystroke, where the picker
|
||||
@@ -305,8 +317,25 @@ while {[gets $fh line] >= 0} {
|
||||
send -- [subst -nocommands -novariables [lindex $rest 1]]
|
||||
after 200
|
||||
}
|
||||
type { foreach ch [split [lindex $rest 1] ""] { send -- $ch; after 80 }
|
||||
after 500; pump; set ::lastmarks [marks_settled] }
|
||||
type {
|
||||
# fzf reads the keyboard asynchronously, so a fixed sleep after the
|
||||
# last character samples whatever it happens to have consumed by
|
||||
# then. That is how `nvidia` was once measured as `n` -- 59 rows
|
||||
# matching instead of three, and a filtered session that was not
|
||||
# filtered. Wait for fzf to REPORT the whole query, then for the
|
||||
# rows it produced to hold still.
|
||||
set want [lindex $rest 1]
|
||||
foreach ch [split $want ""] { send -- $ch; after 80 }
|
||||
set deadline [expr {[clock milliseconds] + 15000}]
|
||||
set got ""
|
||||
while {[clock milliseconds] < $deadline} {
|
||||
pump
|
||||
set got [api query]
|
||||
if {$got eq $want} { break }
|
||||
after 100
|
||||
}
|
||||
if {$got ne $want} { bail "type $want: fzf's query still reads '$got' 15s after the last key" }
|
||||
pump; set ::lastmarks [marks_settled] }
|
||||
at { goto [lindex $rest 0] }
|
||||
wait { wait_change [lindex $rest 0] }
|
||||
scr { wait_screen [lindex $rest 0] [lindex $rest 1] }
|
||||
|
||||
Reference in New Issue
Block a user