Files
IfcOpenShell/src/ifcquery/tests/test_relations.py
T
Bruno Postle 7cd40bf8cb Add ifcquery CLI tool for IFC model interrogation (#7845)
ifcquery is a new command-line tool for querying and inspecting IFC models. All output is JSON.

Subcommands:

    summary — schema version, entity counts, project metadata
    tree — full spatial hierarchy (Project → Site → Building → Storeys → Spaces → Elements)
    info <id> — deep inspection of any entity by step ID (attributes, psets, placement matrix, type, material)
    select <query> — filter elements using ifcopenshell selector syntax
    relations <id> — relationships for an element; --traverse up walks to IfcProject
    clash <id> — geometric intersection and clearance detection
    validate — schema/constraint validation; --rules adds EXPRESS checks
    schedule — work schedules with nested task trees
    cost — cost schedules with nested cost item trees
    schema <class> — IFC class documentation from the model's schema version
    plot — SVG plan drawing
    render — 3D geometry rendering
    contexts — geometric representation contexts
    materials — material assignments

Usage:

python3 -m ifcquery <file.ifc> <subcommand> [args]

Generated with the assistance of an AI coding tool.
2026-03-23 23:29:32 +00:00

159 lines
5.5 KiB
Python

# This file was generated with the assistance of an AI coding tool.
import json
import os
import subprocess
import sys
import tempfile
from ifcquery.relations import relations
class TestWallRelations:
def test_wall_has_container(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall)
assert result["id"] == wall.id()
assert result["type"] == "IfcWall"
assert result["hierarchy"]["container"]["type"] == "IfcBuildingStorey"
assert result["hierarchy"]["container"]["name"] == "Ground Floor"
def test_wall_has_parent(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall)
assert result["hierarchy"]["parent"]["type"] == "IfcBuildingStorey"
def test_wall_no_children(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall)
assert "children" not in result
def test_wall_empty_categories_omitted(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall)
assert "groups" not in result
assert "systems" not in result
assert "zones" not in result
assert "connections" not in result
assert "referenced_structures" not in result
class TestStoreyRelations:
def test_storey_has_contained(self, model):
storey = model.by_type("IfcBuildingStorey")[0]
result = relations(model, storey)
contained_types = {e["type"] for e in result["children"]["contained"]}
assert "IfcWall" in contained_types
assert "IfcSlab" in contained_types
def test_storey_has_aggregate_parent(self, model):
storey = model.by_type("IfcBuildingStorey")[0]
result = relations(model, storey)
assert result["hierarchy"]["aggregate"]["type"] == "IfcBuilding"
assert result["hierarchy"]["aggregate"]["name"] == "TestBuilding"
class TestProjectRelations:
def test_project_has_parts(self, model):
project = model.by_type("IfcProject")[0]
result = relations(model, project)
parts = result["children"]["parts"]
assert any(p["type"] == "IfcSite" for p in parts)
def test_project_no_hierarchy(self, model):
project = model.by_type("IfcProject")[0]
result = relations(model, project)
assert "hierarchy" not in result
class TestTraverseUp:
def test_wall_to_project(self, model):
wall = model.by_type("IfcWall")[0]
chain = relations(model, wall, traverse="up")
assert isinstance(chain, list)
assert chain[0]["type"] == "IfcWall"
assert chain[-1]["type"] == "IfcProject"
types = [e["type"] for e in chain]
assert "IfcBuildingStorey" in types
assert "IfcBuilding" in types
assert "IfcSite" in types
def test_project_traverse(self, model):
project = model.by_type("IfcProject")[0]
chain = relations(model, project, traverse="up")
assert len(chain) == 1
assert chain[0]["type"] == "IfcProject"
def test_storey_to_project(self, model):
storey = model.by_type("IfcBuildingStorey")[0]
chain = relations(model, storey, traverse="up")
assert chain[0]["type"] == "IfcBuildingStorey"
assert chain[-1]["type"] == "IfcProject"
assert len(chain) == 4 # storey -> building -> site -> project
class TestJsonSerializable:
def test_relations_serializable(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall)
json.dumps(result)
def test_traverse_serializable(self, model):
wall = model.by_type("IfcWall")[0]
result = relations(model, wall, traverse="up")
json.dumps(result)
class TestCLI:
@staticmethod
def _ifc_path(model):
f = tempfile.NamedTemporaryFile(suffix=".ifc", delete=False)
model.write(f.name)
f.close()
return f.name
def test_relations_json(self, model):
path = self._ifc_path(model)
try:
wall = model.by_type("IfcWall")[0]
result = subprocess.run(
[sys.executable, "-m", "ifcquery", path, "relations", str(wall.id())],
capture_output=True,
text=True,
)
assert result.returncode == 0
data = json.loads(result.stdout)
assert data["type"] == "IfcWall"
assert "hierarchy" in data
finally:
os.unlink(path)
def test_relations_traverse_up(self, model):
path = self._ifc_path(model)
try:
wall = model.by_type("IfcWall")[0]
result = subprocess.run(
[sys.executable, "-m", "ifcquery", path, "relations", str(wall.id()), "--traverse", "up"],
capture_output=True,
text=True,
)
assert result.returncode == 0
data = json.loads(result.stdout)
assert isinstance(data, list)
assert data[0]["type"] == "IfcWall"
assert data[-1]["type"] == "IfcProject"
finally:
os.unlink(path)
def test_relations_bad_id(self, model):
path = self._ifc_path(model)
try:
result = subprocess.run(
[sys.executable, "-m", "ifcquery", path, "relations", "999999"],
capture_output=True,
text=True,
)
assert result.returncode != 0
assert "Error" in result.stderr
finally:
os.unlink(path)