pcbjam/tests
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Istvan Matejcsok c421d724b0 findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work
A review of the group-E fixes found 13 further defects; ten were introduced by
those fixes, two pre-existed and were merely relocated, one is deferred.

Services / transport
  E-10  retireWorker synthesized no bg/exit frame, so sharedspice's s_bgRunning
        mirror stayed latched true after a mid-run worker death: Run stayed
        disabled and the promised fresh-worker restart was unreachable for the
        whole session. Retirement now dispatches a synthetic controlled-exit
        straight to the installed handler (never through dispatchEvt — a
        fabricated frame must not touch the credit ledger). Driving the repro
        exposed two further defects, both fixed here: a replacement worker
        trapped on pre-init engine reads, and the rerun's cm_input_path/circ hit
        that uninitialized engine before KiCad's validate() re-init (the native
        flow assumes a crashed engine survives in-process — true for the dll,
        false for a dead worker). Reads now answer their empty shapes pre-init,
        writes lazy-init, and init is idempotent per worker engine.
  E-19  dispatchEvt acked only AFTER handler(evt) returned, and the sharedspice
        client deliberately rethrows non-trap errors — so each throw leaked one
        unit of the 64-frame credit window until the stream died with a
        misattributed "transport exceeded". The ack moves to a finally in both
        service copies; the throw still propagates (the trap machinery needs it).
  E-20  the oversize-line path promises to transfer the accepted prefix, but
        with the window full that flush only DEFERS, and stopEventStream wiped
        the deferred queue — losing the diagnostics that explain the failure.
        The terminal notice now carries them as pendingEvents; both hosts
        deliver them in order, unacked (the fatal frame is outside the credit
        protocol).
  E-21  the 30s prefetch deadline discarded every model already collected and
        reported nothing. A caller-owned progress sink ships the partials and
        the omission reaches the export report. (Awaiting the aborted collection
        was rejected: an in-flight source fetch is not abortable — E-4's
        original disease.) Plus a serving-candidate memo, so a .wrl ref served
        by its .step fallback stops re-probing the miss on every export.

Scheduler
  E-14  _terminalizeNativeTrap classified by message substring, so any plain JS
        error QUOTING 'Aborted(' or 'out of bounds' permanently bricked a
        healthy instance. Now structural only: instanceof RuntimeError plus a
        duck-typed name check (verified in this build's glue that abort() throws
        a genuine RuntimeError both pre- and post-runtime-init). Module.onAbort
        now latches the gate — the authoritative notification, previously
        ignored.
  E-15  the shim half: _pumpResume gates on terminal (catching wakes already
        queued at latch time) and resolveWait refuses on terminal WITHOUT
        consuming the entry, so a frame stays visibly parked rather than
        resuming inside a trapped module.
  E-16  the E-5 handler read the realm-global scheduler at dispatch instead of
        its installing module's; also frees the per-line buffer on the non-trap
        rethrow path.
  E-11  get_vec trusted the worker's res.length over the transferred arrays.
        Observed death shape: a 4 GiB std::vector threw an unhandled
        std::length_error that exited the editor's main loop. Now clamped, with
        the buffers freed on every failure path.

Guardrails (replacing two deferred refactors: e2e→production-code injection and
collapsing the four copies of the worker-lifecycle machinery)
  E-18  the source contract asserted comment-string counts — rewording failed
        CI while moving a guard outside its #ifdef passed. It now parses the
        #ifdef regions and asserts on code.
        service-stub-parity.ts pins what the four lifecycle copies must share:
        credit-window equality parsed from source, the finally-ack, boot
        deadlines, terminal-notice consumption. The transport numbers are now
        single-sourced from the worker.
        CI actually runs the gates: the web/standalone vitest suites (which had
        NEVER run in CI), the reducer, the source contract and the parity tool —
        with a NON_PLAYWRIGHT_GATES check so deleting a step re-fails the lint.
  E-22  the e2e occ stub's 60s boot watchdog, deleted in a66e109, is restored in
        the ngspice-stub shape with a wedgeNextBoot() repro hook.

Every behavioral fix has red-then-green evidence (the reds were captured first).
E-17 (a stale RUNNING cross-stamping the next run's generation under E-6's
transport deferral) is DEFERRED with its analysis recorded — a real fix needs
run identity on the bg frames.

