kicad-mcp/tests/unit/test_config.py
Ryan Malloy 995dfd57c1 Add comprehensive advanced KiCad features and fix MCP compatibility issues
- Implement 3D model analysis and mechanical constraints checking
- Add advanced DRC rule customization for HDI, RF, and automotive applications
- Create symbol library management with analysis and validation tools
- Implement PCB layer stack-up analysis with impedance calculations
- Fix Context parameter validation errors causing client failures
- Add enhanced tool annotations with examples for better LLM compatibility
- Include comprehensive test coverage improvements (22.21% coverage)
- Add CLAUDE.md documentation for development guidance

New Advanced Tools:
• 3D model analysis: analyze_3d_models, check_mechanical_constraints
• Advanced DRC: create_drc_rule_set, analyze_pcb_drc_violations
• Symbol management: analyze_symbol_library, validate_symbol_library
• Layer analysis: analyze_pcb_stackup, calculate_trace_impedance

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-11 15:57:46 -06:00

228 lines
9.3 KiB
Python

"""
Tests for the kicad_mcp.config module.
"""
import os
import platform
from unittest.mock import patch, MagicMock
import pytest
class TestConfigModule:
"""Test config module constants and platform-specific behavior."""
def test_system_detection(self):
"""Test that system is properly detected."""
from kicad_mcp.config import system
assert system in ['Darwin', 'Windows', 'Linux'] or isinstance(system, str)
assert system == platform.system()
def test_macos_paths(self):
"""Test macOS-specific path configuration."""
with patch('platform.system', return_value='Darwin'):
# Need to reload the config module after patching
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import KICAD_USER_DIR, KICAD_APP_PATH, KICAD_PYTHON_BASE
assert KICAD_USER_DIR == os.path.expanduser("~/Documents/KiCad")
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
assert "Contents/Frameworks/Python.framework" in KICAD_PYTHON_BASE
def test_windows_paths(self):
"""Test Windows-specific path configuration."""
with patch('platform.system', return_value='Windows'):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import KICAD_USER_DIR, KICAD_APP_PATH, KICAD_PYTHON_BASE
assert KICAD_USER_DIR == os.path.expanduser("~/Documents/KiCad")
assert KICAD_APP_PATH == r"C:\Program Files\KiCad"
assert KICAD_PYTHON_BASE == ""
def test_linux_paths(self):
"""Test Linux-specific path configuration."""
with patch('platform.system', return_value='Linux'):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import KICAD_USER_DIR, KICAD_APP_PATH, KICAD_PYTHON_BASE
assert KICAD_USER_DIR == os.path.expanduser("~/KiCad")
assert KICAD_APP_PATH == "/usr/share/kicad"
assert KICAD_PYTHON_BASE == ""
def test_unknown_system_defaults_to_macos(self):
"""Test that unknown systems default to macOS paths."""
with patch('platform.system', return_value='FreeBSD'):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import KICAD_USER_DIR, KICAD_APP_PATH
assert KICAD_USER_DIR == os.path.expanduser("~/Documents/KiCad")
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
def test_kicad_extensions(self):
"""Test KiCad file extension mappings."""
from kicad_mcp.config import KICAD_EXTENSIONS
expected_keys = ["project", "pcb", "schematic", "design_rules",
"worksheet", "footprint", "netlist", "kibot_config"]
for key in expected_keys:
assert key in KICAD_EXTENSIONS
assert isinstance(KICAD_EXTENSIONS[key], str)
assert KICAD_EXTENSIONS[key].startswith(('.', '_'))
def test_data_extensions(self):
"""Test data file extensions list."""
from kicad_mcp.config import DATA_EXTENSIONS
assert isinstance(DATA_EXTENSIONS, list)
assert len(DATA_EXTENSIONS) > 0
expected_extensions = [".csv", ".pos", ".net", ".zip", ".drl"]
for ext in expected_extensions:
assert ext in DATA_EXTENSIONS
def test_circuit_defaults(self):
"""Test circuit default parameters."""
from kicad_mcp.config import CIRCUIT_DEFAULTS
required_keys = ["grid_spacing", "component_spacing", "wire_width",
"text_size", "pin_length"]
for key in required_keys:
assert key in CIRCUIT_DEFAULTS
# Test specific types
assert isinstance(CIRCUIT_DEFAULTS["text_size"], list)
assert len(CIRCUIT_DEFAULTS["text_size"]) == 2
assert all(isinstance(x, (int, float)) for x in CIRCUIT_DEFAULTS["text_size"])
def test_common_libraries_structure(self):
"""Test common libraries configuration structure."""
from kicad_mcp.config import COMMON_LIBRARIES
expected_categories = ["basic", "power", "connectors"]
for category in expected_categories:
assert category in COMMON_LIBRARIES
assert isinstance(COMMON_LIBRARIES[category], dict)
for component, info in COMMON_LIBRARIES[category].items():
assert "library" in info
assert "symbol" in info
assert isinstance(info["library"], str)
assert isinstance(info["symbol"], str)
def test_default_footprints_structure(self):
"""Test default footprints configuration structure."""
from kicad_mcp.config import DEFAULT_FOOTPRINTS
# Test that at least some common components are present
common_components = ["R", "C", "LED", "D"]
for component in common_components:
assert component in DEFAULT_FOOTPRINTS
assert isinstance(DEFAULT_FOOTPRINTS[component], list)
assert len(DEFAULT_FOOTPRINTS[component]) > 0
# All footprints should be strings
for footprint in DEFAULT_FOOTPRINTS[component]:
assert isinstance(footprint, str)
assert ":" in footprint # Should be in format "Library:Footprint"
def test_timeout_constants(self):
"""Test timeout constants are reasonable values."""
from kicad_mcp.config import TIMEOUT_CONSTANTS
required_keys = ["kicad_cli_version_check", "kicad_cli_export",
"application_open", "subprocess_default"]
for key in required_keys:
assert key in TIMEOUT_CONSTANTS
timeout = TIMEOUT_CONSTANTS[key]
assert isinstance(timeout, (int, float))
assert 0 < timeout <= 300 # Reasonable timeout range
def test_progress_constants(self):
"""Test progress constants are valid percentages."""
from kicad_mcp.config import PROGRESS_CONSTANTS
required_keys = ["start", "detection", "setup", "processing",
"finishing", "validation", "complete"]
for key in required_keys:
assert key in PROGRESS_CONSTANTS
progress = PROGRESS_CONSTANTS[key]
assert isinstance(progress, int)
assert 0 <= progress <= 100
def test_display_constants(self):
"""Test display constants are reasonable values."""
from kicad_mcp.config import DISPLAY_CONSTANTS
assert "bom_preview_limit" in DISPLAY_CONSTANTS
limit = DISPLAY_CONSTANTS["bom_preview_limit"]
assert isinstance(limit, int)
assert limit > 0
def test_empty_search_paths_environment(self):
"""Test behavior with empty KICAD_SEARCH_PATHS."""
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": ""}):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
# Should still have default locations if they exist
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
assert isinstance(ADDITIONAL_SEARCH_PATHS, list)
def test_nonexistent_search_paths_ignored(self):
"""Test that nonexistent search paths are ignored."""
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": "/nonexistent/path1,/nonexistent/path2"}), \
patch('os.path.exists', return_value=False):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
# Should not contain the nonexistent paths
assert "/nonexistent/path1" not in ADDITIONAL_SEARCH_PATHS
assert "/nonexistent/path2" not in ADDITIONAL_SEARCH_PATHS
def test_search_paths_expansion_and_trimming(self):
"""Test that search paths are expanded and trimmed."""
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": "~/test_path1, ~/test_path2 "}), \
patch('os.path.exists', return_value=True), \
patch('os.path.expanduser', side_effect=lambda x: x.replace("~", "/home/user")):
import importlib
import kicad_mcp.config
importlib.reload(kicad_mcp.config)
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS
# Should contain expanded paths
assert "/home/user/test_path1" in ADDITIONAL_SEARCH_PATHS
assert "/home/user/test_path2" in ADDITIONAL_SEARCH_PATHS
def test_default_project_locations_expanded(self):
"""Test that default project locations are properly expanded."""
from kicad_mcp.config import DEFAULT_PROJECT_LOCATIONS
assert isinstance(DEFAULT_PROJECT_LOCATIONS, list)
assert len(DEFAULT_PROJECT_LOCATIONS) > 0
# All should start with ~/
for location in DEFAULT_PROJECT_LOCATIONS:
assert location.startswith("~/")