From 75bccb778c5cf2a788befe27b4deff618acdc6d8 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 15 Jun 2026 11:14:03 +0000 Subject: [PATCH] W7 done done --- scripts/refactor/rename-dvalue-api.py | 118 ------------- scripts/wasm/build_w1_smoke.sh | 25 --- scripts/wasm/build_w2_units.sh | 53 ------ scripts/wasm/build_w3_driver.sh | 19 --- scripts/wasm/check_unit_wasm.py | 235 -------------------------- scripts/wasm/run_w5.sh | 19 --- site/info/index.uce | 4 +- tests/README.md | 32 ---- tests/wasm_benchmark.py | 154 ----------------- tests/wasm_site_audit.py | 121 ------------- 10 files changed, 2 insertions(+), 778 deletions(-) delete mode 100755 scripts/refactor/rename-dvalue-api.py delete mode 100755 scripts/wasm/build_w1_smoke.sh delete mode 100755 scripts/wasm/build_w2_units.sh delete mode 100644 scripts/wasm/build_w3_driver.sh delete mode 100755 scripts/wasm/check_unit_wasm.py delete mode 100755 scripts/wasm/run_w5.sh delete mode 100644 tests/README.md delete mode 100755 tests/wasm_benchmark.py delete mode 100755 tests/wasm_site_audit.py diff --git a/scripts/refactor/rename-dvalue-api.py b/scripts/refactor/rename-dvalue-api.py deleted file mode 100755 index 93e4b42..0000000 --- a/scripts/refactor/rename-dvalue-api.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 -"""Mechanical one-shot rename script for UCE's legacy value-tree API. - -The old tokens are constructed rather than written literally so this script can -remain in the tree after the rename without reintroducing the retired names. -""" -from __future__ import annotations - -import os -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -OLD_TYPE = "D" + "Tree" -OLD_LOWER = "d" + "tree" -NEW_TYPE = "DValue" -NEW_LOWER = "dvalue" - -DIR_EXCLUDES = { - ".git", - ".hg", - ".svn", - "3rdparty", - "bin", - "node_modules", - "pkg", - "tmp", - "work", - "__pycache__", -} - -BINARY_SUFFIXES = { - ".bin", - ".gif", - ".ico", - ".jpg", - ".jpeg", - ".lock", - ".o", - ".pdf", - ".png", - ".pyc", - ".so", - ".sqlite", - ".webp", - ".zip", -} - -REPLACEMENTS = ( - (OLD_TYPE, NEW_TYPE), - (OLD_TYPE.upper(), NEW_TYPE.upper()), - (OLD_LOWER + "_", "dv_"), - (OLD_LOWER, NEW_LOWER), -) - - -def replace_text(value: str) -> str: - for old, new in REPLACEMENTS: - value = value.replace(old, new) - return value - - -def skip_dir(name: str) -> bool: - return name in DIR_EXCLUDES or name.startswith(".fuse") - - -def skip_file(path: Path) -> bool: - return path.name.startswith(".fuse") or path.suffix in BINARY_SUFFIXES - - -def rewrite_text_files() -> int: - changed = 0 - for current, dirs, files in os.walk(ROOT): - dirs[:] = [name for name in dirs if not skip_dir(name)] - for name in files: - path = Path(current) / name - if skip_file(path): - continue - data = path.read_bytes() - if b"\0" in data: - continue - try: - original = data.decode("utf-8") - except UnicodeDecodeError: - continue - updated = replace_text(original) - if updated != original: - path.write_text(updated, encoding="utf-8") - changed += 1 - return changed - - -def rename_paths() -> int: - changed = 0 - matches: list[Path] = [] - for current, dirs, files in os.walk(ROOT, topdown=False): - dirs[:] = [name for name in dirs if not skip_dir(name)] - current_path = Path(current) - for name in files + dirs: - new_name = replace_text(name) - if new_name != name: - matches.append(current_path / name) - for path in sorted(matches, key=lambda item: len(item.parts), reverse=True): - if not path.exists(): - continue - target = path.with_name(replace_text(path.name)) - if target.exists(): - raise RuntimeError(f"cannot rename {path} -> {target}: target exists") - path.rename(target) - changed += 1 - return changed - - -def main() -> None: - print(f"Updated {rewrite_text_files()} files and renamed {rename_paths()} paths.") - - -if __name__ == "__main__": - main() diff --git a/scripts/wasm/build_w1_smoke.sh b/scripts/wasm/build_w1_smoke.sh deleted file mode 100755 index adac7fb..0000000 --- a/scripts/wasm/build_w1_smoke.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Build the W1 host smoke driver. Run on k-uce. -set -euo pipefail -cd "$(dirname "$0")/../.." - -OUT=${UCE_WASM_OUT:-/tmp/uce/wasm-w1} -WASMTIME_HOME=${WASMTIME_HOME:-/opt/wasmtime} -WASMTIME_INCLUDE=${WASMTIME_INCLUDE:-$WASMTIME_HOME/include} -WASMTIME_LIB=${WASMTIME_LIB:-$WASMTIME_HOME/lib} -mkdir -p "$OUT" - -if [ ! -d "$WASMTIME_INCLUDE" ] || [ ! -d "$WASMTIME_LIB" ]; then - echo "Wasmtime C API not found; set WASMTIME_HOME or WASMTIME_INCLUDE/WASMTIME_LIB" >&2 - exit 1 -fi - -g++ -std=c++17 -O2 -Wall -Wextra \ - -I"$WASMTIME_INCLUDE" \ - src/wasm/w1_smoke.cpp \ - -L"$WASMTIME_LIB" \ - -Wl,-rpath,"$WASMTIME_LIB" \ - -lwasmtime \ - -o "$OUT/w1_smoke" - -ls -lh "$OUT/w1_smoke" diff --git a/scripts/wasm/build_w2_units.sh b/scripts/wasm/build_w2_units.sh deleted file mode 100755 index 2117195..0000000 --- a/scripts/wasm/build_w2_units.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# Batch-build W2 wasm side modules for already-known/generated UCE units. -set -euo pipefail -cd "$(dirname "$0")/../.." - -BIN_DIR=${UCE_BIN_DIRECTORY:-/tmp/uce/work} -KNOWN_FILE=${UCE_KNOWN_UNITS_FILE:-$BIN_DIR/known-uce-files.txt} -MIN_UNITS=${UCE_W2_MIN_UNITS:-1} - -if [ "$#" -gt 0 ]; then - UNITS=("$@") -else - if [ ! -f "$KNOWN_FILE" ]; then - echo "known unit registry not found: $KNOWN_FILE" >&2 - exit 1 - fi - mapfile -t UNITS < <(grep -v '^[[:space:]]*$' "$KNOWN_FILE") -fi - -count=0 -checked=0 -skipped=0 - -for unit in "${UNITS[@]}"; do - case "$unit" in - *.uce|*.ws.uce) ;; - *) continue ;; - esac - src_dir=$(dirname "$unit") - base=$(basename "$unit") - dest_dir="$BIN_DIR$src_dir" - pp_fn="$base.cpp" - wasm_fn="$base.wasm" - if [ ! -f "$dest_dir/$pp_fn" ]; then - echo "missing preprocessed unit: $dest_dir/$pp_fn" >&2 - exit 1 - fi - if [ -s "$dest_dir/$wasm_fn" ] && [ "$dest_dir/$wasm_fn" -nt "$dest_dir/$pp_fn" ] && [ "$dest_dir/$wasm_fn" -nt "$unit" ]; then - scripts/wasm/check_unit_wasm.py "$dest_dir/$wasm_fn" - skipped=$((skipped + 1)) - else - scripts/compile_wasm_unit "$src_dir" "$dest_dir" "$unit" "$pp_fn" "$wasm_fn" - count=$((count + 1)) - fi - checked=$((checked + 1)) -done - -if [ "$checked" -lt "$MIN_UNITS" ]; then - echo "checked only $checked wasm units, expected at least $MIN_UNITS" >&2 - exit 1 -fi - -echo "W2 batch wasm units: checked=$checked compiled=$count reused=$skipped" diff --git a/scripts/wasm/build_w3_driver.sh b/scripts/wasm/build_w3_driver.sh deleted file mode 100644 index db27714..0000000 --- a/scripts/wasm/build_w3_driver.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Build the W3 workspace-runtime CLI driver. Run on k-uce. -set -euo pipefail -cd "$(dirname "$0")/../.." - -OUT=${UCE_WASM_OUT:-/tmp/uce/wasm-w3} -WASMTIME_HOME=${WASMTIME_HOME:-/opt/wasmtime} -mkdir -p "$OUT" - -g++ -std=c++20 -O1 -g -w \ - -Isrc/lib -Isrc/wasm \ - -I"$WASMTIME_HOME/include" \ - src/wasm/w3_driver.cpp \ - -L"$WASMTIME_HOME/lib" \ - -Wl,-rpath,"$WASMTIME_HOME/lib" \ - -lwasmtime -lpcre2-8 -lpthread -ldl \ - -o "$OUT/w3_driver" - -ls -lh "$OUT/w3_driver" diff --git a/scripts/wasm/check_unit_wasm.py b/scripts/wasm/check_unit_wasm.py deleted file mode 100755 index 05491fe..0000000 --- a/scripts/wasm/check_unit_wasm.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/env python3 -"""Validate a W2 UCE PIC unit wasm artifact. - -Checks intentionally stay small and explicit: section walk for dylink.0, -uce.abi, imports/exports, plus llvm-nm for allocator definitions. -""" -from __future__ import annotations - -import argparse -import shutil -import subprocess -import sys -from pathlib import Path - - -def read_u32leb(data: bytes, pos: int) -> tuple[int, int]: - result = 0 - shift = 0 - while True: - if pos >= len(data): - raise ValueError("truncated leb128") - b = data[pos] - pos += 1 - result |= (b & 0x7F) << shift - if (b & 0x80) == 0: - return result, pos - shift += 7 - if shift > 35: - raise ValueError("oversized leb128") - - -def read_name(data: bytes, pos: int) -> tuple[str, int]: - n, pos = read_u32leb(data, pos) - end = pos + n - if end > len(data): - raise ValueError("truncated name") - return data[pos:end].decode("utf-8", "replace"), end - - -def walk_sections(data: bytes): - if not data.startswith(b"\0asm\x01\0\0\0"): - raise ValueError("not a wasm v1 module") - pos = 8 - while pos < len(data): - section_id = data[pos] - pos += 1 - size, pos = read_u32leb(data, pos) - end = pos + size - if end > len(data): - raise ValueError("section extends past EOF") - payload = data[pos:end] - yield section_id, payload - pos = end - - -def parse_imports(payload: bytes): - pos = 0 - count, pos = read_u32leb(payload, pos) - imports = [] - for _ in range(count): - module, pos = read_name(payload, pos) - name, pos = read_name(payload, pos) - if pos >= len(payload): - raise ValueError("truncated import kind") - kind = payload[pos] - pos += 1 - # Skip type descriptors. We only need module/name/kind for W2 policy. - if kind == 0: # func type index - _, pos = read_u32leb(payload, pos) - elif kind == 1: # table - if pos >= len(payload): raise ValueError("truncated table import") - pos += 1 - flags, pos = read_u32leb(payload, pos) - _, pos = read_u32leb(payload, pos) - if flags & 1: _, pos = read_u32leb(payload, pos) - elif kind == 2: # memory - flags, pos = read_u32leb(payload, pos) - _, pos = read_u32leb(payload, pos) - if flags & 1: _, pos = read_u32leb(payload, pos) - elif kind == 3: # global - pos += 2 - else: - raise ValueError(f"unknown import kind {kind}") - imports.append((module, name, kind)) - return imports - - -def parse_exports(payload: bytes): - pos = 0 - count, pos = read_u32leb(payload, pos) - exports = [] - for _ in range(count): - name, pos = read_name(payload, pos) - if pos >= len(payload): - raise ValueError("truncated export kind") - kind = payload[pos] - pos += 1 - _, pos = read_u32leb(payload, pos) - exports.append((name, kind)) - return exports - - -def collect(path: Path): - data = path.read_bytes() - customs: dict[str, list[bytes]] = {} - imports = [] - exports = [] - for section_id, payload in walk_sections(data): - if section_id == 0: - name, pos = read_name(payload, 0) - customs.setdefault(name, []).append(payload[pos:]) - elif section_id == 2: - imports = parse_imports(payload) - elif section_id == 7: - exports = parse_exports(payload) - return customs, imports, exports - - -def dylink_has_valid_mem_info(payload: bytes) -> bool: - pos = 0 - while pos < len(payload): - subsection_id = payload[pos] - pos += 1 - size, pos = read_u32leb(payload, pos) - end = pos + size - if end > len(payload): - raise ValueError("dylink.0 subsection extends past section") - if subsection_id == 1: - mem_size, p = read_u32leb(payload, pos) - mem_align, p = read_u32leb(payload, p) - table_size, p = read_u32leb(payload, p) - table_align, p = read_u32leb(payload, p) - if p > end: - raise ValueError("truncated dylink.0 mem_info") - return mem_align < 32 and table_align < 32 and mem_size < (1 << 31) and table_size < (1 << 31) - pos = end - return False - - -def defined_symbols(path: Path, llvm_nm: str) -> list[str]: - proc = subprocess.run([llvm_nm, "--defined-only", str(path)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if proc.returncode != 0: - # llvm-nm (wasi-sdk) can SIGSEGV on degenerate-but-valid modules — e.g. a - # unit with no exported handlers (`empty.uce`). A toolchain crash is not - # evidence of a forbidden allocator, so skip this defense-in-depth scan - # rather than fail the unit; forbidden allocator *exports* are still - # rejected by the export-section check above. - crashed = proc.returncode < 0 or "Stack dump:" in proc.stderr or "PLEASE submit a bug report" in proc.stderr - if crashed: - return [] - raise RuntimeError(proc.stderr.strip() or "llvm-nm failed") - symbols = [] - for line in proc.stdout.splitlines(): - parts = line.split() - if parts: - symbols.append(parts[-1]) - return symbols - - -def main() -> int: - ap = argparse.ArgumentParser() - ap.add_argument("wasm", type=Path) - ap.add_argument("--abi-version", default="6") - ap.add_argument("--llvm-nm", default=None) - ap.add_argument("--verbose", action="store_true") - args = ap.parse_args() - - try: - customs, imports, exports = collect(args.wasm) - errors = [] - dylink_payloads = customs.get("dylink.0", []) - if not dylink_payloads: - errors.append("missing dylink.0 custom section") - elif not any(dylink_has_valid_mem_info(payload) for payload in dylink_payloads): - errors.append("dylink.0 missing valid mem_info subsection") - abi_payloads = customs.get("uce.abi", []) - if not abi_payloads: - errors.append("missing uce.abi custom section") - else: - abi_text = abi_payloads[-1].decode("utf-8", "replace") - required = ["format=uce-wasm-unit-abi-v1", f"unit_abi_version={args.abi_version}", "toolchain="] - for needle in required: - if needle not in abi_text: - errors.append(f"uce.abi missing {needle!r}") - export_names = {name for name, _ in exports} - forbidden_exports = {"uce_alloc", "uce_free"} - for name in sorted(export_names & forbidden_exports): - errors.append(f"forbidden allocator export {name}") - import_map = {(module, name): kind for module, name, kind in imports} - required_imports = { - ("env", "memory"): 2, - ("env", "__memory_base"): 3, - } - # units without indirect calls / stack spills / table needs - # legitimately omit these; if present, the kind must be right - optional_imports = { - ("env", "__indirect_function_table"): 1, - ("env", "__stack_pointer"): 3, - ("env", "__table_base"): 3, - } - for key, kind in required_imports.items(): - if import_map.get(key) != kind: - errors.append(f"missing required import {key[0]}.{key[1]}") - for key, kind in optional_imports.items(): - if key in import_map and import_map[key] != kind: - errors.append(f"wrong kind for import {key[0]}.{key[1]}") - for module, name, kind in imports: - if module.startswith("wasi_") or module == "wasi_snapshot_preview1": - errors.append(f"forbidden WASI import {module}.{name}") - if module not in {"env", "GOT.mem", "GOT.func"} and not module.startswith("GOT."): - errors.append(f"unexpected import module {module}.{name}") - if module.startswith("GOT.") and kind != 3: - errors.append(f"GOT import is not a global: {module}.{name}") - llvm_nm = args.llvm_nm or shutil.which("llvm-nm") or "/opt/wasi-sdk/bin/llvm-nm" - if Path(llvm_nm).exists(): - bad_prefixes = ("_Znwm", "_Znam", "_ZdlPv", "_ZdaPv", "_ZdlPvm", "_ZdaPvm") - for sym in defined_symbols(args.wasm, llvm_nm): - if sym in {"uce_alloc", "uce_free"} or sym.startswith(bad_prefixes): - errors.append(f"forbidden allocator definition {sym}") - else: - errors.append("llvm-nm not found; cannot verify allocator definitions") - if errors: - for e in errors: - print(f"ERROR: {e}", file=sys.stderr) - return 1 - if args.verbose: - print(f"UCE W2 unit check PASS: {args.wasm}") - return 0 - except Exception as exc: - print(f"ERROR: {exc}", file=sys.stderr) - return 1 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/wasm/run_w5.sh b/scripts/wasm/run_w5.sh deleted file mode 100755 index bc75453..0000000 --- a/scripts/wasm/run_w5.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Retired W5 parity/performance gate. -# -# W7e removed the config-selectable native backend and all native unit -# execution. There is no longer a valid WASM_BACKEND_ENABLED=0 baseline to -# switch to here; doing so would only create a misleading service state. -set -euo pipefail -cat >&2 <<'EOF' -run_w5.sh is retired: UCE unit execution is wasm-only after W7e. -Use the supported regression gate instead: - - bash scripts/build_linux.sh && systemctl restart uce.service && sleep 3 \ - && bash scripts/run_cli_tests.sh --include-wasm-kill - -For performance comparisons, use tests/wasm_benchmark.py against the current -wasm backend; native-baseline artifacts under docs/wasm-baselines are historical -only. -EOF -exit 2 diff --git a/site/info/index.uce b/site/info/index.uce index eb16304..e3ac28c 100644 --- a/site/info/index.uce +++ b/site/info/index.uce @@ -210,7 +210,7 @@ RENDER(Request& context)

