velxio/backend/test_compilation.py

259 lines
8.2 KiB
Python

"""
Backend Test Suite for Arduino Compilation
Tests arduino-cli integration and compilation API endpoint
"""
import asyncio
import subprocess
import sys
import os
from pathlib import Path
# Set UTF-8 encoding for Windows console
if os.name == 'nt':
import sys
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8')
if hasattr(sys.stderr, 'reconfigure'):
sys.stderr.reconfigure(encoding='utf-8')
# Test arduino-cli installation and AVR core
def test_arduino_cli_installed():
"""Test that arduino-cli is installed and accessible"""
print("\n=== Test 1: Arduino CLI Installation ===")
try:
result = subprocess.run(
["arduino-cli", "version"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
print(f"✓ arduino-cli is installed")
print(f" Version: {result.stdout.strip()}")
return True
else:
print(f"✗ arduino-cli returned error code {result.returncode}")
print(f" stderr: {result.stderr}")
return False
except FileNotFoundError:
print("✗ arduino-cli not found in PATH")
print(" Please install arduino-cli: https://arduino.github.io/arduino-cli/")
return False
except Exception as e:
print(f"✗ Error checking arduino-cli: {e}")
return False
def test_avr_core_installed():
"""Test that Arduino AVR core is installed"""
print("\n=== Test 2: AVR Core Installation ===")
try:
result = subprocess.run(
["arduino-cli", "core", "list"],
capture_output=True,
text=True,
timeout=10
)
if "arduino:avr" in result.stdout:
print("✓ Arduino AVR core is installed")
# Extract version
for line in result.stdout.splitlines():
if "arduino:avr" in line:
print(f" {line.strip()}")
return True
else:
print("✗ Arduino AVR core not installed")
print(" Run: arduino-cli core install arduino:avr")
return False
except Exception as e:
print(f"✗ Error checking AVR core: {e}")
return False
async def test_compile_service():
"""Test the ArduinoCLIService compilation"""
print("\n=== Test 3: Compilation Service ===")
try:
# Import the service
sys.path.insert(0, str(Path(__file__).parent))
from app.services.arduino_cli import ArduinoCLIService
# Create service instance
service = ArduinoCLIService()
print("✓ ArduinoCLIService instantiated")
# Test code - simple blink
test_code = """
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
"""
print(" Compiling test sketch...")
result = await service.compile(test_code)
if result["success"]:
print("✓ Compilation successful")
print(f" HEX size: {len(result['hex_content'])} characters")
print(f" HEX preview: {result['hex_content'][:50]}...")
# Verify HEX format (Intel HEX starts with :)
if result["hex_content"].startswith(":"):
print("✓ HEX format valid (starts with ':')")
else:
print("✗ HEX format invalid")
return False
# Check for end-of-file record
if ":00000001FF" in result["hex_content"]:
print("✓ HEX contains EOF record")
else:
print("⚠ Warning: HEX missing EOF record")
return True
else:
print("✗ Compilation failed")
print(f" Error: {result.get('error', 'Unknown')}")
print(f" Stdout: {result.get('stdout', '')}")
print(f" Stderr: {result.get('stderr', '')}")
return False
except ImportError as e:
print(f"✗ Failed to import service: {e}")
print(" Make sure you're running from backend directory")
return False
except Exception as e:
print(f"✗ Error during compilation test: {e}")
import traceback
traceback.print_exc()
return False
async def test_api_endpoint():
"""Test the FastAPI compilation endpoint"""
print("\n=== Test 4: API Endpoint ===")
try:
import httpx
# Test if server is running
async with httpx.AsyncClient(follow_redirects=True) as client:
try:
response = await client.get("http://localhost:8001/api/compile/boards", timeout=5.0)
if response.status_code == 200:
print("✓ Backend server is running")
boards = response.json().get("boards", [])
print(f" Available boards: {len(boards)}")
else:
print(f"⚠ Server returned status {response.status_code}")
except httpx.ConnectError:
print("✗ Backend server not running")
print(" Start server: cd backend && uvicorn app.main:app --port 8001")
return False
# Test compilation endpoint
test_code = """
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}
"""
print(" Testing /api/compile endpoint...")
response = await client.post(
"http://localhost:8001/api/compile",
json={"code": test_code, "board_fqbn": "arduino:avr:uno"},
timeout=30.0
)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("✓ API compilation successful")
print(f" HEX size: {len(data.get('hex_content', ''))} characters")
return True
else:
print("✗ API compilation failed")
print(f" Error: {data.get('error', 'Unknown')}")
return False
else:
print(f"✗ API returned status {response.status_code}")
print(f" Response: {response.text}")
return False
except ImportError:
print("⚠ httpx not installed, skipping API test")
print(" Install: pip install httpx")
return None # Skip test
except Exception as e:
print(f"✗ Error testing API: {e}")
return False
async def run_all_tests():
"""Run all backend tests"""
print("=" * 60)
print("BACKEND TEST SUITE")
print("=" * 60)
results = {}
# Test 1: arduino-cli installation
results["arduino_cli"] = test_arduino_cli_installed()
# Test 2: AVR core installation
results["avr_core"] = test_avr_core_installed()
# Test 3: Compilation service
if results["arduino_cli"] and results["avr_core"]:
results["compile_service"] = await test_compile_service()
else:
print("\n⚠ Skipping compilation test (prerequisites not met)")
results["compile_service"] = None
# Test 4: API endpoint
results["api_endpoint"] = await test_api_endpoint()
# Summary
print("\n" + "=" * 60)
print("TEST SUMMARY")
print("=" * 60)
passed = sum(1 for v in results.values() if v is True)
failed = sum(1 for v in results.values() if v is False)
skipped = sum(1 for v in results.values() if v is None)
for test_name, result in results.items():
status = "✓ PASS" if result is True else ("✗ FAIL" if result is False else "⚠ SKIP")
print(f"{status:10} {test_name}")
print(f"\nTotal: {passed} passed, {failed} failed, {skipped} skipped")
if failed > 0:
print("\n❌ Some tests failed. Please fix the issues above.")
return False
elif passed == len(results):
print("\n✅ All tests passed! Backend is working correctly.")
return True
else:
print("\n⚠ Some tests were skipped. Backend may be partially functional.")
return True
if __name__ == "__main__":
success = asyncio.run(run_all_tests())
sys.exit(0 if success else 1)