Capture SPF specific errors in validation test

This commit is contained in:
Thomas Krijnen
2025-09-10 09:40:20 +02:00
parent a50fc3184b
commit 231d047ce9
2 changed files with 44 additions and 13 deletions
@@ -311,17 +311,22 @@ def assert_valid(
return True
def log_internal_cpp_errors(f: ifcopenshell.file, filename: str, logger: Union[Logger, json_logger]) -> None:
def log_internal_cpp_errors(
f: Optional[ifcopenshell.file], filename: str, logger: Union[Logger, json_logger], log_content: Optional[str] = None
) -> None:
import re
import bisect
chr_offset_re = re.compile(r"at offset (\d+)\s*")
for_instance_re = re.compile(r"\s*for instance #(\d+)\s*")
log = ifcopenshell.get_log()
msgs = list(map(json.loads, filter(None, log.split("\n"))))
if log_content is None:
log_content = ifcopenshell.get_log()
msgs = list(map(json.loads, filter(None, log_content.split("\n"))))
chr_offsets = [chr_offset_re.findall(m["message"]) for m in msgs]
if chr_offsets:
instance_messages = [for_instance_re.findall(m["message"]) for m in msgs]
if chr_offsets or (instance_messages and f is None):
# The file is opened in binary mode, in order
# to correspond with the offsets reported by
# IfcOpenShell C++
@@ -342,15 +347,25 @@ def log_internal_cpp_errors(f: ifcopenshell.file, filename: str, logger: Union[L
else:
logger.error("For instance:\n %s\n%s", line, m)
instance_messages = [for_instance_re.findall(m["message"]) for m in msgs]
if instance_messages:
for instid, msg in zip(instance_messages, msgs):
if instid:
m = for_instance_re.sub("", msg["message"])
try:
inst = f[int(instid[0])]
except:
inst = None
if f is not None:
try:
inst = f[int(instid[0])]
except:
inst = None
else:
inst = next(
(
l
for l in lines
if l.strip().startswith(f"#{instid}".encode("ascii"))
and re.sub(r"\s+", "", l).startswith(f"#{instid}=".encode("ascii"))
),
None,
)
if isinstance(logger, json_logger):
logger.set_state("instance", inst)
logger.set_state("attribute", None)
@@ -629,7 +644,7 @@ def to_string_header_entity(header_entity):
def validate_ifc_header(
f: Union[ifcopenshell.file, ifcopenshell.simple_spf.file], logger: Union[Logger, json_logger]
) -> None:
# @todo now that we have the header schema compiled into ifcopenshell, and the
# @todo now that we have the header schema compiled into ifcopenshell, and the
# header instances being conventional entity instances, this specific logic should
# not be necessary anymore.
header: Union[ifcopenshell.entity_instance, types.SimpleNamespace] = f.header
@@ -637,7 +652,11 @@ def validate_ifc_header(
STRING_TYPE = "STRING (256)"
def log_error(
header_entity: Union[ifcopenshell.entity_instance, tuple], name: str, index: int, expected_type: str, provided_type: str
header_entity: Union[ifcopenshell.entity_instance, tuple],
name: str,
index: int,
expected_type: str,
provided_type: str,
) -> None:
logger.error(
(
@@ -652,7 +671,9 @@ def validate_ifc_header(
provided_type,
)
def validate_attribute(header_entity: ifcopenshell.entity_instance, name: str, index: int, *, aggregate: bool = False) -> None:
def validate_attribute(
header_entity: ifcopenshell.entity_instance, name: str, index: int, *, aggregate: bool = False
) -> None:
try:
value = getattr(header_entity, name)
except RuntimeError as _:
@@ -34,11 +34,21 @@ def test_file(file):
logger = ifcopenshell.validate.json_logger()
with tempfile.TemporaryDirectory() as d:
rocks = os.path.join(d, os.path.basename(file) + ".rdb")
ifcopenshell.convert_path_to_rocksdb(file, rocks, errors=os.path.basename(file) + ".json")
# certain errors such as attribute counts / invalid enumeration literals
# are only captured during parsing of SPF as they are not represented in
# rocksdb, these errors need to be captured during conversion to rocksdb
# but can still be handled ifcopenshell.validate logger.
ifcopenshell.get_log()
ifcopenshell.ifcopenshell_wrapper.set_log_format_json()
ifcopenshell.convert_path_to_rocksdb(file, rocks)
log = ifcopenshell.get_log()
try:
ifcopenshell.validate.validate(rocks, logger)
except ifcopenshell.SchemaError as e:
pytest.skip()
# ifcopenshell.validate.log_internal_cpp_errors(None, file, logger, log_content=log)
file = os.path.basename(file)
if file.startswith("fail-"):
assert len(logger.statements) > 0