Skip to main content
Glama
freecad_mock.py10.8 kB
""" Mock FreeCAD classes and modules for testing without requiring actual FreeCAD installation. This module provides mock implementations of FreeCAD's core classes and functions to enable comprehensive testing of the MCP-FreeCAD addon. """ import math from typing import Any, Dict, List, Optional, Union from unittest.mock import MagicMock class MockVector: """Mock FreeCAD Vector class.""" def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0): self.x = x self.y = y self.z = z def add(self, other: "MockVector") -> "MockVector": """Add two vectors.""" return MockVector(self.x + other.x, self.y + other.y, self.z + other.z) def sub(self, other: "MockVector") -> "MockVector": """Subtract two vectors.""" return MockVector(self.x - other.x, self.y - other.y, self.z - other.z) def normalize(self) -> "MockVector": """Return normalized vector.""" length = math.sqrt(self.x**2 + self.y**2 + self.z**2) if length > 0: return MockVector(self.x / length, self.y / length, self.z / length) return MockVector(0, 0, 0) def __iter__(self): """Make vector iterable for unpacking.""" return iter([self.x, self.y, self.z]) def __repr__(self): return f"Vector({self.x}, {self.y}, {self.z})" class MockRotation: """Mock FreeCAD Rotation class.""" def __init__(self, axis: MockVector = None, angle: float = 0.0): self.axis = axis or MockVector(0, 0, 1) self.angle = angle def multiply(self, other: "MockRotation") -> "MockRotation": """Multiply rotations.""" # Simplified rotation multiplication return MockRotation(self.axis, self.angle + other.angle) class MockPlacement: """Mock FreeCAD Placement class.""" def __init__(self, base: MockVector = None, rotation: MockRotation = None): self.Base = base or MockVector(0, 0, 0) self.Rotation = rotation or MockRotation() class MockMatrix: """Mock FreeCAD Matrix class.""" def __init__(self): # 4x4 transformation matrix elements self.A11 = 1.0 self.A12 = 0.0 self.A13 = 0.0 self.A14 = 0.0 self.A21 = 0.0 self.A22 = 1.0 self.A23 = 0.0 self.A24 = 0.0 self.A31 = 0.0 self.A32 = 0.0 self.A33 = 1.0 self.A34 = 0.0 self.A41 = 0.0 self.A42 = 0.0 self.A43 = 0.0 self.A44 = 1.0 def unity(self): """Set to identity matrix.""" self.__init__() class MockShape: """Mock FreeCAD Shape class.""" def __init__(self, shape_type: str = "Solid"): self.ShapeType = shape_type self.Volume = 1000.0 # Default volume self.Area = 600.0 # Default surface area self.Edges = [MockEdge() for _ in range(12)] # Default edges for a box self.Faces = [MockFace() for _ in range(6)] # Default faces for a box self.Vertexes = [MockVertex() for _ in range(8)] # Default vertices for a box def fuse(self, other: "MockShape") -> "MockShape": """Boolean union operation.""" result = MockShape("Solid") result.Volume = self.Volume + other.Volume result.Area = self.Area + other.Area * 0.8 # Approximate fusion return result def cut(self, other: "MockShape") -> "MockShape": """Boolean difference operation.""" result = MockShape("Solid") result.Volume = max(0, self.Volume - other.Volume) result.Area = self.Area + other.Area * 0.5 # Approximate cutting return result def common(self, other: "MockShape") -> "MockShape": """Boolean intersection operation.""" result = MockShape("Solid") result.Volume = min(self.Volume, other.Volume) * 0.5 # Approximate intersection result.Area = min(self.Area, other.Area) * 0.7 return result def makeFillet(self, radius: float, edges: List["MockEdge"]) -> "MockShape": """Create fillet on edges.""" result = MockShape("Solid") result.Volume = self.Volume * 0.95 # Slightly reduced volume result.Area = self.Area * 1.05 # Slightly increased area return result def transformed(self, matrix: MockMatrix) -> "MockShape": """Apply transformation matrix.""" result = MockShape(self.ShapeType) result.Volume = self.Volume result.Area = self.Area return result class MockEdge: """Mock FreeCAD Edge class.""" def __init__(self): self.Length = 10.0 class MockFace: """Mock FreeCAD Face class.""" def __init__(self): self.Area = 100.0 class MockVertex: """Mock FreeCAD Vertex class.""" def __init__(self): self.Point = MockVector(0, 0, 0) class MockObject: """Mock FreeCAD Document Object.""" def __init__(self, name: str, object_type: str = "Part::Feature"): self.Name = name self.TypeId = object_type self.Label = name self.Placement = MockPlacement() self.Shape = MockShape() self._properties = {} # Add type-specific properties if object_type == "Part::Box": self.Length = 10.0 self.Width = 10.0 self.Height = 10.0 elif object_type == "Part::Cylinder": self.Radius = 5.0 self.Height = 10.0 elif object_type == "Part::Sphere": self.Radius = 5.0 elif object_type == "Part::Cone": self.Radius1 = 5.0 self.Radius2 = 0.0 self.Height = 10.0 def __setattr__(self, name, value): """Override setattr to track property changes.""" if hasattr(self, "_properties"): self._properties[name] = value super().__setattr__(name, value) class MockDocument: """Mock FreeCAD Document.""" def __init__(self, name: str = "Unnamed"): self.Name = name self.Label = name self.Objects = [] self._object_counter = 0 self._objects_by_name = {} def addObject(self, object_type: str, name: str = None) -> MockObject: """Add an object to the document.""" if name is None: self._object_counter += 1 name = f"Object{self._object_counter:03d}" # Ensure unique name original_name = name counter = 1 while name in self._objects_by_name: name = f"{original_name}{counter:03d}" counter += 1 obj = MockObject(name, object_type) self.Objects.append(obj) self._objects_by_name[name] = obj return obj def getObject(self, name: str) -> Optional[MockObject]: """Get an object by name.""" return self._objects_by_name.get(name) def getObjectsByLabel(self, label: str) -> List[MockObject]: """Get objects by label.""" return [obj for obj in self.Objects if obj.Label == label] def removeObject(self, name: str) -> bool: """Remove an object from the document.""" if name in self._objects_by_name: obj = self._objects_by_name[name] self.Objects.remove(obj) del self._objects_by_name[name] return True return False def recompute(self) -> None: """Recompute the document.""" # In real FreeCAD, this updates all objects # In our mock, we just simulate the operation pass def saveAs(self, filename: str) -> bool: """Save document to file.""" # Mock implementation return True class MockGui: """Mock FreeCAD Gui module.""" @staticmethod def hideObject(obj: MockObject) -> None: """Hide an object in the GUI.""" pass @staticmethod def showObject(obj: MockObject) -> None: """Show an object in the GUI.""" pass class MockPart: """Mock FreeCAD Part module.""" @staticmethod def makeBox(length: float, width: float, height: float) -> MockShape: """Create a box shape.""" shape = MockShape("Solid") shape.Volume = length * width * height shape.Area = 2 * (length * width + width * height + height * length) return shape @staticmethod def makeCylinder(radius: float, height: float) -> MockShape: """Create a cylinder shape.""" shape = MockShape("Solid") shape.Volume = math.pi * radius**2 * height shape.Area = 2 * math.pi * radius * (radius + height) return shape @staticmethod def makeSphere(radius: float) -> MockShape: """Create a sphere shape.""" shape = MockShape("Solid") shape.Volume = (4 / 3) * math.pi * radius**3 shape.Area = 4 * math.pi * radius**2 return shape class MockFreeCAD: """Mock FreeCAD application.""" def __init__(self): self.ActiveDocument = None self.Documents = {} self._doc_counter = 0 # Add static classes/modules self.Vector = MockVector self.Rotation = MockRotation self.Placement = MockPlacement self.Matrix = MockMatrix self.Gui = MockGui() def newDocument(self, name: str = None) -> MockDocument: """Create a new document.""" if name is None: self._doc_counter += 1 name = f"Unnamed{self._doc_counter}" doc = MockDocument(name) self.Documents[name] = doc self.ActiveDocument = doc return doc def openDocument(self, filename: str) -> MockDocument: """Open a document from file.""" # Mock implementation - just create a new document name = filename.split("/")[-1].split(".")[0] return self.newDocument(name) def closeDocument(self, name: str) -> None: """Close a document.""" if name in self.Documents: del self.Documents[name] if self.ActiveDocument and self.ActiveDocument.Name == name: self.ActiveDocument = None def setActiveDocument(self, name: str) -> None: """Set the active document.""" if name in self.Documents: self.ActiveDocument = self.Documents[name] def getDocument(self, name: str) -> Optional[MockDocument]: """Get a document by name.""" return self.Documents.get(name) # Create global mock instances that can be imported mock_freecad = MockFreeCAD() mock_part = MockPart() # Function to get a clean mock instance for testing def get_mock_freecad() -> MockFreeCAD: """Get a fresh MockFreeCAD instance for testing.""" return MockFreeCAD() def get_mock_part() -> MockPart: """Get a MockPart instance for testing.""" return MockPart()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/mcp-freecad'

If you have feedback or need assistance with the MCP directory API, please join our Discord server