TestExtruderStack.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. import os.path #To find the test stack files.
  4. import pytest #This module contains automated tests.
  5. import unittest.mock #For the mocking and monkeypatching functionality.
  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. import cura.Settings.ExtruderStack #The module we're testing.
  11. from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
  12. ## Fake container registry that always provides all containers you ask of.
  13. @pytest.fixture()
  14. def container_registry():
  15. registry = unittest.mock.MagicMock()
  16. def findContainers(id = None):
  17. if not id:
  18. return [UM.Settings.ContainerRegistry._EmptyInstanceContainer("test_container")]
  19. else:
  20. return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
  21. registry.findContainers = findContainers
  22. return registry
  23. ## An empty extruder stack to test with.
  24. @pytest.fixture()
  25. def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
  26. return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
  27. ## Place-in function for findContainer that finds only containers that start
  28. # with "some_".
  29. def findSomeContainers(container_id = "*", container_type = None, type = None, category = "*"):
  30. if container_id.startswith("some_"):
  31. return UM.Settings.ContainerRegistry._EmptyInstanceContainer(container_id)
  32. if container_type == DefinitionContainer:
  33. definition_mock = unittest.mock.MagicMock()
  34. definition_mock.getId = unittest.mock.MagicMock(return_value = "some_definition") #getId returns some_definition.
  35. return definition_mock
  36. ## Helper function to read the contents of a container stack in the test
  37. # stack folder.
  38. #
  39. # \param filename The name of the file in the "stacks" folder to read from.
  40. # \return The contents of that file.
  41. def readStack(filename):
  42. with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)) as file_handle:
  43. serialized = file_handle.read()
  44. return serialized
  45. class DefinitionContainerSubClass(DefinitionContainer):
  46. def __init__(self):
  47. super().__init__(container_id = "SubDefinitionContainer")
  48. class InstanceContainerSubClass(InstanceContainer):
  49. def __init__(self):
  50. super().__init__(container_id = "SubInstanceContainer")
  51. #############################START OF TEST CASES################################
  52. ## Tests whether adding a container is properly forbidden.
  53. def test_addContainer(extruder_stack):
  54. with pytest.raises(InvalidOperationError):
  55. extruder_stack.addContainer(unittest.mock.MagicMock())
  56. ## Tests whether the container types are properly enforced on the stack.
  57. #
  58. # When setting a field to have a different type of stack than intended, we
  59. # should get an exception.
  60. @pytest.mark.parametrize("definition_container, instance_container", [
  61. (DefinitionContainer(container_id = "TestDefinitionContainer"), InstanceContainer(container_id = "TestInstanceContainer")),
  62. (DefinitionContainerSubClass(), InstanceContainerSubClass())
  63. ])
  64. def test_constrainContainerTypes(definition_container, instance_container, extruder_stack):
  65. with pytest.raises(InvalidContainerError): #Putting a definition container in the user changes is not allowed.
  66. extruder_stack.userChanges = definition_container
  67. extruder_stack.userChanges = instance_container #Putting an instance container in the user changes is allowed.
  68. with pytest.raises(InvalidContainerError):
  69. extruder_stack.qualityChanges = definition_container
  70. extruder_stack.qualityChanges = instance_container
  71. with pytest.raises(InvalidContainerError):
  72. extruder_stack.quality = definition_container
  73. extruder_stack.quality = instance_container
  74. with pytest.raises(InvalidContainerError):
  75. extruder_stack.material = definition_container
  76. extruder_stack.material = instance_container
  77. with pytest.raises(InvalidContainerError):
  78. extruder_stack.variant = definition_container
  79. extruder_stack.variant = instance_container
  80. with pytest.raises(InvalidContainerError): #Putting an instance container in the definition is not allowed.
  81. extruder_stack.definition = instance_container
  82. extruder_stack.definition = definition_container #Putting a definition container in the definition is allowed.
  83. ## Tests whether definitions are being read properly from an extruder stack.
  84. @pytest.mark.parametrize("filename, definition_id", [
  85. ("Left.extruder.cfg", "empty"),
  86. ("ExtruderLegacy.stack.cfg", "empty"),
  87. ("OnlyDefinition.extruder.cfg", "empty"),
  88. ("Complete.extruder.cfg", "some_definition")
  89. ])
  90. def test_deserializeDefinition(filename, definition_id, container_registry, extruder_stack):
  91. serialized = readStack(filename)
  92. #Mock the loading of the instance containers.
  93. extruder_stack.findContainer = findSomeContainers
  94. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  95. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  96. extruder_stack.deserialize(serialized)
  97. assert extruder_stack.definition.getId() == definition_id
  98. #Restore.
  99. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  100. ## Tests whether materials are being read properly from an extruder stack.
  101. @pytest.mark.parametrize("filename, material_id", [
  102. ("Left.extruder.cfg", "some_instance"),
  103. ("ExtruderLegacy.stack.cfg", "some_instance"),
  104. ("OnlyMaterial.extruder.cfg", "some_instance"),
  105. ("OnlyDefinition.extruder.cfg", "empty"),
  106. ("Complete.extruder.cfg", "some_material")
  107. ])
  108. def test_deserializeMaterial(filename, material_id, container_registry, extruder_stack):
  109. serialized = readStack(filename)
  110. #Mock the loading of the instance containers.
  111. extruder_stack.findContainer = findSomeContainers
  112. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  113. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  114. extruder_stack.deserialize(serialized)
  115. assert extruder_stack.material.getId() == material_id
  116. #Restore.
  117. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  118. ## Tests that when an extruder is loaded with an unknown instance, it raises an
  119. # exception.
  120. def test_deserializeMissingContainer(extruder_stack):
  121. serialized = readStack("Left.extruder.cfg")
  122. with pytest.raises(Exception):
  123. extruder_stack.deserialize(serialized)
  124. try:
  125. extruder_stack.deserialize(serialized)
  126. except Exception as e:
  127. #Must be exactly Exception, not one of its subclasses, since that is what gets raised when a stack has an unknown container.
  128. #That's why we can't use pytest.raises.
  129. assert type(e) == Exception
  130. ## Tests whether qualities are being read properly from an extruder stack.
  131. @pytest.mark.parametrize("filename, quality_id", [
  132. ("Left.extruder.cfg", "empty"),
  133. ("ExtruderLegacy.stack.cfg", "empty"),
  134. ("OnlyQuality.extruder.cfg", "some_instance"),
  135. ("Complete.extruder.cfg", "some_quality")
  136. ])
  137. def test_deserializeQuality(filename, quality_id, container_registry, extruder_stack):
  138. serialized = readStack(filename)
  139. #Mock the loading of the instance containers.
  140. extruder_stack.findContainer = findSomeContainers
  141. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  142. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  143. extruder_stack.deserialize(serialized)
  144. assert extruder_stack.quality.getId() == quality_id
  145. #Restore.
  146. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  147. ## Tests whether quality changes are being read properly from an extruder
  148. # stack.
  149. @pytest.mark.parametrize("filename, quality_changes_id", [
  150. ("Left.extruder.cfg", "empty"),
  151. ("ExtruderLegacy.stack.cfg", "empty"),
  152. ("OnlyQualityChanges.extruder.cfg", "some_instance"),
  153. ("Complete.extruder.cfg", "some_quality_changes")
  154. ])
  155. def test_deserializeQualityChanges(filename, quality_changes_id, container_registry, extruder_stack):
  156. serialized = readStack(filename)
  157. #Mock the loading of the instance containers.
  158. extruder_stack.findContainer = findSomeContainers
  159. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  160. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  161. extruder_stack.deserialize(serialized)
  162. assert extruder_stack.qualityChanges.getId() == quality_changes_id
  163. #Restore.
  164. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  165. ## Tests whether user changes are being read properly from an extruder stack.
  166. @pytest.mark.parametrize("filename, user_changes_id", [
  167. ("Left.extruder.cfg", "empty"),
  168. ("ExtruderLegacy.stack.cfg", "empty"),
  169. ("OnlyUser.extruder.cfg", "some_instance"),
  170. ("Complete.extruder.cfg", "some_user_changes")
  171. ])
  172. def test_deserializeUserChanges(filename, user_changes_id, container_registry, extruder_stack):
  173. serialized = readStack(filename)
  174. #Mock the loading of the instance containers.
  175. extruder_stack.findContainer = findSomeContainers
  176. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  177. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  178. extruder_stack.deserialize(serialized)
  179. assert extruder_stack.userChanges.getId() == user_changes_id
  180. #Restore.
  181. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  182. ## Tests whether variants are being read properly from an extruder stack.
  183. @pytest.mark.parametrize("filename, variant_id", [
  184. ("Left.extruder.cfg", "empty"),
  185. ("ExtruderLegacy.stack.cfg", "empty"),
  186. ("OnlyVariant.extruder.cfg", "some_instance"),
  187. ("Complete.extruder.cfg", "some_variant")
  188. ])
  189. def test_deserializeVariant(filename, variant_id, container_registry, extruder_stack):
  190. serialized = readStack(filename)
  191. #Mock the loading of the instance containers.
  192. extruder_stack.findContainer = findSomeContainers
  193. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  194. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all profiles you ask of.
  195. extruder_stack.deserialize(serialized)
  196. assert extruder_stack.variant.getId() == variant_id
  197. #Restore.
  198. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  199. ## Tests whether getProperty properly applies the stack-like behaviour on its
  200. # containers.
  201. def test_getPropertyFallThrough(extruder_stack):
  202. #A few instance container mocks to put in the stack.
  203. layer_height_5 = unittest.mock.MagicMock() #Sets layer height to 5.
  204. layer_height_5.getProperty = lambda key, property: 5 if (key == "layer_height" and property == "value") else None
  205. layer_height_5.hasProperty = lambda key: key == "layer_height"
  206. layer_height_10 = unittest.mock.MagicMock() #Sets layer height to 10.
  207. layer_height_10.getProperty = lambda key, property: 10 if (key == "layer_height" and property == "value") else None
  208. layer_height_10.hasProperty = lambda key: key == "layer_height"
  209. no_layer_height = unittest.mock.MagicMock() #No settings at all.
  210. no_layer_height.getProperty = lambda key, property: None
  211. no_layer_height.hasProperty = lambda key: False
  212. extruder_stack.userChanges = no_layer_height
  213. extruder_stack.qualityChanges = no_layer_height
  214. extruder_stack.quality = no_layer_height
  215. extruder_stack.material = no_layer_height
  216. extruder_stack.variant = no_layer_height
  217. extruder_stack.definition = layer_height_5 #Here it is!
  218. assert extruder_stack.getProperty("layer_height", "value") == 5
  219. extruder_stack.variant = layer_height_10
  220. assert extruder_stack.getProperty("layer_height", "value") == 10
  221. extruder_stack.material = layer_height_5
  222. assert extruder_stack.getProperty("layer_height", "value") == 5
  223. extruder_stack.quality = layer_height_10
  224. assert extruder_stack.getProperty("layer_height", "value") == 10
  225. extruder_stack.qualityChanges = layer_height_5
  226. assert extruder_stack.getProperty("layer_height", "value") == 5
  227. extruder_stack.userChanges = layer_height_10
  228. assert extruder_stack.getProperty("layer_height", "value") == 10
  229. ## Tests whether inserting a container is properly forbidden.
  230. def test_insertContainer(extruder_stack):
  231. with pytest.raises(InvalidOperationError):
  232. extruder_stack.insertContainer(0, unittest.mock.MagicMock())
  233. ## Tests whether removing a container is properly forbidden.
  234. def test_removeContainer(extruder_stack):
  235. with pytest.raises(InvalidOperationError):
  236. extruder_stack.removeContainer(unittest.mock.MagicMock())
  237. ## Tests setting definitions by specifying an ID of a definition that exists.
  238. def test_setDefinitionByIdExists(extruder_stack, container_registry):
  239. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  240. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all the profiles you ask of.
  241. extruder_stack.setDefinitionById("some_definition") #The container registry always has a container with the ID.
  242. #Restore.
  243. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  244. ## Tests setting definitions by specifying an ID of a definition that doesn't
  245. # exist.
  246. def test_setDefinitionByIdDoesntExist(extruder_stack):
  247. with pytest.raises(KeyError):
  248. extruder_stack.setDefinitionById("some_definition") #Container registry is empty now.
  249. ## Tests setting materials by specifying an ID of a material that exists.
  250. def test_setMaterialByIdExists(extruder_stack, container_registry):
  251. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  252. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all the profiles you ask of.
  253. extruder_stack.setMaterialById("some_material") #The container registry always has a container with the ID.
  254. #Restore.
  255. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  256. ## Tests setting materials by specifying an ID of a material that doesn't
  257. # exist.
  258. def test_setMaterialByIdDoesntExist(extruder_stack):
  259. with pytest.raises(KeyError):
  260. extruder_stack.setMaterialById("some_material") #Container registry is empty now.
  261. ## Tests setting properties directly on the extruder stack.
  262. @pytest.mark.parametrize("key, property, value, output_value", [
  263. ("layer_height", "value", "0.1337", 0.1337),
  264. ("foo", "value", "100", 100),
  265. ("support_enabled", "value", "True", True),
  266. ("layer_height", "default_value", 0.1337, 0.1337),
  267. ("layer_height", "is_bright_pink", "of course", "of course")
  268. ])
  269. def test_setPropertyUser(key, property, value, output_value, extruder_stack):
  270. extruder_stack.setProperty(key, value, property)
  271. assert extruder_stack.userChanges.getProperty(key, property) == output_value
  272. ## Tests setting properties on specific containers on the extruder stack.
  273. @pytest.mark.parametrize("target_container", [
  274. "user",
  275. "quality_changes",
  276. "quality",
  277. "material",
  278. "variant",
  279. "definition"
  280. ])
  281. def test_setPropertyOtherContainers(target_container, extruder_stack):
  282. #Other parameters that don't need to be varied.
  283. key = "layer_height"
  284. property = "value",
  285. value = "0.1337",
  286. output_value = 0.1337
  287. extruder_stack.setProperty(key, value, property, target_container = target_container)
  288. containers = {
  289. "user": extruder_stack.userChanges,
  290. "quality_changes": extruder_stack.qualityChanges,
  291. "quality": extruder_stack.quality,
  292. "material": extruder_stack.material,
  293. "variant": extruder_stack.variant,
  294. "definition_changes": extruder_stack.definition_changes,
  295. "definition": extruder_stack.definition
  296. }
  297. assert containers[target_container].getProperty(key, property) == output_value
  298. ## Tests setting qualities by specifying an ID of a quality that exists.
  299. def test_setQualityByIdExists(extruder_stack, container_registry):
  300. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  301. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all the profiles you ask of.
  302. extruder_stack.setQualityById("some_quality") #The container registry always has a container with the ID.
  303. #Restore.
  304. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  305. ## Tests setting qualities by specifying an ID of a quality that doesn't exist.
  306. def test_setQualityByIdDoesntExist(extruder_stack):
  307. with pytest.raises(KeyError):
  308. extruder_stack.setQualityById("some_quality") #Container registry is empty now.
  309. ## Tests setting quality changes by specifying an ID of a quality change that
  310. # exists.
  311. def test_setQualityChangesByIdExists(extruder_stack, container_registry):
  312. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  313. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all the profiles you ask of.
  314. extruder_stack.setQualityChangesById("some_quality_changes") #The container registry always has a container with the ID.
  315. #Restore.
  316. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  317. ## Tests setting quality changes by specifying an ID of a quality change that
  318. # doesn't exist.
  319. def test_setQualityChangesByIdDoesntExist(extruder_stack):
  320. with pytest.raises(KeyError):
  321. extruder_stack.setQualityChangesById("some_quality_changes") #Container registry is empty now.
  322. ## Tests setting variants by specifying an ID of a variant that exists.
  323. def test_setVariantByIdExists(extruder_stack, container_registry):
  324. original_container_registry = UM.Settings.ContainerStack._containerRegistry
  325. UM.Settings.ContainerStack._containerRegistry = container_registry #Always has all the profiles you ask of.
  326. extruder_stack.setVariantById("some_variant") #The container registry always has a container with the ID.
  327. #Restore.
  328. UM.Settings.ContainerStack._containerRegistry = original_container_registry
  329. ## Tests setting variants by specifying an ID of a variant that doesn't exist.
  330. def test_setVariantByIdDoesntExist(extruder_stack):
  331. with pytest.raises(KeyError):
  332. extruder_stack.setVariantById("some_variant") #Container registry is empty now.