Fix unit tests for SendMaterialJob

CURA-6035
This commit is contained in:
Lipu Fei 2018-12-20 14:20:31 +01:00
parent 68372aff60
commit e79f312fa0
2 changed files with 102 additions and 43 deletions

View file

@ -6,9 +6,9 @@ from typing import Dict, TYPE_CHECKING, Set
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from UM.Application import Application
from UM.Job import Job from UM.Job import Job
from UM.Logger import Logger from UM.Logger import Logger
from cura.CuraApplication import CuraApplication
# Absolute imports don't work in plugins # Absolute imports don't work in plugins
from .Models import ClusterMaterial, LocalMaterial from .Models import ClusterMaterial, LocalMaterial
@ -34,7 +34,6 @@ class SendMaterialJob(Job):
# #
# \param reply The reply from the printer, a json file. # \param reply The reply from the printer, a json file.
def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None: def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None:
# Got an error from the HTTP request. If we did not receive a 200 something happened. # Got an error from the HTTP request. If we did not receive a 200 something happened.
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
Logger.log("e", "Error fetching materials from printer: %s", reply.errorString()) Logger.log("e", "Error fetching materials from printer: %s", reply.errorString())
@ -49,7 +48,6 @@ class SendMaterialJob(Job):
# #
# \param remote_materials_by_guid The remote materials by GUID. # \param remote_materials_by_guid The remote materials by GUID.
def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None: def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None:
# Collect local materials # Collect local materials
local_materials_by_guid = self._getLocalMaterials() local_materials_by_guid = self._getLocalMaterials()
if len(local_materials_by_guid) == 0: if len(local_materials_by_guid) == 0:
@ -88,8 +86,8 @@ class SendMaterialJob(Job):
# #
# \param materials_to_send A set with id's of materials that must be sent. # \param materials_to_send A set with id's of materials that must be sent.
def _sendMaterials(self, materials_to_send: Set[str]) -> None: def _sendMaterials(self, materials_to_send: Set[str]) -> None:
container_registry = CuraApplication.getInstance().getContainerRegistry() container_registry = Application.getInstance().getContainerRegistry()
material_manager = CuraApplication.getInstance().getMaterialManager() material_manager = Application.getInstance().getMaterialManager()
material_group_dict = material_manager.getAllMaterialGroups() material_group_dict = material_manager.getAllMaterialGroups()
for root_material_id in material_group_dict: for root_material_id in material_group_dict:
@ -167,7 +165,7 @@ class SendMaterialJob(Job):
# \return a dictionary of LocalMaterial objects by GUID # \return a dictionary of LocalMaterial objects by GUID
def _getLocalMaterials(self) -> Dict[str, LocalMaterial]: def _getLocalMaterials(self) -> Dict[str, LocalMaterial]:
result = {} # type: Dict[str, LocalMaterial] result = {} # type: Dict[str, LocalMaterial]
material_manager = CuraApplication.getInstance().getMaterialManager() material_manager = Application.getInstance().getMaterialManager()
material_group_dict = material_manager.getAllMaterialGroups() material_group_dict = material_manager.getAllMaterialGroups()

View file

