. "$BST_LIB" require pv pv/present t=${TMPDIR:-/tmp}/bst-pv.$$ rm -rf "$t"; mkdir -p "$t" || { bst_notok pv/scratch "cannot create $t"; exit 0; } cd "$t" || { bst_notok pv/scratch "cannot cd $t"; exit 0; } # pv writes its display to stderr with \r, no trailing newline, so the naive # `tail -1` after tr reads the EMPTY line the final \r produces. Every display # assertion below goes through this instead. lastline() { tr '\r' '\n' < "$1" | grep -v '^$' | tail -1; } # Reference payloads. Assertions compare BYTES against these rather than reading # a message, so a transfer that half-works still fails. printf 'abcdefghij' > ten # 10 bytes printf 'l1\nl2\nl3\n' > lines3 # 3 LF-terminated lines printf 'n1\000n2\000' > nulls2 # 2 NUL-terminated records dd if=/dev/zero bs=1024 count=64 2>/dev/null > z64k i=0; : > bin256 # every one of the 256 byte values while [ $i -lt 256 ]; do printf "$(printf '\\%03o' $i)" >> bin256; i=$((i+1)); done # --- identity ----------------------------------------------------------------- check_out pv/version "pv 1.6.20" pv --version check_out pv/license "Artistic License 2.0" pv --version check_rc pv/version-rc 0 pv -V check_rc pv/help-rc 0 pv -h check_out pv/help-splice "--no-splice" pv -h check_out pv/help-watchfd "--watchfd" pv -h # --- data integrity: pv is a pipeline element FIRST, a meter second ------------ printf hello | pv -q > i1.out 2>/dev/null printf hello > i1.exp check pv/pipe-exact cmp i1.out i1.exp pv -q < ten > i2.out 2>/dev/null check pv/stdin-exact cmp i2.out ten pv ten > i3.out 2>/dev/null check pv/file-arg-exact cmp i3.out ten pv ten lines3 > i4.out 2>/dev/null cat ten lines3 > i4.exp check pv/multi-file-exact cmp i4.out i4.exp pv -q < z64k > i5.out 2>/dev/null check pv/64k-exact cmp i5.out z64k check_eq pv/64k-size 65536 "$(wc -c < i5.out | tr -d ' ')" pv -q < bin256 > i6.out 2>/dev/null check pv/binary-exact cmp i6.out bin256 # splice() is compiled in (glibc has it) but the kernel may refuse it; -C forces # the read/write path. Both must move the same bytes, and must agree. pv -C -q < z64k > i7.out 2>/dev/null check pv/nosplice-exact cmp i7.out z64k check pv/splice-eq-nosplice cmp i5.out i7.out pv -q -B 1024 < z64k > i8.out 2>/dev/null check pv/small-buffer-exact cmp i8.out z64k pv -q -L 1M < z64k > i9.out 2>/dev/null check pv/rate-limited-exact cmp i9.out z64k pv -q -E < ten > i10.out 2>/dev/null check pv/skip-errors-exact cmp i10.out ten # CONTROL: the comparison above must be capable of failing. A cmp that returned # 0 for everything would sweep every integrity case silently. check_rc pv/control-cmp-differs 1 cmp ten lines3 # --- metering. stderr is not a tty here, so every display needs -f ------------- pv -f -b < ten > /dev/null 2>d1 check_eq pv/bytes-10 "10.0 B" "$(lastline d1)" pv -f -b < z64k > /dev/null 2>d2 check_eq pv/bytes-64k "64.0KiB" "$(lastline d2)" pv -f -b ten lines3 > /dev/null 2>d3 check_eq pv/bytes-multi-file "19.0 B" "$(lastline d3)" pv -f -N TAG -b < ten > /dev/null 2>d4 case "$(lastline d4)" in *"TAG:"*) bst_ok pv/name-prefix ;; *) bst_notok pv/name-prefix "want TAG: got '$(lastline d4)'" ;; esac pv -f -F 'X%bX' < ten > /dev/null 2>d5 check_eq pv/format-bytes "X10.0 BX" "$(lastline d5)" pv -f -l -b < lines3 > /dev/null 2>d6 check_eq pv/line-mode "3.00" "$(lastline d6 | tr -d ' ')" pv -f -l -0 -b < nulls2 > /dev/null 2>d7 check_eq pv/line-mode-null "2.00" "$(lastline d7 | tr -d ' ')" pv -f -p -s 10 < ten > /dev/null 2>d8 case "$(lastline d8)" in *"100%"*) bst_ok pv/progress-bar ;; *) bst_notok pv/progress-bar "want 100% got '$(lastline d8)'" ;; esac pv -f -p -w 40 -s 10 < ten > /dev/null 2>d9 check_eq pv/width-40 40 "$(printf '%s' "$(lastline d9)" | wc -c | tr -d ' ')" pv -f -t < ten > /dev/null 2>d10 check_eq pv/timer "0:00:00" "$(lastline d10)" pv -f -r < ten > /dev/null 2>d11 case "$(lastline d11)" in *"B/s]"*) bst_ok pv/rate-counter ;; *) bst_notok pv/rate-counter "want B/s] got '$(lastline d11)'" ;; esac pv -f -a < ten > /dev/null 2>d12 case "$(lastline d12)" in *"B/s]"*) bst_ok pv/average-rate ;; *) bst_notok pv/average-rate "want B/s] got '$(lastline d12)'" ;; esac pv -f -T < ten > /dev/null 2>d13 case "$(lastline d13)" in *"%}"*) bst_ok pv/buffer-percent ;; *) bst_notok pv/buffer-percent "want %} got '$(lastline d13)'" ;; esac pv -f -A 4 < ten > /dev/null 2>d14 if [ -s d14 ]; then bst_ok pv/last-written; else bst_notok pv/last-written "no output"; fi pv -f -i 0.1 -b < z64k > /dev/null 2>d15 check_eq pv/interval-flag "64.0KiB" "$(lastline d15)" pv -n -s 10 < ten > /dev/null 2>d16 check_eq pv/numeric-percent "100" "$(lastline d16)" # quiet, and the non-tty default: pv must stay SILENT unless forced. pv -q < ten > /dev/null 2>d17 check_eq pv/quiet-silent 0 "$(wc -c < d17 | tr -d ' ')" pv -b < ten > /dev/null 2>d18 check_eq pv/nontty-silent 0 "$(wc -c < d18 | tr -d ' ')" # --- behaviour ---------------------------------------------------------------- # -S truncates at --size; --size ALONE is only an estimate and must not truncate. # The pair is deliberate: without the second case, a pv that ignored -s entirely # would still pass the first. pv -q -S -s 4 < ten > b1.out 2>/dev/null printf 'abcd' > b1.exp check pv/stop-at-size cmp b1.out b1.exp pv -q -s 4 < ten > b2.out 2>/dev/null check_eq pv/size-is-an-estimate 10 "$(wc -c < b2.out | tr -d ' ')" pv -q -P pf < ten > /dev/null 2>/dev/null if [ -e pf ]; then bst_notok pv/pidfile-removed "pidfile left behind at exit" else bst_ok pv/pidfile-removed; fi check_rc pv/exit-success 0 pv -q -f /dev/null pv /no/such/file > /dev/null 2>e1; rc=$? check_eq pv/exit-missing-file 10 "$rc" check_out pv/missing-file-msg "No such file or directory" cat e1 pv --no-such-option < ten > /dev/null 2>e2; rc=$? check_eq pv/exit-bad-option 64 "$rc" mkdir -p adir pv adir > /dev/null 2>e3; rc=$? check_eq pv/exit-directory 16 "$rc" check_out pv/directory-msg "Is a directory" cat e3 # --- platform gaps, shipped RED on purpose so they self-clear upstream --------- # pv --remote reaches the kernel through System V IPC message queues. The procfs # side is present (/proc/sysvipc/{msg,sem,shm} all read back their headers), but # msgget(2) itself returns ENOSYS -- no SysV IPC row is wired in the wasm32 # syscall table -- so -R cannot work here (brintos/linux#31). Native pv 1.6.20 on # the same source gets as far as "message not received"; this target reports # "Function not implemented". pv -R $$ > /dev/null 2>g1; rc=$? check_eq pv/remote-rc 1 "$rc" case "$(cat g1)" in *"Function not implemented"*) bst_notok pv/remote-msgget "msgget(2) ENOSYS (brintos/linux#31): $(cat g1)" ;; *) bst_ok pv/remote-msgget ;; esac # pv checks a watched pid exists with kill(pid, 0). On this kernel kill(2) does # no existence check at all -- `kill -0 999999` succeeds -- because the wasm32 # shim short-circuits before find_task_by_vpid, so pv walks past a dead pid and # fails later, at readlink, with ENOENT instead of ESRCH (brintos/linux#21). pv # still exits 2 either way, which is what the first case pins; the second is the # gap. /proc//fd and fdinfo are FINE here and were measured, not assumed. pv -d 999999 > /dev/null 2>g2; rc=$? check_eq pv/watchfd-missing-rc 2 "$rc" # ESRCH message, not ENOENT -- red until brintos/linux#21 lands. check_out pv/watchfd-missing-msg "No such process" cat g2 kill -0 999999 2>/dev/null if [ $? -eq 0 ]; then bst_notok pv/kill0-nonexistent "kill(999999,0) returned success; ESRCH expected (brintos/linux#21)" else bst_ok pv/kill0-nonexistent; fi cd /; rm -rf "$t"