Skip to content

🛑 Add QEMU-based E2E testing with Playwright using V4 Mainline Ethernet Debug build (16MB, QIO flash, WLED_QEMU workaround) with enhanced network diagnostics #16

🛑 Add QEMU-based E2E testing with Playwright using V4 Mainline Ethernet Debug build (16MB, QIO flash, WLED_QEMU workaround) with enhanced network diagnostics

🛑 Add QEMU-based E2E testing with Playwright using V4 Mainline Ethernet Debug build (16MB, QIO flash, WLED_QEMU workaround) with enhanced network diagnostics #16

Workflow file for this run

name: QEMU E2E Testing
on:
pull_request:
branches: [ mdev, main ]
push:
branches: [ mdev, main ]
workflow_dispatch:
jobs:
# Job 1: Build firmware for QEMU testing
build-firmware:
name: Build ESP32 Firmware for QEMU
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: ~/.platformio
key: ${{ runner.os }}-pio-esp32_16MB_V4_M_eth_debug-${{ hashFiles('**/platformio.ini') }}
restore-keys: |
${{ runner.os }}-pio-esp32_16MB_V4_M_eth_debug-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Install Node.js dependencies
run: npm ci
- name: Build Web UI
run: npm run build
- name: Build ESP32 firmware
run: pio run -e esp32_16MB_V4_M_eth_debug
- name: Upload firmware artifacts
uses: actions/upload-artifact@v4
with:
name: esp32-firmware
path: .pio/build/esp32_16MB_V4_M_eth_debug/
retention-days: 1
# Job 2: Test with QEMU ESP32
test-qemu:
name: QEMU E2E Tests
runs-on: ubuntu-22.04
needs: build-firmware
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Download firmware artifacts
uses: actions/download-artifact@v4
with:
name: esp32-firmware
path: .pio/build/esp32_16MB_V4_M_eth_debug/
- name: Install Node.js dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Install ESP32 exception decoder
run: |
pip install esptool
# Install the exception decoder from platformio
pip install platformio
# The xtensa toolchain should be available from the firmware build artifacts
- name: Install QEMU dependencies
run: |
sudo apt-get update
sudo apt-get install -y libsdl2-2.0-0 libpixman-1-0 libglib2.0-0 binutils
- name: Setup QEMU ESP32
run: |
bash .github/scripts/setup-qemu.sh
- name: Make decoder script executable
run: |
chmod +x .github/scripts/monitor-qemu.py
- name: Start QEMU with WLED firmware in background
run: |
chmod +x .github/scripts/run-qemu.sh
bash .github/scripts/run-qemu.sh .pio/build/esp32_16MB_V4_M_eth_debug qemu-esp32 8080 > qemu-output.log 2>&1 &
echo "Waiting for QEMU to start and WLED to boot..."
sleep 45
- name: Check QEMU status and wait for HTTP server
run: |
if [ ! -f qemu.pid ]; then
echo "ERROR: qemu.pid not found"
echo "=== QEMU Output (last 200 lines) ==="
tail -200 qemu-output.log || true
exit 1
fi
QEMU_PID=$(cat qemu.pid)
if ! kill -0 $QEMU_PID 2>/dev/null; then
echo "ERROR: QEMU process not running"
echo "=== QEMU Output (last 200 lines) ==="
tail -200 qemu-output.log || true
exit 1
fi
echo "QEMU is running (PID: $QEMU_PID)"
echo "Testing if WLED HTTP server is responding..."
# Wait up to 2 minutes for HTTP server to respond
for i in {1..60}; do
if curl -f -m 5 http://localhost:8080/ > /dev/null 2>&1; then
echo "SUCCESS: WLED HTTP server is responding!"
exit 0
fi
echo "Attempt $i/60: Waiting for HTTP server..."
sleep 2
done
echo "ERROR: HTTP server not responding after 2 minutes"
echo "=== QEMU Output (last 200 lines) ==="
tail -200 qemu-output.log || true
echo ""
echo "=== Checking for ESP32 exceptions/crashes ==="
if grep -i "exception\|abort\|backtrace\|panic" qemu-output.log; then
echo "FOUND: Firmware crash detected in QEMU output"
else
echo "No obvious crash patterns found"
fi
exit 1
- name: Run Playwright tests against QEMU
env:
WLED_BASE_URL: http://localhost:8080
run: npm run test:e2e
- name: Analyze QEMU output for crashes
if: always()
run: |
echo "=== Analyzing QEMU output for ESP32 crashes ==="
if [ -f qemu-output.log ]; then
if grep -i "exception\|abort\|backtrace\|panic\|guru meditation" qemu-output.log > /dev/null; then
echo "ESP32 Exception/Crash detected in QEMU output!"
echo ""
echo "=== Exception Context ==="
grep -A 25 -B 5 -i "exception\|abort\|backtrace\|panic\|guru meditation" qemu-output.log | head -150
echo ""
echo "=== Stack Trace Analysis ==="
# Extract backtrace if present
if grep -i "Backtrace:" qemu-output.log > /dev/null; then
BACKTRACE=$(grep -i "Backtrace:" qemu-output.log | tail -1)
echo "Raw Backtrace: $BACKTRACE"
echo ""
echo "Analyzing crash location:"
# Extract first address (PC/crash location)
CRASH_ADDR=$(echo "$BACKTRACE" | grep -oP '0x[0-9a-fA-F]+' | head -1)
if [ -n "$CRASH_ADDR" ]; then
echo " - Crash at address: $CRASH_ADDR"
echo " - This is likely in firmware code or ROM"
fi
fi
echo ""
echo "=== Manual Exception Decoder Instructions ==="
echo "To decode this crash manually:"
echo ""
echo "1. Download the 'esp32-firmware' artifact from this GitHub Actions run"
echo "2. Extract the firmware.elf file"
echo "3. Install ESP-IDF or use PlatformIO's exception decoder:"
echo ""
echo " Method A - Using PlatformIO:"
echo " pio device monitor --filter esp32_exception_decoder"
echo " (Then paste the backtrace and exception info)"
echo ""
echo " Method B - Using ESP-IDF addr2line:"
echo " ~/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-addr2line \\"
echo " -pfiaC -e .pio/build/esp32_16MB_V4_M_eth_debug/firmware.elf \\"
echo " 0x401771aa 0x4015b4c5 0x40134813 ..."
echo ""
echo " Method C - Using online decoder:"
echo " https://github.com/me-no-dev/EspExceptionDecoder"
echo ""
echo "4. The decoded output will show:"
echo " - Function names where the crash occurred"
echo " - Source file locations (file:line)"
echo " - Call stack leading to the crash"
echo ""
echo "=== Crash Analysis Guidance ==="
echo "Common crash causes in QEMU:"
echo " - LoadStorePIFAddrError (0x0000000f): Invalid memory access"
echo " * Often caused by accessing uninitialized pointers"
echo " * Or accessing hardware registers not emulated by QEMU"
echo " * Check if crash is in hardware/peripheral initialization code"
echo ""
echo " - If crash is in ethernet/network code: May be QEMU limitation"
echo " - If crash is in WiFi code: Expected - WiFi not emulated"
echo " - If crash is in application code: Likely real firmware bug"
echo ""
echo "Note: This could be a QEMU-specific issue or a real firmware bug."
echo "QEMU ESP32 emulation has limitations:"
echo " - Many peripherals are not fully emulated"
echo " - Some hardware features may cause crashes in QEMU but work on real hardware"
echo " - Network/ethernet emulation may have issues"
else
echo "No ESP32 exceptions detected in QEMU output"
fi
else
echo "No QEMU output log found"
fi
- name: Upload QEMU logs
uses: actions/upload-artifact@v4
if: always()
with:
name: qemu-logs
path: qemu-output.log
retention-days: 7
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
- name: Stop QEMU
if: always()
run: |
if [ -f qemu.pid ]; then
QEMU_PID=$(cat qemu.pid)
echo "Stopping QEMU (PID: $QEMU_PID)"
kill $QEMU_PID || true
sleep 2
kill -9 $QEMU_PID 2>/dev/null || true
fi