Test hygiene: the dwell lint now requires the mandated ": <why>" and all 47 bare
markers carry their reason; three export-report dwells became modal-lease polls;
exact-ledger assertions became relative deltas; the dead data-wx-dom-id branch,
an unused fault hook and unused receipt plumbing are gone; abort scans, wx
dialog drivers, the sim harness and the vitest FakeWorker are each one copy now.

Bumps kicad and wxwidgets to their findings-group-e tips.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 18:19:16 +02:00
..
3d-regression fix(3d): blank viewer after raytracing round-trip — owner-context FFP routing + VAO isolation (gl1) 2026-08-25 11:25:34 +02:00
apps findings group O: chooser Enter + infobar close fixes (wx → 15e5315244) with repro specs and uipolish guards 2026-08-28 11:31:32 +02:00
collab feat(drift-trio): 3-client drift harness phase A + child-wire root-lift fix 2026-07-21 09:16:38 +02:00
e2e findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work 2026-08-31 18:19:16 +02:00
fixtures/demo feat(standalone): floating sheet navigator panel backed by a C++ hierarchy export (sheet-panel) 2026-08-29 16:24:57 +02:00
gal-regression jspi: fix the dead-tools ownership bug, emscripten-6 fallout, and green the full suite on Playwright 1.62 2026-08-13 17:41:28 +02:00
jspi jspi cleanup: remove the asyncify-era residue — dead code, conditionals, pipeline scaffolding, stale prose 2026-08-14 09:25:32 +02:00
kicad findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work 2026-08-31 18:19:16 +02:00
scripts eeschema simulator: lazy ngspice_service worker — static sharedspice (XSPICE registry + CIDER), init_dll ifdef, e2e both engines 2026-07-19 15:59:21 +02:00
tools findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work 2026-08-31 18:19:16 +02:00
web findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work 2026-08-31 18:19:16 +02:00
.gitignore screenshots: R2-hosted manifest becomes the baseline source of truth (morelli cutover) 2026-08-19 14:35:08 +02:00
find-hardcoded-coords.sh Extend element registry and migrate all tests to semantic selectors 2025-12-30 14:17:51 +01:00
GL_README.md feat(test): Update wizard test to use dynamic button labels 2026-01-12 15:17:03 +01:00
global-setup.ts e2e/CI: dual-engine suites, per-engine screenshots, SwiftShader retired, prod web suite, CI-coverage gate 2026-07-17 12:21:54 +02:00
package-lock.json tests: move screenshot baselines from git to R2 (content-addressed manifest v2) 2026-08-19 09:21:28 +02:00
package.json findings(E-10..E-22): fix the defects a code review found in the E-1..E-9 work 2026-08-31 18:19:16 +02:00
playwright-web.config.ts jspi: fix the dead-tools ownership bug, emscripten-6 fallout, and green the full suite on Playwright 1.62 2026-08-13 17:41:28 +02:00
playwright.config.ts eeschema copy/paste fix: wx UTF-8 text dataobj (c1f1477) + regression specs 2026-08-26 11:21:00 +02:00
README.md screenshots: R2-hosted manifest becomes the baseline source of truth (morelli cutover) 2026-08-19 14:35:08 +02:00
serve.json feat(wasm-eh): migrate the WASM build to native wasm exceptions (+ 3D viewer default-on) 2026-06-30 09:40:26 +02:00
TESTING.md screenshots: R2-hosted manifest becomes the baseline source of truth (morelli cutover) 2026-08-19 14:35:08 +02:00
tsconfig.json Add TypeScript configuration for Playwright tests 2025-11-29 12:39:30 +01:00
WHATWORKS.md screenshots: R2-hosted manifest becomes the baseline source of truth (morelli cutover) 2026-08-19 14:35:08 +02:00
wizard-04-finish-headless-firefox.png tests: dark-mode spec diffs against a per-engine light-mode reference 2026-08-28 23:24:20 +02:00
wizard-04-finish-headless.png staging CI green-up: P-1 apply-slot wedge under JSPI, popup P-4 regression, ysync double-seed header, spec fixes 2026-08-28 22:25:21 +02:00

KiCad WASM Tests

Playwright tests for verifying the wxWidgets WASM port.

