mirror of
https://github.com/lexmount/moli.git
synced 2026-09-30 08:01:36 +00:00
Add completion, failure, panic, crash, and timeout counts to the existing aggregate PR comment. Validate summary counts, bound failure details, and keep missing or incomplete evidence visible with a link to the source run.
437 lines
18 KiB
Markdown
437 lines
18 KiB
Markdown
# Moli Benchmark
|
|
|
|
`moli-benchmark` is the reproducible benchmark runner used to evaluate Moli.
|
|
It answers four practical questions:
|
|
|
|
- How quickly does Moli start and complete common browser workloads?
|
|
- How much memory and CPU does it use?
|
|
- Does it return correct, useful content across synthetic and public websites?
|
|
- How does it compare with Chrome, Lightpanda, and Obscura on the same work?
|
|
|
|
The runner records the environment and browser versions, keeps raw measurements,
|
|
and produces a self-contained HTML report. It supports quick local checks,
|
|
cross-engine investigations, and formal release-readiness runs.
|
|
|
|
## Quick start
|
|
|
|
You need Python 3.11 or newer, [`uv`](https://docs.astral.sh/uv/), and a release
|
|
build of Moli. From the repository root:
|
|
|
|
```bash
|
|
cargo build --release -p moli
|
|
cd moli-benchmark
|
|
uv run moli-benchmark run
|
|
```
|
|
|
|
The default `smoke` run exercises startup and deterministic local fixtures. It
|
|
does not require the optional comparison browsers.
|
|
|
|
The command prints its result directory when it finishes. Open `index.html` in
|
|
that directory to view the report; no web server is required.
|
|
|
|
## Common workflows
|
|
|
|
Run a compact fetch/CDP comparison across the configured engines:
|
|
|
|
```bash
|
|
uv run moli-benchmark run --profile horizontal --timeout 10
|
|
```
|
|
|
|
Run one deterministic case while working on Moli:
|
|
|
|
```bash
|
|
uv run moli-benchmark synthetic \
|
|
--case static-html \
|
|
--runs 5
|
|
```
|
|
|
|
### WebMainBench fetch stability CI
|
|
|
|
The `WebMainBench · 545 pages` job runs on pull requests, pushes to `main`, and
|
|
manual CI runs. It reuses the verified HEAD release artifact and replays all
|
|
545 frozen HTML pages with `moli fetch --dump markdown --wait done`, a 45-second
|
|
page deadline, and a 55-second process watchdog. Cases run serially without
|
|
retries. Each page must return nonempty Markdown; panics, crashes, timeouts,
|
|
missing results, and unexpected errors fail the job.
|
|
|
|
The [public dataset](https://huggingface.co/datasets/opendatalab/WebMainBench)
|
|
is pinned to revision `5da0972e9b58d0c7891ae75053ced97c268f52e3` and SHA-256
|
|
`0efaa4b49a45e320a27fe6e5a0b6aad5b57259fc3321ac3448519cacc74c537e`.
|
|
Its original UTF-8 HTML, including inline scripts, is served over local HTTPS.
|
|
The runner requires a fresh Linux network namespace with only loopback; missing
|
|
fixture paths return 404 and external resources are unreachable. Original URLs
|
|
are metadata, not live fetch targets.
|
|
|
|
One known exception is recorded explicitly: case
|
|
`ccb6033c-0a12-4f9c-8c68-794f26129841` redirects to `langrensha.163.com` and may
|
|
return that host's DNS resolution error. Only this case and error combination
|
|
is accepted; a different failure on that page still fails CI. A successful
|
|
result on that page also passes. The gate checks every case, not a minimum
|
|
aggregate success count.
|
|
|
|
To run locally from the repository root, use Python 3.11+, `openssl`, `ip`, and
|
|
`unshare` (Debian/Ubuntu packages `iproute2` and `util-linux`):
|
|
|
|
```bash
|
|
export PYTHONPATH="$PWD/moli-benchmark"
|
|
python3 -m moli_benchmark.webmainbench download \
|
|
--output target/webmainbench-data/WebMainBench_545.jsonl
|
|
unshare --user --map-root-user --net -- \
|
|
python3 -m moli_benchmark.webmainbench run \
|
|
--moli-bin target/release/moli \
|
|
--dataset target/webmainbench-data/WebMainBench_545.jsonl \
|
|
--output target/webmainbench-results \
|
|
--revision "$(git rev-parse HEAD)"
|
|
```
|
|
|
|
Use a new output directory for each run. On systems that disallow unprivileged
|
|
user namespaces, use `sudo unshare --net -- env PYTHONPATH="$PYTHONPATH" python3 ...`
|
|
as the CI job does. The runner refuses to run against an ordinary network.
|
|
|
|
The job summary and the existing PR `CI Regression Report` comment report the
|
|
verdict, completion/success counts, expected DNS failures, unexpected failures,
|
|
panics, crashes, timeouts, and empty outputs. The comment includes bounded
|
|
failure details and a link to the source run and artifacts. Missing or malformed
|
|
reports remain visibly unavailable. As with the other aggregate checks, comments
|
|
are updated for same-repository PRs by the trusted default-branch workflow;
|
|
this integration takes effect after these workflow and renderer changes reach
|
|
the default branch.
|
|
|
|
The `webmainbench-results` artifact retains every page's Markdown, stderr, exit
|
|
status, elapsed time, and HTML/output hashes for seven days, including when the
|
|
check fails. Partial runs retain an incrementally flushed `results.jsonl` for
|
|
diagnosis. This job checks fetch stability on Linux. Official content quality
|
|
scoring is a separate local evaluation: dynamic page content and the upstream
|
|
TEDS adapter issue make the raw composite score unsuitable as a CI threshold.
|
|
|
|
### Script-authored semantic WPT
|
|
|
|
The default semantic profile includes both Window and DedicatedWorker variants
|
|
of `streams/` and `compression/`, plus their `.window.js` and `.worker.js`
|
|
cases. Secure-context `.any.js` cases use the fixture's trustworthy loopback
|
|
origin. To run the complete compression suite through the CLI path:
|
|
|
|
```bash
|
|
uv run python -m moli_benchmark.wpt_cross \
|
|
--wpt-root ../../wpt \
|
|
--engine moli --mode cli \
|
|
--dir-prefix compression \
|
|
--output-dir /tmp/moli-compression-wpt
|
|
```
|
|
|
|
Use `--mode cdp` to check the same cases through CDP. Unsupported formats
|
|
remain failures in the report; they are not filtered out of this suite.
|
|
|
|
HTML crashtests, identified by WPT's `-crash` filename flag or a `crashtests`
|
|
directory, are also included within the semantic profile's existing filters.
|
|
Runs containing crashtests use CDP in `--mode auto`; explicit CLI mode rejects
|
|
them. The runner waits for load, fonts, two animation frames, and removal of
|
|
`test-wait`, dispatching `TestRendered` when needed. Reports record these as
|
|
`crashtest` with zero subtests. An outstanding wait remains a timeout, and a
|
|
renderer crash is reported even when the browser process stays alive.
|
|
|
|
### Cross-engine layout WPT
|
|
|
|
The standalone cross-engine runner has separate layout profiles, so its
|
|
existing semantic baseline is unchanged. Layout runs require an upstream WPT
|
|
checkout with `MANIFEST.json` and use CDP with a fixed `800x600` viewport at
|
|
DPR 1:
|
|
|
|
```bash
|
|
uv run python -m moli_benchmark.wpt_cross \
|
|
--wpt-root ../../wpt \
|
|
--engine moli --engine chrome \
|
|
--output-dir /tmp/moli-layout-wpt \
|
|
--profile layout-testharness
|
|
|
|
uv run python -m moli_benchmark.wpt_cross \
|
|
--wpt-root ../../wpt \
|
|
--engine moli --engine chrome \
|
|
--output-dir /tmp/moli-layout-reftest \
|
|
--profile layout-reftest
|
|
|
|
uv run python -m moli_benchmark.wpt_cross \
|
|
--wpt-root ../../wpt \
|
|
--engine moli \
|
|
--output-dir /tmp/moli-wpt-all \
|
|
--profile all
|
|
```
|
|
|
|
`--profile layout` combines both layout sets; `--profile all` merges the
|
|
default semantic baseline and both layout sets into one deduplicated matrix.
|
|
The stable layout profile covers
|
|
`css/css-flexbox`, `css/css-grid`, `css/css-sizing`, and `css/cssom-view`;
|
|
repeat `--dir-prefix` to override that list. Reftests are loaded from the
|
|
manifest and support `==`, `!=`, and fuzzy bounds. The initial static subset
|
|
filters wptserve Python handlers, HTTP/2, testdriver, animation, media, and
|
|
canvas dependencies. Failed reftests retain `test.png`, `reference-N.png`, and
|
|
`diff-N.png` under `OUTPUT_DIR/artifacts/ENGINE/`, with links in `index.html`.
|
|
An unfiltered full `default` or `all` run refreshes the unified status lists
|
|
directly under `wpt-cross-current/`.
|
|
|
|
Public-web suites read a `rank,target` CSV seed list. A minimal `sites.csv` looks like:
|
|
|
|
```csv
|
|
rank,target
|
|
1,https://example.com/
|
|
2,https://www.rust-lang.org/
|
|
```
|
|
|
|
Compare Moli and Chrome on that sample:
|
|
|
|
```bash
|
|
uv run moli-benchmark top-sites \
|
|
--list-path sites.csv \
|
|
--profile quick \
|
|
--target moli \
|
|
--target chrome \
|
|
--timeout 30
|
|
```
|
|
|
|
Compare visible content with Chrome as the baseline:
|
|
|
|
```bash
|
|
uv run moli-benchmark render-compare \
|
|
--list-path sites.csv \
|
|
--profile quick \
|
|
--target moli \
|
|
--baseline-target chrome \
|
|
--timeout 30
|
|
```
|
|
|
|
Run a formal synthetic concurrency matrix:
|
|
|
|
```bash
|
|
uv run moli-benchmark synthetic-matrix \
|
|
--profile formal \
|
|
--timeout 30
|
|
```
|
|
|
|
Every suite has focused help:
|
|
|
|
```bash
|
|
uv run moli-benchmark --help
|
|
uv run moli-benchmark top-sites --help
|
|
```
|
|
|
|
## Long-running navigation stress reports
|
|
|
|
`moli-stress` repeatedly navigates one long-lived CDP target, retains the
|
|
100 ms process-tree RSS/PSS/CPU samples, and produces a self-contained D3.js
|
|
report. Its default workload matches the sequential-navigation soak shape:
|
|
600 navigations across CSDN, SegmentFault, Huaban, and example.com.
|
|
|
|
From the repository root:
|
|
|
|
```bash
|
|
cargo build --release --locked -p moli
|
|
uv sync --project moli-benchmark --locked
|
|
uv run --project moli-benchmark --no-sync moli-stress run
|
|
```
|
|
|
|
Results are written under `moli-benchmark/results/stress-TIMESTAMP/` as:
|
|
|
|
- `result.json`: full navigation and 100 ms resource samples;
|
|
- `summary.json`: compact machine-readable metrics;
|
|
- `report.html`: offline interactive RSS/PSS/CPU and latency charts.
|
|
|
|
Choose another exact navigation count or URL sequence with `--navigations`
|
|
and repeated `--url`. The navigation count must be divisible by the selected
|
|
URL count. An existing retained result can be rendered again without rerunning
|
|
the workload:
|
|
|
|
```bash
|
|
uv run --project moli-benchmark --no-sync moli-stress report \
|
|
moli-benchmark/results/stress-TIMESTAMP/result.json
|
|
```
|
|
|
|
The HTML embeds the vendored D3.js runtime, so opening it does not require a
|
|
network connection or a local web server.
|
|
|
|
## Choosing a suite
|
|
|
|
| Suite | What it measures |
|
|
| --- | --- |
|
|
| `startup` | Binary/package size, startup latency, readiness, optional first/warm CDP pages, and idle resource use |
|
|
| `synthetic` | Correctness and performance on deterministic local HTML, JavaScript, DOM, storage, and event fixtures |
|
|
| `synthetic-matrix` | Stability across repeated concurrency levels |
|
|
| `synthetic-compare` | The same fetch-style fixture workload across multiple engines |
|
|
| `cdp-session` | Repeated navigation through a long-lived CDP page session |
|
|
| `agent-episode` | Deterministic, agent-shaped CDP workflows against Moli and Chromium |
|
|
| `crawler` / `amiibo-crawler` | Multi-page crawling, including the 933-page Amiibo workload |
|
|
| `wild-web` / `top-sites` | Extraction and lifecycle behavior on real public websites |
|
|
| `render-compare` | Visible-text similarity against a baseline browser, normally Chrome |
|
|
| `cdp-smoke` | Raw CDP, Playwright, and Puppeteer compatibility smoke coverage |
|
|
| `wpt` | Selected Web Platform Test compatibility reports |
|
|
| `collect-env` | Browser discovery plus environment and version metadata only |
|
|
|
|
Use `run --suite NAME` to combine supported suites into one report. Repeating
|
|
`--suite`, `--target`, or `--case` selects multiple values.
|
|
|
|
## Targets and browser discovery
|
|
|
|
The harness distinguishes the browser engine from the way it is driven:
|
|
|
|
- `moli` uses the normal CLI fetch path.
|
|
- `moli-full` uses the same binary with `--layout --resource`.
|
|
- Targets ending in `-cdp` use a CDP server instead of the fetch command.
|
|
- `lightpanda`, `chrome`, and `obscura` select comparison engines.
|
|
|
|
Not every suite accepts every target. Its `--help` output lists the valid
|
|
choices.
|
|
|
|
The predefined public-web CSV sources ship under `fixtures/top-sites/`, including
|
|
the WebFetch longtail corpus used by `webfetch-mix`. Markdown remains supported
|
|
for custom `--list-path` inputs.
|
|
|
|
Public-web results distinguish an individual attempt from a stable per-site
|
|
outcome. `raw-runs.csv` and `runs.json` retain every attempt, while
|
|
`site-outcomes.json` groups repeated attempts into `all-pass`, `all-fail`, and
|
|
`flaky` sites. Pairwise engine rows are marked `SINGLE SAMPLE` unless the suite
|
|
was run with at least three attempts per site; use `--runs 3` or more before
|
|
treating an engine-only result as repeat-validated.
|
|
|
|
Public-web summaries keep three different populations explicit. `raw_*`
|
|
metrics include reachable and unreachable observations before cross-engine site
|
|
exclusions; the normal pass rate uses counted, comparable attempts; and
|
|
`successful_*` latency/memory metrics contain successful attempts only. The
|
|
`common_success` cohort contains the exact run/site attempts that succeeded on
|
|
every selected target and is the source for cross-engine speed and memory
|
|
claims in the HTML report.
|
|
|
|
Multi-engine public-web runs are scheduled in site-paired groups. Each group
|
|
runs the selected targets sequentially, rotates which target goes first, and
|
|
allows multiple site groups to run concurrently. Raw rows record the schedule
|
|
and target-order indexes, UTC start/finish times, output hashes and samples,
|
|
response MIME/body-capture evidence, and the final URL when CDP exposes it.
|
|
This makes order effects and response changes auditable without retaining every
|
|
successful response body.
|
|
|
|
When the driver exposes the main-document HTTP status, that status is
|
|
authoritative: a rendered 4xx or 5xx error document is a failure even if its
|
|
body exceeds the content-size threshold. The report includes HTTP-status
|
|
coverage, evidence source, and classification basis for each target. Moli CLI
|
|
failures that name a terminal main-document status retain it as
|
|
`cli-diagnostic` evidence. CLI fetch drivers that expose neither protocol nor
|
|
diagnostic status fall back to conservative error-document markers; for a
|
|
protocol-aligned comparison with status coverage, select the `moli-cdp` and
|
|
`lightpanda-cdp` targets alongside `chrome`.
|
|
|
|
A CDP browser reports a binary main-document MIME type before it exposes a
|
|
download body. That is retained as `binary-response-headers`, not fabricated
|
|
into a successful PDF or archive. Header-only observations are neutral and do
|
|
not enter body-success or latency denominators. A CLI transfer that times out
|
|
after receiving only part of a binary response remains a transfer failure. For
|
|
an explicit `.pdf` main-resource URL, the Moli CLI adapter omits DOM-only page
|
|
wait options and counts the result only when the real PDF body is returned.
|
|
|
|
The built-in `wild-web` targets use the same DOMContentLoaded snapshot boundary
|
|
for Moli, Lightpanda, and Chrome: after the adapter observes DOMContentLoaded,
|
|
the page event loop must advance for at least 50 ms before the DOM is dumped.
|
|
The total readiness deadline still applies to that settle period. Seed
|
|
extraction checks require the expected site identity in the title plus a
|
|
non-trivial body; the brand name does not need to be repeated in the first
|
|
body-text sample.
|
|
|
|
Moli is discovered from `MOLI_BIN`, `../target/release/moli`, or `PATH`, in
|
|
that order. Comparison browsers are optional and can be selected through
|
|
`LIGHTPANDA_BIN`, `CHROME_BIN`, and `OBSCURA_BIN` or discovered from `PATH`.
|
|
For example:
|
|
|
|
```bash
|
|
MOLI_BIN=/opt/moli/bin/moli \
|
|
CHROME_BIN=/usr/bin/chromium \
|
|
uv run moli-benchmark run --profile horizontal
|
|
```
|
|
|
|
Unavailable comparison targets remain visible in comparison reports. Most
|
|
comparison suites fail the command only when the selected `--gate-target`
|
|
fails; the default gate target is Moli.
|
|
|
|
## Profiles and formal reports
|
|
|
|
Profiles describe the amount and purpose of work:
|
|
|
|
- `smoke` is the quick default for local development.
|
|
- `horizontal` is a top-level `run` preset for fetch and CDP comparisons.
|
|
- `formal` is available on suites with benchmark-standard coverage and uses
|
|
larger run, repeat, or concurrency requirements.
|
|
|
|
To write a dated report under `benchmarks/results/`, pass `--report-date`:
|
|
|
|
```bash
|
|
uv run moli-benchmark startup \
|
|
--profile formal \
|
|
--report-date 2026-08-11
|
|
```
|
|
|
|
`--report-date` only changes where artifacts are written. It does not make a
|
|
smoke workload formal by itself.
|
|
|
|
## Reading the results
|
|
|
|
Development runs are written to `moli-benchmark/results/<timestamp>/` by
|
|
default. A report contains:
|
|
|
|
| File | Purpose |
|
|
| --- | --- |
|
|
| `index.html` | Human-readable offline dashboard |
|
|
| `summary.md` / `summary.json` | Compact suite outcomes |
|
|
| `publish-readiness.json` | Machine-readable checks that say whether the evidence is publishable or still investigative |
|
|
| `report-data.json` | Renderer-independent data behind the dashboard |
|
|
| `environment.json` / `versions.json` | Host details and exact browser binaries |
|
|
| Suite subdirectories | Raw rows, traces, failures, and suite-specific summaries |
|
|
|
|
Compare a report with an earlier result directory or `summary.json` using
|
|
`--baseline-report`:
|
|
|
|
```bash
|
|
uv run moli-benchmark run \
|
|
--baseline-report ../benchmarks/results/2026-08-01
|
|
```
|
|
|
|
A completed command is not automatically publishable evidence. Smoke and
|
|
horizontal runs are normally investigations. Treat a report as formal only
|
|
when the required formal workloads were run and `publish-readiness.json`
|
|
reports that all gates passed.
|
|
|
|
Public-web measurements also depend on the network and changing site content.
|
|
Use local synthetic suites for deterministic regression checks, and public-web
|
|
suites for compatibility evidence rather than exact repeatability.
|
|
|
|
On Linux, the sampler records process-tree PSS from `/proc` when available and
|
|
falls back to RSS otherwise. Startup runs also retain available GNU `time`,
|
|
procfs, and cgroup evidence instead of silently inventing missing metrics.
|
|
|
|
## Spider Bench
|
|
|
|
`browser-spider-local/` is a separate Node.js/Playwright runner used for
|
|
multi-site spider comparisons and pull-request benchmark artifacts. It records
|
|
correctness, per-site outcomes, and process-tree resource samples in its own
|
|
offline report.
|
|
|
|
```bash
|
|
cd browser-spider-local
|
|
npm ci
|
|
npm run bench -- --help
|
|
```
|
|
|
|
Run the command with `--help` to see fixture, public-site, sampling, and output
|
|
options. The pull-request workflows under `.github/workflows/` are the source
|
|
of truth for CI execution and permissions.
|
|
|
|
## Development
|
|
|
|
Run the core CLI tests from `moli-benchmark/`:
|
|
|
|
```bash
|
|
uv run python -m unittest discover -s tests -p 'test_cli.py'
|
|
```
|
|
|
|
The complete test suite uses `uv run python -m unittest discover -s tests`; its
|
|
curated public-web seed CSV files are versioned under `fixtures/top-sites/`.
|
|
|
|
Keep benchmark claims tied to archived raw data, exact binary versions, and
|
|
the readiness checks. When adding a suite or target, update its CLI help and
|
|
report metadata before expanding this overview.
|