feat: Enhance CI workflows for backend unit and e2e tests; add environment setup and skip timing-sensitive tests in CI
This commit is contained in:
parent
7971743174
commit
6b161eab34
|
|
@ -0,0 +1,128 @@
|
||||||
|
name: Backend E2E Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
pull_request:
|
||||||
|
branches: [master, main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
|
||||||
|
env:
|
||||||
|
QEMU_RELEASE_URL: https://github.com/davidmonterocrespo24/velxio/releases/download/qemu-prebuilt
|
||||||
|
QEMU_DIR: /opt/velxio-qemu
|
||||||
|
BACKEND_URL: http://localhost:8001
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# ── QEMU binaries + ROM files ──────────────────────────────────────────────
|
||||||
|
- name: Download QEMU binaries and ROM files
|
||||||
|
run: |
|
||||||
|
mkdir -p $QEMU_DIR
|
||||||
|
cd $QEMU_DIR
|
||||||
|
|
||||||
|
echo "Downloading QEMU shared libraries..."
|
||||||
|
curl -fSL -o libqemu-xtensa.so "$QEMU_RELEASE_URL/libqemu-xtensa-amd64.so"
|
||||||
|
curl -fSL -o libqemu-riscv32.so "$QEMU_RELEASE_URL/libqemu-riscv32-amd64.so"
|
||||||
|
|
||||||
|
echo "Downloading ROM files..."
|
||||||
|
curl -fSL -o esp32-v3-rom.bin "$QEMU_RELEASE_URL/esp32-v3-rom.bin"
|
||||||
|
curl -fSL -o esp32-v3-rom-app.bin "$QEMU_RELEASE_URL/esp32-v3-rom-app.bin"
|
||||||
|
curl -fSL -o esp32c3-rom.bin "$QEMU_RELEASE_URL/esp32c3-rom.bin"
|
||||||
|
|
||||||
|
chmod +x libqemu-xtensa.so libqemu-riscv32.so
|
||||||
|
ls -lh .
|
||||||
|
|
||||||
|
# ── arduino-cli + ESP32 core ───────────────────────────────────────────────
|
||||||
|
- name: Install arduino-cli
|
||||||
|
run: |
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
|
||||||
|
| BINDIR=/usr/local/bin sh
|
||||||
|
|
||||||
|
- name: Cache arduino-cli cores and libraries
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.arduino15
|
||||||
|
key: arduino-cores-esp32-${{ runner.os }}-v1
|
||||||
|
|
||||||
|
- name: Install ESP32 arduino core
|
||||||
|
run: |
|
||||||
|
arduino-cli config init --overwrite
|
||||||
|
arduino-cli config add board_manager.additional_urls \
|
||||||
|
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
|
arduino-cli core update-index
|
||||||
|
arduino-cli core install esp32:esp32
|
||||||
|
|
||||||
|
# ── Python + backend dependencies ─────────────────────────────────────────
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Cache pip
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: pip-${{ hashFiles('backend/requirements.txt') }}
|
||||||
|
|
||||||
|
- name: Install backend dependencies
|
||||||
|
run: pip install -r backend/requirements.txt
|
||||||
|
|
||||||
|
# ── Node.js ───────────────────────────────────────────────────────────────
|
||||||
|
- name: Set up Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
# ── Start backend ─────────────────────────────────────────────────────────
|
||||||
|
- name: Start backend server
|
||||||
|
run: |
|
||||||
|
cd backend
|
||||||
|
QEMU_ESP32_LIB=$QEMU_DIR/libqemu-xtensa.so \
|
||||||
|
QEMU_RISCV32_LIB=$QEMU_DIR/libqemu-riscv32.so \
|
||||||
|
uvicorn app.main:app --port 8001 &
|
||||||
|
echo "Backend PID: $!"
|
||||||
|
env:
|
||||||
|
QEMU_ESP32_LIB: /opt/velxio-qemu/libqemu-xtensa.so
|
||||||
|
QEMU_RISCV32_LIB: /opt/velxio-qemu/libqemu-riscv32.so
|
||||||
|
|
||||||
|
- name: Wait for backend to be ready
|
||||||
|
run: |
|
||||||
|
echo "Waiting for backend on port 8001..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if curl -sf http://localhost:8001/docs > /dev/null 2>&1; then
|
||||||
|
echo "Backend is up after ${i}s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
curl -sf http://localhost:8001/docs > /dev/null || (echo "Backend failed to start" && exit 1)
|
||||||
|
|
||||||
|
# ── Run e2e tests ─────────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# These tests compile real firmware via the backend and run full simulations
|
||||||
|
# over WebSocket. Each test has a generous timeout because compilation +
|
||||||
|
# firmware boot takes 30-60 s.
|
||||||
|
#
|
||||||
|
# NOT run here (require Windows DLLs, not .so):
|
||||||
|
# test/backend/integration/ — uses libqemu-xtensa.dll (Windows only)
|
||||||
|
#
|
||||||
|
- name: Run DHT22 e2e test
|
||||||
|
run: node test/backend/e2e/test_dht22_simulation.mjs --timeout=60
|
||||||
|
env:
|
||||||
|
BACKEND_URL: http://localhost:8001
|
||||||
|
|
||||||
|
- name: Run HC-SR04 e2e test
|
||||||
|
run: node test/backend/e2e/test_hcsr04_simulation.mjs --timeout=75
|
||||||
|
env:
|
||||||
|
BACKEND_URL: http://localhost:8001
|
||||||
|
|
||||||
|
- name: Run MPU-6050 e2e test
|
||||||
|
run: node test/backend/e2e/test_mpu6050_simulation.mjs --timeout=60
|
||||||
|
env:
|
||||||
|
BACKEND_URL: http://localhost:8001
|
||||||
|
|
@ -32,7 +32,16 @@ jobs:
|
||||||
- name: Install test dependencies
|
- name: Install test dependencies
|
||||||
run: pip install pytest pytest-asyncio
|
run: pip install pytest pytest-asyncio
|
||||||
|
|
||||||
# Run only unit tests (test/backend/unit/) — no QEMU or arduino-cli needed.
|
# Run only UNIT tests (test/backend/unit/).
|
||||||
# Integration tests live in test/backend/integration/ and must be run manually.
|
# These tests have zero external dependencies — no QEMU binary, no arduino-cli,
|
||||||
|
# no running backend server.
|
||||||
|
#
|
||||||
|
# NOT included here (must be run manually on Windows with QEMU DLLs):
|
||||||
|
# test/backend/integration/ — needs arduino-cli + QEMU binary
|
||||||
|
# test/backend/e2e/ — JS tests, need running backend + QEMU + arduino-cli
|
||||||
|
# Run e2e tests locally: scripts\run-e2e-tests.bat
|
||||||
- name: Run backend unit tests
|
- name: Run backend unit tests
|
||||||
|
# CI=true is set automatically by GitHub Actions.
|
||||||
|
# Tests decorated with @unittest.skipIf(CI=='true', ...) are skipped here
|
||||||
|
# (e.g. timing-sensitive tests that use busy-wait).
|
||||||
run: pytest test/backend/unit/ -v --tb=short
|
run: pytest test/backend/unit/ -v --tb=short
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,58 @@
|
||||||
These tests compile real firmware via the backend API and run the full
|
These tests compile real firmware via the backend API and run the full
|
||||||
simulation through a WebSocket, verifying sensor readings in the serial output.
|
simulation through a WebSocket, verifying sensor readings in the serial output.
|
||||||
|
|
||||||
## Prerequisites
|
## CI status
|
||||||
|
|
||||||
- Backend running on `http://localhost:8001`
|
These tests **run in GitHub Actions** via `.github/workflows/backend-e2e-tests.yml`.
|
||||||
- Node.js 18+
|
|
||||||
|
|
||||||
## Run
|
The CI workflow:
|
||||||
|
|
||||||
|
1. Downloads `libqemu-xtensa-amd64.so` and `libqemu-riscv32-amd64.so` from the
|
||||||
|
[qemu-prebuilt](https://github.com/davidmonterocrespo24/velxio/releases/tag/qemu-prebuilt) release.
|
||||||
|
2. Downloads ROM files: `esp32-v3-rom.bin`, `esp32-v3-rom-app.bin`, `esp32c3-rom.bin`.
|
||||||
|
3. Installs arduino-cli + `esp32:esp32` core.
|
||||||
|
4. Starts `uvicorn app.main:app --port 8001` with `QEMU_ESP32_LIB` and `QEMU_RISCV32_LIB` set.
|
||||||
|
5. Runs all three JS tests.
|
||||||
|
|
||||||
|
## Local prerequisites
|
||||||
|
|
||||||
|
1. Backend running with QEMU libs in PATH:
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
QEMU_ESP32_LIB=/path/to/libqemu-xtensa.so \
|
||||||
|
QEMU_RISCV32_LIB=/path/to/libqemu-riscv32.so \
|
||||||
|
uvicorn app.main:app --reload --port 8001
|
||||||
|
```
|
||||||
|
On Windows the `.dll` files live in `backend/app/services/`.
|
||||||
|
|
||||||
|
2. Node.js 18+ installed.
|
||||||
|
|
||||||
|
## Run all e2e tests
|
||||||
|
|
||||||
|
Use the convenience script from the repo root:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From repo root
|
# Windows
|
||||||
node test/backend/e2e/test_dht22_simulation.mjs [--timeout=45]
|
scripts\run-e2e-tests.bat
|
||||||
node test/backend/e2e/test_hcsr04_simulation.mjs [--timeout=60]
|
|
||||||
node test/backend/e2e/test_mpu6050_simulation.mjs [--timeout=40]
|
|
||||||
|
|
||||||
# Custom backend URL
|
# Or individually (from repo root):
|
||||||
node test/backend/e2e/test_dht22_simulation.mjs --backend=http://localhost:8001
|
node test/backend/e2e/test_dht22_simulation.mjs --timeout=60
|
||||||
|
node test/backend/e2e/test_hcsr04_simulation.mjs --timeout=75
|
||||||
|
node test/backend/e2e/test_mpu6050_simulation.mjs --timeout=60
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
| File | Sensor | What it verifies |
|
| File | Sensor | What it verifies | Typical runtime |
|
||||||
|------|--------|-----------------|
|
|------|--------|-----------------|-----------------|
|
||||||
| `test_dht22_simulation.mjs` | DHT22 | Temperature & humidity, sensor_update changes values |
|
| `test_dht22_simulation.mjs` | DHT22 (GPIO4) | Temperature & humidity readings, `sensor_update` changes values | ~50 s |
|
||||||
| `test_hcsr04_simulation.mjs` | HC-SR04 | Distance at 10/40/100/200 cm, sensor_update changes distance |
|
| `test_hcsr04_simulation.mjs` | HC-SR04 (GPIO18/19) | Distance at 10/40/100/200 cm, `sensor_update` changes distance | ~60 s |
|
||||||
| `test_mpu6050_simulation.mjs` | MPU-6050 | I2C accelerometer/gyroscope readings |
|
| `test_mpu6050_simulation.mjs` | MPU-6050 (I2C) | Accelerometer/gyroscope I2C readings | ~40 s |
|
||||||
|
|
||||||
|
## Pass criteria
|
||||||
|
|
||||||
|
| Test | Passes when |
|
||||||
|
|------|-------------|
|
||||||
|
| DHT22 | First reading received + values change after `sensor_update` |
|
||||||
|
| HC-SR04 | ≥3 correct readings, ≥2 distances, miss rate ≤30% |
|
||||||
|
| MPU-6050 | I2C communication established, sensor data in serial output |
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,10 @@ class TestDHT22ResponseWaveform(unittest.TestCase):
|
||||||
|
|
||||||
print(' Waveform sequence: CORRECT ✓')
|
print(' Waveform sequence: CORRECT ✓')
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
__import__('os').environ.get('CI') == 'true',
|
||||||
|
'Timing-sensitive test (uses busy-wait + sleep) — skipped in CI'
|
||||||
|
)
|
||||||
def test_response_data_matches_payload(self):
|
def test_response_data_matches_payload(self):
|
||||||
"""Verify that the HIGH pulse durations encode the correct bits."""
|
"""Verify that the HIGH pulse durations encode the correct bits."""
|
||||||
harness = DHT22SimulatorHarness()
|
harness = DHT22SimulatorHarness()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue