Commit graph pcbjam/tests/e2e
Author SHA1 Message Date
Gergő Törcsvári
8fa6bae084
fix: hollow sheet rooms wiped subsheets on entry (ysync 0011) + hierarchy pane icon sizing/selection colours
Collab: a save-all's layout-only sync into a never-entered sheet room left a
doc with layout but zero items; the first entry adopted it and removed every
item on screen. seed() now file-seeds a hollow doc, syncLayoutFromSave skips
never-seeded empty rooms, and the ydoc-boot / sibling-restage paths fall back
to the file. Tree: new tree-hier standalone app + e2e spec covering the
wxwidgets bundle-size and unfocused-selection fixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189z2siCsezE39ugcDKNBMs
2026-08-27 08:59:01 +02:00
Istvan Matejcsok
d7cb570f07 fix(wasm): 3D-viewer toolbar clicks hijacked by hidden main-frame DOM controls — input barrier + canvas anchoring
wxwidgets bump: main-window DOM controls join the input barrier when a
secondary window overlaps them; secondary windows/GL canvases anchored to the
.window.toplevel border→outline.

Tests (TDD, red on the old wx.js): standalone secondary-frame app (main-frame
wxChoice under a secondary wxFrame's AUI toolbar) + e2e/secondary-frame-input
spec (fall-through hit-testing, click delivery, barrier follows drags), and
kicad/3d-viewer-toolbar-hijack spec (viewer at top-left over pcbnew's combos —
the user-reported repro). modal.spec border assertion updated to the outline
ring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 13:19:34 +02:00
Istvan Matejcsok
6034a0550d eeschema copy/paste fix: wx UTF-8 text dataobj (c1f1477) + regression specs
Cmd+C/Cmd+V in the wasm eeschema pasted a stray "(" SCH_TEXT — the stroke
font renders it as a small blue arc — instead of the copied symbol, and any
copy reached navigator.clipboard as a single character. Root cause and fix
live in wxwidgets (90bbad29822: UTF-8 wxDF_UNICODETEXT + the port's missing
GetAllFormats key function; c1f14775ba3: empty-read cache fallback + no
browser pre-clear on copy). This bump carries them plus the guards:

- tests/kicad/eeschema-copy-paste.spec.ts: chromium asserts the full
  multi-form (lib_symbols …)(symbol …) blob reaches navigator.clipboard;
  both engines assert copy → paste → save yields a second symbol and no
  stray (text …) item (firefox exercises the m_textCache fallback path).
  The kicad-chromium project now grants clipboard-read/write. NB the kicad
  projects' device UAs claim Windows, so specs must send plain Control+…
  — ControlOrMeta resolves to Meta on a mac host, which the "Windows" app
  ignores, and the bare key fires eeschema hotkeys instead.
- tests/e2e/textdataobj.spec.ts + Makefile.wasm target `textdataobj`:
  drives the wx-repo harness app (wxwidgets/tests/wasm/textdataobj_test.cpp)
  and asserts the SUITE-DONE failure counters plus the full-string browser
  round-trip, sentinel-seeded so a failed write cannot read as stale success.
- tests/e2e/clipboard.spec.ts: the Copy test now asserts the full input
  text reaches the clipboard (previously truncated to "S").

Verified locally: all specs red on the old wx, green after; wx-chromium
320/320, kicad-chromium 121/121 (collab specs env-gated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 11:21:00 +02:00
Istvan Matejcsok
daaff1f973 fix(3d): blank viewer after raytracing round-trip — owner-context FFP routing + VAO isolation (gl1)
Switching OpenGL → raytracing → OpenGL could leave the viewer showing only the
background gradient, with mid-session "[gl1] WebGL context changed" thrash and
INVALID_OPERATION storms on BOTH WebGL contexts. Traced mechanism: the shim's
FFP draw routing keyed on one process-global client-array flag; an interrupted
fixed-function window (MODEL_3D::BeginDrawMulti loops, or the switch-back
reload re-recording display lists with the GL context lock released across
JSPI suspensions) left it set, after which the raytracer blit's and the 2D
GAL's glDrawArrays were routed through the FFP pipeline — and one misrouted
draw permanently repointed the VICTIM's own VAO attributes at shim buffers
(the blit's attribute 0 collides with ATTR_POSITION), so both stayed
broken/blank even after the flag cleared.

Shim fixes (wasm/gl1):
- Owner-context routing gate: __wrap_glDrawArrays/Elements route into the FFP
  pipeline only under the shim's owner context (adopted at the first FFP
  client-state mutation or programSync in a context); foreign-context draws
  always pass through — the 2D GAL can never be misrouted and the context
  guard can never thrash.
- VAO isolation (ScopedDefaultVAO): draw executors do their attribute setup on
  VAO 0 and restore the caller's binding — a misrouted draw can no longer
  corrupt the caller.
- contextSync() resets the whole client-array mirror on a context change
  (enables/pointers/VBO names all described the dead context).

kicad pointer bump (d6e3dc1a87a): blit preamble disables the four client
arrays (same-context firewall) + DoRePaint hidden-parent early return now
clears m_is_currently_painting like its six siblings (a standalone
sufficient cause of a permanently blank viewer).

TDD (each observed red before its fix, green after; harness = authoritative):
- T1 VAO corruption, T2 foreign-context routing + guard thrash, T3 stale
  client-state surviving context recreation — tests/e2e/3d-webgl.spec.ts over
  new harness choreography (appQuad/ffpMakeStale/createSecondContext/
  quadDrawFresh). Parity stays 47/47 with zero drift.
- tests/kicad/3d-viewer-engine-toggle.spec.ts (new, CI-skipped like the
  deadlock spec): real round-trip happy-path gate — board re-renders, zero
  [gl1] lines, zero INVALID_OPERATION. (The raytraced image itself never
  displays on the wasm build — the pre-existing inert-toggle KNOWN ISSUE in
  3d-viewer-deadlock.spec.ts, out of scope here; the engine switch and the
  poisoning reload path run regardless.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:25:34 +02:00
Istvan Matejcsok
3722891d48 fix(3d): blank render + lost position after 3D viewer close/reopen (gl1 context guard)
Closing the viewer destroys its wxGLCanvas's WebGL context; reopening mints a
new one. The gl1 shim cached GL names (FFP program, stream/scratch VBOs) in
never-reset statics behind `if (!handle)` guards — in the new context every
draw died with INVALID_OPERATION and the viewer showed only the clear color
("Reload time 0.031 s" is benign: warm model caches make the rebuild fast).

contextSync() (gl1_state.cpp) now detects the context change in programSync()
— the one choke point every shim draw crosses, and a path the 2D GAL never
reaches (a first attempt checking in the glBindTexture wrap saw the GAL's
context and thrash-rebuilt the program 23x per run) — and drops the cached
names for lazy rebuild in the new context. Context identity is a monotonic id
stamped on Emscripten's per-context record: the numeric
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE is recycled, so a destroy-then-create can
return the same number and a handle comparison detects nothing.

The lost-position half is a wxwidgets wasm fix (pointer bump: GetFromWindow
reports display 0; saved geometry used to carry display=(unsigned)-1, which
LoadWindowState treats as "display not found" and re-centres the frame).

TDD (red observed before each fix, green after):
- tests/kicad/3d-viewer-reopen.spec.ts (new, own worker like the deadlock
  spec): load board, open viewer, render-gate, drag by the titlebar, close
  via the x, reopen; asserts the board re-renders (was: 1 distinct colour for
  90 s) and the window position is restored (was: re-centred to 0,0 after
  closing at 40,90). Green run logs exactly one [gl1] context-change line.
- 3d-regression harness: recreateContext() destroys the context AND swaps in
  a fresh canvas element (a browser canvas keeps its context for life, so
  same-element recreation hands back the live old context and hides the bug);
  the new 3d-webgl spec test renders redraw-mini-board-navigator before and
  after recreation and requires pixel-identical output. Parity: 47/47, zero
  drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 16:18:22 +02:00
Viktor Vaczi
9c475a804e jspi cleanup: remove the asyncify-era residue — dead code, conditionals, pipeline scaffolding, stale prose
The runtime is JSPI-only; this removes everything that still pretended
otherwise. Three exhaustive sweeps (C++/JS+build+CI/tests+docs) drove
the inventory; every deletion verified by grep closure + full gates.

Broken-right-now fixes:
- deploy-staging.yml passed the retired opt_level input — the workflow
  could not even start. Removed.
- env.sh carried dead exports with a live -sASYNCIFY=1 inside
  (WASM_LDFLAGS/PTHREAD_LDFLAGS, zero consumers). Removed; the
  WASM_LEGACY_EXCEPTIONS rationale rewritten to the real reason.
- docker/build.sh exported PCBJAM_ASYNC_BACKEND (read nowhere). Gone.

Dead weight removed:
- binaryen submodule (nothing builds or invokes it), wasm-opt-bench
  workflow + scripts/bench/, get-wasm-opt.sh, diagnostics.js (242 lines
  of Asyncify-API-only code), the KICAD_PIPELINE background-postprocess
  scaffolding (existed to parallelize the deleted wasm-opt phase; the
  postprocess is a seconds-long node script and now runs inline),
  build-monitor's dead asyncify rows, sched-context orphan build
  output, dead .gitignore entries, the .jspi-assets spike dir (the two
  wf-result research JSONs moved to docs/features/async/migration-evidence/).
- bindings: fiber_park.h + its 12 embind registrations (broken-if-
  called under JSPI), the kicadOpenFileStart/OPEN_JOB starter route,
  main_stack_runner.h + 5 includes, the always-null context-sleep weak
  hook in nanosleep_yield.c.
- shim: the backend field (installed-flag idempotency instead),
  noteContextWait (dead both sides), the __wxAsyncifyDump alias (+ the
  WasmTool fallback and string-dump normalize branch).
- web: the emscripten-6-ignored mainScriptUrlOrBlob option in boot.ts
  (gerber-demo keeps it: it loads the deployed CDN release, which
  predates emscripten 6 — noted inline).

Conditionals: all 'backend === jspi' checks reduced to scheduler-
presence checks; races_quiescent re-keyed from Asyncify.state (vacuous)
to real backlog quiescence (resumeReady/mutatorQueue — NOT _windowLive,
which is the probing activation's own window by definition).

Renames (identifiers only, no file renames): ASYNC_LINK_FLAGS→
JSPI_LINK_FLAGS and Makefile ASYNC_LDFLAGS→JSPI_LDFLAGS,
kicadCollabFiberBusy→kicadCollabBusy (embind + web + tests),
collab_common.h fiber*→apply*/coroutine naming, asyncifySignatures→
wasmTrapSignatures (lists byte-identical).

Tests: the two remaining vacuous [wx-asyncify]/fiber-resume-refused
asserts re-keyed to live JSPI beacons; eeschema-load's failure message
no longer sends the developer to a deleted script; wait-beacons' dead
families/parser deleted; lane-0 legacy-glue guards removed (lane 0 is
unconstructible); the embind test.fail re-gated with the JSPI reason
(plain embind invokers cannot suspend — verified still failing);
lint-determinism now scans tests/jspi (166 files clean);
eeschema-collab local-move gated to chromium (~50% flaky on FF even
solo; pcbnew twin covers both engines).

Docs: DEBUG.md rewritten as the JSPI debugging guide; build.md
describes the single-phase build; docs/features/async/README.md
banner-marked historical and repointed at the NEW
23-jspi-runtime.md (current architecture: export census, turnstile,
libcontext ownership + refusal contract, embind call shapes, the
em-pthread service-wrapper trick, exception policy, known gaps).

Gates on the cleaned tree: test:e2e 725 passed / 0 failed (after the
quiescence-probe fix; the 3 other reds were verified contention flakes
solo-green or the documented FF gate), web 76/0, jspi 18/18 both
engines, vitest 295/295 + 17/17, all lints green, live-app census
clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016X9eh1s5sTx1o9Em9KBuwR
2026-08-14 09:25:32 +02:00
Viktor Vaczi
3ee174e9b4 tests: un-skip sweep — 26 tests revived on the JSPI build, failures re-gated with fresh evidence
Empirical pass over every skip/fixme whose premise the JSPI migration
could have changed. Revived (verified green):

- Firefox wasm-budget guards RETIRED (drift-trio, drift-trio-fuzz,
  drift-trio-scenarios, ysync-two-tab, ysync-libsymbols): the JSPI
  build (~half the asyncify size) fits three editor tabs inside
  Firefox 153's per-process budget — +20 firefox collab tests.
- roundtrip 'pcbnew preserves items through a yjs round trip': the
  asyncify-fragile envelope parse it waited on is gone — both engines.
- drift-trio-scenarios S4/S4b: converge now (was KNOWN ~5-8%).
- pcbnew-collab + eeschema-collab 'a local move propagates A→B'.
- web eeschema-fp-selector, read-only-editor's fixme'd writer-stream
  test, footprint-browse-remote read path (chromium; firefox gated:
  FootprintEnumerate rows never appear in 60s — slow wasm tier
  suspected).

Still broken, re-gated with re-verified reasons:

- 3d-viewer raytracer engine toggle: still inert, both engines.
- maximize display geometry: wxDisplay reports 0x0 in the harness.
- web editor WRITE bridge (symbol/footprint × remote/spike): wedges at
  the New Symbol/Footprint dialog step on both engines — the
  web-e2e-rot 01 gap stands for writes.

Verify runs: kicad+wx touched files 93 passed / 0 failed / 7 skipped
(intended gates); web touched files 7 passed / 0 failed / 9 skipped.
lint:determinism + lint:ci-coverage green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016X9eh1s5sTx1o9Em9KBuwR
2026-08-13 18:43:36 +02:00
Viktor Vaczi
db819850ee jspi: fix the dead-tools ownership bug, emscripten-6 fallout, and green the full suite on Playwright 1.62
Live-app fix (Place Footprints / routing dead in Chrome): submodule
bumps carry the coroutine ownership fix (kicad 012d95ecb4) and the
handler-exception survival fix (wxwidgets 1b5f0e31f4).

Emscripten-6 fallout:
- occ/ngspice worker wrappers: mainScriptUrlOrBlob was removed
  upstream; pthread children re-run the wrapper blob, so an em-pthread
  realm now importScripts the glue and gets out of the way (before:
  recursive service boots, pool never fills, silent 180s boot hangs —
  every occ spec and ngspice bg_run).
- Makefile.wasm: -sASYNCIFY frankenlinks on the no-wx coroutine repro
  targets ported to -sJSPI (the JSPI-only libcontext crashed at first
  yield under them); mainloop/gl repro pages drive their tick through a
  promising export (emscripten_set_main_loop callbacks cannot suspend);
  retired inject-dyncall-shims lines removed (targets were unbuildable
  since Phase 8); $stringToNewUTF8 force-included (the EM_ASM value
  bridge aborted the runtime on the first decoded exception).
- fiber-park levers: neither embind shape can drive suspending levers
  (plain throws on strict-JSPI Firefox; emscripten::async() re-executes
  its invoker on settle) — kept sync for manual Chromium probing, spec
  coverage moved to the jspi-coroutine harness (18 cases).

Suite work:
- Playwright 1.61.1 -> 1.62.1 (Firefox 153: JSPI on by default).
- fiber-resume-park.spec retired -> coroutine-lifecycle.spec: census
  gate over boot / board load / chooser open / cancel (deterministically
  red on the pre-fix build).
- Blind asyncify-era pins re-keyed: quasimodal-strand + wait-beacons
  beacon regexes, footprint-chooser-close liveness -> wx parking-timer
  heartbeat (scheduler counters idle flat on Firefox).
- occ/ngspice test providers: 60s boot timeout + worker error
  surfacing (a worker death used to be a silent 180s timeout).
- Harness pages: stale 9.99 config dir -> 10.0 (library_manager wxCHECK
  noise, chooser had no libraries).
- gal-webgl harness: missing artifacts rebuilt (boost/glm extracted to
  the host sysroot), PgmOrNull stub added for the rebased GAL.
- jspi-scheduler: clean-shutdown console line restored (app-quit
  contract), quarantine never yanks SP from a live window.

Gates: test:e2e 699 passed / 0 failed (wx-chromium, kicad-firefox,
kicad-chromium, jspi-firefox, coroutine-firefox); web ff/cr/mobile 71
passed; lint:ci-coverage 166, lint:determinism 163, screenshots
manifest 492 current, corpus 7/7, tools:contract green. Offline
screenshot baselines show expected mass drift from the engine bump —
re-baseline (screenshots:noise -> promote) is a follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016X9eh1s5sTx1o9Em9KBuwR
2026-08-13 17:41:28 +02:00
Gergő Törcsvári
7dfd4f1834
docs 22 D-on probe + app-quit teardown spec; bump wx (alignment) + kicad (root identity)
The D-on probe (wxWASM_STAR_DISPATCH=1 on top of D5) is measured and
recorded in doc 22 §10: the week's 'environmental cliff' was adopted-fiber-
stack alignment (trap 1, fixed in wx - retires the D5 entry's findings 2-3
with a mechanism); with it fixed D5+D reached 388 passed; the remaining
reds decompose into three named Phase B gaps (wake ordering on a running
context, finished-coroutine transfer livelock, invocation-aware root
routing), each with a deterministic repro suite. D parks again behind the
switch until Phase B owns coroutine lifetimes.

app-quit.spec.ts + a wx_test_quit hook in minimal_test drive a real
File->Quit-shaped exit through D5's detached teardown (loop exits on its
context, S6 latch 'clean', OnExit + wxUninitialize there) - green in the
D-on probe, closing the D5 teardown-gate open item.

Landing state (D off): coroutine + coroutine-pthread + app-quit green;
nested red at case 3 in the pre-existing doc-19 wake-window family
(cases 1-2, yesterday's blockers, now pass); races wakeup_during_transition
stays layout-sensitive. Both are the class the C+B+E flip removes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LBjomQfKyRa3jBdeAKpmTw
2026-08-10 10:14:18 +02:00
Gergő Törcsvári
ab4e8090fe
test: uipolish regression app + spec for the wasm-ui-polish fixes
Self-asserting wx app (tests/apps/standalone/uipolish) with 8 checks:
clip-clear / clip-empty / clip-box (DC clip box reaches the canvas — the
collapsed wire-properties-panel bug), blit-origin (wxBufferedDC device
origin), mask-alpha (ConvertToImage carries wxMask — infobar close button),
scaled-dims (physical size for scaled bitmaps), checkbox-floor (selection-
filter density) and statbmp-best (bundle logical size — layer-panel icons).
The spec runs a default-DPR pass plus a deviceScaleFactor:2 pass that also
asserts the statbmp <img> ships the 32px asset at 16 CSS px.

Bumps wxwidgets for the CloneGDIRefData SOURCE_NONE fix the @2x pass
surfaced (empty statbmp data URL on hi-DPI).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mh188aysvgeaTRYh6syztQ
2026-07-28 18:00:35 +02:00
Viktor Vaczi
d538b73557 fix(wx): thread-safe wxString for the AsyncLoad fan-out — CvPcb-open trap solved
The ~1/9 wasm trap on CvPcb open ("index out of bounds" / "indirect call to
null" in a footprint AsyncLoad pool worker, then eeschema aborting on the
broken future — and the eeschema-fp-selector "CI-only" trap family, which was
never llvmpipe-specific) was wxString's UTF-8 build mutating SHARED strings on
read-only access from concurrent pool workers: every iterator ctor/dtor
spliced an intrusive list inside the string object, and torn splices wrote
through dead node pointers into other threads' stack frames. Second defect:
the UTF-8 position cache returned stale offsets when another thread's string
died and its address was reused.

Fixed in the wxwidgets fork (per-thread iterator registry + position cache
disabled under Emscripten) — kicad is untouched and AsyncLoad keeps its full
multi-worker fan-out. Falsified along the way (all perturbation masks, not
fixes): serializing the items, mimalloc vs dlmalloc, pthread stack size,
ASYNCIFY_STACK_SIZE, private-copy EnumFromStr, hot-path logging.

New red-first standalone app tests/apps/standalone/wxstring-mt (+ spec
coroutine-wxstring-mt.spec.ts, wx-chromium + coroutine-firefox): shared-string
compares alternating with wide-literal conversions reproduce the exact editor
trap signatures on the unfixed wx and run 4.7M rounds clean on the fixed one;
the pos-cache address-reuse dance corrupts on the first reuse before and
survives 673 after; two guard modes keep the iterator fix-up feature honest
(incl. an anti-elision liveness check — balanced register/unregister pairs in
tight loops can legally be optimized away, so a naive red test tests nothing).

Verification: wx+coroutine suites 382 passed; in-app AsyncLoad hammer 3x1000
rounds clean (baseline died <10); eeschema-assign-footprints spec 20/20
firefox + 20/20 chromium on the final build; lint:determinism clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rb9jsqtHsC3tHTaJ45244j
2026-07-20 11:56:20 +02:00
Viktor Vaczi
63ed1f3c1f e2e/CI: dual-engine suites, per-engine screenshots, SwiftShader retired, prod web suite, CI-coverage gate
Squash of experiment/ff-big-modules vs main.

Big-module routing removed: native-EH shrank kicad_editor below
SpiderMonkey's x86-64 code budget (runs 29355049705/29356152413 green on
stock Firefox), so BIG_MODULE_SPECS routing and the baseline-only-JIT
crutch are gone — kicad-firefox and kicad-chromium both run the full
suite, with the module compiled the way real users' browsers compile it.

Per-engine screenshots end to end: stableShot/shotPath write
test-results/<engine>/<name>.png; baselines move to
baseline-screenshots/{chromium,firefox}/ and the whole tools/screenshots
pipeline (compare/promote/manifest/spec-map/changelog/Discord) keys on
<engine>/<name>. Previously Firefox and Chromium renders of one spec
overwrote each other and Firefox renders were never actually gated.
Seeded from CI run 29421380806 (92 new firefox baselines, +24 chromium
web-suite shots); manifest generated from the baseline tree.

One merged playwright.config.ts (kicad/asyncify/coroutine/perf as
projects); ~25 dead npm scripts dropped. The web suite is gated in CI for
the first time ever (4 rotted specs fixed, 5 broken lib-bridge specs
triaged as fixme in docs/features/web-e2e-rot/); cheap lint step after
npm ci; last 26 blind-sleep violations fixed.

SwiftShader retired: CI Chromium renders WebGL on ANGLE → Mesa llvmpipe
(--use-gl=angle --use-angle=gl --ignore-gpu-blocklist; the blocklist flag
is mandatory — llvmpipe is blocklisted and WebGL is silently unavailable
without it) in BOTH configs. Under WORKERS=4 congestion SwiftShader
transiently failed the first post-board-load draw and the recovery
cascade ended in a silent permanent Cairo fallback — that engine flip was
the "~1.2% changedRatio both directions" occ-export baseline flake.
Validated 160/160 across two 80-repeat rigs; full analysis in
docs/features/wx-parity-bugs/occ-export-context-eviction.md. Chromium
baselines shift slightly on llvmpipe — promote once from the first green
run. Deflakes the new coverage exposed: presence baselines settle before
capture; presence fixtures declare current file formats; perf gets its
own outputDir so CI evidence survives; occ-export settles the board paint
before the export dialog; menu-item waits (waitForRenderedByLabel before
clickMenuItem) in 4 specs + the TESTING.md rule.

Web suite runs the PROD build, in parallel: webServer becomes backend
`start` + the standalone's e2e:preview (build-preview.mjs: link-wasm →
stash the public/wasm symlink aside during vite build, build-demo.mjs's
move — then vite preview as the persistent server). The wasm middleware
serves /wasm/* in preview and emits COOP/COEP/CORP itself (a pthread
worker script's own response must carry COEP or Chrome kills it with
ERR_BLOCKED_BY_RESPONSE). VITE_* flags bake at build time;
VITE_ALLOW_USER_OVERRIDE joins turbo globalEnv. fullyParallel + default
workers: 5.2m → 1.4m. Determinism fixes the parallel run exposed:
shared-page specs become serial groups; locks.spec grabs alice's exact
item via the new kicadCollabTestSelectByUuid hook (cross-tab "first
footprint" order is not a ysync invariant); quit specs poll page.url()
(quit supersedes its own navigation — NS_BINDING_ABORTED on Firefox).
Suite: 51 passed / 12 skipped / 0 failed in 1.6m.

CI-coverage gate (lint:ci-coverage): every tests/**/*.spec.ts must be
reachable from the npm scripts the workflows invoke — scraped from
.github/workflows/, resolved through package.json, coverage asked from
playwright --list itself. Rules: uncovered-spec + orphan-project (with a
documented LOCAL_ONLY_PROJECTS allowlist). Gating next to
lint:determinism; 138 spec files / 13 projects accounted for.

Product fixes kept from the investigations (reachable on real GPUs too):
wx 7799fd1be5 — paint flags clear before dispatch + Invalidate always
propagates; kicad 3dcfea5e45 — SwiftShader pass-boundary flush +
per-instance font texture + first-frame GL-error drain (GAL recovery
recovers instead of falling back to Cairo) + the user-facing eeschema
switch navigates again under __EMSCRIPTEN__ (project-sync's
FaceRegistered gate had rerouted it into the hidden sync player; caught
by the newly-gated web suite).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eUxiPApHgGiu9NFyQfhAq
2026-07-17 12:21:54 +02:00
Viktor Vaczi
252aea245f test(screenshots): hybrid render-settle — fast rAF converge, then wide confirmations
waitForRenderStable now runs two phases (still entirely in-page, still just the
canvas hash — no CDP screenshots):

  1. Fast per-rAF convergence — hash every animation frame until stableFrames are
     identical (~48ms). Catches high-frequency motion, cheap.
  2. Wide confirmation — then re-hash confirmFrames times, each `interval` ms apart
     (default 2 × 250ms ≈ 500ms). A slow async repaint (e.g. a file list arriving
     after an asyncify readdir) that a few 16ms frames would sail past now forces a
     reset back to phase 1.

This closes the hole where three ~16ms samples could declare "settled" ~200ms
before a delayed paint lands. Genuinely-animating states still never converge and
are captured at the timeout, as before.

Cost (local, 4 workers): e2e 62s->103s, kicad 155s->212s — pure waiting, no extra
CPU. Both suites green (e2e 340, kicad 92). Tunable via confirmFrames/interval.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk
2026-07-07 12:33:14 +02:00
Viktor Vaczi
c1eb08108b test(screenshots): restore screenshot-neutrality after the determinism refactor
The determinism conversion silently dropped/renamed screenshots vs the committed
baselines, so CI flagged ~35 baselines as "removed". Restore them:

- pl_editor-*: re-add the "pl_editor-" prefix the conversion dropped (5).
- Buffer screenshots that lost their path: (eeschema draw-wires/loaded-css,
  pcbnew draw-lines/loaded-css, pcbnew-move, dark-mode, eeschema-crosshair,
  aui-resize) re-add path:'test-results/<name>' so they SAVE and still feed the
  in-test pixel-diff analysis (17).
- Dropped animating/loading captures re-added via stableShot / raw screenshot:
  timer-03/05/06, dialogs-timer-running/started, wxgrid-02/03, 01-loading (7).

Delete 5 obsolete wizard baselines (eeschema/gerbview/pl_editor/symbol_editor/
generic -wizard-00-initial): every app is now seeded to bypass the first-run
setup wizard (that was the point of removing the wizard for/if loop), so those
states are no longer produced — pl_editor.spec asserts "no wizard appears"
instead.

Net: every committed baseline has a producer again (0 "removed"); determinism
guard green; affected specs pass (e2e 40, kicad 12) and emit the restored PNGs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk
2026-07-07 11:35:39 +02:00
Viktor Vaczi
4c3a4cacd4 test(determinism): deterministic waits + stableShot screenshots; drop blind sleeps/ifs/retries
Make the Playwright e2e + kicad suites deterministic so screenshot flake stops
tracing to timing races.

- Blind page.waitForTimeout -> condition waits (expect.poll, web-first
  assertions, waitUntil) + readiness helpers (waitForWxApp, waitForCanvasApp).
  Remaining sleeps are documented interaction dwells (annotated).
- Defensive "if element exists" branches -> loud asserts; label-fallback chains
  -> normalized clickMenuItemByText. First-run wizard for/if loops removed by
  seeding calculator/gerbview/pcbnew HTMLs.
- Screenshots: new stableShot(page, name) settles the render in-page (canvas
  hash over rAF) then writes a raw PNG to test-results/ for the existing offline
  gate (tools/screenshots vs baseline-screenshots). Replaces toHaveScreenshot,
  which did inline compare + its own baselines and had decoupled the specs from
  the real gate. scale:'css' pinned.
- retries: 0 in both configs.
- Guard: tests/tools/lint-determinism.ts (npm run lint:determinism) bans blind
  sleeps / toHaveScreenshot / inline retries / swallowed catches in specs;
  documented exceptions carry a marker. Rules in tests/TESTING.md.

Assertions, coverage, and renders unchanged (semantic-equivalence reviewed;
captures pixel-identical modulo inherent timer/timestamp/3d-raytrace variance).
Both suites green at retries:0 (e2e 340, kicad 92); ~35-61% faster.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk
2026-07-07 10:50:24 +02:00
Istvan Matejcsok
d9264549bf test: 💍 regression test for the notebook layer-list collapse 2026-07-06 09:17:10 +02:00
Istvan Matejcsok
ce1473c9ab test(3d): screenshot-baseline TDD suite for the 3D viewer OpenGL->WebGL port — 47 native goldens + red-state WebGL harness
tests/3d-regression mirrors the gal-regression pattern at renderer scale:
shared C++ scenarios call real KiCad 3D-viewer code (opengl_utils, display
lists + DrawCulled stencil subtraction, MODEL_3D VBOs, private generators via
a rob-template accessor, and full reload()+Redraw() composites over a
synthetic BOARD_ADAPTER). A native macOS harness renders them on real OpenGL
into 47 committed goldens (bit-deterministic, FBO capture); the wasm harness
compiles the same TUs against wasm/stubs/gl_ffp_stub.c no-ops so every
scenario renders blank — the TDD red state (parity meter: 47/47 changed).
Comparisons use the CI pixelmatch engine via the new generic compare-dirs.ts
(floors.json levels; manifest.json cmp-guards registry drift).

Documents an upstream bug: appendPostMachiningGeometry's countersink path
adds middle quads without normals, silently erasing the walls of any
display list it is batched into (3d-post-machining.png keeps the lists
separate to record it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00
Viktor Vaczi
3efcdcf91c fix(tests): enforce screenshot gate; drop shadow dir, flaky retinascale, 12 stale
Finalize the screenshot review system after the first Linux re-baseline
(that run showed 355/356 stable — the placeholder floor is fine as-is):

- ENFORCE the gate: `screenshots:check --fail-on-change` now fails the build on any
  changed/added/removed vs baselines (it still posts the drift report first, so the
  failure is actionable; a real render change → re-promote). The generic "CI failed"
  notice is suppressed for gate failures so we don't double-post on drift.
- Exclude retinascale-01-loaded: a fullPage HiDPI test whose captured height + DPR
  scaling vary run-to-run (~60% inter-run diff) — a flaky test, not render noise.
  IGNORE_SCREENSHOTS in config.ts; compare/promote/gen-manifest skip it; baseline removed.
- Kill the baseline-dir shadowing: drop e2e/baseline-screenshots/ from BASELINE_DIRS +
  delete its 3 files (grid-tab-final, wxgrid-controls, wxgrid-dedicated-page) that
  duplicated names in baseline-screenshots/ with different bytes.
- Prune 12 stale baselines (renamed/removed specs: wizard-01..04, gerbview-wizard-01..04,
  zoom-pl_editor-*, popup-03-palette).
- Delete the dead compare-screenshots.sh / update-baseline-screenshots.sh.
- Regenerate screenshot-manifest.json (355 entries).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:30:33 +02:00
Istvan Matejcsok
3628a090dd feat: 🎸 wxWidgets dialog/frame header/drag/resize 2026-06-30 15:46:52 +02:00
Istvan Matejcsok
cf1fdf5c3d feat: 🎸 wxWidgets dialog/frame DOM header 2026-06-30 15:46:49 +02:00
Viktor Vaczi
c1ef489cfa feat(wasm-eh): migrate the WASM build to native wasm exceptions (+ 3D viewer default-on)
Replace the legacy Emscripten JS-exceptions model with native wasm-EH (legacy
encoding) across the whole build, keeping Asyncify coroutines working via a
from-source Binaryen --hoist-cpp-catches pre-pass. Net result: native-EH is the
only build mode, the 3D viewer is on by default, and pcbnew shrinks substantially.

Highlights:
- Binaryen submodule everywhere + --hoist-cpp-catches integration in apply-asyncify;
  post-link Asyncify covers every app wasm (not just standalone test wasm).
- Build deps (incl. OpenCASCADE without OCC_CONVERT_SIGNALS) and all KiCad apps
  with -fwasm-exceptions; emscripten_sleep added to the post-link asyncify-imports.
- libcontext fiber entry wired under native exceptions; while-loop main loop +
  currData shim injected into all wx apps.
- Native-EH collab apply fixed: DEBUG-define the embind TU + match all out-of-CMake
  C++ TUs' ABI flags to the core, fixing the vtable-layout skew / mis-dispatch.
- 3D viewer enabled by default (real raytracer linked, not the stub).
- Retire the EH-spike scaffolding; flip the asyncify-races ablation pins to
  shim-redundancy pins (native-EH stays clean with the legacy shims ablated).
- Fix the asyncify-races quiescence check to not require Asyncify.currData==0:
  under the native-EH per-frame-yield top loop the main stack is asyncify-suspended
  every frame, so currData legitimately churns (a freed-but-not-yet-nulled buffer,
  not a leak). Refresh the pcbnew toolbar screenshot baseline for the new kicad.
- CI: drop the obsolete binaryen_version input/env (the build uses the binaryen
  submodule fork's wasm-opt, not a version download); key the wasm-output cache on
  the binaryen submodule SHA instead.

Bumps the wxwidgets + binaryen submodules to their squashed feature commits.

Validated green: all 7 apps native-EH (real 3D in pcbnew); KiCad e2e 63/63
Firefox + Chromium (3D viewer renders); wx 336; coroutine 34/34 both engines;
asyncify 7/7 both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:40:26 +02:00
Istvan Matejcsok
13cac06778 fix(wasm): radio groups no longer merge into one
Bump wxwidgets to the %p->%zu radio-group name fix and add a standalone
wxWidgets harness plus an e2e regression that asserts three radio groups
in one window select independently. Build artifacts are gitignored; only
radiogroups_test.cpp is tracked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 12:54:35 +02:00
Istvan Matejcsok
a4dd0eaf3b fix: 🐛 wxWdigets popup position 2026-06-23 14:32:38 +02:00
Istvan Matejcsok
6bbd211c4a fix: 🐛 modal border + background after dnd
 Closes: #22
2026-06-22 13:41:01 +02:00
Istvan Matejcsok
cee569dc5d fix: 🐛 kicad aui resize
 Closes: #20
2026-06-22 08:49:22 +02:00
Istvan Matejcsok
4c199dff9f fix: 🐛 tooltip + test cases
 Closes: #18, #16
2026-06-19 14:32:09 +02:00
Istvan Matejcsok
5d18bd0c45 test: 💍 deadlock test + readme update 2026-06-19 09:22:58 +02:00
Istvan Matejcsok
1b1e7884ee test: 💍 wxWidgets secondary canvas 2026-06-19 09:22:58 +02:00
Istvan Matejcsok
77a04821ef test: 💍 multithread nested asyncify 2026-06-19 09:22:56 +02:00
Istvan Matejcsok
5f02608a91 fix: 🐛 wxWidgets text ellipsis 2026-06-17 14:26:03 +02:00
Istvan Matejcsok
aa9d613f50 fix: 🐛 press m & move w/ arrows 2026-06-16 15:46:43 +02:00
Istvan Matejcsok
f4af42514a test: 💍 add tab switch fix test 2026-06-16 11:22:18 +02:00
Istvan Matejcsok
28615aba02 test: 💍 select height wxWidget 2026-06-15 18:41:18 +02:00
Viktor Vaczi
68430fa87c feat(wasm-dom): tests for context menus + scrollbars; fix clean wx build
- Standalone wx test apps (contextmenu, scrollbar) + e2e specs proving the
  wxScrollBar control, the wxScrolledWindow gutter, and DoPopupMenu.
- KiCad in-app specs: pl_editor + pcbnew canvas right-click context menus and
  scrolled-panel scrollbar gutters, with baseline screenshots.
- build-wx-wasm.sh: serial -j1 fallback when the parallel build trips the
  intermittent clean-build race (a generated header read mid-rewrite by
  concurrent compiles; never happens at -j1). Recovery resumes from built
  objects; a genuine error still fails the serial pass.
- Bump wxwidgets submodule (context menus + draggable scrollbars).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 23:15:49 +02:00
Viktor Vaczi
4186ea490f test(wasm-dom): repros + fixes for the text-ctrl reentry and tooltip UAF bugs
Bump wxwidgets (8814ddb) for the two DOM-port fixes and add their reproductions:

- tests/apps/standalone/{textctrl-reentry,tooltip-lifetime}: standalone wx repro
  apps + Makefile.wasm targets (textctrl links -fexceptions to throw from a
  wxEVT_TEXT handler), driven by tests/e2e/dom-port-bugs.spec.ts. Each app is
  deterministic and self-contained (no UB, ASAN, or timing dependence).
- docs/features/wx-dom-port/branch-review.md: branch review with findings #2/#3
  marked fixed and a "Bug reproductions and fixes" section, including the
  asyncify + legacy-EH gotcha (catch/destructor landing pads are unreliable
  while unwinding through asyncify frames).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 17:39:34 +02:00
Viktor Vaczi
7dc1bf27e0 refactor: collapse dual-mode plumbing — the DOM port is the only WASM build
The canvas (wxUniversal) mode is gone (wxwidgets submodule); remove every
piece of side-by-side plumbing so there is exactly one build and one test
flow:

- scripts/build-wxuniversal-wasm.sh -> scripts/build-wx-wasm.sh; no
  --dom/--enable-universal; builds into build-wasm/wxwidgets
- build-wasm-test.sh: no DOM_BUILD / apps-dom rsync mirror / PORT=dom;
  apps build straight into tests/apps (Makefile.wasm PORT conditionals
  collapsed; wx.js + wx-dom.js always pre-js)
- docker/build.sh, build-kicad-target.sh, env.sh: WX_PORT / -dom /
  -universal suffixes removed; kicad builds to kicad-<app>, outputs to
  output/; wx.js/wx-dom.js copied from the real source path
  (/workspace/wxwidgets/build/wasm — the old build-wasm path never
  existed and silently failed)
- setup-kicad-wasm.sh: single target dir; the perl wx-dom.js injection is
  gone — the 7 checked-in kicad pages now reference wx-dom.js directly
- playwright configs serve apps/; fixtures drop the test-results/dom and
  logs/wxwidgets/dom namespacing; boot.spec asserts wxDomPort
  unconditionally; pcbnew.spec uses one reference image;
  appearance.spec assertions unconditional
- compare/update-baseline-screenshots.sh: --port removed
- tests/gal-regression/wasm/Makefile: links build-wasm/wxwidgets and
  carries wx-dom.js as a second pre-js — the gal-webgl suite (30 specs)
  now actually builds and runs here (it needed host-side boost+glm via
  scripts/deps; the bundle had been missing, timing the whole spec out)
- tests: clickCanvas() dispatches via page.mouse (DOM widgets
  legitimately cover the canvas; locator actionability refused the
  click); the comprehensive spec drives wxChoice through its native
  <select> (browser-owned popup cannot be coordinate-clicked)
- docs: README/CLAUDE.md/build.md script names and dirs;
  features/wx-dom-port README reframed (DOM is THE port), visual-notes
  bugs 26-28; FindwxWidgets.cmake config label drops 'wasmuniv'
- wxwidgets submodule -> 9dbacc9448 (DOM-only port, fork diff shrunk)

Gate: full wx e2e suite 292 passed / 1 skipped / 0 failed — first run
ever with the gal-webgl specs green (28 scenarios + load + sequential).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:19:48 +02:00
Viktor Vaczi
249010a582 feat(wasm-dom): dual-mode build scaffold — native DOM port builds, links and boots alongside canvas
Build/test plumbing for the second wxWidgets mode: --dom on
build-wxuniversal-wasm.sh (build-wasm/wxwidgets-dom, no universal,
--disable-tooltips, libwx_wasmu_* stubs) and build-wasm-test.sh
(mirrors sources into tests/apps-dom, PORT=dom). Build script now
reconfigures when Makefile.in or autoconf_inc.m4 regenerate (bakefile
chain). Harness: WX_PORT=dom serves apps-dom, namespaces screenshots
into test-results/dom and logs into logs/wxwidgets/dom; compare/update
screenshot scripts take --port dom with baseline-screenshots-dom.
New port-agnostic boot.spec.ts (green on both ports).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:17:09 +02:00
Viktor Vaczi
7629ab978d test(e2e): fix wx suite — header deps, port pinning, drop legacy-GL tests, wx+pthread worker fix
Suite went 64 failed → 291 passed / 0 failed:
- Makefile.wasm: compile with -MMD -MP and include .d files — stale objects
  relinked against a newer wx lib crashed apps at startup with "function
  signature mismatch" after any header/vtable change
- playwright(.coroutine).config.ts: pin the server port for the whole run
  (resolvePort from playwright-kicad.config.ts); the 60s freshness window made
  workers restarted after a failure rotate to a dead port (ERR_CONNECTION_REFUSED
  cascade across ~50 tests)
- drop legacy-GL testing: KiCad renders via WebGL GAL (gal-webgl.spec.ts);
  remove minimal_test's OpenGL tab/GLTestCanvas, opengl.spec.ts, GL z-order
  describes, -sLEGACY_GL_EMULATION + gl_immediate_shim.js from the test build,
  and the orphaned GL baselines
- coroutine-pthread repro apps join the `all` target (make clean used to delete
  them while all never rebuilt them); clean no longer eats checked-in JS like
  worker_dom_stub.js
- bump wxwidgets: guard module-eval document access so wx+pthread apps
  (threadpool tests) survive Web Worker eval

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:40:00 +02:00
Istvan Matejcsok
8db6cfadc3 test: 💍 fix test cases 2026-06-05 11:42:01 +02:00
Balint Ipkovich
d735779e23 feat: pl_editor WASM port + browser file dialog fixes
Brings up KiCad's pagelayout_editor (drawing-sheet editor) in the
browser, to roughly the same "boots, canvas visible, partially usable
in-session" level as the existing pcbnew/eeschema/calculator ports.

Build:
- docker/build.sh: add pl_editor to the unified app dispatch (case,
  subdir map, all-loop).
- scripts/kicad/build-kicad-target.sh: add pl_editor to the case;
  upstream target name pl_editor under source subdir pagelayout_editor.
- scripts/kicad/build-pl_editor.sh: 7-line thin wrapper matching the
  pcbnew/eeschema/calculator pattern.
- tests/scripts/setup-kicad-wasm.sh: copy_app pl_editor.

App glue:
- wasm/stubs/nl_pl_editor_plugin_stub.cpp: no-op SpaceMouse plugin so
  pl_editor_frame.cpp's NL_PL_EDITOR_PLUGIN symbols resolve. Mirrors
  nl_pcbnew_plugin_stub.cpp.
- tests/apps/kicad/pl_editor.html: browser shell. preRun creates
  /home/kicad and FS.chdir there so file dialogs land somewhere
  friendly instead of MEMFS root (/dev/, /proc/, etc.).

E2E coverage:
- tests/kicad/pl_editor.spec.ts: 5 tests — smoke (canvas, no abort),
  wizard, File menu has Open/Save As, file-dialog folder-navigation
  regression, canvas + toolbar metrics.
- tests/e2e/filedialog-folder-nav.spec.ts: wxWidgets-level twin of
  the regression test (exercises the underlying widget directly via
  the standalone filedialog_test app).

Submodule bumps:
- kicad → feature/pl-editor (WASM gating in pagelayout_editor's
  CMakeLists + navlib stub).
- wxwidgets → feature/pl-editor (wxGenericFileDialog::OnOk navigates
  into selected directories; wasm/mouse.cpp emits wxEVT_LEFT_DCLICK
  via timestamp-based double-click detection — the latter benefits
  every wxWidgets-WASM app).

See features/pl-editor/ for the design doc + per-repo diff patches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:30:56 +02:00
Viktor Vaczi
7331619404 diagnostics: configurable --diag logging flags + asyncify setupUIConditions fix
- build-pcbnew.sh: add --diag=<gal,coroutine,ctor,all> -> -DKICAD_DIAG_*,
  off by default (forwarded by docker/build.sh)
- diagnostics.js: emit at console.log level (no longer error/warn); still
  gated by SHIM_DIAGNOSTICS=1
- apply-asyncify.sh: exclude PCB_EDIT_FRAME::setupUIConditions() from
  asyncify instrumentation (V8 cannot run the instrumented huge function
  on the rewound ctor stack -> Chrome startup stall; Firefox unaffected)
- DEBUG.md: reusable WASM/asyncify/browser debugging guide, diagnostic
  flag docs, and a production-build (release + -O2 asyncify) recipe
- tests: standalone coroutine vcall/gl repro probes
- bump kicad + wxwidgets submodules (diagnostic gating / debug cleanup)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 16:14:03 +02:00
Viktor Vaczi
01dce40dc9 test(wasm): coroutine crash reproduction harness + per-engine runner
Investigation scaffolding for the Chrome-only KiCad coroutine renderer crash.
Adds isolated reproduction probes exercising the coroutine/Asyncify/fiber layer
under KiCad-like conditions, runnable in BOTH Firefox and system Chrome.

- tests/playwright-coroutine.config.ts + test:coroutine:firefox|chrome npm
  scripts: run the coroutine specs in Firefox AND system Chrome (the old e2e
  config only used bundled Chromium, which never reproduced the crash).
- tests/apps/standalone/coroutine-pthread/: no-wx + pthreads reproduction probes
  (fiber-in-main, nested invoke_/dynCall boundaries, RunMainStack, embind,
  main-loop/rAF activation) + worker_dom_stub.js for wx+pthreads builds.
- tests/apps/Makefile.wasm: coroutine-pthread{,-main,-nested,-nested-ex,-wx,
  -embind,-mainloop} targets.
- scripts/common/shims/diagnostics.js: add EM_ASYNC_JS handleSleep enter/wake
  tracking (DIAG_SLEEP) to detect nested-async at the crash.

Findings (details in research notes): every isolated factor so far — direct /
nested / RunMainStack fiber, wx event loop + all 13 scenarios incl EM_ASYNC_JS,
pthreads, and main-loop/rAF activation — runs CLEAN in system Chrome. The
coroutine/Asyncify layer is exonerated; GL/WebGL is the remaining untested factor
(next). The reliable FF-pass/Chrome-fail repro is still the KiCad pcbnew e2e.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:44:12 +02:00
Viktor Vaczi
9a04217788 wip: nested-asyncify fix, wxAuiToolBar registration, tests, research docs
Main-repo side of a multi-part WIP covering the KiCad WASM tool-selection
and nested-Asyncify work. Submodule commits are in kicad@f6e9239aaa
(libcontext hygiene) and wxwidgets@bb80f91e8b (auibar registration +
dialog diagnostics).

## scripts/common/inject-dyncall-shims.sh

Wrap Asyncify.handleSleep / allocateData to save-and-restore Asyncify.currData
around each EM_ASYNC_JS sleep. This fixes the nested Asyncify collision where
a fiber swap that fired during a modal's event loop clobbered currData, and
the modal's later doRewind used the fiber's buffer and hit "RuntimeError:
index out of bounds". Root cause documented as Emscripten Issue #9153
(wontfix upstream).

Diagnostic-rewind logging (forcedBottomOfCallStack, callStack traces) is
retained to help future debugging of Asyncify state corruption.

## tests/

- tests/playwright-kicad.config.ts: add `channel: 'chrome'` for the
  chromium project so --project=chromium --headed uses system Chrome
  (real GPU) instead of SwiftShader on ARM Mac. Also switch trace to
  retain-on-failure + screenshot on-failure for easier E2E debugging.
- tests/kicad/pcbnew.spec.ts: replace `tool.checked` assertions with a
  label-suffix check (`[checked]`) since our auibar registration encodes
  checked state in the label (no schema change to the registry).
- tests/apps/Makefile.wasm: add `coroutine-nested` build target + include
  it in the all: list.
- tests/apps/standalone/coroutine/: kicad_coroutine_harness.h + test app
  reproducing KiCad COROUTINE semantics against real libcontext.
- tests/apps/standalone/coroutine-nested/: nested_test.cpp reproduces the
  EM_ASYNC_JS-modal + fiber-swap nesting bug in isolation. 8 scenarios
  from baseline_modal_alone through nested_fibers_inside_modal.
- tests/e2e/coroutine.spec.ts + coroutine-nested.spec.ts: Playwright specs
  that load the standalone apps and assert all case cases pass via
  [COROUTINE_TEST] SUMMARY log parsing.

## research/ and features/browser-tools/

Three background docs capturing the investigation trajectory:

- features/browser-tools/0001-kicad-wasm-tool-activation-investigation.md
  Early investigation: why tools don't activate; initial dynCall-empty-
  callback hypothesis.
- features/browser-tools/0002-wasm-coroutine-deep-dive.md
  Deep dive on Asyncify internals, fiber API, QEMU's coroutine-wasm
  reference implementation.
- features/browser-tools/0003-wxauitoolbar-registration-fix.md
  The narrow fix: why wxAuiToolBar needs a registration block, where to
  add it, what the fallback plan is.
- research/threading_1.md: corrected root-cause analysis after reading
  runtime logs — nested-Asyncify currData collision, Emscripten #9153.
- research/threading_2.md: extended research on alternative approaches
  (JSPI/WasmFX/state-machines) and why they don't help here.

## Submodule pointer updates

kicad: f6e9239aaa (wip: libcontext WASM hygiene cleanup)
wxwidgets: bb80f91e8b (wip: wxAuiToolBar element-registry registration +
            dialog diagnostics)

## Open threads not yet in scope

- Firefox/Chrome divergent behavior: "indirect call signature mismatch"
  traps in Firefox vs renderer crash in system Chrome (tracked in
  plans/peaceful-hugging-pnueli.md and the research docs).
- E2E pixel-diff for Draw Lines fails because the test's diff region does
  not cover where the line is actually drawn; tool activation works, the
  line is visible in test-results/pcbnew-draw-lines-02-after-drawing.png.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 13:58:16 +02:00
Viktor Vaczi
a49ed49d5d wasm: validate kicad hi-dpi scaling 2026-03-22 12:48:05 +01:00
Viktor Vaczi
8f803b45ea fix(test): Fix WebGL screenshot capture and comparison
1. Fix gal-webgl.spec.ts sequential test:
   - Wait for isReady() not just module existence
   - Use .gl-canvas selector (same as individual tests)
   - Increase timeout from 50ms to 100ms
   - This fixes blank screenshots in full test runs

2. Fix test-gal-regression.sh comparison:
   - Normalize PNG format before comparing (flatten + sRGB TrueColor)
   - This handles RGBA vs RGB and palette differences
   - gal-basic-lines now passes comparison (0.34% different)

Results after fixes:
- Native vs Baseline: PASSED (28/28)
- WebGL screenshots now capture actual rendered content
- gal-basic-lines matches native (first successful scenario!)
- Other scenarios have rendering differences to investigate

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:32:39 +01:00
Viktor Vaczi
d0e2500048 feat(webgl): Enable all 28 scenarios for WebGL test harness
- Update wasm/Makefile to compile ALL scenario files using wildcard
- Add conditional compilation to scenario_bitmap.cpp (#ifdef __EMSCRIPTEN__)
  to handle OpenGL-specific shader workaround code for native only
- Change canvas dimensions to 1600x1200 to match native baseline (2x Retina)
- Set white background color to match native screenshots
- Update gal-webgl.spec.ts to hide UI overlay before taking screenshots
- Improve test-gal-regression.sh with better ImageMagick comparison

All 28 scenarios now compile and run on both native and WebGL backends.
Native vs baseline: PASSED (28/28 matching)
WebGL rendering: In progress (primitives not yet visible)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:11:03 +01:00
Viktor Vaczi
18c73c355e feat(webgl): Fix WebGL GAL rendering with proper coordinate setup
- Fix coordinate system for 1:1 pixel mapping (was using nm scale)
- Set worldUnitLength to 1/96 to match native test setup
- Add SetLookAtPoint and SetZoomFactor for proper view transformation
- Fix glDrawBuffers for WebGL 2.0 (array index must match attachment)
- Add legacy_gl_stubs.js for wxWidgets compatibility
- Add isReady() check to prevent race condition in Playwright tests

All 28 GAL test scenarios now render correctly in WebGL.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:13:11 +01:00
Viktor Vaczi
6c2cf85177 feat(webgl): Fix GL initialization and add GLSL ES shader conversion
Progress on WEBGL_GAL rendering:

- Fix GLImmediateShim initialization by adding GL function dependencies
  (glBegin, glEnd, glVertex2f, etc.) to $GLImmediateShim__deps
- Add COLOR4D(EDA_COLOR_T) constructor stub to wasm_stubs.cpp
- Fix GAL context locking - add LockContext/UnlockContext calls
- Add exception catching to build (-sNO_DISABLE_EXCEPTION_CATCHING)
- Create GLSL ES 1.00 shader converter in generate_shaders.py:
  - Remove #version 120 directive (ES 1.00 default)
  - Add precision highp float/int qualifiers
  - Convert int * float to float literals (2 * x -> 2.0 * x)
- Update build script to clean output files for linker flag changes
- Add console capture to test spec for debugging

Current status:
- WASM builds and loads successfully
- GLImmediateShim initializes correctly
- WEBGL_GAL creates successfully
- Shader compilation fails due to Emscripten LEGACY_GL_EMULATION
  prepending code that conflicts with custom shader precision

Next: Investigate shader compilation with LEGACY_GL_EMULATION or
consider alternative approaches for GL compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:59:21 +01:00
Viktor Vaczi
c23bc974be feat(webgl): Wire up WEBGL_GAL test harness with proper canvas setup
- Add wasm_stubs.cpp with WASM-specific stub implementations for
  COLOR4D::BLACK/WHITE, GLU tesselator, PGM_BASE, and other KiCad
  dependencies
- Update Makefile to include KiCad sources (GAL base class, display
  options, HiDPI canvas) and test scenarios
- Fix kiglew.h to define GLEW guard (__glew_h__) preventing conflicts
  with Emscripten's GLEW header
- Update test HTML to create canvas before module load (MODULARIZE
  requires passing canvas in Module config, not preRun)
- Update Playwright tests to find canvas elements correctly
- Add #window-container for wxWidgets GL canvas support

Build produces 8.4MB WASM with full WEBGL_GAL implementation.
Tests pass but rendering still shows dark output (debugging in progress).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:32:53 +01:00
Viktor Vaczi
a4f444fea8 feat(webgl): Add WebGL GAL test infrastructure (Phase 1)
Add complete test infrastructure for WebGL GAL visual regression testing:

- scripts/test-gal-regression.sh: Master script that builds both backends,
  runs tests, and performs two-level comparison (native vs baseline,
  webgl vs native)
- scripts/build-gal-webgl-test.sh: WASM build using Makefile with em++
- tests/gal-regression/wasm/: WebGL test harness (stub WEBGL_GAL)
- tests/e2e/gal-webgl.spec.ts: Playwright test for screenshot capture

Fix Homebrew Emscripten environment in scripts/common/env.sh:
- Set EMSDK_PYTHON for Python 3.10+ (em++ reads this, not $PYTHON)
- Add bundled LLVM to PATH (Emscripten needs its clang with WASM backend)

Verified: Native vs Baseline passes (28/28), WebGL generates blank
screenshots as expected (WEBGL_GAL implementation is Phase 2).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 16:11:31 +01:00