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>
306 lines
10 KiB
Markdown
306 lines
10 KiB
Markdown
# KiCad WASM Tests
|
|
|
|
Playwright tests for verifying the wxWidgets WASM port.
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js 18+
|
|
- Emscripten SDK (for building)
|
|
|
|
## Building the Test App
|
|
|
|
```bash
|
|
../scripts/build-wasm-test.sh
|
|
```
|
|
|
|
This builds `apps/minimal_test.{html,js,wasm}` and standalone test apps.
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
npm install
|
|
npm test # wx e2e specs + the asyncify race harness
|
|
```
|
|
|
|
KiCad application tests are a separate, heavier suite (they need the
|
|
docker-built KiCad WASM): `npm run test:kicad`. Run only the wx e2e specs with
|
|
`npm run test:wx`.
|
|
|
|
To run specific tests:
|
|
```bash
|
|
npx playwright test menu.spec.ts # Run menu tests only
|
|
npx playwright test --grep "wxTimer" # Run tests matching pattern
|
|
```
|
|
|
|
## 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/ # Screenshots (auto-generated)
|
|
├── baseline-screenshots/ # Reference screenshots for comparison
|
|
├── apps/ # Built WASM test applications
|
|
│ ├── minimal_test.html # Main test app
|
|
│ └── standalone/ # Individual component test apps
|
|
└── playwright.config.ts # Playwright configuration
|
|
```
|
|
|
|
## 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 screenshots to `test-results/`. Compare against baselines:
|
|
```bash
|
|
../scripts/compare-screenshots.sh
|
|
```
|
|
|
|
## Viewing the App Directly
|
|
|
|
Start a local server in the apps directory:
|
|
|
|
```bash
|
|
cd apps
|
|
npx serve .
|
|
```
|
|
|
|
Then open http://localhost:3000/minimal_test.html in your browser.
|
|
|
|
Alternative using Python:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
## Element Registry (Recommended)
|
|
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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?** Two
|
|
`scripts/common/inject-dyncall-shims.sh` fixes — the fiber trampoline self-heal
|
|
(§3c) and the nested-Asyncify handleSleep save/restore (§3) — were written for the
|
|
legacy-EH *park-throw* model. Under native wasm-EH the top loop is a per-frame-yield
|
|
while-loop with no park-throw, so ablating either shim
|
|
(`SHIM_DISABLE_TRAMPOLINE_HEAL` / `SHIM_DISABLE_HANDLESLEEP`, wired in
|
|
`tests/apps/Makefile.wasm`) no longer reproduces the disease it guarded — the old
|
|
red "ablation pins" in `asyncify/asyncify-races.spec.ts` have been flipped to
|
|
green "shim-redundancy pins" that now prove the native-EH path stays clean *with the
|
|
shim ablated*. **To do:** sweep the real apps (modals, nested fibers, long sleeps,
|
|
pthread pool) with each shim ablated; if all stay green, drop the shim injection and
|
|
these pins. Until proven, they stay injected (belt-and-suspenders).
|