wasm-viewer: open by URL, lazily, with a request counter

The page gets a URL box; ?file=<url> now opens with openUrl instead of
downloading the file, and the header shows the requests made and bytes
fetched so far ("5 requests, 5 MiB of 191 MiB fetched (2.6%)"). Every
file call is awaited, so local files (open) and remote ones share the
code; a selection that finishes after another was made is not shown.

browser.sh serves the page and fixtures with test/serve.py (no symlinks
into the repository any more) and also checks a server without range
support and, on the 200 MB file, that a small dataset and a window of
the big one render with a single-digit percentage fetched.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-27 06:44:51 -05:00
co-authored by Claude Opus 5.5
parent 5107583b97
commit b8c85f7627
2 changed files with 140 additions and 74 deletions
+46 -30
View File
@@ -3,10 +3,13 @@
#
# browser.sh FIXTURE_DIR
#
# FIXTURE_DIR holds fixture.h5 from make_fixture.py; ../pkg must be built.
# The page is opened with ?file=fixture.h5&path=<object>, which fetches the
# file, builds the tree down to <object> and shows it; the rendered DOM is
# dumped and checked for the values libhdf5 reads.
# FIXTURE_DIR holds fixture.h5 from make_fixture.py (and big.h5 when it was
# run with WASM_BIG_MB); ../pkg must be built. test/serve.py serves the
# page and, under /fix, the fixtures, with HTTP range requests (no copies,
# no links). The page is opened with ?file=fix/<name>&path=<object>, which
# opens the file with openUrl (range requests), builds the tree down to
# <object> and shows it; the rendered DOM is dumped and checked for the
# values libhdf5 reads and for the request counter.
#
# Browser: $CHROME, else chromium/google-chrome on PATH, else a Playwright
# download under ~/.cache/ms-playwright. Exit 3 when none is found.
@@ -31,63 +34,76 @@ if [ -z "$chrome" ] || [ ! -x "$chrome" ]; then
exit 3
fi
root="$(mktemp -d)"
profiles="$(mktemp -d)"
server=""
cleanup() {
[ -n "$server" ] && kill "$server" 2>/dev/null || true
rm -rf "$root"
rm -rf "$profiles"
}
trap cleanup EXIT
ln -s "$HERE/../index.html" "$HERE/../viewer-lib.js" "$HERE/../pkg" "$FIX/fixture.h5" "$root/"
port=$("$PY" -c 'import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1])')
"$PY" -m http.server --bind 127.0.0.1 --directory "$root" "$port" >/dev/null 2>&1 &
"$PY" "$HERE/serve.py" --root "fix=$FIX" --root "=$HERE/.." > "$profiles/port" &
server=$!
for _ in $(seq 50); do
"$PY" -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:$port/index.html')" 2>/dev/null && break
[ -s "$profiles/port" ] && break
sleep 0.1
done
port="$(head -1 "$profiles/port")"
fails=0
# A fresh profile per page: a second instance on the same profile fails.
render() {
local profile
profile="$(mktemp -d "$root/profile.XXXXXX")"
profile="$(mktemp -d "$profiles/profile.XXXXXX")"
"$chrome" --headless --no-sandbox --disable-gpu --user-data-dir="$profile" \
--virtual-time-budget=20000 \
--dump-dom "http://127.0.0.1:$port/index.html?file=fixture.h5&path=$1" 2>/dev/null
--dump-dom "http://127.0.0.1:$port/index.html?file=$1&path=$2" 2>/dev/null
}
# expect PATH TEXT...: every TEXT appears in the page rendered for PATH.
# expect FILE PATH TEXT...: every TEXT appears in the page rendered for PATH
# of FILE (a URL relative to the page); "re:TEXT" is an extended regex.
expect() {
local path="$1" dom
shift
dom="$(render "$path")"
[ -n "${BROWSER_DEBUG:-}" ] && printf "%s\n" "$dom" > "$BROWSER_DEBUG.$(echo "$path" | tr / _).html"
local file="$1" path="$2" dom
shift 2
dom="$(render "$file" "$path")"
[ -n "${BROWSER_DEBUG:-}" ] && printf "%s\n" "$dom" > "$BROWSER_DEBUG.$(echo "$file$path" | tr / _).html"
for text in "$@"; do
if ! grep -qF -- "$text" <<<"$dom"; then
echo "FAIL: page for $path lacks: $text" >&2
local flag=-qF
if [ "${text#re:}" != "$text" ]; then flag=-qE; text="${text#re:}"; fi
if ! grep $flag -- "$text" <<<"$dom"; then
echo "FAIL: page for $file $path lacks: $text" >&2
fails=$((fails + 1))
fi
done
echo "rendered $path"
echo "rendered $file $path"
}
H5=fix/fixture.h5
# Tree (root expanded; the group row carries its path) and root attributes.
expect "/" 'data-path="/sensors"' 'data-path="/grid"' '<td>title</td><td>"wasm fixture"</td>' \
expect $H5 "/" 'data-path="/sensors"' 'data-path="/grid"' '<td>title</td><td>"wasm fixture"</td>' \
'<td>big</td><td>9223372036854775813</td>' '(compound{x: f64, n: i32})'
# A chunked, deflated 2-D dataset: type, shape, the first window of values.
expect "/grid" '<dd>f64</dd>' '<dd>(6, 10)</dd>' '<th>9</th>' '<td>0.25</td>' '<td>14.75</td>' \
'showing rows 0–5, columns 0–9 of 60 values'
# A chunked, deflated 2-D dataset: type, shape, the first window of values;
# the request counter.
expect $H5 "/grid" '<dd>f64</dd>' '<dd>(6, 10)</dd>' '<th>9</th>' '<td>0.25</td>' '<td>14.75</td>' \
'showing rows 0–5, columns 0–9 of 60 values' 'request' 'fetched (100.0%)'
# Nested path revealed through the tree; big-endian float32.
expect "/sensors/temp" 'data-path="/sensors/temp"' '<dd>f32</dd>' '<td>21.5</td>' '<td>22.25</td>'
expect $H5 "/sensors/temp" 'data-path="/sensors/temp"' '<dd>f32</dd>' '<td>21.5</td>' '<td>22.25</td>'
# 64-bit integers stay exact; strings; array datatype cells.
expect "/u64" '<td>18446744073709551615</td>'
expect "/vlen_str" '<td>"двa"</td>' '<dd>vlen string</dd>'
expect "/pairs" '<td>[2, 3]</td>' '<dd>array[2]&lt;i32&gt;</dd>'
expect $H5 "/u64" '<td>18446744073709551615</td>'
expect $H5 "/vlen_str" '<td>"двa"</td>' '<dd>vlen string</dd>'
expect $H5 "/pairs" '<td>[2, 3]</td>' '<dd>array[2]&lt;i32&gt;</dd>'
# 3-D: leading dimension held at 0, window over the last two.
expect "/cube" '<dd>(2, 5, 6)</dd>' '<td>29</td>' 'dim 0'
expect $H5 "/cube" '<dd>(2, 5, 6)</dd>' '<td>29</td>' 'dim 0'
# Unsupported type: an error, not values.
expect "/table" 'class="error"' 'reading compound{x: f64, n: i32} datasets is not supported'
expect $H5 "/table" 'class="error"' 'reading compound{x: f64, n: i32} datasets is not supported'
# A server without range support: the file is downloaded whole, and says so.
expect norange/$H5 "/grid" '<td>14.75</td>' 'downloaded whole'
# A large file: a small dataset, and a window of the large one, fetch a few
# blocks (the counter shows a small percentage: "(1.5%)", not "(100.0%)").
if [ -f "$FIX/big.h5" ]; then
expect fix/big.h5 "/small" '<td>1.5</td>' '<td>3.25</td>' 're:MiB of 191 MiB fetched \([0-9]\.[0-9]%\)'
expect fix/big.h5 "/big" '<dd>(25000000)</dd>' '<td>0</td>' '<td>24.5</td>' \
're:MiB of 191 MiB fetched \([0-9]\.[0-9]%\)'
fi
if [ "$fails" -gt 0 ]; then
echo "browser: $fails checks failed" >&2