native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359#!/bin/bashset -euo pipefail
ROOT=$(cd "$(dirname "$0")/.." && pwd)TEST_ROOT=$(mktemp -d)trap 'rm -rf "$TEST_ROOT"' EXIT
BIN="$TEST_ROOT/bin"PERL_LIB="$TEST_ROOT/perl5"MEASUREMENTS="$TEST_ROOT/measurements"mkdir -p "$BIN" "$PERL_LIB" "$MEASUREMENTS"
REAL_PS=$(command -v ps)
# Every stub appends "<name> <pid>" to PROWL_FAKE_CALLS when it is set. PIDs are# handed out in increasing order, so a smaller PID than sample's ran before it.cat > "$BIN/ps" <<EOF#!/bin/bashset -euo pipefailif [[ "\$*" == *"-o time="* ]]; then if [ -n "\${PROWL_FAKE_CALLS:-}" ]; then echo "ps-time \$\$" >> "\$PROWL_FAKE_CALLS"; fi state="$TEST_ROOT/ps-state" value=0 if [ -f "\$state" ]; then value=1; fi : > "\$state" printf '00:00:0%s\n' "\$value" exit 0fiif [ -n "\${PROWL_FAKE_CALLS:-}" ]; then echo "ps \$\$" >> "\$PROWL_FAKE_CALLS"; fiif [[ "\$*" == *"-o etime="* ]]; then echo '00:10'elif [[ "\$*" == *"pid,%cpu,comm"* ]]; then printf ' PID %%CPU COMM\n%s 75.0 /Applications/Prowl Debug.app/Contents/MacOS/ProwlApp\n' "\${PROWL_PID:-1}"else exec "$REAL_PS" "\$@"fiEOF
cat > "$BIN/sleep" <<'EOF'#!/bin/bashexit 0EOF
cat > "$BIN/uptime" <<'EOF'#!/bin/bashif [ -n "${PROWL_FAKE_CALLS:-}" ]; then echo "uptime $$" >> "$PROWL_FAKE_CALLS"; fiecho '01:00 up 1 day, load averages: 1.00 2.00 3.00'EOF
cat > "$BIN/sysctl" <<'EOF'#!/bin/bashif [ -n "${PROWL_FAKE_CALLS:-}" ]; then echo "sysctl $$" >> "$PROWL_FAKE_CALLS"; fiecho 12EOF
# The capture script calls /usr/bin/perl by absolute path, so PERL5OPT is the only# way to see those calls.cat > "$PERL_LIB/CallLog.pm" <<'EOF'package CallLog;if ($ENV{PROWL_FAKE_CALLS}) { open my $log, '>>', $ENV{PROWL_FAKE_CALLS} or die "CallLog: $!"; print $log "perl $$\n"; close $log;}1;EOF
cat > "$BIN/prowl" <<'EOF'#!/bin/bashset -euo pipefailif [ -n "${PROWL_FAKE_CALLS:-}" ]; then echo "prowl $$" >> "$PROWL_FAKE_CALLS"; fiwait_for() { for _ in $(seq 1 3000); do [ -f "$1" ] && return 0 /bin/sleep 0.01 done}case "$1" in agents) if [ -n "${PROWL_FAKE_AGENTS_STALLS:-}" ]; then exec /bin/sleep 30; fi echo '{"ok":true,"data":{"agents":[{"status":"working"},{"status":"idle"}]}}' ;; list) if [ -n "${PROWL_FAKE_LIST_STALLS:-}" ]; then exec /bin/sleep 30; fi # Answer only once sampling has ended. if [ -n "${PROWL_FAKE_LIST_AFTER_SAMPLE:-}" ]; then wait_for "$PROWL_FAKE_TIMES_DIR/sample-ended"; fi # Answer this many seconds after sampling began. if [ -n "${PROWL_FAKE_LIST_DELAY:-}" ]; then wait_for "$PROWL_FAKE_TIMES_DIR/sample-started" /bin/sleep "$PROWL_FAKE_LIST_DELAY" fi if [ -n "${PROWL_FAKE_TIMES_DIR:-}" ]; then /usr/bin/perl -MTime::HiRes=time -e 'printf "%.3f\n", time' > "$PROWL_FAKE_TIMES_DIR/list-answered" fi cat <<'JSON'{"ok":true,"data":{"items":[ {"worktree":{"id":"w1"},"tab":{"id":"t1","selected":true},"pane":{"id":"p1","focused":true,"visible":true}}, {"worktree":{"id":"w1"},"tab":{"id":"t1","selected":true},"pane":{"id":"p2","focused":false,"visible":true}}, {"worktree":{"id":"w2"},"tab":{"id":"t2","selected":true},"pane":{"id":"p3","focused":false,"visible":false}}]}}JSON ;; *) exit 64 ;;esacEOF
# Only PROWL_FAKE_SOCKET_OWNER (default: PROWL_PID) serves a socket, at# PROWL_FAKE_SERVED_SOCKET (default: the CLI's default path).cat > "$BIN/lsof" <<'EOF'#!/bin/bashif [ -n "${PROWL_FAKE_CALLS:-}" ]; then echo "lsof $$" >> "$PROWL_FAKE_CALLS"; fipid=while [ "$#" -gt 0 ]; do if [ "$1" = -p ]; then pid=$2 shift 2 else shift fidone[ "$pid" = "${PROWL_FAKE_SOCKET_OWNER:-$PROWL_PID}" ] || exit 1printf 'p%s\nf5\nn%s\n' "$pid" \ "${PROWL_FAKE_SERVED_SOCKET:-$HOME/Library/Application Support/com.onevcat.prowl/cli.sock}"EOF
cat > "$BIN/top" <<EOF#!/bin/bashfor _ in \$(seq 1 20); do echo "\${PROWL_PID:-1} 50.0"; doneEOF
# Like sample(1), the fake writes a Date/Time header stamped when sampling begins. In# the timing cases it also takes PROWL_FAKE_SAMPLE_ATTACH seconds to begin, samples# for its requested duration, and records its start and end.cat > "$BIN/sample" <<'EOF'#!/bin/bashset -euo pipefailif [ -n "${PROWL_FAKE_CALLS:-}" ]; then echo "sample $$" >> "$PROWL_FAKE_CALLS"; fiseconds=$2output=while [ "$#" -gt 0 ]; do if [ "$1" = -f ]; then output=$2 shift 2 else shift fidoneif [ -n "${PROWL_FAKE_SAMPLE_ATTACH:-}" ]; then /bin/sleep "$PROWL_FAKE_SAMPLE_ATTACH"; fistamp=$(/usr/bin/perl -MPOSIX=strftime -MTime::HiRes=time -e ' my $t = time; printf "%.3f|%s.%03d %s\n", $t, strftime("%Y-%m-%d %H:%M:%S", localtime $t), ($t - int $t) * 1000, strftime("%z", localtime $t);')if [ -n "${PROWL_FAKE_TIMES_DIR:-}" ]; then echo "${stamp%%|*}" > "$PROWL_FAKE_TIMES_DIR/sample-started" /bin/sleep "$seconds"fi{ echo "Date/Time: ${stamp#*|}" cat <<'SAMPLE' 100 Thread_1 DispatchQueue_1: com.apple.main-thread 11 stepTransactionFlush 13 GraphHost.flushTransactions() 7 flushTransactions 17 addGlyph 19 rebuildRow 23 wyhashSAMPLE} > "$output"if [ -n "${PROWL_FAKE_TIMES_DIR:-}" ]; then /usr/bin/perl -MTime::HiRes=time -e 'printf "%.3f\n", time' > "$PROWL_FAKE_TIMES_DIR/sample-ended.tmp" mv "$PROWL_FAKE_TIMES_DIR/sample-ended.tmp" "$PROWL_FAKE_TIMES_DIR/sample-ended"fiEOF
chmod +x "$BIN"/*
# Runs capture-cpu-spike.sh against the stubs with a one-second sample; extra# VAR=value arguments configure the stubs.run_spike() { local dir=$1 shift rm -f "$TEST_ROOT/ps-state" mkdir -p "$dir" env PATH="$BIN:$PATH" PROWL_PID=$$ PROWL_MEASURE_DIR="$dir" \ PROWL_SPIKE_INTERVAL=1 PROWL_SPIKE_MAX_WAIT=1 PROWL_SPIKE_CONSECUTIVE=1 \ "$@" bash "$ROOT/scripts/capture-cpu-spike.sh" 50 1}
spike_dir() { find "$1/spikes" -mindepth 1 -maxdepth 1 -type d}
# The saved pane snapshot must have been answered while the fake sample was sampling.# sample-started holds the same value as the Date/Time header the fake writes, so the# start bound is the one the capture script reads.assert_snapshot_inside_sample() { local times=$1 local started ended answered started=$(cat "$times/sample-started") ended=$(cat "$times/sample-ended") answered=$(cat "$times/list-answered") awk -v start="$started" -v end="$ended" -v answered="$answered" \ 'BEGIN { exit !(answered >= start && answered <= end) }' || { echo "pane snapshot answered at $answered, outside the sample from $started to $ended" >&2 exit 1 }}
# Nothing runs between confirming the spike and launching sample.CALLS="$TEST_ROOT/calls"SPIKE_OUTPUT=$(run_spike "$MEASUREMENTS" PROWL_FAKE_CALLS="$CALLS" PERL5LIB="$PERL_LIB" PERL5OPT=-MCallLog)test -f "$(spike_dir "$MEASUREMENTS")/panes.json"grep -Fq \ 'pane mix: total=3 visible=2 focused=1 tabs=2 selected_tabs=2 worktrees=2' \ <<< "$SPIKE_OUTPUT"awk ' $1 == "sample" { sample = $2 } { name[NR] = $1; pid[NR] = $2 } END { if (!sample) { print "sample never ran"; exit 1 } for (i = 1; i <= NR; i++) if (name[i] != "sample" && name[i] != "ps-time" && pid[i] < sample) { print name[i] " (pid " pid[i] ") ran before sample (pid " sample ")" failed = 1 } exit failed }' "$CALLS" >&2
# A stalled prowl list neither blocks nor delays the capture.STALL_MEASUREMENTS="$TEST_ROOT/stall-measurements"STALL_STARTED=$SECONDSSTALL_OUTPUT=$(run_spike "$STALL_MEASUREMENTS" PROWL_FAKE_LIST_STALLS=1)STALL_ELAPSED=$((SECONDS - STALL_STARTED))[ "$STALL_ELAPSED" -lt 10 ] || { echo "stalled prowl list held the spike capture for ${STALL_ELAPSED}s" >&2; exit 1; }STALL_DIR=$(spike_dir "$STALL_MEASUREMENTS")grep -Fq 'stepTransactionFlush' "$STALL_DIR/sample.txt"grep -Fq '"ok":false' "$STALL_DIR/panes.json"grep -Fq 'pane mix: CLI unavailable (no answer within 6s)' <<< "$STALL_OUTPUT"
# A slow prowl agents does not hold back the pane snapshot.TIMES="$TEST_ROOT/times"mkdir -p "$TIMES"SLOW_AGENTS_OUTPUT=$(run_spike "$TEST_ROOT/slow-agents-measurements" \ PROWL_FAKE_AGENTS_STALLS=1 PROWL_FAKE_TIMES_DIR="$TIMES")SLOW_AGENTS_DIR=$(spike_dir "$TEST_ROOT/slow-agents-measurements")grep -Fq '"ok":false' "$SLOW_AGENTS_DIR/agents.json"grep -Fq '"ok":true' "$SLOW_AGENTS_DIR/panes.json"grep -Fq 'pane mix: total=3' <<< "$SLOW_AGENTS_OUTPUT"assert_snapshot_inside_sample "$TIMES"
# A snapshot that arrives after sampling has ended is refused.LATE_TIMES="$TEST_ROOT/late-times"mkdir -p "$LATE_TIMES"LATE_LIST_OUTPUT=$(run_spike "$TEST_ROOT/late-list-measurements" \ PROWL_FAKE_AGENTS_STALLS=1 PROWL_FAKE_LIST_AFTER_SAMPLE=1 PROWL_FAKE_TIMES_DIR="$LATE_TIMES")LATE_LIST_DIR=$(spike_dir "$TEST_ROOT/late-list-measurements")if grep -Fq '"ok":true' "$LATE_LIST_DIR/panes.json"; then assert_snapshot_inside_sample "$LATE_TIMES"figrep -Fq '"ok":false' "$LATE_LIST_DIR/panes.json"grep -Fq 'pane mix: CLI unavailable (answered after sampling ended)' <<< "$LATE_LIST_OUTPUT"
# sample(1) takes time to attach before it samples. Here the fake takes 0.5 s, longer# than the query offset, so a prowl list that answers at once does so before sampling# begins. That snapshot is refused.EARLY_TIMES="$TEST_ROOT/early-times"mkdir -p "$EARLY_TIMES"EARLY_OUTPUT=$(run_spike "$TEST_ROOT/early-measurements" \ PROWL_FAKE_SAMPLE_ATTACH=0.5 PROWL_FAKE_TIMES_DIR="$EARLY_TIMES")EARLY_DIR=$(spike_dir "$TEST_ROOT/early-measurements")awk -v start="$(cat "$EARLY_TIMES/sample-started")" -v answered="$(cat "$EARLY_TIMES/list-answered")" \ 'BEGIN { exit !(answered < start) }' || { echo "the early prowl list answered after sampling began; the case tests nothing" >&2 exit 1}grep -Fq 'answered before sampling started' "$EARLY_DIR/panes.json" || { echo "pane snapshot answered before sampling started was kept: $(tr -d '\n' < "$EARLY_DIR/panes.json" | cut -c1-60)" >&2 exit 1}grep -Fq 'pane mix: CLI unavailable (answered before sampling started)' <<< "$EARLY_OUTPUT"
# With the same attach delay, a snapshot answered late in the sampling, but still# inside it, is kept.ATTACH_TIMES="$TEST_ROOT/attach-times"mkdir -p "$ATTACH_TIMES"ATTACH_OUTPUT=$(run_spike "$TEST_ROOT/attach-measurements" \ PROWL_FAKE_SAMPLE_ATTACH=0.5 PROWL_FAKE_LIST_DELAY=0.6 PROWL_FAKE_TIMES_DIR="$ATTACH_TIMES")ATTACH_DIR=$(spike_dir "$TEST_ROOT/attach-measurements")grep -Fq '"ok":true' "$ATTACH_DIR/panes.json" || { echo "pane snapshot answered inside the sample was discarded: $(cat "$ATTACH_DIR/panes.json")" >&2 exit 1}grep -Fq 'pane mix: total=3' <<< "$ATTACH_OUTPUT"assert_snapshot_inside_sample "$ATTACH_TIMES"
# The CLI answers for another app than the sampled one: its socket points elsewhere,# or another process serves the default socket. Neither answer is kept.FOREIGN_SOCKET="$TEST_ROOT/other-app.sock"FOREIGN_OUTPUT=$(run_spike "$TEST_ROOT/foreign-measurements" PROWL_CLI_SOCKET="$FOREIGN_SOCKET")FOREIGN_DIR=$(spike_dir "$TEST_ROOT/foreign-measurements")for file in agents panes; do grep -Fq "\"reason\":\"CLI socket $FOREIGN_SOCKET is not served by pid $$\"" "$FOREIGN_DIR/$file.json" || { echo "$file snapshot from another app's socket was kept: $(cut -c1-60 < "$FOREIGN_DIR/$file.json")" >&2 exit 1 }donegrep -Fq "CLI unavailable (CLI socket $FOREIGN_SOCKET is not served by pid $$)" <<< "$FOREIGN_OUTPUT"grep -Fq "pane mix: CLI unavailable (CLI socket $FOREIGN_SOCKET is not served by pid $$)" <<< "$FOREIGN_OUTPUT"
OTHER_OWNER_OUTPUT=$(run_spike "$TEST_ROOT/other-owner-measurements" PROWL_FAKE_SOCKET_OWNER=1)grep -Fq 'pane mix: CLI unavailable (CLI socket ' <<< "$OTHER_OWNER_OUTPUT"grep -Fq "is not served by pid $$" "$(spike_dir "$TEST_ROOT/other-owner-measurements")/panes.json"
# A socket override that the sampled process serves is honored.OVERRIDE_OUTPUT=$(run_spike "$TEST_ROOT/override-measurements" \ PROWL_CLI_SOCKET="$FOREIGN_SOCKET" PROWL_FAKE_SERVED_SOCKET="$FOREIGN_SOCKET")grep -Fq 'pane mix: total=3' <<< "$OVERRIDE_OUTPUT"
rm -f "$TEST_ROOT/ps-state"MEASURE_OUTPUT=$( PATH="$BIN:$PATH" \ PROWL_PID=$$ \ PROWL_MEASURE_DIR="$MEASUREMENTS" \ bash "$ROOT/scripts/measure-agent-detection-cpu.sh")
MEASURE_DIR=$(find "$MEASUREMENTS" -mindepth 1 -maxdepth 1 -type d ! -name spikes)test -f "$MEASURE_DIR/panes.json"grep -Fq \ 'total=3 visible=2 focused=1 tabs=2 selected_tabs=2 worktrees=2' \ <<< "$MEASURE_OUTPUT"grep -Eq '11\.00% +-[[:space:]]+stepTransactionFlush' <<< "$MEASURE_OUTPUT"grep -Eq '13\.00% +-[[:space:]]+GraphHost\.flushTransactions\(\)' <<< "$MEASURE_OUTPUT"grep -Eq '7\.00% +-[[:space:]]+flushTransactions \(excluding GraphHost\)' <<< "$MEASURE_OUTPUT"grep -Eq '17\.00% +-[[:space:]]+addGlyph' <<< "$MEASURE_OUTPUT"grep -Eq '19\.00% +-[[:space:]]+rebuildRow' <<< "$MEASURE_OUTPUT"grep -Eq '23\.00% +-[[:space:]]+wyhash' <<< "$MEASURE_OUTPUT"
# The steady-state profiler does not query a CLI that answers for another app.FOREIGN_CALLS="$TEST_ROOT/foreign-calls"FOREIGN_MEASURE_OUTPUT=$( PATH="$BIN:$PATH" \ PROWL_PID=$$ \ PROWL_MEASURE_DIR="$TEST_ROOT/foreign-steady-measurements" \ PROWL_CLI_SOCKET="$FOREIGN_SOCKET" \ PROWL_FAKE_CALLS="$FOREIGN_CALLS" \ bash "$ROOT/scripts/measure-agent-detection-cpu.sh")FOREIGN_MEASURE_DIR=$(find "$TEST_ROOT/foreign-steady-measurements" -mindepth 1 -maxdepth 1 -type d)if grep -q '^prowl ' "$FOREIGN_CALLS"; then echo "the steady-state profiler queried a CLI socket that another app serves" >&2 exit 1figrep -Fq "is not served by pid $$" "$FOREIGN_MEASURE_DIR/agents.json"grep -Fq "is not served by pid $$" "$FOREIGN_MEASURE_DIR/panes.json"[ "$(grep -Fc "CLI unavailable (CLI socket $FOREIGN_SOCKET is not served by pid $$)" <<< "$FOREIGN_MEASURE_OUTPUT")" -eq 2 ]
echo 'performance measurement script tests passed'