test_plate_object_extraction.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. """Unit tests for plate object extraction from 3MF model_settings.config."""
  2. import pytest
  3. from defusedxml import ElementTree as ET
  4. class TestPlateObjectExtraction:
  5. """Tests for extracting object IDs and names from model_settings.config XML."""
  6. def test_extract_object_names_from_xml(self):
  7. """Verify object names are extracted from model_settings.config XML."""
  8. xml_content = """<?xml version="1.0" encoding="UTF-8"?>
  9. <config>
  10. <object id="1">
  11. <metadata key="name" value="Cube"/>
  12. </object>
  13. <object id="2">
  14. <metadata key="name" value="Sphere"/>
  15. </object>
  16. <object id="3">
  17. <metadata key="name" value="Cylinder"/>
  18. </object>
  19. </config>
  20. """
  21. root = ET.fromstring(xml_content)
  22. object_names_by_id = {}
  23. for obj in root.findall(".//object"):
  24. obj_id = obj.get("id")
  25. if obj_id:
  26. name_meta = obj.find("./metadata[@key='name']")
  27. if name_meta is not None:
  28. object_names_by_id[obj_id] = name_meta.get("value", f"Object {obj_id}")
  29. else:
  30. object_names_by_id[obj_id] = f"Object {obj_id}"
  31. assert object_names_by_id == {
  32. "1": "Cube",
  33. "2": "Sphere",
  34. "3": "Cylinder",
  35. }
  36. def test_extract_object_names_missing_name(self):
  37. """Verify objects without names get default names."""
  38. xml_content = """<?xml version="1.0" encoding="UTF-8"?>
  39. <config>
  40. <object id="1">
  41. <metadata key="name" value="Named Object"/>
  42. </object>
  43. <object id="2">
  44. <!-- No name metadata -->
  45. </object>
  46. </config>
  47. """
  48. root = ET.fromstring(xml_content)
  49. object_names_by_id = {}
  50. for obj in root.findall(".//object"):
  51. obj_id = obj.get("id")
  52. if obj_id:
  53. name_meta = obj.find("./metadata[@key='name']")
  54. if name_meta is not None:
  55. object_names_by_id[obj_id] = name_meta.get("value", f"Object {obj_id}")
  56. else:
  57. object_names_by_id[obj_id] = f"Object {obj_id}"
  58. assert object_names_by_id == {
  59. "1": "Named Object",
  60. "2": "Object 2",
  61. }
  62. def test_extract_plate_object_associations(self):
  63. """Verify plate-to-object associations are extracted correctly."""
  64. xml_content = """<?xml version="1.0" encoding="UTF-8"?>
  65. <config>
  66. <plate>
  67. <metadata key="plater_id" value="1"/>
  68. <model_instance>
  69. <metadata key="object_id" value="1"/>
  70. </model_instance>
  71. <model_instance>
  72. <metadata key="object_id" value="2"/>
  73. </model_instance>
  74. </plate>
  75. <plate>
  76. <metadata key="plater_id" value="2"/>
  77. <model_instance>
  78. <metadata key="object_id" value="3"/>
  79. </model_instance>
  80. </plate>
  81. </config>
  82. """
  83. root = ET.fromstring(xml_content)
  84. plate_object_ids = {}
  85. for plate in root.findall(".//plate"):
  86. plate_id = None
  87. for meta in plate.findall("metadata"):
  88. if meta.get("key") in ("plater_id", "plate_id"):
  89. plate_id = meta.get("value")
  90. break
  91. if plate_id:
  92. object_ids = []
  93. for instance in plate.findall(".//model_instance"):
  94. for meta in instance.findall("metadata"):
  95. if meta.get("key") == "object_id":
  96. object_ids.append(meta.get("value"))
  97. plate_object_ids[plate_id] = object_ids
  98. assert plate_object_ids == {
  99. "1": ["1", "2"],
  100. "2": ["3"],
  101. }
  102. def test_extract_plate_object_associations_empty_plate(self):
  103. """Verify empty plates have empty object lists."""
  104. xml_content = """<?xml version="1.0" encoding="UTF-8"?>
  105. <config>
  106. <plate>
  107. <metadata key="plater_id" value="1"/>
  108. <!-- No model_instances -->
  109. </plate>
  110. </config>
  111. """
  112. root = ET.fromstring(xml_content)
  113. plate_object_ids = {}
  114. for plate in root.findall(".//plate"):
  115. plate_id = None
  116. for meta in plate.findall("metadata"):
  117. if meta.get("key") in ("plater_id", "plate_id"):
  118. plate_id = meta.get("value")
  119. break
  120. if plate_id:
  121. object_ids = []
  122. for instance in plate.findall(".//model_instance"):
  123. for meta in instance.findall("metadata"):
  124. if meta.get("key") == "object_id":
  125. object_ids.append(meta.get("value"))
  126. plate_object_ids[plate_id] = object_ids
  127. assert plate_object_ids == {"1": []}
  128. def test_object_count_matches_objects_length(self):
  129. """Verify object_count equals len(objects)."""
  130. objects = ["Cube", "Sphere", "Cylinder"]
  131. object_count = len(objects)
  132. assert object_count == 3
  133. def test_resolve_object_names_from_ids(self):
  134. """Verify object IDs are resolved to names."""
  135. object_names_by_id = {
  136. "1": "Cube",
  137. "2": "Sphere",
  138. "3": "Cylinder",
  139. }
  140. plate_object_ids = ["1", "3"]
  141. resolved_names = [object_names_by_id.get(obj_id, f"Object {obj_id}") for obj_id in plate_object_ids]
  142. assert resolved_names == ["Cube", "Cylinder"]
  143. def test_resolve_object_names_missing_id(self):
  144. """Verify missing object IDs get fallback names."""
  145. object_names_by_id = {
  146. "1": "Cube",
  147. }
  148. plate_object_ids = ["1", "99"] # 99 doesn't exist
  149. resolved_names = [object_names_by_id.get(obj_id, f"Object {obj_id}") for obj_id in plate_object_ids]
  150. assert resolved_names == ["Cube", "Object 99"]
  151. def test_plate_id_alternatives(self):
  152. """Verify both 'plater_id' and 'plate_id' keys are supported."""
  153. xml_with_plater_id = """<?xml version="1.0" encoding="UTF-8"?>
  154. <config>
  155. <plate>
  156. <metadata key="plater_id" value="1"/>
  157. </plate>
  158. </config>
  159. """
  160. xml_with_plate_id = """<?xml version="1.0" encoding="UTF-8"?>
  161. <config>
  162. <plate>
  163. <metadata key="plate_id" value="2"/>
  164. </plate>
  165. </config>
  166. """
  167. def extract_plate_id(xml_content):
  168. root = ET.fromstring(xml_content)
  169. for plate in root.findall(".//plate"):
  170. for meta in plate.findall("metadata"):
  171. if meta.get("key") in ("plater_id", "plate_id"):
  172. return meta.get("value")
  173. return None
  174. assert extract_plate_id(xml_with_plater_id) == "1"
  175. assert extract_plate_id(xml_with_plate_id) == "2"