From a1f02154f1579dbde6b1a65997e0f2cd67af6274 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 27 Jul 2023 09:02:28 +0200 Subject: [PATCH 1/9] Add used backend plugins to 3mf metadata These are appended to project metadata. A warning can now be shown to the user when trying to load a project file while the correct plugins are not installed. These missing plugins can concequently be downloaded from the marketplace. To show the warning and install missing package dialog the same system we use to install missing materials is used. CURA-10719 --- plugins/3MFWriter/ThreeMFWriter.py | 88 ++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 57c667145e..cbbb635d21 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -1,8 +1,9 @@ # Copyright (c) 2015-2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json +import re -from typing import Optional, cast, List, Dict +from typing import Optional, cast, List, Dict, Pattern, Set from UM.Mesh.MeshWriter import MeshWriter from UM.Math.Vector import Vector @@ -17,6 +18,7 @@ from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer from cura.CuraApplication import CuraApplication from cura.CuraPackageManager import CuraPackageManager +from cura.Settings import CuraContainerStack from cura.Utils.Threading import call_on_qt_thread from cura.Snapshot import Snapshot @@ -175,13 +177,15 @@ class ThreeMFWriter(MeshWriter): archive.writestr(thumbnail_file, thumbnail_buffer.data()) # Add PNG to content types file - thumbnail_type = ET.SubElement(content_types, "Default", Extension = "png", ContentType = "image/png") + thumbnail_type = ET.SubElement(content_types, "Default", Extension="png", ContentType="image/png") # Add thumbnail relation to _rels/.rels file - thumbnail_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/" + THUMBNAIL_PATH, Id = "rel1", Type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail") + thumbnail_relation_element = ET.SubElement(relations_element, "Relationship", + Target="/" + THUMBNAIL_PATH, Id="rel1", + Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail") # Write material metadata - material_metadata = self._getMaterialPackageMetadata() - self._storeMetadataJson({"packages": material_metadata}, archive, PACKAGE_METADATA_PATH) + packages_metadata = self._getMaterialPackageMetadata() + self._getPluginPackageMetadata() + self._storeMetadataJson({"packages": packages_metadata}, archive, PACKAGE_METADATA_PATH) savitar_scene = Savitar.Scene() @@ -253,7 +257,64 @@ class ThreeMFWriter(MeshWriter): metadata_file = zipfile.ZipInfo(path) # We have to set the compress type of each file as well (it doesn't keep the type of the entire archive) metadata_file.compress_type = zipfile.ZIP_DEFLATED - archive.writestr(metadata_file, json.dumps(metadata, separators=(", ", ": "), indent=4, skipkeys=True, ensure_ascii=False)) + archive.writestr(metadata_file, + json.dumps(metadata, separators=(", ", ": "), indent=4, skipkeys=True, ensure_ascii=False)) + + @staticmethod + def _getPluginPackageMetadata() -> List[Dict[str, str]]: + """Get metadata for all backend plugins that are used in the project. + + :return: List of material metadata dictionaries. + """ + + backend_plugin_enum_value_regex = re.compile( + r"PLUGIN::(?P\w+)@(?P\d+.\d+.\d+)::(?P\w+)") + # This regex parses enum values to find if they contain custom + # backend engine values. These custom enum values are in the format + # PLUGIN::@:: + # where + # - plugin_id is the id of the plugin + # - version is in the semver format + # - value is the value of the enum + + plugin_ids = set() + + def add_plugin_ids_in_stack(stack: CuraContainerStack): + for key in stack.getAllKeys(): + value = str(stack.getProperty(key, "value")) + for plugin_id, _version, _value in backend_plugin_enum_value_regex.findall(value): + plugin_ids.add(plugin_id) + + # go through all stacks and find all the plugin id contained in the project + global_stack = Application.getInstance().getMachineManager().activeMachine + add_plugin_ids_in_stack(global_stack) + + for container in global_stack.getContainers(): + add_plugin_ids_in_stack(container) + + for extruder_stack in global_stack.extruderList: + add_plugin_ids_in_stack(extruder_stack) + + for container in extruder_stack.getContainers(): + add_plugin_ids_in_stack(container) + + metadata = {} + + package_manager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager()) + for plugin_id in plugin_ids: + package_data = package_manager.getInstalledPackageInfo(plugin_id) + + metadata[plugin_id] = { + "id": plugin_id, + "display_name": package_data.get("display_name") if package_data.get("display_name") else "", + "package_version": package_data.get("package_version") if package_data.get("package_version") else "", + "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get( + "sdk_version_semver") else "", + "type": "backend_plugin", + } + + # Storing in a dict and fetching values to avoid duplicates + return list(metadata.values()) @staticmethod def _getMaterialPackageMetadata() -> List[Dict[str, str]]: @@ -278,7 +339,8 @@ class ThreeMFWriter(MeshWriter): # Don't export bundled materials continue - package_id = package_manager.getMaterialFilePackageId(extruder.material.getFileName(), extruder.material.getMetaDataEntry("GUID")) + package_id = package_manager.getMaterialFilePackageId(extruder.material.getFileName(), + extruder.material.getMetaDataEntry("GUID")) package_data = package_manager.getInstalledPackageInfo(package_id) # We failed to find the package for this material @@ -286,10 +348,14 @@ class ThreeMFWriter(MeshWriter): Logger.info(f"Could not find package for material in extruder {extruder.id}, skipping.") continue - material_metadata = {"id": package_id, - "display_name": package_data.get("display_name") if package_data.get("display_name") else "", - "package_version": package_data.get("package_version") if package_data.get("package_version") else "", - "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get("sdk_version_semver") else ""} + material_metadata = { + "id": package_id, + "display_name": package_data.get("display_name") if package_data.get("display_name") else "", + "package_version": package_data.get("package_version") if package_data.get("package_version") else "", + "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get( + "sdk_version_semver") else "", + "type": "material", + } metadata[package_id] = material_metadata From 42002dac3690e3d0f9a72e5a5a33ff05ad2f09da Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 27 Jul 2023 09:03:35 +0200 Subject: [PATCH 2/9] Update copy Now more then just materials can be installed. Update copy to reflect this. CURA-10719 --- plugins/3MFReader/WorkspaceDialog.qml | 2 +- .../Marketplace/resources/qml/InstallMissingPackagesDialog.qml | 2 +- plugins/Marketplace/resources/qml/MissingPackages.qml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index d1a000bae4..3def26277d 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -404,7 +404,7 @@ UM.Dialog Cura.PrimaryButton { visible: warning - text: catalog.i18nc("@action:button", "Install missing material") + text: catalog.i18nc("@action:button", "Install missing packages") onClicked: manager.installMissingPackages() } ] diff --git a/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml b/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml index edad18f1a8..7ac0a241e6 100644 --- a/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml +++ b/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml @@ -12,7 +12,7 @@ import Cura 1.6 as Cura Marketplace { modality: Qt.ApplicationModal - title: catalog.i18nc("@title", "Install missing Materials") + title: catalog.i18nc("@title", "Install missing packages") pageContentsSource: "MissingPackages.qml" showSearchHeader: false showOnboadBanner: false diff --git a/plugins/Marketplace/resources/qml/MissingPackages.qml b/plugins/Marketplace/resources/qml/MissingPackages.qml index 316d048317..a1d29addf9 100644 --- a/plugins/Marketplace/resources/qml/MissingPackages.qml +++ b/plugins/Marketplace/resources/qml/MissingPackages.qml @@ -5,7 +5,7 @@ import UM 1.4 as UM Packages { - pageTitle: catalog.i18nc("@header", "Install Materials") + pageTitle: catalog.i18nc("@header", "Install Packages") bannerVisible: false showUpdateButton: false From 0e77a05c742460339547b29a047dea762e18ac98 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 27 Jul 2023 10:15:27 +0200 Subject: [PATCH 3/9] Correctly differentiate between plugins and materials in missing packages dialog CURA-10719 --- plugins/3MFWriter/ThreeMFWriter.py | 2 +- plugins/Marketplace/MissingPackageList.py | 10 ++++++++-- plugins/Marketplace/PackageModel.py | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index cbbb635d21..2bfb152217 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -310,7 +310,7 @@ class ThreeMFWriter(MeshWriter): "package_version": package_data.get("package_version") if package_data.get("package_version") else "", "sdk_version_semver": package_data.get("sdk_version_semver") if package_data.get( "sdk_version_semver") else "", - "type": "backend_plugin", + "type": "plugin", } # Storing in a dict and fetching values to avoid duplicates diff --git a/plugins/Marketplace/MissingPackageList.py b/plugins/Marketplace/MissingPackageList.py index 385e78b95f..018e977823 100644 --- a/plugins/Marketplace/MissingPackageList.py +++ b/plugins/Marketplace/MissingPackageList.py @@ -20,7 +20,6 @@ class MissingPackageList(RemotePackageList): def __init__(self, packages_metadata: List[Dict[str, str]], parent: Optional["QObject"] = None) -> None: super().__init__(parent) self._packages_metadata: List[Dict[str, str]] = packages_metadata - self._package_type_filter = "material" self._search_type = "package_ids" self._requested_search_string = ",".join(map(lambda package: package["id"], packages_metadata)) @@ -38,7 +37,14 @@ class MissingPackageList(RemotePackageList): for package_metadata in self._packages_metadata: if package_metadata["id"] not in returned_packages_ids: - package = PackageModel.fromIncompletePackageInformation(package_metadata["display_name"], package_metadata["package_version"], self._package_type_filter) + package_type = package_metadata["type"] if "type" in package_metadata else "material" + # When this feature was originally introduced only missing materials were detected. With the inclusion + # of backend plugins this system was extended to also detect missing plugins. With that change the type + # of the package was added to the metadata. Project files before this change do not have this type. So + # if the type is not present we assume it is a material. + package = PackageModel.fromIncompletePackageInformation(package_metadata["display_name"], + package_metadata["package_version"], + package_type) self.appendItem({"package": package}) self.itemsChanged.emit() diff --git a/plugins/Marketplace/PackageModel.py b/plugins/Marketplace/PackageModel.py index fa909b4120..afc6e0ce73 100644 --- a/plugins/Marketplace/PackageModel.py +++ b/plugins/Marketplace/PackageModel.py @@ -87,12 +87,22 @@ class PackageModel(QObject): self._is_missing_package_information = False @classmethod - def fromIncompletePackageInformation(cls, display_name: str, package_version: str, package_type: str) -> "PackageModel": + def fromIncompletePackageInformation(cls, display_name: str, package_version: str, + package_type: str) -> "PackageModel": + description = "" + match package_type: + case "material": + description = catalog.i18nc("@label:label Ultimaker Marketplace is a brand name, don't translate", + "The material package associated with the Cura project could not be found on the Ultimaker Marketplace. Use the partial material profile definition stored in the Cura project file at your own risk.") + case "plugin": + description = catalog.i18nc("@label:label Ultimaker Marketplace is a brand name, don't translate", + "The plugin associated with the Cura project could not be found on the Ultimaker Marketplace. As the plugin may be required to slice the project it might not be possible to correctly slice the file.") + package_data = { "display_name": display_name, "package_version": package_version, "package_type": package_type, - "description": catalog.i18nc("@label:label Ultimaker Marketplace is a brand name, don't translate", "The material package associated with the Cura project could not be found on the Ultimaker Marketplace. Use the partial material profile definition stored in the Cura project file at your own risk.") + "description": description, } package_model = cls(package_data) package_model.setIsMissingPackageInformation(True) From 31cb11ce63b2c6f529f10086b53d757c9f2b9479 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 27 Jul 2023 10:31:38 +0200 Subject: [PATCH 4/9] Update more copies CURA-10719 --- plugins/3MFReader/WorkspaceDialog.py | 29 ++++++++++++++------------- plugins/3MFReader/WorkspaceDialog.qml | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index afa1deecfd..cce195e2fa 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -408,26 +408,27 @@ class WorkspaceDialog(QObject): @pyqtSlot() def showMissingMaterialsWarning(self) -> None: result_message = Message( - i18n_catalog.i18nc("@info:status", "The material used in this project relies on some material definitions not available in Cura, this might produce undesirable print results. We highly recommend installing the full material package from the Marketplace."), + i18n_catalog.i18nc("@info:status", + "Some of the packages used in the project file are currently not installed in Cura, this might produce undesirable print results. We highly recommend installing the all required packages from the Marketplace."), lifetime=0, - title=i18n_catalog.i18nc("@info:title", "Material profiles not installed"), + title=i18n_catalog.i18nc("@info:title", "Some required packages are not installed"), message_type=Message.MessageType.WARNING ) result_message.addAction( - "learn_more", - name=i18n_catalog.i18nc("@action:button", "Learn more"), - icon="", - description="Learn more about project materials.", - button_align=Message.ActionButtonAlignment.ALIGN_LEFT, - button_style=Message.ActionButtonStyle.LINK + "learn_more", + name=i18n_catalog.i18nc("@action:button", "Learn more"), + icon="", + description=i18n_catalog.i18nc("@label", "Learn more about project packages."), + button_align=Message.ActionButtonAlignment.ALIGN_LEFT, + button_style=Message.ActionButtonStyle.LINK ) result_message.addAction( - "install_materials", - name=i18n_catalog.i18nc("@action:button", "Install Materials"), - icon="", - description="Install missing materials from project file.", - button_align=Message.ActionButtonAlignment.ALIGN_RIGHT, - button_style=Message.ActionButtonStyle.DEFAULT + "install_materials", + name=i18n_catalog.i18nc("@action:button", "Install Packages"), + icon="", + description=i18n_catalog.i18nc("@label", "Install missing packages from project file."), + button_align=Message.ActionButtonAlignment.ALIGN_RIGHT, + button_style=Message.ActionButtonStyle.DEFAULT ) result_message.actionTriggered.connect(self._onMessageActionTriggered) result_message.show() diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 3def26277d..d5f9b1817d 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -364,7 +364,7 @@ UM.Dialog UM.Label { id: warningText - text: catalog.i18nc("@label", "The material used in this project is currently not installed in Cura.
Install the material profile and reopen the project.") + text: catalog.i18nc("@label", "This project contains materials or plugins that are currently not installed in Cura.
Install the missing packages and reopen the project.") } } From b1797111d58d3894501a56e3b7e7be56d444b426 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 31 Jul 2023 17:06:58 +0200 Subject: [PATCH 5/9] Update message id to properly reflect action CURA-10719 --- plugins/3MFReader/WorkspaceDialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index cce195e2fa..ed42485691 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -423,7 +423,7 @@ class WorkspaceDialog(QObject): button_style=Message.ActionButtonStyle.LINK ) result_message.addAction( - "install_materials", + "install_packages", name=i18n_catalog.i18nc("@action:button", "Install Packages"), icon="", description=i18n_catalog.i18nc("@label", "Install missing packages from project file."), From e3d482514ba1041b4573a7180f72428dfba8bad7 Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 31 Jul 2023 17:09:27 +0200 Subject: [PATCH 6/9] Use camelCase for python function definition CURA-10719 Co-authored-by: Jaime van Kessel --- plugins/3MFWriter/ThreeMFWriter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 2bfb152217..a3636a98b9 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -279,7 +279,7 @@ class ThreeMFWriter(MeshWriter): plugin_ids = set() - def add_plugin_ids_in_stack(stack: CuraContainerStack): + def addPluginIdsInStack(stack: CuraContainerStack) -> None: for key in stack.getAllKeys(): value = str(stack.getProperty(key, "value")) for plugin_id, _version, _value in backend_plugin_enum_value_regex.findall(value): @@ -287,16 +287,16 @@ class ThreeMFWriter(MeshWriter): # go through all stacks and find all the plugin id contained in the project global_stack = Application.getInstance().getMachineManager().activeMachine - add_plugin_ids_in_stack(global_stack) + addPluginIdsInStack(global_stack) for container in global_stack.getContainers(): - add_plugin_ids_in_stack(container) + addPluginIdsInStack(container) for extruder_stack in global_stack.extruderList: - add_plugin_ids_in_stack(extruder_stack) + addPluginIdsInStack(extruder_stack) for container in extruder_stack.getContainers(): - add_plugin_ids_in_stack(container) + addPluginIdsInStack(container) metadata = {} From 9b6abf80d74528e662fa9d53f72e647aee4d7d9b Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Mon, 31 Jul 2023 17:10:06 +0200 Subject: [PATCH 7/9] Capitalize first word of comment comment CURA-10719 Co-authored-by: Jaime van Kessel --- plugins/3MFWriter/ThreeMFWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index a3636a98b9..3be9a7478d 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -285,7 +285,7 @@ class ThreeMFWriter(MeshWriter): for plugin_id, _version, _value in backend_plugin_enum_value_regex.findall(value): plugin_ids.add(plugin_id) - # go through all stacks and find all the plugin id contained in the project + # Go through all stacks and find all the plugin id contained in the project global_stack = Application.getInstance().getMachineManager().activeMachine addPluginIdsInStack(global_stack) From c436dd36cf77f8a3032ce6fb34e8963f3149969e Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 2 Aug 2023 14:03:55 +0200 Subject: [PATCH 8/9] Add unit test CURA-10719 --- plugins/3MFWriter/ThreeMFWriter.py | 2 +- plugins/3MFWriter/tests/TestMFWriter.py | 56 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 plugins/3MFWriter/tests/TestMFWriter.py diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 3be9a7478d..10b040e01f 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -286,7 +286,7 @@ class ThreeMFWriter(MeshWriter): plugin_ids.add(plugin_id) # Go through all stacks and find all the plugin id contained in the project - global_stack = Application.getInstance().getMachineManager().activeMachine + global_stack = CuraApplication.getInstance().getMachineManager().activeMachine addPluginIdsInStack(global_stack) for container in global_stack.getContainers(): diff --git a/plugins/3MFWriter/tests/TestMFWriter.py b/plugins/3MFWriter/tests/TestMFWriter.py new file mode 100644 index 0000000000..addef2788e --- /dev/null +++ b/plugins/3MFWriter/tests/TestMFWriter.py @@ -0,0 +1,56 @@ +import sys +import os.path +from typing import Dict, Optional + +from unittest.mock import patch, MagicMock, PropertyMock + +from UM.PackageManager import PackageManager +from cura.CuraApplication import CuraApplication + +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) + +import ThreeMFWriter + +PLUGIN_ID = "my_plugin" +DISPLAY_NAME = "MyPlugin" +PACKAGE_VERSION = "0.0.1" +SDK_VERSION = "8.0.0" + + +def package_manager() -> MagicMock: + pm = MagicMock(spec=PackageManager) + pm.getInstalledPackageInfo.return_value = { + "display_name": DISPLAY_NAME, + "package_version": PACKAGE_VERSION, + "sdk_version_semver": SDK_VERSION + } + return pm + + +def machine_manager() -> MagicMock: + mm = MagicMock(spec=PackageManager) + active_machine = MagicMock() + active_machine.getAllKeys.return_value = ["infill_pattern", "layer_height", "material_bed_temperature"] + active_machine.getProperty.return_value = f"PLUGIN::{PLUGIN_ID}@{PACKAGE_VERSION}::custom_value" + active_machine.getContainers.return_value = [] + active_machine.extruderList = [] + mm.activeMachine = active_machine + return mm + + +def application(): + app = MagicMock() + app.getPackageManager.return_value = package_manager() + app.getMachineManager.return_value = machine_manager() + return app + + +def test_enumParsing(): + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application())): + packages_metadata = ThreeMFWriter.ThreeMFWriter._getPluginPackageMetadata()[0] + + assert packages_metadata.get("id") == PLUGIN_ID + assert packages_metadata.get("display_name") == DISPLAY_NAME + assert packages_metadata.get("package_version") == PACKAGE_VERSION + assert packages_metadata.get("sdk_version_semver") == SDK_VERSION + assert packages_metadata.get("type") == "plugin" From 83d35ccd8b3100df9dc4cbfe9b3426bd7bd1f47c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 2 Aug 2023 14:46:37 +0200 Subject: [PATCH 9/9] Use pytest fixtures CURA-10719 --- plugins/3MFWriter/tests/TestMFWriter.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/3MFWriter/tests/TestMFWriter.py b/plugins/3MFWriter/tests/TestMFWriter.py index addef2788e..7bee581105 100644 --- a/plugins/3MFWriter/tests/TestMFWriter.py +++ b/plugins/3MFWriter/tests/TestMFWriter.py @@ -1,6 +1,7 @@ import sys import os.path from typing import Dict, Optional +import pytest from unittest.mock import patch, MagicMock, PropertyMock @@ -17,6 +18,7 @@ PACKAGE_VERSION = "0.0.1" SDK_VERSION = "8.0.0" +@pytest.fixture def package_manager() -> MagicMock: pm = MagicMock(spec=PackageManager) pm.getInstalledPackageInfo.return_value = { @@ -27,6 +29,7 @@ def package_manager() -> MagicMock: return pm +@pytest.fixture def machine_manager() -> MagicMock: mm = MagicMock(spec=PackageManager) active_machine = MagicMock() @@ -38,15 +41,16 @@ def machine_manager() -> MagicMock: return mm -def application(): +@pytest.fixture +def application(package_manager, machine_manager): app = MagicMock() - app.getPackageManager.return_value = package_manager() - app.getMachineManager.return_value = machine_manager() + app.getPackageManager.return_value = package_manager + app.getMachineManager.return_value = machine_manager return app -def test_enumParsing(): - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application())): +def test_enumParsing(application): + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): packages_metadata = ThreeMFWriter.ThreeMFWriter._getPluginPackageMetadata()[0] assert packages_metadata.get("id") == PLUGIN_ID