Fix #3970. Show passing elements in IDS test report.

This commit is contained in:
Dion Moult
2024-06-20 16:36:18 +10:00
parent d700262e7f
commit fc0e9e900f
4 changed files with 87 additions and 18 deletions
+1
View File
@@ -81,6 +81,7 @@ class Facet:
def __init__(self, *parameters):
self.status = None
self.passed_entities: set[ifcopenshell.entity_instance] = set()
self.failures: list[FacetFailure] = []
for i, name in enumerate(self.parameters):
setattr(self, name.replace("@", ""), parameters[i])
+9 -1
View File
@@ -174,6 +174,7 @@ class Specification:
self.instructions = instructions
self.applicable_entities: list[ifcopenshell.entity_instance] = []
self.passed_entities: set[ifcopenshell.entity_instance] = set()
self.failed_entities: set[ifcopenshell.entity_instance] = set()
self.status = None
@@ -238,6 +239,7 @@ class Specification:
def reset_status(self):
self.applicable_entities.clear()
self.passed_entities: set[ifcopenshell.entity_instance] = set()
self.failed_entities: set[ifcopenshell.entity_instance] = set()
for facet in self.requirements:
facet.status = None
@@ -270,13 +272,19 @@ class Specification:
result = facet(element)
is_pass = bool(result)
if self.maxOccurs != 0: # This is a required or optional specification
if not is_pass:
if is_pass:
self.passed_entities.add(element)
facet.passed_entities.add(element)
else:
self.failed_entities.add(element)
facet.failures.append(FacetFailure(element=element, reason=str(result)))
else: # This is a prohibited specification
if is_pass:
self.failed_entities.add(element)
facet.failures.append(FacetFailure(element=element, reason=str(result)))
else:
self.passed_entities.add(element)
facet.passed_entities.add(element)
self.status = True
for facet in self.requirements:
+35 -9
View File
@@ -96,7 +96,8 @@ class ResultsRequirement(TypedDict):
value: str
description: str
status: bool
failed_entities: list[ResultsFailedEntity]
passed_entities: list[ResultsEntity]
failed_entities: list[ResultsEntity]
total_applicable: int
total_pass: int
total_fail: int
@@ -104,8 +105,8 @@ class ResultsRequirement(TypedDict):
# use different syntax because of the "class" key
ResultsFailedEntity = TypedDict(
"ResultsFailedEntity",
ResultsEntity = TypedDict(
"ResultsEntity",
{
"reason": str,
"element": str,
@@ -339,6 +340,7 @@ class Json(Reporter):
value=value,
description=requirement.to_string("requirement", specification, requirement),
status=requirement.status,
passed_entities=self.report_passed_entities(requirement),
failed_entities=self.report_failed_entities(requirement),
total_applicable=total_applicable,
total_pass=total_pass,
@@ -370,9 +372,27 @@ class Json(Reporter):
requirements=requirements,
)
def report_failed_entities(self, requirement: Facet) -> list[ResultsFailedEntity]:
def report_passed_entities(self, requirement: Facet) -> list[ResultsEntity]:
return [
ResultsFailedEntity(
ResultsEntity(
{
"element": e,
"element_type": ifcopenshell.util.element.get_type(e),
"class": e.is_a(),
"predefined_type": ifcopenshell.util.element.get_predefined_type(e),
"name": getattr(e, "Name", None),
"description": getattr(e, "Description", None),
"id": e.id(),
"global_id": getattr(e, "GlobalId", None),
"tag": getattr(e, "Tag", None),
}
)
for e in requirement.passed_entities
]
def report_failed_entities(self, requirement: Facet) -> list[ResultsEntity]:
return [
ResultsEntity(
{
"reason": f["reason"],
"element": f["element"],
@@ -413,11 +433,17 @@ class Html(Json):
entity_limit = 100
for spec in self.results["specifications"]:
for requirement in spec["requirements"]:
total = len(requirement["failed_entities"])
total_passed_entities = len(requirement["passed_entities"])
total_failed_entities = len(requirement["failed_entities"])
requirement["passed_entities"] = requirement["passed_entities"][0:entity_limit]
requirement["failed_entities"] = requirement["failed_entities"][0:entity_limit]
requirement["has_omitted"] = total > entity_limit
requirement["total_entities"] = total
requirement["total_omitted"] = total - entity_limit
requirement["total_failed_entities"] = total_failed_entities
requirement["total_omitted_failures"] = total_failed_entities - entity_limit
requirement["has_omitted_failures"] = total_failed_entities > entity_limit
requirement["total_passed_entities"] = total_passed_entities
requirement["total_omitted_passes"] = total_passed_entities - entity_limit
requirement["has_omitted_passes"] = total_passed_entities > entity_limit
def to_string(self) -> str:
import pystache
+42 -8
View File
@@ -59,10 +59,13 @@
.percent { text-align: left; padding-top: 5px; padding-left: 5px; padding-bottom: 5px; color: white; border-radius: 5px; white-space: nowrap; }
.pass { background-color: var(--green); }
.fail { background-color: var(--red); }
table { width: 100%; border-bottom: 2px solid var(--red); border-spacing: 0; border-radius: 5px; margin-top: 10px; }
th, td { padding: 5px; }
thead>tr { background-color: var(--red); font-weight: bold; color: #fff; }
tbody tr { border-bottom: 1px solid var(--red); }
table { width: 100%; border-spacing: 0; border-radius: 5px; margin-top: 10px; }
th, td { padding: 5px; color: #000; }
table.pass { border-bottom: 2px solid var(--green); }
table.fail { border-bottom: 2px solid var(--red); }
table thead>tr { font-weight: bold; color: #000; }
table.pass tbody tr { border-bottom: 1px solid var(--green); }
table.fail tbody tr { border-bottom: 1px solid var(--red); }
tbody tr:nth-child(odd) { background-color: rgba(1, 1, 1, 0.05); }
tbody tr:nth-child(even) { background-color: rgba(1, 1, 1, 0.1); }
tbody tr:hover { background-color: rgba(0, 0, 0, 0); }
@@ -130,8 +133,39 @@
<summary>
{{description}}
</summary>
{{#total_pass}}
<table class="pass">
<thead>
<tr>
<th>Class</th>
<th>PredefinedType</th>
<th>Name</th>
<th>Description</th>
<th>GlobalId</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{{#passed_entities}}
<tr>
<td>{{class}}</td>
<td>{{predefined_type}}</td>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{global_id}}</td>
<td>{{tag}}</td>
</tr>
{{/passed_entities}}
{{#has_omitted_passes}}
<tr>
<td colspan="7"> ... {{total_omitted_passes}} more passing elements not shown out of {{total_passed_entities}} total ...</td>
</tr>
{{/has_omitted_passes}}
</tbody>
</table>
{{/total_pass}}
{{#total_fail}}
<table>
<table class="fail">
<thead>
<tr>
<th>Class</th>
@@ -155,11 +189,11 @@
<td>{{tag}}</td>
</tr>
{{/failed_entities}}
{{#has_omitted}}
{{#has_omitted_failures}}
<tr>
<td colspan="7"> ... {{total_omitted}} more elements not shown out of {{total_entities}} total ...</td>
<td colspan="7"> ... {{total_omitted_failures}} more failing elements not shown out of {{total_failed_entities}} total ...</td>
</tr>
{{/has_omitted}}
{{/has_omitted_failures}}
</tbody>
</table>
{{/total_fail}}