#!/bin/sh # Streams a message into the sandbox session on screen and prints the app's # render report for the time it took -- the standard measurement for "is a # reply that is still arriving cheap", the way transcript-bench.sh is for # scrolling one that has settled. # # ./stream-bench.sh FILE restart the app, open the first # session, stream FILE into it, report # ./stream-bench.sh -k FILE keep whatever session is open now # # The session is the first one the sandbox lists (ui-sandbox.sh spawn makes # one), and the file is echoed back a word at a time, which is the shape a # real reply arrives in. The numbers to read are `markdown reparsed while # streaming` -- how many times, and how long each -- and the worst # `record: one block`; both are proportional to how much of the reply the # tail reparse has to cover, which is what the live parse exists to bound. set -eu cd "$(dirname "$0")" . ./android-env.sh >/dev/null 2>&1 keep="" while getopts k flag; do case $flag in k) keep=1 ;; *) exit 2 ;; esac done shift $((OPTIND - 1)) file=${1:?usage: stream-bench.sh [-k] FILE} sid=$(./ui-sandbox.sh api /sessions | python3 -c 'import json,sys; print(json.load(sys.stdin)[0]["id"])') if [ -z "$keep" ]; then adb shell am force-stop com.example.aiapp adb shell am start -n com.example.aiapp/.MainActivity >/dev/null sleep 5 ui-trace record -d 3000 --do 'tap 500 545' -o /tmp/bench-open.txt >/dev/null 2>&1 sleep 3 fi # Pinned to the newest end before anything is sent. The transcript never # moves under a reader who is further back (see TranscriptList), so a reply # streaming into a session parked at an older row arrives entirely # off-screen: nothing recomposes, nothing draws, and the report comes back # with two recompositions in it and no streaming counters at all. That # reads exactly like a build where the work vanished. The control is the # app's own "Jump to latest", which is only there while the newest message # is off screen -- so when it is absent the list is already where it needs # to be. jump=$(ui-trace record -d 1200 -o /tmp/bench-jump.txt >/dev/null 2>&1 ui-trace show /tmp/bench-jump.txt -m 'Jump to latest' --field box | grep -o '[0-9]*,[0-9]*\.\.[0-9]*,[0-9]*' | tail -1) if [ -n "$jump" ]; then x=$(echo "$jump" | awk -F'[,.]' '{print int(($1 + $4) / 2)}') y=$(echo "$jump" | awk -F'[,.]' '{print int(($2 + $5) / 2)}') ui-trace record -d 1500 --do "tap $x $y" -o /tmp/bench-tolatest.txt >/dev/null 2>&1 fi # The first tap resets the report's window; see transcript-bench.sh. ui-trace record -d 2000 --do 'tap 723 205' -o /tmp/bench-reset.txt >/dev/null 2>&1 adb logcat -c ./ui-sandbox.sh send "$sid" "@$file" >/dev/null # Until the reply has finished, measured by the transcript rather than by # the status. A session is idle at both ends of a turn, and polling for # "idle" answers on the first poll -- before the turn has even started -- # so the report then covers the moment between the send and the first # delta, and prints a window with nothing in it. The event count only # grows, so "it stopped growing" is the one signal that cannot be true # before the work begins. events() { ./ui-sandbox.sh api "/sessions/$sid/transcript?limit=1" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d[-1]["seq"] if d else 0)' } last="" still=0 i=0 while [ "$i" -lt 180 ]; do now=$(events) if [ "$now" = "$last" ]; then still=$((still + 1)) [ "$still" -ge 2 ] && break else still=0 fi last=$now sleep 2 i=$((i + 1)) done sleep 1 ui-trace record -d 2000 --do 'tap 723 205' -o /tmp/bench-report.txt >/dev/null 2>&1 sleep 1 adb logcat -d -s ai-app:I | sed -n '/ai-app render report/,$p' | sed 's/^.*ai-app : //'