Prerequisites

  • Node.js 18+
  • Emscripten SDK (for building)

Building the Test App

../scripts/build-wasm-test.sh

This builds apps/minimal_test.{html,js,wasm} and standalone test apps.

Running Tests

npm install
npm test          # setup:kicad + the full merged run (same projects as CI)

One merged config (playwright.config.ts) drives every wasm suite as Playwright projects; npm run test:e2e runs the CI set: wx-chromium, kicad-firefox, kicad-chromium, jspi-firefox, coroutine-firefox. The KiCad specs (heavier — they need the docker-built KiCad WASM) run on BOTH engines; npm run test:kicad is the firefox-only shortcut. The React web app suite is separate: npm run test:web (see playwright-web.config.ts).

To run a subset, pick a project (and optionally a spec):

npx playwright test --project=wx-chromium menu.spec.ts     # wx menu tests only
npx playwright test --project=kicad-firefox kicad/pcbnew.spec.ts
npx playwright test --project=wx-chromium --grep "wxTimer"

Test Structure

tests/
├── e2e/                    # Playwright test specs
│   ├── utils/              # Shared test utilities
│   │   ├── fixtures.ts     # Playwright fixtures with auto-logging
│   │   ├── element-tracker.ts  # Element registry utilities (clickByLabel, etc.)
│   │   └── test-utils.ts   # Logging and helper functions
│   ├── menu.spec.ts        # wxMenuBar tests
│   ├── timer.spec.ts       # wxTimer tests
│   ├── dialog.spec.ts      # wxDialog/wxMessageBox tests
│   ├── tree.spec.ts        # wxTreeCtrl tests
│   ├── grid.spec.ts        # wxGrid/wxSpinCtrl/wxSearchCtrl tests
│   ├── wxwidgets.spec.ts   # Comprehensive UI interaction tests
│   └── ...
├── logs/                   # Test logs (auto-generated)
├── test-results/{chromium,firefox}/   # Screenshots per engine (auto-generated)
├── baseline-screenshots/{chromium,firefox}/  # Reference screenshots per engine — gitignored cache of the R2 bucket (npm run screenshots:fetch)
├── .baseline-manifest.json  # Gitignored copy of the R2-hosted baseline manifest (npm run screenshots:fetch-manifest)
├── apps/              # Built WASM test applications
│   ├── minimal_test.html  # Main test app
│   └── standalone/        # Individual component test apps
├── playwright.config.ts       # THE merged config (wx / kicad / jspi / coroutine / perf projects)
└── playwright-web.config.ts   # React web-app suite (own server stack)

Logging

Each test automatically captures:

  • Console logs with timestamps and log levels
  • Page errors with full stack traces

Log files are written to logs/ after each test:

  • <test-name>.log - All console output
  • <test-name>.errors.log - Errors only (created if errors occurred)

Example log format:

[2025-11-29T19:39:42.165Z] [LOG] [EVENT] Application started
[2025-11-29T19:39:42.733Z] [WARNING] GPU stall due to ReadPixels
[2025-11-29T19:39:42.801Z] [ERROR] Some error message

Screenshots

Tests capture raw PNGs to test-results/<engine>/ (engine-scoped via stableShot/shotPath). The offline gate compares them against the per-engine baselines pinned by the R2-hosted manifest:

npm run screenshots:fetch-manifest && npm run screenshots:fetch
npm run screenshots:check

CI's Linux render is the source of truth — update baselines by promoting a CI run in the morelli review app (https://pcbjam-morelli-staging.pcbjam-staging.workers.dev), never by copying local renders. Rules and details: TESTING.md.

Viewing the App Directly

Start a local server in the apps directory:

cd apps
npx serve .

Then open http://localhost:3000/minimal_test.html in your browser.

Alternative using Python:

cd apps
python3 -m http.server 8000

Then open http://localhost:8000/minimal_test.html

Test Categories