@ -1,26 +1,29 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import copy
import io import io
import json import json
from unittest import TestCase, mock from unittest import TestCase, mock
from unittest.mock import patch, call from unittest.mock import patch, call, MagicMock
from PyQt5.QtCore import QByteArray from PyQt5.QtCore import QByteArray
from UM.MimeTypeDatabase import MimeType
from UM.Application import Application from UM.Application import Application
from cura.Machines.MaterialGroup import MaterialGroup
from cura.Machines.MaterialNode import MaterialNode
from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob
_FILES_MAP = {"generic_pla_white": "/materials/generic_pla_white.xml.fdm_material",
"generic_pla_black": "/materials/generic_pla_black.xml.fdm_material",
}
@patch("builtins.open", lambda _, __: io.StringIO("<xml></xml>")) @patch("builtins.open", lambda _, __: io.StringIO("<xml></xml>"))
@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile",
lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile",
suffixes = ["xml.fdm_material"]))
@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"])
@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice")
@patch("PyQt5.QtNetwork.QNetworkReply")
class TestSendMaterialJob(TestCase): class TestSendMaterialJob(TestCase):
# version 1
_LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White", "brand": "Generic", "material": "PLA", "color_name": "White",
@ -29,6 +32,37 @@ class TestSendMaterialJob(TestCase):
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True} "definition": "fdmprinter", "compatible": True}
# version 2
_LOCAL_MATERIAL_WHITE_NEWER = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White",
"GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "2",
"color_code": "#ffffff",
"description": "Test PLA White", "adhesion_info": "Use glue.",
"approximate_diameter": "3",
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
# invalid version: "one"
_LOCAL_MATERIAL_WHITE_INVALID_VERSION = {"type": "material", "status": "unknown", "id": "generic_pla_white",
"base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
"brand": "Generic", "material": "PLA", "color_name": "White",
"GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "one",
"color_code": "#ffffff",
"description": "Test PLA White", "adhesion_info": "Use glue.",
"approximate_diameter": "3",
"properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True}
_LOCAL_MATERIAL_WHITE_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE))}
_LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE_NEWER))}
_LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white",
MaterialNode(_LOCAL_MATERIAL_WHITE_INVALID_VERSION))}
_LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black",
"base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE", "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE",
"brand": "Ultimaker", "material": "CPE", "color_name": "Black", "brand": "Ultimaker", "material": "CPE", "color_name": "Black",
@ -37,6 +71,9 @@ class TestSendMaterialJob(TestCase):
"properties": {"density": "1.01", "diameter": "2.85", "weight": "750"}, "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"},
"definition": "fdmprinter", "compatible": True} "definition": "fdmprinter", "compatible": True}
_LOCAL_MATERIAL_BLACK_ALL_RESULT = {"generic_pla_black": MaterialGroup("generic_pla_black",
MaterialNode(_LOCAL_MATERIAL_BLACK))}
_REMOTE_MATERIAL_WHITE = { _REMOTE_MATERIAL_WHITE = {
"guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce",
"material": "PLA", "material": "PLA",
@ -55,14 +92,17 @@ class TestSendMaterialJob(TestCase):
"density": 1.00 "density": 1.00
} }
def test_run(self, device_mock, reply_mock): def test_run(self):
device_mock = MagicMock()
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
job.run() job.run()
# We expect the materials endpoint to be called when the job runs. # We expect the materials endpoint to be called when the job runs.
device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials)
def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): def test__onGetRemoteMaterials_withFailedRequest(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 404 reply_mock.attribute.return_value = 404
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
job._onGetRemoteMaterials(reply_mock) job._onGetRemoteMaterials(reply_mock)
@ -70,7 +110,9 @@ class TestSendMaterialJob(TestCase):
# We expect the device not to be called for any follow up. # We expect the device not to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): def test__onGetRemoteMaterials_withWrongEncoding(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500")) reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500"))
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
@ -79,7 +121,9 @@ class TestSendMaterialJob(TestCase):
# Given that the parsing fails we do no expect the device to be called for any follow up. # Given that the parsing fails we do no expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): def test__onGetRemoteMaterials_withBadJsonAnswer(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.")
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
@ -88,7 +132,9 @@ class TestSendMaterialJob(TestCase):
# Given that the parsing fails we do no expect the device to be called for any follow up. # Given that the parsing fails we do no expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self):
reply_mock = MagicMock()
device_mock = MagicMock()
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy()
del remote_material_without_guid["guid"] del remote_material_without_guid["guid"]
@ -99,18 +145,20 @@ class TestSendMaterialJob(TestCase):
# Given that parsing fails we do not expect the device to be called for any follow up. # Given that parsing fails we do not expect the device to be called for any follow up.
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
@patch("cura.Machines.MaterialManager.MaterialManager")
@patch("cura.Settings.CuraContainerRegistry") @patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application") @patch("UM.Application")
def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock,
reply_mock, device_mock): material_manager_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
application_mock.getContainerRegistry.return_value = container_registry_mock
application_mock.getMaterialManager.return_value = material_manager_mock
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy() material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT.copy()
localMaterialWhiteWithInvalidVersion["version"] = "one"
container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion]
application_mock.getContainerRegistry.return_value = container_registry_mock
with mock.patch.object(Application, "getInstance", new = lambda: application_mock): with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
@ -118,15 +166,19 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
@patch("cura.Machines.MaterialManager")
@patch("cura.Settings.CuraContainerRegistry") @patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application") @patch("UM.Application")
def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock,
device_mock): material_manager_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
application_mock.getContainerRegistry.return_value = container_registry_mock application_mock.getContainerRegistry.return_value = container_registry_mock
application_mock.getMaterialManager.return_value = material_manager_mock
device_mock.createFormPart.return_value = "_xXx_" device_mock.createFormPart.return_value = "_xXx_"
container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE] material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy()
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
@ -138,22 +190,23 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.createFormPart.call_count)
self.assertEqual(0, device_mock.postFormWithParts.call_count) self.assertEqual(0, device_mock.postFormWithParts.call_count)
@patch("cura.Settings.CuraContainerRegistry") @patch("UM.Application.Application.getInstance")
@patch("UM.Application") def test__onGetRemoteMaterials_withUpdatedMaterial(self, get_instance_mock):
def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, reply_mock = MagicMock()
device_mock): device_mock = MagicMock()
application_mock.getContainerRegistry.return_value = container_registry_mock application_mock = get_instance_mock.return_value
container_registry_mock = application_mock.getContainerRegistry.return_value
material_manager_mock = application_mock.getMaterialManager.return_value
container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x)
device_mock.createFormPart.return_value = "_xXx_" device_mock.createFormPart.return_value = "_xXx_"
localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy() material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT.copy()
localMaterialWhiteWithHigherVersion["version"] = "2"
container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion]
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
job = SendMaterialJob(device_mock) job = SendMaterialJob(device_mock)
job._onGetRemoteMaterials(reply_mock) job._onGetRemoteMaterials(reply_mock)
@ -164,16 +217,24 @@ class TestSendMaterialJob(TestCase):
call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
device_mock.method_calls) device_mock.method_calls)
@patch("cura.Machines.MaterialManager.MaterialManager")
@patch("cura.Settings.CuraContainerRegistry") @patch("cura.Settings.CuraContainerRegistry")
@patch("UM.Application") @patch("UM.Application")
def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock,
device_mock): material_manager_mock):
reply_mock = MagicMock()
device_mock = MagicMock()
application_mock.getContainerRegistry.return_value = container_registry_mock application_mock.getContainerRegistry.return_value = container_registry_mock
application_mock.getMaterialManager.return_value = material_manager_mock
container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x)
device_mock.createFormPart.return_value = "_xXx_" device_mock.createFormPart.return_value = "_xXx_"
container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE, all_results = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy()
self._LOCAL_MATERIAL_BLACK] for key, value in self._LOCAL_MATERIAL_BLACK_ALL_RESULT.items():
all_results[key] = value
material_manager_mock.getAllMaterialGroups.return_value = all_results
reply_mock.attribute.return_value = 200 reply_mock.attribute.return_value = 200
reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii"))