UART-controlled ESP32 peripheral for automated E2E Bluetooth testing. Dual-mode (Classic BT + BLE) via Bluedroid on original ESP32. Firmware (ESP-IDF v5.x, 2511 lines C): - NDJSON protocol over UART1 (115200 baud) - System commands: ping, reset, get_info, get_status - Classic BT: GAP, SPP, all 4 SSP pairing modes - BLE: GATTS, advertising, GATT service/characteristic management - 6 device personas: headset, speaker, keyboard, sensor, phone, bare - Event reporter: thread-safe async event queue to host Python MCP server (FastMCP, 1626 lines): - Async serial client with command/response correlation - Event queue with wait_for pattern matching - Tools: connection, configure, classic, ble, persona, events - MCP resources: esp32://status, esp32://events, esp32://personas Tests: 74 unit tests passing, 5 integration test stubs (skip without hardware)
41 lines
987 B
Python
41 lines
987 B
Python
"""Integration test fixtures — detect real ESP32 hardware on the serial port."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import pytest
|
|
import serial
|
|
|
|
ESP32_PORT = os.environ.get("ESP32_SERIAL_PORT", "/dev/ttyUSB4")
|
|
|
|
|
|
def esp32_available() -> bool:
|
|
"""Check if an ESP32 is connected on the expected port."""
|
|
try:
|
|
s = serial.Serial(ESP32_PORT, 115200, timeout=1)
|
|
s.close()
|
|
return True
|
|
except (serial.SerialException, OSError):
|
|
return False
|
|
|
|
|
|
requires_esp32 = pytest.mark.skipif(
|
|
not esp32_available(),
|
|
reason=f"ESP32 not found on {ESP32_PORT}",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
async def esp32_client():
|
|
"""Connected SerialClient fixture -- skips if no hardware."""
|
|
if not esp32_available():
|
|
pytest.skip(f"ESP32 not available on {ESP32_PORT}")
|
|
|
|
from mcbluetooth_esp32.serial_client import SerialClient
|
|
|
|
client = SerialClient(port=ESP32_PORT)
|
|
await client.connect()
|
|
yield client
|
|
await client.disconnect()
|