TestExtruderStack.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import pytest #This module contains automated tests.
  4. import unittest.mock #For the mocking and monkeypatching functionality.
  5. import cura.Settings.CuraContainerStack #To get the list of container types.
  6. import UM.Settings.ContainerRegistry #To create empty instance containers.
  7. import UM.Settings.ContainerStack #To set the container registry the container stacks use.
  8. from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
  9. from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer.
  10. from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
  11. from cura.Settings.ExtruderManager import ExtruderManager
  12. from cura.Settings.cura_empty_instance_containers import empty_container
  13. ## Gets an instance container with a specified container type.
  14. #
  15. # \param container_type The type metadata for the instance container.
  16. # \return An instance container instance.
  17. def getInstanceContainer(container_type) -> InstanceContainer:
  18. container = InstanceContainer(container_id = "InstanceContainer")
  19. container.setMetaDataEntry("type", container_type)
  20. return container
  21. class DefinitionContainerSubClass(DefinitionContainer):
  22. def __init__(self):
  23. super().__init__(container_id = "SubDefinitionContainer")
  24. class InstanceContainerSubClass(InstanceContainer):
  25. def __init__(self, container_type):
  26. super().__init__(container_id = "SubInstanceContainer")
  27. self.setMetaDataEntry("type", container_type)
  28. #############################START OF TEST CASES################################
  29. ## Tests whether adding a container is properly forbidden.
  30. def test_addContainer(extruder_stack):
  31. with pytest.raises(InvalidOperationError):
  32. extruder_stack.addContainer(unittest.mock.MagicMock())
  33. #Tests setting user changes profiles to invalid containers.
  34. @pytest.mark.parametrize("container", [
  35. getInstanceContainer(container_type = "wrong container type"),
  36. getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
  37. DefinitionContainer(container_id = "wrong class")
  38. ])
  39. def test_constrainUserChangesInvalid(container, extruder_stack):
  40. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  41. extruder_stack.userChanges = container
  42. #Tests setting user changes profiles.
  43. @pytest.mark.parametrize("container", [
  44. getInstanceContainer(container_type = "user"),
  45. InstanceContainerSubClass(container_type = "user")
  46. ])
  47. def test_constrainUserChangesValid(container, extruder_stack):
  48. extruder_stack.userChanges = container #Should not give an error.
  49. #Tests setting quality changes profiles to invalid containers.
  50. @pytest.mark.parametrize("container", [
  51. getInstanceContainer(container_type = "wrong container type"),
  52. getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
  53. DefinitionContainer(container_id = "wrong class")
  54. ])
  55. def test_constrainQualityChangesInvalid(container, extruder_stack):
  56. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  57. extruder_stack.qualityChanges = container
  58. #Test setting quality changes profiles.
  59. @pytest.mark.parametrize("container", [
  60. getInstanceContainer(container_type = "quality_changes"),
  61. InstanceContainerSubClass(container_type = "quality_changes")
  62. ])
  63. def test_constrainQualityChangesValid(container, extruder_stack):
  64. extruder_stack.qualityChanges = container #Should not give an error.
  65. #Tests setting quality profiles to invalid containers.
  66. @pytest.mark.parametrize("container", [
  67. getInstanceContainer(container_type = "wrong container type"),
  68. getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
  69. DefinitionContainer(container_id = "wrong class")
  70. ])
  71. def test_constrainQualityInvalid(container, extruder_stack):
  72. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  73. extruder_stack.quality = container
  74. #Test setting quality profiles.
  75. @pytest.mark.parametrize("container", [
  76. getInstanceContainer(container_type = "quality"),
  77. InstanceContainerSubClass(container_type = "quality")
  78. ])
  79. def test_constrainQualityValid(container, extruder_stack):
  80. extruder_stack.quality = container #Should not give an error.
  81. #Tests setting materials to invalid containers.
  82. @pytest.mark.parametrize("container", [
  83. getInstanceContainer(container_type = "wrong container type"),
  84. getInstanceContainer(container_type = "quality"), #Existing, but still wrong type.
  85. DefinitionContainer(container_id = "wrong class")
  86. ])
  87. def test_constrainMaterialInvalid(container, extruder_stack):
  88. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  89. extruder_stack.material = container
  90. #Test setting materials.
  91. @pytest.mark.parametrize("container", [
  92. getInstanceContainer(container_type = "material"),
  93. InstanceContainerSubClass(container_type = "material")
  94. ])
  95. def test_constrainMaterialValid(container, extruder_stack):
  96. extruder_stack.material = container #Should not give an error.
  97. #Tests setting variants to invalid containers.
  98. @pytest.mark.parametrize("container", [
  99. getInstanceContainer(container_type = "wrong container type"),
  100. getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
  101. DefinitionContainer(container_id = "wrong class")
  102. ])
  103. def test_constrainVariantInvalid(container, extruder_stack):
  104. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  105. extruder_stack.variant = container
  106. #Test setting variants.
  107. @pytest.mark.parametrize("container", [
  108. getInstanceContainer(container_type = "variant"),
  109. InstanceContainerSubClass(container_type = "variant")
  110. ])
  111. def test_constrainVariantValid(container, extruder_stack):
  112. extruder_stack.variant = container #Should not give an error.
  113. #Tests setting definition changes profiles to invalid containers.
  114. @pytest.mark.parametrize("container", [
  115. getInstanceContainer(container_type = "wrong container type"),
  116. getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
  117. DefinitionContainer(container_id = "wrong class")
  118. ])
  119. def test_constrainDefinitionChangesInvalid(container, global_stack):
  120. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  121. global_stack.definitionChanges = container
  122. #Test setting definition changes profiles.
  123. @pytest.mark.parametrize("container", [
  124. getInstanceContainer(container_type = "definition_changes"),
  125. InstanceContainerSubClass(container_type = "definition_changes")
  126. ])
  127. def test_constrainDefinitionChangesValid(container, global_stack):
  128. global_stack.definitionChanges = container #Should not give an error.
  129. #Tests setting definitions to invalid containers.
  130. @pytest.mark.parametrize("container", [
  131. getInstanceContainer(container_type = "wrong class"),
  132. getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
  133. ])
  134. def test_constrainDefinitionInvalid(container, extruder_stack):
  135. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
  136. extruder_stack.definition = container
  137. #Test setting definitions.
  138. @pytest.mark.parametrize("container", [
  139. DefinitionContainer(container_id = "DefinitionContainer"),
  140. DefinitionContainerSubClass()
  141. ])
  142. def test_constrainDefinitionValid(container, extruder_stack):
  143. extruder_stack.definition = container #Should not give an error.
  144. ## Tests whether deserialising completes the missing containers with empty ones.
  145. def test_deserializeCompletesEmptyContainers(extruder_stack):
  146. extruder_stack._containers = [DefinitionContainer(container_id = "definition"), extruder_stack.definitionChanges] #Set the internal state of this stack manually.
  147. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  148. extruder_stack.deserialize("")
  149. assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
  150. for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
  151. if container_type_index in \
  152. (cura.Settings.CuraContainerStack._ContainerIndexes.Definition,
  153. cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): # We're not checking the definition or definition_changes
  154. continue
  155. assert extruder_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
  156. ## Tests whether an instance container with the wrong type gets removed when deserialising.
  157. def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
  158. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
  159. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
  160. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  161. extruder_stack.deserialize("")
  162. assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
  163. ## Tests whether a container with the wrong class gets removed when deserialising.
  164. def test_deserializeRemovesWrongContainerClass(extruder_stack):
  165. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
  166. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
  167. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  168. extruder_stack.deserialize("")
  169. assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
  170. ## Tests whether an instance container in the definition spot results in an error.
  171. def test_deserializeWrongDefinitionClass(extruder_stack):
  172. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
  173. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  174. with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
  175. extruder_stack.deserialize("")
  176. ## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
  177. def test_deserializeMoveInstanceContainer(extruder_stack):
  178. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
  179. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
  180. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  181. extruder_stack.deserialize("")
  182. assert extruder_stack.quality == empty_container
  183. assert extruder_stack.material != empty_container
  184. ## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
  185. def test_deserializeMoveDefinitionContainer(extruder_stack):
  186. extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
  187. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
  188. extruder_stack.deserialize("")
  189. assert extruder_stack.material == empty_container
  190. assert extruder_stack.definition != empty_container
  191. ## Tests whether getProperty properly applies the stack-like behaviour on its containers.
  192. def test_getPropertyFallThrough(global_stack, extruder_stack):
  193. # ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager
  194. ExtruderManager._ExtruderManager__instance = unittest.mock.MagicMock()
  195. #A few instance container mocks to put in the stack.
  196. mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
  197. mock_no_settings = {} #For each container type, a mock container that has no settings at all.
  198. container_indices = cura.Settings.CuraContainerStack._ContainerIndexes #Cache.
  199. for type_id, type_name in container_indices.IndexTypeMap.items():
  200. container = unittest.mock.MagicMock()
  201. # Return type_id when asking for value and -1 when asking for settable_per_extruder
  202. container.getProperty = lambda key, property, context = None, type_id = type_id: type_id if (key == "layer_height" and property == "value") else (None if property != "settable_per_extruder" else "-1") #Returns the container type ID as layer height, in order to identify it.
  203. container.hasProperty = lambda key, property: key == "layer_height"
  204. container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
  205. mock_layer_heights[type_id] = container
  206. container = unittest.mock.MagicMock()
  207. container.getProperty = unittest.mock.MagicMock(return_value = None) #Has no settings at all.
  208. container.hasProperty = unittest.mock.MagicMock(return_value = False)
  209. container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
  210. mock_no_settings[type_id] = container
  211. extruder_stack.userChanges = mock_no_settings[container_indices.UserChanges]
  212. extruder_stack.qualityChanges = mock_no_settings[container_indices.QualityChanges]
  213. extruder_stack.quality = mock_no_settings[container_indices.Quality]
  214. extruder_stack.material = mock_no_settings[container_indices.Material]
  215. extruder_stack.variant = mock_no_settings[container_indices.Variant]
  216. with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
  217. extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
  218. extruder_stack.setNextStack(global_stack)
  219. assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
  220. extruder_stack.variant = mock_layer_heights[container_indices.Variant]
  221. assert extruder_stack.getProperty("layer_height", "value") == container_indices.Variant
  222. extruder_stack.material = mock_layer_heights[container_indices.Material]
  223. assert extruder_stack.getProperty("layer_height", "value") == container_indices.Material
  224. extruder_stack.quality = mock_layer_heights[container_indices.Quality]
  225. assert extruder_stack.getProperty("layer_height", "value") == container_indices.Quality
  226. extruder_stack.qualityChanges = mock_layer_heights[container_indices.QualityChanges]
  227. assert extruder_stack.getProperty("layer_height", "value") == container_indices.QualityChanges
  228. extruder_stack.userChanges = mock_layer_heights[container_indices.UserChanges]
  229. assert extruder_stack.getProperty("layer_height", "value") == container_indices.UserChanges
  230. ## Tests whether inserting a container is properly forbidden.
  231. def test_insertContainer(extruder_stack):
  232. with pytest.raises(InvalidOperationError):
  233. extruder_stack.insertContainer(0, unittest.mock.MagicMock())
  234. ## Tests whether removing a container is properly forbidden.
  235. def test_removeContainer(extruder_stack):
  236. with pytest.raises(InvalidOperationError):
  237. extruder_stack.removeContainer(unittest.mock.MagicMock())
  238. ## Tests setting properties directly on the extruder stack.
  239. @pytest.mark.parametrize("key, property, value", [
  240. ("layer_height", "value", 0.1337),
  241. ("foo", "value", 100),
  242. ("support_enabled", "value", True),
  243. ("layer_height", "default_value", 0.1337),
  244. ("layer_height", "is_bright_pink", "of course")
  245. ])
  246. def test_setPropertyUser(key, property, value, extruder_stack):
  247. user_changes = unittest.mock.MagicMock()
  248. user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
  249. extruder_stack.userChanges = user_changes
  250. extruder_stack.setProperty(key, property, value) #The actual test.
  251. extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.