diff --git a/src/ifctester/ifctester/ids.py b/src/ifctester/ifctester/ids.py index 24bed309b8..0071f48fb6 100644 --- a/src/ifctester/ifctester/ids.py +++ b/src/ifctester/ifctester/ids.py @@ -171,8 +171,8 @@ class Specification: self.minOccurs = ids_dict["@minOccurs"] self.maxOccurs = ids_dict["@maxOccurs"] self.ifcVersion = ids_dict["@ifcVersion"] - self.applicability = self.parse_clause(ids_dict["applicability"]) - self.requirements = self.parse_clause(ids_dict["requirements"]) + self.applicability = self.parse_clause(ids_dict["applicability"]) if "applicability" in ids_dict else [] + self.requirements = self.parse_clause(ids_dict["requirements"]) if "requirements" in ids_dict else [] return self def parse_clause(self, clause): @@ -201,14 +201,16 @@ class Specification: return elements = [] - for i, facet in enumerate(self.applicability): - # Usually, we rely on an entity applicability to give us our first - # shortlist of elements, as it's the most efficient way to filter - # elements. If this does not exist, then we have no choice but to - # check everything. - if i == 0 and not isinstance(facet, Entity): - elements = list(ifc_file) - elements = facet.filter(ifc_file, elements) + + if self.applicability: + for i, facet in enumerate(self.applicability): + # Usually, we rely on an entity applicability to give us our first + # shortlist of elements, as it's the most efficient way to filter + # elements. If this does not exist, then we have no choice but to + # check everything. + if i == 0 and not isinstance(facet, Entity): + elements = list(ifc_file) + elements = facet.filter(ifc_file, elements) for element in elements: is_applicable = True @@ -237,6 +239,7 @@ class Specification: facet.status = bool(facet.failed_entities) self.status = True + # if spec is required if self.minOccurs != 0: if not self.applicable_entities: self.status = False @@ -244,11 +247,15 @@ class Specification: facet.status = False elif self.failed_entities: self.status = False - elif self.minOccurs == 0 and self.maxOccurs != 0: + # if spec is optional + elif self.minOccurs == 0 and self.maxOccurs != 0: if self.failed_entities: self.status = False + # if spec is prohibited elif self.maxOccurs == 0: - if (len(self.applicable_entities) - len(self.failed_entities)) > 0: + if (len(self.applicable_entities)) > 0 and len(self.requirements) == 0: + self.status = False + if (len(self.applicable_entities)) > 0 and (len(self.applicable_entities) - len(self.failed_entities)) > 0: self.status = False def get_usage(self): diff --git a/src/ifctester/test/test_ids.py b/src/ifctester/test/test_ids.py index 6a4eeef436..2bdcde22f2 100644 --- a/src/ifctester/test/test_ids.py +++ b/src/ifctester/test/test_ids.py @@ -250,3 +250,43 @@ class TestSpecification: "applicability": {}, "requirements": {}, } + + def test_specification_has_no_requirements(self): + model = ifcopenshell.file() + wall = model.createIfcWall() + waldo = model.createIfcWall(Name="Waldo") + + test_ids = ids.Ids(title="Title") + spec = ids.Specification(name="Name") + spec.applicability.append(ids.Entity(name="IFCWALL")) + test_ids.specifications.append(spec) + spec.minOccurs = 1 + run("A specification that is required and has at least one applicable entity but no requirements shall pass", test_ids, model, True, [wall, waldo], None) + + test_ids = ids.Ids(title="Title") + spec = ids.Specification(name="Name") + test_ids.specifications.append(spec) + spec.minOccurs = 1 + run("A specification that is required but has no applicable entities or requirements shall fail", test_ids, model, False, None, None) + + test_ids = ids.Ids(title="Title") + spec = ids.Specification(name="Name") + spec.applicability.append(ids.Entity(name="IFCWALL")) + test_ids.specifications.append(spec) + spec.minOccurs = 0 + run("A specification that is optional and has at least one applicable entity but no requirements shall pass", test_ids, model, True, [wall, waldo], None) + + test_ids = ids.Ids(title="Title") + spec = ids.Specification(name="Name") + spec.applicability.append(ids.Entity(name="IFCWALL")) + test_ids.specifications.append(spec) + spec.minOccurs = 0 + spec.maxOccurs = 0 + run("A specification that is prohibited and has at least one applicable entity but no requirements shall fail", test_ids, model, False, [wall, waldo], None) + + test_ids = ids.Ids(title="Title") + spec = ids.Specification(name="Name") + test_ids.specifications.append(spec) + spec.minOccurs = 0 + spec.maxOccurs = 0 + run("A specification that is prohibited but has no applicable entities or requirements shall pass", test_ids, model, True, None, None)