test: 💍 select height wxWidget

This commit is contained in:
Istvan Matejcsok 2026-06-15 18:41:18 +02:00
commit 28615aba02
5 changed files with 170 additions and 1 deletions

View file

@ -167,6 +167,7 @@ all: minimal_test.html \
$(S)/regions/regions_test.html \
$(S)/maximize/maximize_test.html \
$(S)/earlysize/earlysize_test.html \
$(S)/selectheight/selectheight_test.html \
$(S)/threadpool/threadpool_test.html \
$(S)/logerror/logerror_test.html \
$(S)/retinascale/retinascale_test.html \
@ -508,6 +509,12 @@ $(S)/earlysize/earlysize_test.o: $(S)/earlysize/earlysize_test.cpp
$(S)/earlysize/earlysize_test.html: $(S)/earlysize/earlysize_test.o $(WX_CORE_LIB) $(JS_FILES)
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
$(S)/selectheight/selectheight_test.o: $(S)/selectheight/selectheight_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/selectheight/selectheight_test.html: $(S)/selectheight/selectheight_test.o $(WX_CORE_LIB) $(JS_FILES)
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Thread Pool test (pthread) - reproduces KiCad deadlock when hardware_concurrency() > PTHREAD_POOL_SIZE
$(S)/threadpool/threadpool_test.o: $(S)/threadpool/threadpool_test.cpp
$(CXX) -c $(CXXFLAGS) -pthread $< -o $@
@ -604,6 +611,7 @@ bitmask: $(S)/bitmask/bitmask_test.html
regions: $(S)/regions/regions_test.html
maximize: $(S)/maximize/maximize_test.html
earlysize: $(S)/earlysize/earlysize_test.html
selectheight: $(S)/selectheight/selectheight_test.html
threadpool: $(S)/threadpool/threadpool_test.html
logerror: $(S)/logerror/logerror_test.html
@ -627,7 +635,7 @@ clean:
rm -f $(S)/*/*_test*.html $(S)/*/*_test*.js $(S)/*/*_test*.wasm
rm -f $(S)/*/*_repro*.html $(S)/*/*_repro*.js $(S)/*/*_repro*.wasm
.PHONY: all clean menu contextmenu scrollbar clipboard filedialog layout aui toolbar grid dialog timer tree dataview htmlwin stc print dnd propgrid pickers collapsible listctrl infobar dataviewvirtual auinotebook wizard gridedit calendar gridrenderers printpreview bitmapbuttons specialized validators ownerdrawn popup xml wasmedge fontenum textdecor bitmask regions maximize earlysize threadpool logerror retinascale coroutine coroutine-nested asyncify-races
.PHONY: all clean menu contextmenu scrollbar clipboard filedialog layout aui toolbar grid dialog timer tree dataview htmlwin stc print dnd propgrid pickers collapsible listctrl infobar dataviewvirtual auinotebook wizard gridedit calendar gridrenderers printpreview bitmapbuttons specialized validators ownerdrawn popup xml wasmedge fontenum textdecor bitmask regions maximize earlysize selectheight threadpool logerror retinascale coroutine coroutine-nested asyncify-races
# === Coroutine pthread variant — reproduces the KiCad Asyncify-fiber x pthreads crash ===
# Same modal-free harness as `coroutine`, but compiled/linked with pthreads to match

View file