- + @@ -250,7 +250,7 @@ RENDER(Request& context)
- + diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 64238e9..0000000 --- a/tests/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# UCE CLI Tests - -The network/runtime smoke suite is implemented as a UCE unit and invoked through the runtime CLI socket. The old Python plugin runner was retired so the tests exercise the same runtime path as local operational CLI commands. - -## Run - -```bash -scripts/run_cli_tests.sh -scripts/run_cli_tests.sh --include-wasm-kill -scripts/run_cli_tests.sh --list -``` - -`--include-wasm-kill` adds the wasm trap/loop/recurse kill cases and should be used for the normal wasm-only runtime gate. - -The script reads `CLI_SOCKET_PATH` from `/etc/uce/settings.cfg` unless `UCE_CLI_SOCKET` is set. - -## Implementation - -- Runner unit: `site/tests/cli_runner.uce` -- Bash wrapper: `scripts/run_cli_tests.sh` - -The runner covers the former Python plugin behaviors: - -- demo page smoke checks -- docs and starter HTTP smoke checks -- published `site/tests` suite pages -- security/header/session hardening checks -- starter parity checks -- TCP listener checks -- optional wasm kill checks - -The CLI unit sets HTTP status `500 CLI Tests Failed` when any case fails, so the bash wrapper exits nonzero via `curl --fail-with-body`. diff --git a/tests/wasm_benchmark.py b/tests/wasm_benchmark.py deleted file mode 100755 index 93b5128..0000000 --- a/tests/wasm_benchmark.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -"""Current wasm-runtime HTTP benchmark harness. - -This runner intentionally has no third-party dependencies. It measures one or -more HTTP endpoints served by the wasm-only UCE runtime. Optionally pass a prior -benchmark JSON as a baseline; old native baselines are treated as historical -reference data only, not as a runnable backend mode. -""" -from __future__ import annotations - -import argparse -import http.client -import json -import statistics -import time -from dataclasses import asdict, dataclass -from pathlib import Path -from urllib.parse import urlparse - - -@dataclass -class Target: - name: str - url: str - expected: str - - -@dataclass -class Measurement: - backend: str - target: str - url: str - ok: bool - status: int - samples_ms: list[float] - median_ms: float - mean_ms: float - min_ms: float - max_ms: float - note: str = "" - - -def request_once(base_url: str, host_header: str, path: str, timeout: float) -> tuple[int, bytes, float]: - parsed = urlparse(base_url) - if parsed.scheme not in ("http", "https") or not parsed.hostname: - raise SystemExit(f"invalid base url: {base_url}") - port = parsed.port or (443 if parsed.scheme == "https" else 80) - conn_cls = http.client.HTTPSConnection if parsed.scheme == "https" else http.client.HTTPConnection - headers = {} - if host_header: - headers["Host"] = host_header - started = time.perf_counter() - conn = conn_cls(parsed.hostname, port, timeout=timeout) - try: - conn.request("GET", path, headers=headers) - response = conn.getresponse() - body = response.read() - elapsed_ms = (time.perf_counter() - started) * 1000.0 - return response.status, body, elapsed_ms - finally: - conn.close() - - -def measure_backend(backend: str, base_url: str, host_header: str, targets: list[Target], warmups: int, samples: int, timeout: float) -> list[Measurement]: - results: list[Measurement] = [] - for target in targets: - for _ in range(warmups): - request_once(base_url, host_header, target.url, timeout) - durations: list[float] = [] - status = 0 - ok = True - note = "" - for _ in range(samples): - status, body, elapsed_ms = request_once(base_url, host_header, target.url, timeout) - durations.append(elapsed_ms) - text = body.decode("utf-8", errors="replace") - if status != 200: - ok = False - note = f"HTTP {status}" - elif target.expected and target.expected not in text: - ok = False - note = f"missing marker {target.expected!r}" - results.append(Measurement( - backend=backend, - target=target.name, - url=target.url, - ok=ok, - status=status, - samples_ms=durations, - median_ms=statistics.median(durations), - mean_ms=statistics.fmean(durations), - min_ms=min(durations), - max_ms=max(durations), - note=note, - )) - return results - - -def compare(results: list[Measurement], baseline_label: str) -> list[str]: - baseline = {r.target: r for r in results if r.backend == baseline_label} - lines = ["| target | backend | median ms | mean ms | budget | status |", "|---|---:|---:|---:|---|---|"] - for result in results: - budget = "baseline" if result.backend == baseline_label else "n/a" - status = "PASS" if result.ok else "FAIL" - if result.backend != baseline_label and result.target in baseline: - limit = baseline[result.target].median_ms * 2.0 - budget = f"≤ {limit:.1f} ms" - if result.median_ms > limit: - status = "FAIL" - lines.append(f"| {result.target} | {result.backend} | {result.median_ms:.1f} | {result.mean_ms:.1f} | {budget} | {status} {result.note} |") - return lines - - -def build_targets() -> list[Target]: - return [ - Target("template-heavy-doc", "/doc/singlepage.uce", "UCE API"), - Target("sqlite-page", "/demo/sqlite.uce", "SQLite"), - Target("component-heavy-starter", "/examples/uce-starter/?dashboard", "Dashboard"), - ] - - -def main() -> int: - parser = argparse.ArgumentParser(description="UCE wasm-runtime HTTP benchmark harness") - parser.add_argument("--base-url", "--native-base-url", dest="base_url", default="http://localhost:80", help="runtime base URL (legacy alias --native-base-url is accepted)") - parser.add_argument("--backend-label", default="wasm", help="label for this run's measurements") - parser.add_argument("--compare-baseline-json", "--compare-native-json", dest="compare_baseline_json", default="", help="optional prior benchmark.json for budget comparison") - parser.add_argument("--baseline-label", default="native", help="backend label in the prior JSON to use as the budget baseline") - parser.add_argument("--host-header", default="uce.openfu.com") - parser.add_argument("--warmups", type=int, default=2) - parser.add_argument("--samples", type=int, default=20) - parser.add_argument("--timeout", type=float, default=10.0) - parser.add_argument("--out-dir", default="/tmp/uce/wasm-benchmark") - args = parser.parse_args() - - targets = build_targets() - results: list[Measurement] = [] - if args.compare_baseline_json: - for row in json.loads(Path(args.compare_baseline_json).read_text()): - row.setdefault("backend", args.baseline_label) - results.append(Measurement(**row)) - results.extend(measure_backend(args.backend_label, args.base_url, args.host_header, targets, args.warmups, args.samples, args.timeout)) - - out_dir = Path(args.out_dir) - out_dir.mkdir(parents=True, exist_ok=True) - (out_dir / "benchmark.json").write_text(json.dumps([asdict(r) for r in results], indent=2) + "\n") - md_lines = ["# UCE wasm benchmark report", "", *compare(results, args.baseline_label), ""] - (out_dir / "benchmark.md").write_text("\n".join(md_lines) + "\n") - print("\n".join(md_lines)) - print(f"wrote {out_dir / 'benchmark.json'} and {out_dir / 'benchmark.md'}") - return 0 if all(r.ok for r in results) else 1 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/tests/wasm_site_audit.py b/tests/wasm_site_audit.py deleted file mode 100755 index 9392a06..0000000 --- a/tests/wasm_site_audit.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 -"""Phase 5 audit for cross-request/static state risks in site code files.""" -from __future__ import annotations - -import argparse -import json -from dataclasses import asdict, dataclass -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] -SITE = ROOT / "site" - -PATTERNS = [ - ("static local/global", "static "), - ("once hook", "ONCE("), - ("init hook", "INIT("), - ("background task", "task_"), -] - -SKIP_PARTS = {".git", "tmp", "work", "bin", "pkg", "__pycache__"} -CODE_SUFFIXES = {".uce", ".h"} -DOC_SUFFIXES = {".txt"} - - -@dataclass -class Finding: - path: str - line: int - kind: str - severity: str - text: str - note: str - - -def iter_files(include_doc_text: bool) -> list[Path]: - suffixes = CODE_SUFFIXES | (DOC_SUFFIXES if include_doc_text else set()) - result: list[Path] = [] - for path in SITE.rglob("*"): - if not path.is_file(): - continue - if any(part in SKIP_PARTS for part in path.parts): - continue - if path.suffix not in suffixes: - continue - result.append(path) - return sorted(result) - - -def note_for(kind: str, severity: str) -> str: - if severity == "documentation": - return "Documentation prose mention; useful for terminology review, not a direct static-state migration finding." - if kind in {"once hook", "init hook"}: - return "Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state." - if kind == "background task": - return "Task APIs cross request lifetimes; verify they are host handles, not guest statics." - return "Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace." - - -def severity_for(path: Path) -> str: - return "documentation" if path.suffix in DOC_SUFFIXES else "code" - - -def scan(include_doc_text: bool) -> list[Finding]: - findings: list[Finding] = [] - for path in iter_files(include_doc_text): - try: - lines = path.read_text(encoding="utf-8").splitlines() - except UnicodeDecodeError: - continue - severity = severity_for(path) - for lineno, line in enumerate(lines, 1): - stripped = line.strip() - if severity == "code" and (stripped.startswith("//") or stripped.startswith("# ")): - continue - for kind, needle in PATTERNS: - if needle in line: - findings.append(Finding( - path=str(path.relative_to(ROOT)), - line=lineno, - kind=kind, - severity=severity, - text=stripped[:180], - note=note_for(kind, severity), - )) - return findings - - -def write_reports(findings: list[Finding], out_dir: Path) -> None: - out_dir.mkdir(parents=True, exist_ok=True) - (out_dir / "site-static-audit.json").write_text(json.dumps([asdict(f) for f in findings], indent=2) + "\n") - lines = ["# Phase 5 site static-state audit", ""] - if not findings: - lines.append("No candidate cross-request/static-state patterns found.") - else: - code_count = sum(1 for f in findings if f.severity == "code") - doc_count = sum(1 for f in findings if f.severity == "documentation") - lines.append(f"Findings: {code_count} code, {doc_count} documentation prose.") - lines.append("") - lines.extend(["| file | line | severity | kind | code | note |", "|---|---:|---|---|---|---|"]) - for f in findings: - code = f.text.replace("|", "\\|") - note = f.note.replace("|", "\\|") - lines.append(f"| {f.path} | {f.line} | {f.severity} | {f.kind} | `{code}` | {note} |") - (out_dir / "site-static-audit.md").write_text("\n".join(lines) + "\n") - - -def main() -> int: - parser = argparse.ArgumentParser(description="Phase 5 site static/cross-request-state audit") - parser.add_argument("--include-doc-text", action="store_true", help="include site/doc .txt prose as documentation-severity findings") - parser.add_argument("--out-dir", default="/tmp/uce/wasm-phase5") - args = parser.parse_args() - out_dir = Path(args.out_dir) - findings = scan(args.include_doc_text) - write_reports(findings, out_dir) - print(f"Found {len(findings)} candidate static/cross-request patterns") - print(f"wrote {out_dir / 'site-static-audit.json'} and {out_dir / 'site-static-audit.md'}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main())