2026-04-15 23:12:28 +01:00
|
|
|
"""Shared pytest fixtures for ifcurl tests."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import ifcopenshell.api.aggregate
|
|
|
|
|
import ifcopenshell.api.context
|
|
|
|
|
import ifcopenshell.api.geometry
|
|
|
|
|
import ifcopenshell.api.owner.settings
|
|
|
|
|
import ifcopenshell.api.project
|
|
|
|
|
import ifcopenshell.api.root
|
|
|
|
|
import ifcopenshell.api.spatial
|
|
|
|
|
import ifcopenshell.api.unit
|
|
|
|
|
import numpy as np
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# IFC model fixtures
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _suppress_owner(f):
|
2026-04-25 08:34:56 +01:00
|
|
|
ifcopenshell.api.owner.settings.get_user = lambda ifc: (
|
|
|
|
|
ifc.by_type("IfcPersonAndOrganization") or [None]
|
|
|
|
|
)[0]
|
|
|
|
|
ifcopenshell.api.owner.settings.get_application = lambda ifc: (
|
|
|
|
|
ifc.by_type("IfcApplication") or [None]
|
|
|
|
|
)[0]
|
2026-04-15 23:12:28 +01:00
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
|
def model_with_geometry():
|
|
|
|
|
"""IFC4 model with two walls that have geometric representations."""
|
|
|
|
|
f = ifcopenshell.api.project.create_file()
|
|
|
|
|
_suppress_owner(f)
|
|
|
|
|
|
2026-04-25 08:34:56 +01:00
|
|
|
project = ifcopenshell.api.root.create_entity(
|
|
|
|
|
f, ifc_class="IfcProject", name="TestProject"
|
|
|
|
|
)
|
2026-04-15 23:12:28 +01:00
|
|
|
ifcopenshell.api.unit.assign_unit(f)
|
|
|
|
|
|
|
|
|
|
site = ifcopenshell.api.root.create_entity(f, ifc_class="IfcSite", name="TestSite")
|
2026-04-25 08:34:56 +01:00
|
|
|
building = ifcopenshell.api.root.create_entity(
|
|
|
|
|
f, ifc_class="IfcBuilding", name="TestBuilding"
|
|
|
|
|
)
|
|
|
|
|
storey = ifcopenshell.api.root.create_entity(
|
|
|
|
|
f, ifc_class="IfcBuildingStorey", name="Ground Floor"
|
|
|
|
|
)
|
2026-04-15 23:12:28 +01:00
|
|
|
|
2026-04-25 08:34:56 +01:00
|
|
|
ifcopenshell.api.aggregate.assign_object(
|
|
|
|
|
f, products=[site], relating_object=project
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.aggregate.assign_object(
|
|
|
|
|
f, products=[building], relating_object=site
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.aggregate.assign_object(
|
|
|
|
|
f, products=[storey], relating_object=building
|
|
|
|
|
)
|
2026-04-15 23:12:28 +01:00
|
|
|
|
|
|
|
|
model_ctx = ifcopenshell.api.context.add_context(f, context_type="Model")
|
|
|
|
|
body = ifcopenshell.api.context.add_context(
|
2026-04-25 08:34:56 +01:00
|
|
|
f,
|
|
|
|
|
context_type="Model",
|
|
|
|
|
context_identifier="Body",
|
|
|
|
|
target_view="MODEL_VIEW",
|
|
|
|
|
parent=model_ctx,
|
2026-04-15 23:12:28 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
wall1 = ifcopenshell.api.root.create_entity(f, ifc_class="IfcWall", name="Wall001")
|
2026-04-25 08:34:56 +01:00
|
|
|
rep1 = ifcopenshell.api.geometry.add_wall_representation(
|
|
|
|
|
f, context=body, length=5, height=3, thickness=0.2
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.geometry.assign_representation(
|
|
|
|
|
f, product=wall1, representation=rep1
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.spatial.assign_container(
|
|
|
|
|
f, products=[wall1], relating_structure=storey
|
|
|
|
|
)
|
2026-04-15 23:12:28 +01:00
|
|
|
|
|
|
|
|
wall2 = ifcopenshell.api.root.create_entity(f, ifc_class="IfcWall", name="Wall002")
|
2026-04-25 08:34:56 +01:00
|
|
|
rep2 = ifcopenshell.api.geometry.add_wall_representation(
|
|
|
|
|
f, context=body, length=4, height=3, thickness=0.2
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.geometry.assign_representation(
|
|
|
|
|
f, product=wall2, representation=rep2
|
|
|
|
|
)
|
|
|
|
|
ifcopenshell.api.spatial.assign_container(
|
|
|
|
|
f, products=[wall2], relating_structure=storey
|
|
|
|
|
)
|
2026-04-15 23:12:28 +01:00
|
|
|
matrix = np.eye(4)
|
|
|
|
|
matrix[1, 3] = 3.0
|
|
|
|
|
ifcopenshell.api.geometry.edit_object_placement(f, product=wall2, matrix=matrix)
|
|
|
|
|
|
|
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Local git repo fixture
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
|
def local_ifc_repo(tmp_path_factory, model_with_geometry):
|
|
|
|
|
"""A local bare-ish git repo containing model.ifc committed at HEAD.
|
|
|
|
|
|
|
|
|
|
Returns a dict with keys:
|
|
|
|
|
path — absolute path to the working-tree repo
|
|
|
|
|
hexsha — the commit hexsha
|
|
|
|
|
bytes — raw IFC file bytes as committed
|
|
|
|
|
"""
|
|
|
|
|
import git as gitpkg
|
|
|
|
|
|
|
|
|
|
repo_dir = tmp_path_factory.mktemp("ifc_repo")
|
|
|
|
|
ifc_path = repo_dir / "model.ifc"
|
|
|
|
|
model_with_geometry.write(str(ifc_path))
|
|
|
|
|
|
|
|
|
|
repo = gitpkg.Repo.init(str(repo_dir))
|
|
|
|
|
with repo.config_writer() as cw:
|
|
|
|
|
cw.set_value("user", "name", "Test")
|
|
|
|
|
cw.set_value("user", "email", "test@example.com")
|
|
|
|
|
repo.index.add(["model.ifc"])
|
|
|
|
|
commit = repo.index.commit("Add model")
|
|
|
|
|
repo.create_tag("v1.0")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"path": str(repo_dir),
|
|
|
|
|
"hexsha": commit.hexsha,
|
|
|
|
|
"bytes": ifc_path.read_bytes(),
|
|
|
|
|
}
|