Skip to content

ifctester: two false passes in prohibited property checks - #9274

Open
BIMvoice wants to merge 2 commits into
IfcOpenShell:v0.8.0from
BIMvoice:fix/ifctester-property-prohibited-empty
Open

ifctester: two false passes in prohibited property checks#9274
BIMvoice wants to merge 2 commits into
IfcOpenShell:v0.8.0from
BIMvoice:fix/ifctester-property-prohibited-empty

Conversation

@BIMvoice

@BIMvoice BIMvoice commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Two distinct false passes in Property.__call__'s prohibited branch. Both let a violation report as compliant, which is the worst direction for a checker to be wrong in, since nothing prompts anyone to look again.

1. Present but empty looks absent

Property.__call__ filters empty string values out before deciding whether the property exists:

elif prop is not None and prop != "":
    props[pset_name][self.baseName] = prop

then the prohibited branch reuses that filtered result. So a property that is present but empty looks absent, and a prohibited requirement silently passes.

property-facet.md is explicit that a prohibited property must not exist even if empty. That filtering is correct for REQUIRED and OPTIONAL, which legitimately treat empty as not populated, and reusing it for prohibited is what inverts a violation into a pass.

This is the Property sibling of #9273, which fixed the same shape in Attribute.

2. Cross-pset masking when a value is required

A separate bug in the same branch, only when value is set. With a pattern propertySet matching several psets, the loop shared one is_pass accumulator across all of them. An earlier pset lacking the property, or holding a mismatching value, drove is_pass to False permanently. A later pset holding exactly the prohibited value never flipped it back, because a value match only left is_pass unchanged rather than setting it.

Proven distinct rather than a restatement: tested against this PR's own previous head, with the fix for defect 1 already in place, and it still failed identically.

Fixed by checking each matching pset independently, split cleanly on whether self.value is set, so the two guards together cover every prohibited case before the shared loop runs. It reuses the existing single-pset value, dataType and unit matching by probing a shallow copy rather than duplicating that logic, which leaves the main loop untouched.

Sibling sweep

Every facet with a cardinality branch, not just this one:

facet existence check affected
Attribute filtered before check yes, fixed in #9273
Property filtered before check yes, this PR
Classification bool(references), no filtering no, already correct
PartOf is not None on relationship objects no
Material is not None from get_material no
Entity class name match, no value concept not applicable

Classification is the reference pattern: existence is checked before any filtering.

Overlap with other open PRs

Tests

Two fixture directories, property_prohibited_empty_string/ and property_prohibited_multi_pset/, each with its own test file. test_facet.py deliberately untouched, since several of our other open ifctester PRs already modify it.

Red before each fix, failing for the right reason in both cases:

test_empty_string_foo_property_fails_the_prohibition FAILED
E       assert True is False

test_a_later_matching_pset_with_the_value_fails_the_prohibition FAILED
E       assert True is False

Green after. Full ifctester suite 43/43.

Produced with AI assistance.

Property.__call__ filtered out empty string property values before
deciding pset/property existence, so a PROHIBITED property set to ""
(present but empty) was treated as absent and silently passed.
property-facet.md is explicit that a prohibited property "must not
exist... even if empty", matching the sibling fix already applied to
Attribute. Property now checks raw existence before that filtering,
only for the no-value prohibited case, leaving REQUIRED and OPTIONAL
untouched.

Generated with the assistance of an AI coding tool.
@BIMvoice BIMvoice self-assigned this Aug 6, 2026
A prohibited Property requirement with a pattern-matched propertySet
checked each matching pset independently with a fresh probe instead
of sharing one pass flag, so an earlier pset's absence or mismatch
could no longer mask a later pset that actually held the value.
Sibling of the empty-value fix already in this branch: that guard
covers prohibited without a value, this one covers prohibited with a
value, split cleanly on whether self.value is set.

Generated with the assistance of an AI coding tool.
@BIMvoice BIMvoice changed the title ifctester: prohibited property passes when empty ifctester: two false passes in prohibited property checks Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant