open-standards/joule-code/joule-code-rs/crates/joule-moq-web/e2e/run.sh
Transaction Science 29b982fbe2
All checks were successful
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (pull_request) Successful in 32s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 5s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 6s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 5s
WAI Studio / does this change touch it (pull_request) Successful in 5s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 31s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 54s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m21s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m19s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 9m24s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 8m56s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m5s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 9m35s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m43s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 11m30s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m40s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 19m26s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 6m1s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m30s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 7m27s
joule-moq-web: a turn a later schedule replaces ends at that schedule's from, and a performer's page keeps its guard before it
The room page took a turn's end from its own schedule only. When a lead's new schedule took effect
in the middle of a turn, the page of the performer holding it went on sending up to the new
schedule's `from`, past its guard (co-performance §8). The relay judged its last objects on
arrival, under the schedule they reached it in, and forwarded them. The room's clock, a hop
later, logged them after the `from`, under the new schedule. Where the new schedule refuses and
gives that turn to someone else, the room with the clock's log then refuses as `turn_violated`.

How often depends on that hop: rarely on a quiet host, and once in #619's browser run, on the
shared build host under load (the room e2e's refusing schedule takes effect in the middle of the
marking schedule's sixth turn, which the other holder held).

Now:
- A turn ends at its segment's boundary, or at the `from` of a later schedule, if that comes
  first (§6.1: "A later schedule replaces an earlier one from its `from` on").
- The guard applies before either.
- The turns ahead step from the cut turn to the new schedule's first turn. Before, they jumped
  to the old turn's end and skipped the new schedule's turns up to it.

Tests:
- `web/room/co.test.mjs` (new, run by `e2e/run.sh` with the page's other module tests): six
  cases. Against the old `co.js`, three of them fail: the turn's end, the guard before the
  `from`, and the turns ahead.
- The room e2e names an object it refuses: its arrival, and how far it is from the nearest turn
  boundary or schedule `from`. If the check fails again, it says which race it was.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 17:40:39 -05:00

118 lines
6.2 KiB
Shell
Executable file

#!/bin/bash
# Build what the browser MoQ client's tests need, and run them.
#
# e2e/run.sh [--only compose,webtransport,websocket,goaway,dvr,service,e2ee,age,co,ageco,binding,cowatch,origin,termination,remote,world,privateworld,messaging,call] [--runs N]
#
# 1. joule-moq-relay, joule-moq-room and joule-moq-remote (each its own workspace) and
# wai_live_web (native, release: the test room, the host's composition, and the remote
# desktop host agent);
# 2. joule-moq-web and wai-live-web for wasm32-unknown-unknown, through wasm-bindgen's web
# target into web/pkg/, each with its own --out-name; and joule-moq-web again with its
# feature e2ee (the member of an end-to-end encrypted room), under the workspace's `wasm`
# profile and with no name section, as joule_moq_web_e2ee, which a page loads for such a
# room alone; and wai-live-web again with its feature int_video, as wai_live_web_decode,
# the decoder on the browser sink's wasm route, which the binding page loads;
# 3. the browser-automation package from e2e/package-lock.json (`npm ci --ignore-scripts`:
# no install script runs), outside the tree unless PLAYWRIGHT_HOST names one installed;
# 4. the Node unit tests of the page's own modules;
# 5. e2e/room.e2e.mjs, in the installed browser whose executable BROWSER_BIN names (the
# CI job names the host's), otherwise the package's own, which PLAYWRIGHT_BROWSERS_PATH
# must then hold;
# 6. e2e/messaging.e2e.mjs, the messaging view and the call flow (WAI live-messaging), in the
# same browser. `--only` picks scenarios of either file by name.
#
# Environment: CARGO_TARGET_DIR (one target directory for both workspaces), BROWSER_BIN,
# PLAYWRIGHT_HOST, MOQ_WEB_OUT (where artifacts go), CARGO_LOCKED (default --locked).
# Nothing listens beyond loopback: the relay and the page server bind 127.0.0.1.
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
CRATE=$(cd "$HERE/.." && pwd)
JC=$(cd "$CRATE/../.." && pwd)
ROOT=$(cd "$JC/../.." && pwd)
WLW="$ROOT/wai/wai-live-web"
LOCKED=${CARGO_LOCKED---locked}
TARGET=${CARGO_TARGET_DIR:-$JC/target}
export CARGO_TARGET_DIR="$TARGET"
OUT=${MOQ_WEB_OUT:-${TMPDIR:-/tmp}/moq-web-e2e}
mkdir -p "$OUT"
# Each run starts from no results: a scenario writes keys and logs there, and the room
# service's keygen refuses a key file a previous run left.
rm -rf "$OUT/results"
PKG="$CRATE/web/pkg"
mkdir -p "$PKG"
step() { echo "== $*"; }
step "native: wai-relay, wai-room, wai_live_web"
(cd "$JC" && cargo build $LOCKED --release -p joule-moq-relay --bin wai-relay)
(cd "$CRATE/../joule-moq-room" && cargo build $LOCKED --release --bin wai-room)
(cd "$CRATE/../joule-moq-remote" && cargo build $LOCKED --release --bin wai-remote-host)
(cd "$WLW" && cargo build $LOCKED --release --bin wai_live_web)
step "wasm: joule-moq-web, wai-live-web"
command -v wasm-bindgen >/dev/null || { echo "wasm-bindgen CLI 0.2.127 is needed (cargo install --locked wasm-bindgen-cli --version 0.2.127)"; exit 2; }
wasm-bindgen --version | grep -q ' 0\.2\.127$' || { echo "the wasm-bindgen CLI must be 0.2.127, the version both crates pin"; exit 2; }
(cd "$JC" && cargo build $LOCKED --release -p joule-moq-web --lib --target wasm32-unknown-unknown)
wasm-bindgen --target web --out-dir "$PKG" --out-name joule_moq_web "$TARGET/wasm32-unknown-unknown/release/joule_moq_web.wasm"
(cd "$JC" && cargo build $LOCKED --profile wasm -p joule-moq-web --lib --target wasm32-unknown-unknown --features e2ee)
wasm-bindgen --target web --remove-name-section --remove-producers-section --out-dir "$PKG" --out-name joule_moq_web_e2ee "$TARGET/wasm32-unknown-unknown/wasm/joule_moq_web.wasm"
(cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown)
wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm"
(cd "$WLW" && cargo build $LOCKED --release --lib --target wasm32-unknown-unknown --features int_video)
wasm-bindgen --target web --out-dir "$PKG" --out-name wai_live_web_decode "$TARGET/wasm32-unknown-unknown/release/wai_live_web.wasm"
ls -l "$PKG"/*.wasm
step "browser automation"
if [ -z "${PLAYWRIGHT_HOST:-}" ]; then
PW="$OUT/pw"
mkdir -p "$PW"
cp "$HERE/package.json" "$HERE/package-lock.json" "$PW/"
(cd "$PW" && npm ci --ignore-scripts --no-audit --no-fund)
export PLAYWRIGHT_HOST="$PW/node_modules"
fi
BROWSER_BIN=${BROWSER_BIN:-}
step "unit tests of the page's modules"
node --test "$CRATE/e2e/compose.test.mjs" "$CRATE/e2e/remote_input.test.mjs" "$CRATE/web/moq/capsule.test.mjs" "$CRATE/web/moq/media.test.mjs" "$CRATE/web/moq/session.test.mjs" "$CRATE/web/ads/presentation.test.mjs" "$CRATE/web/room/privacy-pass.test.mjs" "$CRATE/web/room/binding.test.mjs" "$CRATE/web/room/cowatch.test.mjs" "$CRATE/web/room/co.test.mjs" "$CRATE/web/room/remote.test.mjs" "$CRATE/web/room/markup.test.mjs" "$CRATE/web/room/endpoint.test.mjs" "$CRATE/web/room/termination.test.mjs"
step "end-to-end"
browser=()
[ -n "$BROWSER_BIN" ] && browser=(--browser "$BROWSER_BIN")
# `--only` names scenarios of room.e2e.mjs and of messaging.e2e.mjs: each file runs its own.
room_args=() msg_only="messaging,call" room_only="" only=0
while [ $# -gt 0 ]; do
if [ "$1" = "--only" ] && [ $# -ge 2 ]; then
only=1
msg_only=$(echo "$2" | tr ',' '\n' | grep -xE 'messaging|call' | paste -sd, - || true)
room_only=$(echo "$2" | tr ',' '\n' | grep -vxE 'messaging|call' | paste -sd, - || true)
shift 2
else
room_args+=("$1")
shift
fi
done
if [ "$only" = 0 ] || [ -n "$room_only" ]; then
node "$HERE/room.e2e.mjs" \
--relay "$TARGET/release/wai-relay" \
--room "$TARGET/release/wai-room" \
--wai "$TARGET/release/wai_live_web" \
--remote "$TARGET/release/wai-remote-host" \
--web "$CRATE/web" \
--corpus "$ROOT/wai/live-conformance/compose/vectors.json" \
--wai-dir "$ROOT/wai" \
--out "$OUT/results" \
${browser[@]+"${browser[@]}"} \
${room_only:+--only "$room_only"} \
${room_args[@]+"${room_args[@]}"}
fi
if [ -n "$msg_only" ]; then
step "end-to-end: conversations and calls"
node "$HERE/messaging.e2e.mjs" \
--relay "$TARGET/release/wai-relay" \
--room "$TARGET/release/wai-room" \
--web "$CRATE/web" \
--out "$OUT/results/messaging" \
--only "$msg_only" \
${browser[@]+"${browser[@]}"} \
${room_args[@]+"${room_args[@]}"}
fi