Spec File Tests Description
wxwidgets.spec.ts Comprehensive Full UI interaction, stability
menu.spec.ts wxMenuBar Menu bar visibility and interactions
timer.spec.ts wxTimer Timer start/stop/reset functionality
dialog.spec.ts wxDialog Message boxes and custom dialogs
tree.spec.ts wxTreeCtrl Tree control with expand/collapse
grid.spec.ts wxGrid Grid, SpinCtrl, SearchCtrl
aui.spec.ts wxAuiManager Dockable panels
clipboard.spec.ts wxClipboard Copy/paste operations
dataview.spec.ts wxDataViewCtrl List and tree data views (Zone Manager-like)
filedialog.spec.ts wxFileDialog File open/save dialogs
htmlwin.spec.ts wxHtmlWindow HTML rendering (About dialogs, error formatting)
layout.spec.ts wxSplitter Splitter and scrolled windows
toolbar.spec.ts wxToolBar Toolbar buttons and status bar

Debugging WASM Crashes

When a test fails with a WASM crash (e.g., "memory access out of bounds"), you can build with debug symbols to get meaningful stack traces:

Debug Build

# Build test apps with DWARF symbols and source maps
../scripts/build-wasm-test.sh --debug

This enables:

  • -g for DWARF debug info
  • -gsource-map for browser source maps
  • -O0 for no optimization (preserves debugging context)

Reading Stack Traces

With a debug build, WASM stack traces show actual function names:

Before (release build):

RuntimeError: memory access out of bounds
    at wasm-function[102]:0xfdf8
    at wasm-function[99]:0xe6e0

After (debug build):

RuntimeError: memory access out of bounds
    at grid_test.wasm.GridTestFrame::LogEvent(wxString const&)
    at grid_test.wasm.GridTestFrame::OnGridCellSelect(wxGridEvent&)
    at grid_test.wasm.wxEventFunctorMethod<...>::operator()

Using LLVM Tools

For deeper analysis, use Emscripten's LLVM tools:

LLVM_DIR="/opt/homebrew/Cellar/emscripten/4.0.20/libexec/llvm/bin"

# Check if WASM has DWARF info
$LLVM_DIR/llvm-dwarfdump --debug-info apps/standalone/grid/grid_test.wasm

# Disassemble with function names
$LLVM_DIR/llvm-objdump -d grid_test.wasm | head -200

The wxWidgets WASM port includes an element registry that tracks all wxWindow instances with their positions, labels, and types. This enables tests to find UI elements by semantic identifiers instead of hardcoded pixel coordinates.

Usage

import { waitForRegistry, clickByLabel, findByLabel, findByType } from './utils/fixtures';

// Wait for registry to be available
await waitForRegistry(page);

// Click buttons by label text
await clickByLabel(page, 'Copy to Clipboard');
await clickByLabel(page, 'Save File...');

// Find elements for inspection
const button = await findByLabel(page, 'OK');
if (button) {
  console.log(`Button at (${button.centerX}, ${button.centerY})`);
}

// Find all elements of a type
const buttons = await findByType(page, 'wxButton');

Available Functions

Function Description
waitForRegistry(page) Wait for element registry to initialize
findByLabel(page, label, options?) Find element by label text
findByName(page, name, options?) Find element by wxWindow name
findByType(page, typeName, options?) Find all elements of a type (e.g., 'wxButton')
clickByLabel(page, label, options?) Click element by label
clickByName(page, name, options?) Click element by name

Options

interface FindOptions {
  visible?: boolean;  // Filter by visibility (default: true)
  enabled?: boolean;  // Filter by enabled state
  exact?: boolean;    // Exact label match (default: substring)
  type?: string;      // Filter by type name
}

When to Use

Use the element registry for tests that click on wxButton and other wxWindow-based controls. The registry tracks:

  • wxButton, wxTextCtrl, wxStaticText, wxPanel, wxFrame, etc.

Not trackable (use pixel coordinates instead):

  • wxToolBar tool items (rendered by toolbar)
  • wxMenuBar menu items (rendered by menu system)
  • wxAuiManager panel controls (title bars, close buttons)
  • wxGrid cells (rendered by grid)
  • wxSplitterWindow sash (rendered by splitter)

Migrated Tests

These tests use the element registry:

  • clipboard.spec.ts - Copy, Paste, Check, Clear buttons
  • dialog.spec.ts - Info, Yes/No, Error, Custom dialog buttons
  • timer.spec.ts - Start, Stop, Reset buttons
  • filedialog.spec.ts - Open, Save, Open Multiple buttons
  • logerror.spec.ts - Trigger Error, Flush Log buttons

