From 5918557545822b301d6556ed25bea5b630f61697 Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Sat, 29 Nov 2025 12:58:14 +0100 Subject: [PATCH] Fix Vertex Arrays GL test crash and add OpenGL test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change glDrawElements index type from GL_UNSIGNED_BYTE to GL_UNSIGNED_SHORT to match KiCad's actual usage and Emscripten's legacy GL emulation requirements - Add dedicated opengl.spec.ts test file for OpenGL-specific tests - All 5 GL test types now work: Immediate Mode, Matrix Ops, Vertex Arrays, State Mgmt, and Texture Coords 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/e2e/opengl.spec.ts | 164 ++++++++++++++++++++++++++++++++ tests/wasm-app/minimal_test.cpp | 8 +- 2 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/opengl.spec.ts diff --git a/tests/e2e/opengl.spec.ts b/tests/e2e/opengl.spec.ts new file mode 100644 index 0000000..0eb1cf7 --- /dev/null +++ b/tests/e2e/opengl.spec.ts @@ -0,0 +1,164 @@ +import { test, expect, Page } from '@playwright/test'; + +const MAIN_CANVAS = '#canvas'; + +async function waitForApp(page: Page) { + await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout: 30000 }); + await page.waitForTimeout(500); +} + +async function switchToOpenGLTab(page: Page, box: { x: number; y: number }) { + // Click OpenGL tab (fifth tab, around x=280) + await page.mouse.click(box.x + 280, box.y + 35); + await page.waitForTimeout(1000); +} + +// Open the test dropdown and select by index (0-based) +async function selectGLTest(page: Page, box: { x: number; y: number }, index: number) { + // Click the dropdown arrow to open it (dropdown is at around x=170, y=155, arrow at right edge ~x=290) + await page.mouse.click(box.x + 290, box.y + 155); + await page.waitForTimeout(300); + + // Each dropdown item is approximately 20px tall, starting below the dropdown + const itemY = 175 + (index * 20); + await page.mouse.click(box.x + 170, box.y + itemY); + await page.waitForTimeout(500); +} + +test.describe('OpenGL Tests', () => { + test('Vertex Arrays test - debug freeze', async ({ page }) => { + const errors: string[] = []; + const logs: string[] = []; + + page.on('pageerror', err => { + errors.push(`[PAGE_ERROR] ${err.message}\n${err.stack || 'No stack'}`); + }); + page.on('console', msg => { + const text = msg.text(); + logs.push(`[${msg.type()}] ${text}`); + if (msg.type() === 'error') { + errors.push(`[CONSOLE_ERROR] ${text}`); + } + }); + + await page.goto('/minimal_test.html'); + await waitForApp(page); + + const canvas = page.locator(MAIN_CANVAS); + const box = await canvas.boundingBox(); + if (!box) throw new Error('Canvas not found'); + + // Switch to OpenGL tab + await switchToOpenGLTab(page, box); + await page.screenshot({ path: 'test-results/gl-01-opengl-tab.png', fullPage: true }); + + // The dropdown has these options (from minimal_test.cpp): + // 0: "Immediate Mode (glBegin/glEnd)" + // 1: "Matrix Ops (glPushMatrix)" + // 2: "Vertex Arrays (glVertexPointer)" + // 3: "State Mgmt (glEnable/glBlend)" + // 4: "Texture Coords" + + // Select "Vertex Arrays" test (index 2) + console.log('Selecting Vertex Arrays test...'); + await selectGLTest(page, box, 2); + + await page.screenshot({ path: 'test-results/gl-02-vertex-arrays-selected.png', fullPage: true }); + + // Wait a bit to see if it freezes + await page.waitForTimeout(2000); + + await page.screenshot({ path: 'test-results/gl-03-after-wait.png', fullPage: true }); + + // Print all logs for debugging + console.log('\n=== CONSOLE LOGS ==='); + logs.forEach(log => console.log(log)); + console.log('\n=== ERRORS ==='); + errors.forEach(err => console.log(err)); + + // Check if app is still responsive + const isResponsive = await page.evaluate(() => { + return document.querySelector('#canvas') !== null; + }); + expect(isResponsive).toBe(true); + }); + + test('All GL tests individually', async ({ page }) => { + const errors: string[] = []; + const logs: string[] = []; + + page.on('pageerror', err => { + errors.push(`[PAGE_ERROR] ${err.message}\n${err.stack || 'No stack'}`); + }); + page.on('console', msg => { + logs.push(`[${msg.type()}] ${msg.text()}`); + }); + + await page.goto('/minimal_test.html'); + await waitForApp(page); + + const canvas = page.locator(MAIN_CANVAS); + const box = await canvas.boundingBox(); + if (!box) throw new Error('Canvas not found'); + + await switchToOpenGLTab(page, box); + + const testNames = [ + 'Immediate Mode', + 'Matrix Ops', + 'Vertex Arrays', + 'State Mgmt', + 'Texture Coords' + ]; + + for (let i = 0; i < testNames.length; i++) { + console.log(`\n--- Testing: ${testNames[i]} (index ${i}) ---`); + + try { + await selectGLTest(page, box, i); + await page.screenshot({ + path: `test-results/gl-test-${i}-${testNames[i].replace(/\s+/g, '-').toLowerCase()}.png`, + fullPage: true + }); + console.log(`${testNames[i]}: OK`); + } catch (e) { + console.log(`${testNames[i]}: FAILED - ${e}`); + await page.screenshot({ + path: `test-results/gl-test-${i}-${testNames[i].replace(/\s+/g, '-').toLowerCase()}-error.png`, + fullPage: true + }); + } + + await page.waitForTimeout(500); + } + + console.log('\n=== ERRORS ==='); + errors.forEach(err => console.log(err)); + }); + + test('Run All Tests button', async ({ page }) => { + const errors: string[] = []; + + page.on('pageerror', err => { + errors.push(`${err.message}\n${err.stack || 'No stack'}`); + }); + + await page.goto('/minimal_test.html'); + await waitForApp(page); + + const canvas = page.locator(MAIN_CANVAS); + const box = await canvas.boundingBox(); + if (!box) throw new Error('Canvas not found'); + + await switchToOpenGLTab(page, box); + + // Click "Run All Tests" button (approximately x=360, y=90) + await page.mouse.click(box.x + 360, box.y + 90); + await page.waitForTimeout(2000); + + await page.screenshot({ path: 'test-results/gl-run-all-tests.png', fullPage: true }); + + // App should remain stable + await expect(page.locator(MAIN_CANVAS)).toBeVisible(); + }); +}); diff --git a/tests/wasm-app/minimal_test.cpp b/tests/wasm-app/minimal_test.cpp index 0f6cc0d..8d98c63 100644 --- a/tests/wasm-app/minimal_test.cpp +++ b/tests/wasm-app/minimal_test.cpp @@ -487,7 +487,9 @@ void GLTestCanvas::TestVertexArrays() 0.5f, 0.0f, 1.0f // Purple }; - static GLubyte indices[] = { + // KiCad uses GL_UNSIGNED_SHORT or GL_UNSIGNED_INT for indices, never GL_UNSIGNED_BYTE + // Emscripten's legacy GL emulation only supports GL_UNSIGNED_SHORT for client-side arrays + static GLushort indices[] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, @@ -504,8 +506,8 @@ void GLTestCanvas::TestVertexArrays() glVertexPointer(3, GL_FLOAT, 0, vertices); glColorPointer(3, GL_FLOAT, 0, colors); - // Draw using vertex arrays - glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_BYTE, indices); + // Draw using vertex arrays (GL_UNSIGNED_SHORT matches KiCad's usage) + glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_SHORT, indices); // Disable client state glDisableClientState(GL_COLOR_ARRAY);