259 lines
8.2 KiB
Python
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)
|