Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
8c8d6fdc8This file is pinned to the selected release. Download the original to open it in your design tool.
#!/usr/bin/env python3
"""
Test script for KiCAD IPC Backend
This script tests the real-time UI synchronization capabilities
of the IPC backend. Run this while KiCAD is open with a board.
Prerequisites:
1. KiCAD 9.0+ must be running
2. IPC API must be enabled: Preferences > Plugins > Enable IPC API Server
3. A board should be open in the PCB editor
Usage:
./venv/bin/python python/test_ipc_backend.py
"""
import os
import sys
from typing import Any, Optional
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import logging
# Set up logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def test_connection() -> Optional[Any]:
"""Test basic IPC connection to KiCAD."""
print("\n" + "=" * 60)
print("TEST 1: IPC Connection")
print("=" * 60)
try:
from kicad_api.ipc_backend import IPCBackend
backend = IPCBackend()
print("✓ IPCBackend created")
if backend.connect():
print(f"✓ Connected to KiCAD via IPC")
print(f" Version: {backend.get_version()}")
return backend
else:
print("✗ Failed to connect to KiCAD")
return None
except ImportError as e:
print(f"✗ kicad-python not installed: {e}")
print(" Install with: pip install kicad-python")
return None
except Exception as e:
print(f"✗ Connection failed: {e}")
print("\nMake sure:")
print(" 1. KiCAD is running")
print(" 2. IPC API is enabled (Preferences > Plugins > Enable IPC API Server)")
print(" 3. A board is open in the PCB editor")
return None
def test_board_access(backend: Any) -> Optional[Any]:
"""Test board access and component listing."""
print("\n" + "=" * 60)
print("TEST 2: Board Access")
print("=" * 60)
try:
board_api = backend.get_board()
print("✓ Got board API")
# List components
components = board_api.list_components()
print(f"✓ Found {len(components)} components on board")
if components:
print("\n First 5 components:")
for comp in components[:5]:
ref = comp.get("reference", "N/A")
val = comp.get("value", "N/A")
pos = comp.get("position", {})
x = pos.get("x", 0)
y = pos.get("y", 0)
print(f" - {ref}: {val} @ ({x:.2f}, {y:.2f}) mm")
return board_api
except Exception as e:
print(f"✗ Failed to access board: {e}")
return None
def test_board_info(board_api: Any) -> bool:
"""Test getting board information."""
print("\n" + "=" * 60)
print("TEST 3: Board Information")
print("=" * 60)
try:
# Get board size
size = board_api.get_size()
print(f"✓ Board size: {size.get('width', 0):.2f} x {size.get('height', 0):.2f} mm")
# Get enabled layers
try:
layers = board_api.get_enabled_layers()
print(f"✓ Enabled layers: {len(layers)}")
if layers:
print(f" Layers: {', '.join(layers[:5])}...")
except Exception as e:
print(f" (Layer info not available: {e})")
# Get nets
nets = board_api.get_nets()
print(f"✓ Found {len(nets)} nets")
if nets:
print(f" First 5 nets: {', '.join([n.get('name', '') for n in nets[:5]])}")
# Get tracks
tracks = board_api.get_tracks()
print(f"✓ Found {len(tracks)} tracks")
# Get vias
vias = board_api.get_vias()
print(f"✓ Found {len(vias)} vias")
return True
except Exception as e:
print(f"✗ Failed to get board info: {e}")
return False
def test_realtime_track(board_api: Any, interactive: bool = False) -> bool:
"""Test adding a track in real-time (appears immediately in KiCAD UI)."""
print("\n" + "=" * 60)
print("TEST 4: Real-time Track Addition")
print("=" * 60)
print("\nThis test will add a track that appears IMMEDIATELY in KiCAD UI.")
print("Watch the KiCAD window!")
if interactive:
response = input("\nProceed with adding a test track? [y/N]: ").strip().lower()
if response != "y":
print("Skipped track test")
return False
try:
# Add a track
success = board_api.add_track(
start_x=100.0, start_y=100.0, end_x=120.0, end_y=100.0, width=0.25, layer="F.Cu"
)
if success:
print("✓ Track added! Check the KiCAD window - it should appear at (100, 100) mm")
print(" Track: (100, 100) -> (120, 100) mm, width 0.25mm on F.Cu")
else:
print("✗ Failed to add track")
return success
except Exception as e:
print(f"✗ Error adding track: {e}")
return False
def test_realtime_via(board_api: Any, interactive: bool = False) -> bool:
"""Test adding a via in real-time (appears immediately in KiCAD UI)."""
print("\n" + "=" * 60)
print("TEST 5: Real-time Via Addition")
print("=" * 60)
print("\nThis test will add a via that appears IMMEDIATELY in KiCAD UI.")
print("Watch the KiCAD window!")
if interactive:
response = input("\nProceed with adding a test via? [y/N]: ").strip().lower()
if response != "y":
print("Skipped via test")
return False
try:
# Add a via
success = board_api.add_via(x=120.0, y=100.0, diameter=0.8, drill=0.4, via_type="through")
if success:
print("✓ Via added! Check the KiCAD window - it should appear at (120, 100) mm")
print(" Via: diameter 0.8mm, drill 0.4mm")
else:
print("✗ Failed to add via")
return success
except Exception as e:
print(f"✗ Error adding via: {e}")
return False
def test_realtime_text(board_api: Any, interactive: bool = False) -> bool:
"""Test adding text in real-time."""
print("\n" + "=" * 60)
print("TEST 6: Real-time Text Addition")
print("=" * 60)
print("\nThis test will add text that appears IMMEDIATELY in KiCAD UI.")
if interactive:
response = input("\nProceed with adding test text? [y/N]: ").strip().lower()
if response != "y":
print("Skipped text test")
return False
try:
success = board_api.add_text(text="MCP Test", x=100.0, y=95.0, layer="F.SilkS", size=1.0)
if success:
print("✓ Text added! Check the KiCAD window - should show 'MCP Test' at (100, 95) mm")
else:
print("✗ Failed to add text")
return success
except Exception as e:
print(f"✗ Error adding text: {e}")
return False
def test_selection(board_api: Any, interactive: bool = False) -> bool:
"""Test getting the current selection from KiCAD UI."""
print("\n" + "=" * 60)
print("TEST 7: UI Selection")
print("=" * 60)
if interactive:
print("\nSelect some items in KiCAD, then press Enter...")
input()
else:
print("\nReading current selection...")
try:
selection = board_api.get_selection()
print(f"✓ Found {len(selection)} selected items")
for item in selection[:10]:
print(f" - {item.get('type', 'Unknown')} (ID: {item.get('id', 'N/A')})")
return True
except Exception as e:
print(f"✗ Failed to get selection: {e}")
return False
def run_all_tests(interactive: bool = False) -> bool:
"""Run all IPC backend tests."""
print("\n" + "=" * 60)
print("KiCAD IPC Backend Test Suite")
print("=" * 60)
print("\nThis script tests real-time communication with KiCAD via IPC API.")
print("Make sure KiCAD is running with a board open.\n")
# Test connection
backend = test_connection()
if not backend:
print("\n" + "=" * 60)
print("TESTS FAILED: Could not connect to KiCAD")
print("=" * 60)
return False
# Test board access
board_api = test_board_access(backend)
if not board_api:
print("\n" + "=" * 60)
print("TESTS FAILED: Could not access board")
print("=" * 60)
return False
# Test board info
test_board_info(board_api)
# Test real-time modifications
test_realtime_track(board_api, interactive)
test_realtime_via(board_api, interactive)
test_realtime_text(board_api, interactive)
# Test selection
test_selection(board_api, interactive)
print("\n" + "=" * 60)
print("TESTS COMPLETE")
print("=" * 60)
print("\nThe IPC backend is working! Changes appear in real-time.")
print("No manual reload required - this is the power of the IPC API!")
# Cleanup
backend.disconnect()
return True
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Test KiCAD IPC Backend")
parser.add_argument(
"-i",
"--interactive",
action="store_true",
help="Run in interactive mode (prompts before modifications)",
)
args = parser.parse_args()
success = run_all_tests(interactive=args.interactive)
sys.exit(0 if success else 1)