mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-09-13 16:53:13 +08:00
Merge branch 'master' of ssh://github.com/Ultimaker/Cura
This commit is contained in:
commit
5a51a036d7
@ -10,3 +10,10 @@ build-and-test:
|
|||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build
|
- build
|
||||||
|
|
||||||
|
build-and-test_merge-requests:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- docker/build.sh
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
@ -258,7 +258,7 @@ class BuildVolume(SceneNode):
|
|||||||
node.setOutsideBuildArea(True)
|
node.setOutsideBuildArea(True)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if node.collidesWithArea(self.getDisallowedAreas()):
|
if node.collidesWithAreas(self.getDisallowedAreas()):
|
||||||
node.setOutsideBuildArea(True)
|
node.setOutsideBuildArea(True)
|
||||||
continue
|
continue
|
||||||
# If the entire node is below the build plate, still mark it as outside.
|
# If the entire node is below the build plate, still mark it as outside.
|
||||||
@ -312,7 +312,7 @@ class BuildVolume(SceneNode):
|
|||||||
node.setOutsideBuildArea(True)
|
node.setOutsideBuildArea(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
if node.collidesWithArea(self.getDisallowedAreas()):
|
if node.collidesWithAreas(self.getDisallowedAreas()):
|
||||||
node.setOutsideBuildArea(True)
|
node.setOutsideBuildArea(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
|||||||
self._gcode = [] # type: List[str]
|
self._gcode = [] # type: List[str]
|
||||||
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
raise NotImplementedError("requestWrite needs to be implemented")
|
raise NotImplementedError("requestWrite needs to be implemented")
|
||||||
|
|
||||||
def setAuthenticationState(self, authentication_state: AuthState) -> None:
|
def setAuthenticationState(self, authentication_state: AuthState) -> None:
|
||||||
|
@ -144,7 +144,7 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
file_handler: Optional["FileHandler"] = None, **kwargs: str) -> None:
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
raise NotImplementedError("requestWrite needs to be implemented")
|
raise NotImplementedError("requestWrite needs to be implemented")
|
||||||
|
|
||||||
@pyqtProperty(QObject, notify = printersChanged)
|
@pyqtProperty(QObject, notify = printersChanged)
|
||||||
|
@ -6,13 +6,13 @@ from typing import cast, Dict, List, Optional
|
|||||||
|
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||||
from UM.Math.Polygon import Polygon #For typing.
|
from UM.Math.Polygon import Polygon # For typing.
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator #To cast the deepcopy of every decorator back to SceneNodeDecorator.
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator # To cast the deepcopy of every decorator back to SceneNodeDecorator.
|
||||||
|
|
||||||
import cura.CuraApplication #To get the build plate.
|
import cura.CuraApplication # To get the build plate.
|
||||||
from cura.Settings.ExtruderStack import ExtruderStack #For typing.
|
from cura.Settings.ExtruderStack import ExtruderStack # For typing.
|
||||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator #For per-object settings.
|
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator # For per-object settings.
|
||||||
|
|
||||||
|
|
||||||
## Scene nodes that are models are only seen when selecting the corresponding build plate
|
## Scene nodes that are models are only seen when selecting the corresponding build plate
|
||||||
@ -21,7 +21,7 @@ class CuraSceneNode(SceneNode):
|
|||||||
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False) -> None:
|
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "", no_setting_override: bool = False) -> None:
|
||||||
super().__init__(parent = parent, visible = visible, name = name)
|
super().__init__(parent = parent, visible = visible, name = name)
|
||||||
if not no_setting_override:
|
if not no_setting_override:
|
||||||
self.addDecorator(SettingOverrideDecorator()) # now we always have a getActiveExtruderPosition, unless explicitly disabled
|
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||||
self._outside_buildarea = False
|
self._outside_buildarea = False
|
||||||
|
|
||||||
def setOutsideBuildArea(self, new_value: bool) -> None:
|
def setOutsideBuildArea(self, new_value: bool) -> None:
|
||||||
@ -59,7 +59,7 @@ class CuraSceneNode(SceneNode):
|
|||||||
if extruder_id is not None:
|
if extruder_id is not None:
|
||||||
if extruder_id == extruder.getId():
|
if extruder_id == extruder.getId():
|
||||||
return extruder
|
return extruder
|
||||||
else: # If the id is unknown, then return the extruder in the position 0
|
else: # If the id is unknown, then return the extruder in the position 0
|
||||||
try:
|
try:
|
||||||
if extruder.getMetaDataEntry("position", default = "0") == "0": # Check if the position is zero
|
if extruder.getMetaDataEntry("position", default = "0") == "0": # Check if the position is zero
|
||||||
return extruder
|
return extruder
|
||||||
@ -87,13 +87,13 @@ class CuraSceneNode(SceneNode):
|
|||||||
]
|
]
|
||||||
|
|
||||||
## Return if any area collides with the convex hull of this scene node
|
## Return if any area collides with the convex hull of this scene node
|
||||||
def collidesWithArea(self, areas: List[Polygon]) -> bool:
|
def collidesWithAreas(self, areas: List[Polygon]) -> bool:
|
||||||
convex_hull = self.callDecoration("getConvexHull")
|
convex_hull = self.callDecoration("getConvexHull")
|
||||||
if convex_hull:
|
if convex_hull:
|
||||||
if not convex_hull.isValid():
|
if not convex_hull.isValid():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check for collisions between disallowed areas and the object
|
# Check for collisions between provided areas and the object
|
||||||
for area in areas:
|
for area in areas:
|
||||||
overlap = convex_hull.intersectsPolygon(area)
|
overlap = convex_hull.intersectsPolygon(area)
|
||||||
if overlap is None:
|
if overlap is None:
|
||||||
|
@ -171,8 +171,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via Cloud"))
|
self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via Cloud"))
|
||||||
|
|
||||||
## Called when Cura requests an output device to receive a (G-code) file.
|
## Called when Cura requests an output device to receive a (G-code) file.
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
|
|
||||||
# Show an error message if we're already sending a job.
|
# Show an error message if we're already sending a job.
|
||||||
if self._progress.visible:
|
if self._progress.visible:
|
||||||
|
@ -106,8 +106,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
|
|
||||||
self._active_camera_url = QUrl() # type: QUrl
|
self._active_camera_url = QUrl() # type: QUrl
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
self.writeStarted.emit(self)
|
self.writeStarted.emit(self)
|
||||||
|
|
||||||
self.sendMaterialProfiles()
|
self.sendMaterialProfiles()
|
||||||
|
@ -178,7 +178,8 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||||||
# NotImplementedError. We can simply ignore these.
|
# NotImplementedError. We can simply ignore these.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
if not self.activePrinter:
|
if not self.activePrinter:
|
||||||
# No active printer. Unable to write
|
# No active printer. Unable to write
|
||||||
return
|
return
|
||||||
|
@ -24,11 +24,15 @@ from queue import Queue
|
|||||||
from serial import Serial, SerialException, SerialTimeoutException
|
from serial import Serial, SerialException, SerialTimeoutException
|
||||||
from threading import Thread, Event
|
from threading import Thread, Event
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Union, Optional, List, cast
|
from typing import Union, Optional, List, cast, TYPE_CHECKING
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import functools # Used for reduce
|
import functools # Used for reduce
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from UM.FileHandler.FileHandler import FileHandler
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
@ -117,14 +121,16 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||||||
# \param filter_by_machine Whether to filter MIME types by machine. This
|
# \param filter_by_machine Whether to filter MIME types by machine. This
|
||||||
# is ignored.
|
# is ignored.
|
||||||
# \param kwargs Keyword arguments.
|
# \param kwargs Keyword arguments.
|
||||||
def requestWrite(self, nodes, file_name: str = None, filter_by_machine = False, file_handler = None, **kwargs):
|
def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False,
|
||||||
|
file_handler: Optional["FileHandler"] = None, filter_by_machine: bool = False, **kwargs) -> None:
|
||||||
if self._is_printing:
|
if self._is_printing:
|
||||||
message = Message(text = catalog.i18nc("@message", "A print is still in progress. Cura cannot start another print via USB until the previous print has completed."), title = catalog.i18nc("@message", "Print in Progress"))
|
message = Message(text = catalog.i18nc("@message", "A print is still in progress. Cura cannot start another print via USB until the previous print has completed."), title = catalog.i18nc("@message", "Print in Progress"))
|
||||||
message.show()
|
message.show()
|
||||||
return # Already printing
|
return # Already printing
|
||||||
self.writeStarted.emit(self)
|
self.writeStarted.emit(self)
|
||||||
# cancel any ongoing preheat timer before starting a print
|
# cancel any ongoing preheat timer before starting a print
|
||||||
self._printers[0].getController().stopPreheatTimers()
|
controller = cast(GenericOutputController, self._printers[0].getController())
|
||||||
|
controller.stopPreheatTimers()
|
||||||
|
|
||||||
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
|
CuraApplication.getInstance().getController().setActiveStage("MonitorStage")
|
||||||
|
|
||||||
|
54
tests/TestCuraSceneNode.py
Normal file
54
tests/TestCuraSceneNode.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from UM.Math.Polygon import Polygon
|
||||||
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
class TestConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def getConvexHull(self):
|
||||||
|
return Polygon([[5, 5], [-5, 5], [-5, -5], [5, -5]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInvalidConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def getConvexHull(self):
|
||||||
|
return Polygon()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def cura_scene_node():
|
||||||
|
# Replace the SettingOverrideDecorator with an empty decorator
|
||||||
|
with patch("cura.Scene.CuraSceneNode.SettingOverrideDecorator", SceneNodeDecorator):
|
||||||
|
return CuraSceneNode()
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollidesWithAreas:
|
||||||
|
def test_noConvexHull(self, cura_scene_node):
|
||||||
|
assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])
|
||||||
|
|
||||||
|
def test_convexHullIntersects(self, cura_scene_node):
|
||||||
|
cura_scene_node.addDecorator(TestConvexHullDecorator())
|
||||||
|
assert cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])
|
||||||
|
|
||||||
|
def test_convexHullNoIntersection(self, cura_scene_node):
|
||||||
|
cura_scene_node.addDecorator(TestConvexHullDecorator())
|
||||||
|
|
||||||
|
assert not cura_scene_node.collidesWithAreas([Polygon([[60, 60], [40, 60], [40, 40], [60, 40]])])
|
||||||
|
|
||||||
|
def test_invalidConvexHull(self, cura_scene_node):
|
||||||
|
cura_scene_node.addDecorator(TestInvalidConvexHullDecorator())
|
||||||
|
assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])])
|
||||||
|
|
||||||
|
|
||||||
|
def test_outsideBuildArea(cura_scene_node):
|
||||||
|
cura_scene_node.setOutsideBuildArea(True)
|
||||||
|
assert cura_scene_node.isOutsideBuildArea
|
||||||
|
|
||||||
|
|
31
tests/TestExtruderManager.py
Normal file
31
tests/TestExtruderManager.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
|
||||||
|
def createMockedExtruder(extruder_id):
|
||||||
|
extruder = MagicMock()
|
||||||
|
extruder.getId = MagicMock(return_value = extruder_id)
|
||||||
|
return extruder
|
||||||
|
|
||||||
|
|
||||||
|
def test_getAllExtruderSettings(extruder_manager):
|
||||||
|
extruder_1 = createMockedExtruder("extruder_1")
|
||||||
|
extruder_1.getProperty = MagicMock(return_value ="beep")
|
||||||
|
extruder_2 = createMockedExtruder("extruder_2")
|
||||||
|
extruder_2.getProperty = MagicMock(return_value="zomg")
|
||||||
|
extruder_manager.getActiveExtruderStacks = MagicMock(return_value = [extruder_1, extruder_2])
|
||||||
|
assert extruder_manager.getAllExtruderSettings("whatever", "value") == ["beep", "zomg"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_registerExtruder(extruder_manager):
|
||||||
|
extruder = createMockedExtruder("beep")
|
||||||
|
extruder.getMetaDataEntry = MagicMock(return_value = "0") # because the extruder position gets called
|
||||||
|
|
||||||
|
extruder_manager.extrudersChanged = MagicMock()
|
||||||
|
extruder_manager.registerExtruder(extruder, "zomg")
|
||||||
|
|
||||||
|
assert extruder_manager.extrudersChanged.emit.call_count == 1
|
||||||
|
|
||||||
|
# Doing it again should not trigger anything
|
||||||
|
extruder_manager.registerExtruder(extruder, "zomg")
|
||||||
|
assert extruder_manager.extrudersChanged.emit.call_count == 1
|
@ -2,8 +2,6 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
|
||||||
from cura.Settings.MachineManager import MachineManager
|
from cura.Settings.MachineManager import MachineManager
|
||||||
|
|
||||||
|
|
||||||
@ -13,22 +11,6 @@ def global_stack():
|
|||||||
stack.getId = MagicMock(return_value ="GlobalStack")
|
stack.getId = MagicMock(return_value ="GlobalStack")
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def container_registry() -> ContainerRegistry:
|
|
||||||
return MagicMock(name = "ContainerRegistry")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def extruder_manager(application, container_registry) -> ExtruderManager:
|
|
||||||
if ExtruderManager.getInstance() is not None:
|
|
||||||
# Reset the data
|
|
||||||
ExtruderManager._ExtruderManager__instance = None
|
|
||||||
|
|
||||||
with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)):
|
|
||||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
|
||||||
manager = ExtruderManager()
|
|
||||||
return manager
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager:
|
def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager:
|
||||||
|
@ -4,8 +4,8 @@ from cura.Machines.MaterialManager import MaterialManager
|
|||||||
|
|
||||||
|
|
||||||
mocked_registry = MagicMock()
|
mocked_registry = MagicMock()
|
||||||
material_1 = {"id": "test", "GUID":"TEST!", "base_file": "base_material", "definition": "fdmmachine", "approximate_diameter": 3, "brand": "generic"}
|
material_1 = {"id": "test", "GUID":"TEST!", "base_file": "base_material", "definition": "fdmmachine", "approximate_diameter": "3", "brand": "generic", "material": "pla"}
|
||||||
material_2 = {"id": "base_material", "GUID": "TEST2!", "base_file": "test", "definition": "fdmmachine", "approximate_diameter": 3}
|
material_2 = {"id": "base_material", "GUID": "TEST2!", "base_file": "test", "definition": "fdmmachine", "approximate_diameter": "3", "material": "pla"}
|
||||||
mocked_registry.findContainersMetadata = MagicMock(return_value = [material_1, material_2])
|
mocked_registry.findContainersMetadata = MagicMock(return_value = [material_1, material_2])
|
||||||
|
|
||||||
|
|
||||||
@ -24,21 +24,116 @@ def test_initialize(application):
|
|||||||
assert manager.getMaterialGroup("test").name == "test"
|
assert manager.getMaterialGroup("test").name == "test"
|
||||||
|
|
||||||
|
|
||||||
def test_getAvailableMaterials(application):
|
def test_getMaterialGroupListByGUID(application):
|
||||||
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
manager = MaterialManager(mocked_registry)
|
manager = MaterialManager(mocked_registry)
|
||||||
manager.initialize()
|
manager.initialize()
|
||||||
|
|
||||||
available_materials = manager.getAvailableMaterials(mocked_definition, None, None, 3)
|
assert manager.getMaterialGroupListByGUID("TEST!")[0].name == "test"
|
||||||
|
assert manager.getMaterialGroupListByGUID("TEST2!")[0].name == "base_material"
|
||||||
|
|
||||||
assert "base_material" in available_materials
|
|
||||||
assert "test" in available_materials
|
def test_getRootMaterialIDforDiameter(application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
assert manager.getRootMaterialIDForDiameter("base_material", "3") == "base_material"
|
||||||
|
|
||||||
|
|
||||||
|
def test_getMaterialNodeByTypeMachineHasNoMaterials(application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
mocked_stack = MagicMock()
|
||||||
|
assert manager.getMaterialNodeByType(mocked_stack, "0", "nozzle", "", "") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_getMaterialNodeByTypeMachineHasMaterialsButNoMaterialFound(application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
mocked_stack = MagicMock()
|
||||||
|
mocked_stack.definition.getMetaDataEntry = MagicMock(return_value = True) # For the "has_materials" metadata
|
||||||
|
|
||||||
|
assert manager.getMaterialNodeByType(mocked_stack, "0", "nozzle", "", "") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_getMaterialNodeByTypeMachineHasMaterialsAndMaterialExists(application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
mocked_result = MagicMock()
|
||||||
|
manager.getMaterialNode = MagicMock(return_value = mocked_result)
|
||||||
|
mocked_stack = MagicMock()
|
||||||
|
mocked_stack.definition.getMetaDataEntry = MagicMock(return_value = True) # For the "has_materials" metadata
|
||||||
|
|
||||||
|
assert manager.getMaterialNodeByType(mocked_stack, "0", "nozzle", "", "TEST!") is mocked_result
|
||||||
|
|
||||||
|
|
||||||
|
def test_getAllMaterialGroups(application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
material_groups = manager.getAllMaterialGroups()
|
||||||
|
|
||||||
|
assert "base_material" in material_groups
|
||||||
|
assert "test" in material_groups
|
||||||
|
assert material_groups["base_material"].name == "base_material"
|
||||||
|
assert material_groups["test"].name == "test"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAvailableMaterials:
|
||||||
|
def test_happy(self, application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
available_materials = manager.getAvailableMaterials(mocked_definition, None, None, 3)
|
||||||
|
|
||||||
|
assert "base_material" in available_materials
|
||||||
|
assert "test" in available_materials
|
||||||
|
|
||||||
|
def test_wrongDiameter(self, application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
available_materials = manager.getAvailableMaterials(mocked_definition, None, None, 200)
|
||||||
|
assert available_materials == {} # Nothing found.
|
||||||
|
|
||||||
|
def test_excludedMaterials(self, application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
with patch.object(mocked_definition, "getMetaDataEntry", MagicMock(return_value = ["test"])):
|
||||||
|
available_materials = manager.getAvailableMaterials(mocked_definition, None, None, 3)
|
||||||
|
assert "base_material" in available_materials
|
||||||
|
assert "test" not in available_materials
|
||||||
|
|
||||||
|
|
||||||
|
class Test_getFallbackMaterialIdByMaterialType:
|
||||||
|
def test_happyFlow(self, application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
assert manager.getFallbackMaterialIdByMaterialType("pla") == "test"
|
||||||
|
|
||||||
|
def test_unknownMaterial(self, application):
|
||||||
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
|
manager = MaterialManager(mocked_registry)
|
||||||
|
manager.initialize()
|
||||||
|
assert manager.getFallbackMaterialIdByMaterialType("OMGZOMG") is None
|
||||||
|
|
||||||
|
|
||||||
def test_getMaterialNode(application):
|
def test_getMaterialNode(application):
|
||||||
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
with patch("UM.Application.Application.getInstance", MagicMock(return_value=application)):
|
||||||
manager = MaterialManager(mocked_registry)
|
manager = MaterialManager(mocked_registry)
|
||||||
manager.initialize()
|
manager._updateMaps()
|
||||||
|
|
||||||
assert manager.getMaterialNode("fdmmachine", None, None, 3, "base_material").getMetaDataEntry("id") == "test"
|
assert manager.getMaterialNode("fdmmachine", None, None, 3, "base_material").getMetaDataEntry("id") == "test"
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||||
from cura.Machines.QualityManager import QualityManager
|
from cura.Machines.QualityManager import QualityManager
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mocked_stack = MagicMock()
|
mocked_stack = MagicMock()
|
||||||
mocked_extruder = MagicMock()
|
mocked_extruder = MagicMock()
|
||||||
|
|
||||||
@ -28,6 +27,8 @@ def container_registry():
|
|||||||
{"id": "test_material", "definition": "fdmprinter", "quality_type": "normal", "name": "test_name_material", "material": "base_material", "type": "quality"},
|
{"id": "test_material", "definition": "fdmprinter", "quality_type": "normal", "name": "test_name_material", "material": "base_material", "type": "quality"},
|
||||||
{"id": "quality_changes_id", "definition": "fdmprinter", "type": "quality_changes", "quality_type": "amazing!", "name": "herp"}]
|
{"id": "quality_changes_id", "definition": "fdmprinter", "type": "quality_changes", "quality_type": "amazing!", "name": "herp"}]
|
||||||
result.findContainersMetadata = MagicMock(return_value = mocked_metadata)
|
result.findContainersMetadata = MagicMock(return_value = mocked_metadata)
|
||||||
|
|
||||||
|
result.uniqueName = MagicMock(return_value = "uniqueName")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -58,3 +59,72 @@ def test_getQualityChangesGroup(quality_mocked_application):
|
|||||||
manager.initialize()
|
manager.initialize()
|
||||||
|
|
||||||
assert "herp" in manager.getQualityChangesGroups(mocked_stack)
|
assert "herp" in manager.getQualityChangesGroups(mocked_stack)
|
||||||
|
|
||||||
|
|
||||||
|
def test_getDefaultQualityType(quality_mocked_application):
|
||||||
|
manager = QualityManager(quality_mocked_application)
|
||||||
|
manager.initialize()
|
||||||
|
mocked_stack = MagicMock()
|
||||||
|
mocked_stack.definition.getMetaDataEntry = MagicMock(return_value = "normal")
|
||||||
|
assert manager.getDefaultQualityType(mocked_stack).quality_type == "normal"
|
||||||
|
|
||||||
|
|
||||||
|
def test_createQualityChanges(quality_mocked_application):
|
||||||
|
manager = QualityManager(quality_mocked_application)
|
||||||
|
mocked_quality_changes = MagicMock()
|
||||||
|
manager._createQualityChanges = MagicMock(return_value = mocked_quality_changes)
|
||||||
|
manager.initialize()
|
||||||
|
container_manager = MagicMock()
|
||||||
|
|
||||||
|
manager._container_registry.addContainer = MagicMock() # Side effect we want to check.
|
||||||
|
with patch("cura.Settings.ContainerManager.ContainerManager.getInstance", container_manager):
|
||||||
|
manager.createQualityChanges("derp")
|
||||||
|
assert manager._container_registry.addContainer.called_once_with(mocked_quality_changes)
|
||||||
|
|
||||||
|
|
||||||
|
def test_renameQualityChangesGroup(quality_mocked_application):
|
||||||
|
manager = QualityManager(quality_mocked_application)
|
||||||
|
manager.initialize()
|
||||||
|
|
||||||
|
mocked_container = MagicMock()
|
||||||
|
|
||||||
|
quality_changes_group = QualityChangesGroup("zomg", "beep")
|
||||||
|
quality_changes_group.getAllNodes = MagicMock(return_value = [mocked_container])
|
||||||
|
|
||||||
|
# We need to check for "uniqueName" instead of "NEWRANDOMNAMEYAY" because the mocked registry always returns
|
||||||
|
# "uniqueName" when attempting to generate an unique name.
|
||||||
|
assert manager.renameQualityChangesGroup(quality_changes_group, "NEWRANDOMNAMEYAY") == "uniqueName"
|
||||||
|
assert mocked_container.setName.called_once_with("uniqueName")
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicateQualityChangesQualityOnly(quality_mocked_application):
|
||||||
|
manager = QualityManager(quality_mocked_application)
|
||||||
|
manager.initialize()
|
||||||
|
mocked_quality = MagicMock()
|
||||||
|
quality_group = MagicMock()
|
||||||
|
quality_group.getAllNodes = MagicMock(return_value = mocked_quality)
|
||||||
|
mocked_quality_changes = MagicMock()
|
||||||
|
|
||||||
|
# Isolate what we want to test (in this case, we're not interested if createQualityChanges does it's job!)
|
||||||
|
manager._createQualityChanges = MagicMock(return_value = mocked_quality_changes)
|
||||||
|
manager._container_registry.addContainer = MagicMock() # Side effect we want to check.
|
||||||
|
|
||||||
|
manager.duplicateQualityChanges("zomg", {"quality_group": quality_group, "quality_changes_group": None})
|
||||||
|
assert manager._container_registry.addContainer.called_once_with(mocked_quality_changes)
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicateQualityChanges(quality_mocked_application):
|
||||||
|
manager = QualityManager(quality_mocked_application)
|
||||||
|
manager.initialize()
|
||||||
|
mocked_quality = MagicMock()
|
||||||
|
quality_group = MagicMock()
|
||||||
|
quality_group.getAllNodes = MagicMock(return_value = mocked_quality)
|
||||||
|
quality_changes_group = MagicMock()
|
||||||
|
mocked_quality_changes = MagicMock()
|
||||||
|
quality_changes_group.getAllNodes = MagicMock(return_value=[mocked_quality_changes])
|
||||||
|
mocked_quality_changes.duplicate = MagicMock(return_value = mocked_quality_changes)
|
||||||
|
|
||||||
|
manager._container_registry.addContainer = MagicMock() # Side effect we want to check.
|
||||||
|
|
||||||
|
manager.duplicateQualityChanges("zomg", {"quality_group": quality_group, "quality_changes_group": quality_changes_group})
|
||||||
|
assert manager._container_registry.addContainer.called_once_with(mocked_quality_changes)
|
@ -11,9 +11,12 @@ import Savitar # Dont remove this line
|
|||||||
import Arcus # No really. Don't. It needs to be there!
|
import Arcus # No really. Don't. It needs to be there!
|
||||||
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
from UM.Qt.QtApplication import QtApplication # QtApplication import is required, even though it isn't used.
|
||||||
# Even though your IDE says these files are not used, don't believe it. It's lying. They need to be there.
|
# Even though your IDE says these files are not used, don't believe it. It's lying. They need to be there.
|
||||||
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.UI.MachineActionManager import MachineActionManager
|
from cura.UI.MachineActionManager import MachineActionManager
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
# Create a CuraApplication object that will be shared among all tests. It needs to be initialized.
|
# Create a CuraApplication object that will be shared among all tests. It needs to be initialized.
|
||||||
# Since we need to use it more that once, we create the application the first time and use its instance afterwards.
|
# Since we need to use it more that once, we create the application the first time and use its instance afterwards.
|
||||||
@ -22,7 +25,24 @@ def application() -> CuraApplication:
|
|||||||
app = unittest.mock.MagicMock()
|
app = unittest.mock.MagicMock()
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def container_registry() -> ContainerRegistry:
|
||||||
|
return MagicMock(name = "ContainerRegistry")
|
||||||
|
|
||||||
|
|
||||||
# Returns a MachineActionManager instance.
|
# Returns a MachineActionManager instance.
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def machine_action_manager(application) -> MachineActionManager:
|
def machine_action_manager(application) -> MachineActionManager:
|
||||||
return MachineActionManager(application)
|
return MachineActionManager(application)
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def extruder_manager(application, container_registry) -> ExtruderManager:
|
||||||
|
if ExtruderManager.getInstance() is not None:
|
||||||
|
# Reset the data
|
||||||
|
ExtruderManager._ExtruderManager__instance = None
|
||||||
|
|
||||||
|
with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)):
|
||||||
|
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
||||||
|
manager = ExtruderManager()
|
||||||
|
return manager
|
Loading…
x
Reference in New Issue
Block a user