@ -0,0 +1,105 @@
// Select Height Test - Regression guard for the wxChoice (<select>) height fix.
//
// Bug (fixed in commit "fix: 🐛 select height"): an HTML <select>'s height only
// resolves once the browser lays it out, but DoGetBestSize() is frequently
// queried BEFORE layout (wxAuiToolBar freezes a control's min size at AddControl
// time, panel sizers measure during construction). The DOM reported ~0 height,
// so the layout system pinned the control to an unusable sliver.
//
// The fix is wxChoice::DoGetBestSize() (src/wasm/choice.cpp), which floors the
// height to GetCharHeight() + 8 when the DOM-measured height is too small.
//
// This test creates ONE wxChoice and queries GetBestSize() in the constructor,
// BEFORE the frame is shown / laid out. On pre-fix code best.y is ~0 (FAIL);
// with the fix it is GetCharHeight() + 8 (PASS).
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class SelectHeightTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class SelectHeightTestFrame : public wxFrame
{
public:
SelectHeightTestFrame();
private:
wxSize m_earlyBestSize; // wxChoice best size captured before Show()/layout
};
wxIMPLEMENT_APP(SelectHeightTestApp);
bool SelectHeightTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
SelectHeightTestFrame* frame = new SelectHeightTestFrame();
frame->Show(true);
return true;
}
SelectHeightTestFrame::SelectHeightTestFrame()
: wxFrame(nullptr, wxID_ANY, "Select Height Test",
wxDefaultPosition, wxSize(400, 200))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(new wxStaticText(this, wxID_ANY,
"Select Height Test\n\n"
"One wxChoice; its GetBestSize() is queried in the constructor,\n"
"before layout. Height must NOT collapse to ~0px."),
0, wxALL, 10);
// The single control under test. wxDefaultSize so the caller does not force
// a height -- the height must come from DoGetBestSize().
wxString items[] = { "Red", "Green", "Blue" };
wxChoice* choice = new wxChoice(this, wxID_ANY, wxDefaultPosition,
wxDefaultSize, 3, items);
choice->SetSelection(0);
// === THE KEY MEASUREMENT ===
// Query best size BEFORE Show()/layout, where the bug manifested. Nothing is
// cached yet, so this recomputes via wxChoice::DoGetBestSize().
m_earlyBestSize = choice->GetBestSize();
// The bug was height-specific; width comes from intrinsic content sizing and
// was never broken. A real control is at least a line of text tall.
const int minHeight = GetCharHeight() + 8;
const bool heightOk = (m_earlyBestSize.y >= minHeight);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[SELECTHEIGHT_TEST] Choice best size: ' + $0 + 'x' + $1);
console.log('[SELECTHEIGHT_TEST] Expected min height: ' + $2);
if ($3) {
console.log('[SELECTHEIGHT_TEST] PASS: select has a real height');
} else {
console.error('[SELECTHEIGHT_TEST] FAIL: select height collapsed to '
+ $1 + 'px (expected >= ' + $2 + ')');
}
}, m_earlyBestSize.x, m_earlyBestSize.y, minHeight, heightOk ? 1 : 0);
#endif
choice->SetSelection(0);
mainSizer->Add(choice, 0, wxALL, 10);
SetSizer(mainSizer);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[SELECTHEIGHT_TEST] Select height test app started');
});
#endif
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,56 @@
// Select Height Test - Regression guard for the wxChoice (<select>) height fix.
// A <select>'s height only resolves after layout, but DoGetBestSize() is queried
// before layout; the DOM used to report ~0 height, collapsing the control. The
// fix floors the height in wxChoice::DoGetBestSize(). The C++ app queries one
// wxChoice's GetBestSize() in its constructor (before Show()) and logs it.
import { test, expect, tryLoadApp } from './utils/fixtures';
test.describe('Select Height Tests', () => {
test('Select height test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/selectheight/selectheight_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/selectheight-01-loaded.png', fullPage: true });
const hasStartup = testLogger.consoleLogs.some(l => l.includes('[SELECTHEIGHT_TEST] Select height test app started'));
expect(loaded, 'Select height app should load').toBe(true);
expect(hasStartup, 'Startup log should be present').toBe(true);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('wxChoice best size has a real height before layout', async ({ page, testLogger }) => {
await page.goto('/standalone/selectheight/selectheight_test.html');
const loaded = await tryLoadApp(page);
expect(loaded, 'App should load').toBe(true);
// Wait for the app to finish initialization
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/selectheight-02-result.png', fullPage: true });
// Parse the best size logged from the constructor (before Show()/layout)
const bestSizeLogs = testLogger.consoleLogs.filter(l => l.includes('[SELECTHEIGHT_TEST] Choice best size:'));
expect(bestSizeLogs.length).toBeGreaterThan(0);
const bestMatch = bestSizeLogs[0].match(/Choice best size: (\d+)x(\d+)/);
expect(bestMatch, 'Best size log should contain dimensions').not.toBeNull();
if (bestMatch) {
const height = parseInt(bestMatch[2]);
// The key assertion: the <select> height must NOT collapse to ~0px.
// Pre-fix this was ~0; the DoGetBestSize() floor makes it a line of text tall.
expect(height, `Select best-size height should be > 0 (got ${height})`).toBeGreaterThan(0);
expect(height, `Select best-size height should be a real control height (got ${height})`).toBeGreaterThan(10);
}
// The C++ app cross-checks against GetCharHeight()+8 and emits PASS/FAIL.
const passLog = testLogger.consoleLogs.some(l => l.includes('[SELECTHEIGHT_TEST] PASS'));
const failLog = testLogger.consoleLogs.some(l => l.includes('[SELECTHEIGHT_TEST] FAIL'));
expect(failLog, 'Should not have FAIL log').toBe(false);
expect(passLog, 'Should have PASS log').toBe(true);
});
});