Available Test Apps

App URL Description
/standalone/clipboard/clipboard_test.html Copy, Paste, Check, Clear buttons
/standalone/dataview/dataview_test.html wxDataViewListCtrl and wxDataViewTreeCtrl (Zone Manager-like data)
/standalone/dialog/dialog_test.html Info, Yes/No, Error, Custom dialog buttons
/standalone/htmlwin/htmlwin_test.html wxHtmlWindow with various HTML content
/standalone/tree/tree_test.html Expand All, Collapse All, etc.
/standalone/menu/menu_test.html Menu bar testing
/standalone/grid/grid_test.html Grid controls
/standalone/aui/aui_test.html AUI panel controls
/standalone/toolbar/toolbar_test.html Toolbar buttons
/standalone/timer/timer_test.html Timer controls
/standalone/filedialog/filedialog_test.html File dialog buttons
/standalone/layout/layout_test.html Layout controls

Environment Variables

Variable Default Description
APP_URL (required) URL path to scan
STEP 10 Pixel step size for scanning (smaller = more accurate but slower)
START_X 0 X coordinate to start scanning
END_X canvas width X coordinate to end scanning
START_Y 0 Y coordinate to start scanning
END_Y canvas height Y coordinate to end scanning

Output

The utility outputs:

  • Button positions with labels (from console log keywords)
  • Generated test code snippets
  • Results JSON file at test-results/button-finder-results.json

Example output:

RESULTS: Found 4 buttons

Button positions (relative to canvas):

  Copy         at (352, 196)
    Log: [CLIPBOARD_EVENT] Attempting to copy text to clipboard...

  Paste        at (600, 196)
    Log: [CLIPBOARD_EVENT] Attempting to paste from clipboard...

Known Issues

  • Timer tests: May fail due to timing sensitivity
  • Tree tests: Button click positions may vary

Open tasks

  • Research: are the Asyncify fiber shims still needed under native-EH? Resolved at doc 20 D-1, then mooted by the JSPI migration (2026-08): the ablation builds (races_test_noheal / races_test_nosleepfix) and their shim-redundancy pins (in the since-deleted asyncify/asyncify-races.spec.ts) pinned a runtime that no longer exists, and the asyncify scheduler shim they were measured against retired with the backend. The semantic race battery lives on in jspi/suspend-races.spec.ts against the JSPI runtime.

Collab e2e — legacy vs v2 bundles, and repro markers

Two esbuild bundles (npm run build:collab, rebuilt by the specs' beforeAll):

  • apps/kicad/collab-bundle.js — the LEGACY scalar wire (startCollab / kicadCollabSnapshot/Apply / onDelta). Dead in production (nothing registers onDelta); driven by the pre-existing *-collab.spec.ts two-tab tests. Kept only until the scalar wire is deleted.
  • apps/kicad/collab-bundle-v2.js — the PRODUCTION v2 "items" stack (bindKicadCollab over kicadCollabSnapshotItems/ApplyItems/onItems, Y keys kdoc_*), from collab/browser-entry-v2.ts. Driven by kicad/ysync-two-tab.spec.ts. The build aliases yjs to ONE copy (the two web pnpm workspaces otherwise bundle two, and Y types are instanceof-checked).

ysync repro tests

kicad/ysync-two-tab.spec.ts, kicad/ysync-repros-{pcbnew,eeschema}.spec.ts reproduce the bugs of the 2026-07-02 Yjs⇄KiCad sync review (docs/features/ysync-review/ on the ysync-review branch; unit-level repros live in web/pcbjam-shared/test/ysync-repros.test.ts and web/standalone/src/wasm/collab/ysync-repros.test.ts).

Convention: a repro asserts the CORRECT behavior and is marked expected-fail (test.fail() / vitest it.fails) with a comment naming the bug doc. The suite stays green while the bug is open; fixing the bug flips the repro to "unexpected pass", forcing the marker's removal — the repro becomes the regression test. Green companion tests pin each repro's preconditions (harness, apply path, emit path) so an expected failure can only come from the bug itself. The "local move emits" controls double as the headless-emit probes gating the emit-dependent repros.

Follow-up (tracked in review miss 11): once the v2 specs are trusted, un-skip/retire the legacy two-tab specs together with the legacy wire.