test_plate_object_extraction.py 7.0 KB

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