From 008fa70345257aa09f0402852a4080c4a52d2d2c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 28 Jun 2024 21:15:53 +1000 Subject: [PATCH] Fix failing collector tests to align with new behaviour --- src/blenderbim/blenderbim/__init__.py | 2 +- src/blenderbim/blenderbim/core/tool.py | 1 - src/blenderbim/blenderbim/tool/collector.py | 7 +- src/blenderbim/test/bim/bootstrap.py | 12 ++ src/blenderbim/test/tool/test_collector.py | 212 +++++++------------- 5 files changed, 91 insertions(+), 143 deletions(-) diff --git a/src/blenderbim/blenderbim/__init__.py b/src/blenderbim/blenderbim/__init__.py index 5142ddfc5d..5abc678937 100644 --- a/src/blenderbim/blenderbim/__init__.py +++ b/src/blenderbim/blenderbim/__init__.py @@ -27,10 +27,10 @@ import sys IN_BLENDER = sys.modules.get("bpy", None) if IN_BLENDER: import bpy + import addon_utils import platform import traceback import webbrowser -import addon_utils from collections import deque bl_info = { diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 84433a7d38..99462f8c54 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -169,7 +169,6 @@ class Clash: @interface class Collector: def assign(cls, obj): pass - def sync(cls, obj): pass @interface diff --git a/src/blenderbim/blenderbim/tool/collector.py b/src/blenderbim/blenderbim/tool/collector.py index 5ca8b749dc..d903d60a8c 100644 --- a/src/blenderbim/blenderbim/tool/collector.py +++ b/src/blenderbim/blenderbim/tool/collector.py @@ -58,7 +58,11 @@ class Collector(blenderbim.core.tool.Collector): cls.link_to_collection_safe(obj, collection) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) cls.link_to_collection_safe(collection, project_obj.BIMObjectProperties.collection) - elif tool.Ifc.get_schema() != "IFC2X3" and element.is_a("IfcSpatialElement"): + elif ( + tool.Ifc.get_schema() != "IFC2X3" + and element.is_a("IfcSpatialElement") + and not element.is_a("IfcSpatialZone") + ): if collection := cls._create_own_collection(obj): cls.link_to_collection_safe(obj, collection) project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) @@ -103,6 +107,7 @@ class Collector(blenderbim.core.tool.Collector): def _create_own_collection(cls, obj: bpy.types.Object) -> bpy.types.Collection: """get or create own collection for the element""" if obj.BIMObjectProperties.collection: + obj.BIMObjectProperties.collection.name = obj.name return collection = bpy.data.collections.new(obj.name) obj.BIMObjectProperties.collection = collection diff --git a/src/blenderbim/test/bim/bootstrap.py b/src/blenderbim/test/bim/bootstrap.py index 18e646dd7e..460c4c91d5 100644 --- a/src/blenderbim/test/bim/bootstrap.py +++ b/src/blenderbim/test/bim/bootstrap.py @@ -57,6 +57,18 @@ class NewIfc: bpy.ops.bim.create_project() +class NewIfc4X3: + @pytest.fixture(autouse=True) + def setup(self): + IfcStore.purge() + bpy.ops.wm.read_homefile(app_template="") + bpy.data.batch_remove(bpy.data.objects) + bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True) + blenderbim.bim.handler.load_post(None) + bpy.context.scene.BIMProjectProperties.export_schema = "IFC4X3_ADD2" + bpy.ops.bim.create_project() + + def scenario(function): def subfunction(self): run(function(self)) diff --git a/src/blenderbim/test/tool/test_collector.py b/src/blenderbim/test/tool/test_collector.py index f9bf18d0f1..a8d1fd5eb8 100644 --- a/src/blenderbim/test/tool/test_collector.py +++ b/src/blenderbim/test/tool/test_collector.py @@ -23,7 +23,7 @@ import ifcopenshell.util.element import blenderbim.core.tool import blenderbim.tool as tool from blenderbim.tool.collector import Collector as subject -from test.bim.bootstrap import NewFile +from test.bim.bootstrap import NewFile, NewIfc, NewIfc4X3 class TestImplementsTool(NewFile): @@ -31,9 +31,8 @@ class TestImplementsTool(NewFile): assert isinstance(subject(), blenderbim.core.tool.Collector) -class TestAssign(NewFile): - def test_in_decomposition_mode_walls_are_placed_in_its_spatial_collection(self): - bpy.ops.bim.create_project() +class TestAssign(NewIfc): + def test_walls_are_placed_in_its_spatial_collection(self): wall_obj = bpy.data.objects.new("Object", None) wall_element = tool.Ifc.get().createIfcWall() tool.Ifc.link(wall_element, wall_obj) @@ -45,21 +44,19 @@ class TestAssign(NewFile): relating_structure=tool.Ifc.get().by_type("IfcSite")[0], ) subject.assign(wall_obj) - assert len(wall_obj.users_collection) == 1 + assert len(wall_obj.users_collection) == 2 assert "IfcSite" in wall_obj.users_collection[0].name - def test_in_decomposition_mode_walls_are_placed_in_the_project_if_not_decomposes(self): - bpy.ops.bim.create_project() + def test_walls_are_unsorted_if_not_decomposes(self): wall_obj = bpy.data.objects.new("Object", None) wall_element = tool.Ifc.get().createIfcWall() tool.Ifc.link(wall_element, wall_obj) bpy.context.scene.collection.objects.link(wall_obj) subject.assign(wall_obj) - assert len(wall_obj.users_collection) == 1 - assert "IfcProject" in wall_obj.users_collection[0].name + assert len(wall_obj.users_collection) == 2 + assert "Unsorted" in wall_obj.users_collection[0].name - def test_in_decomposition_mode_spatial_structure_elements_are_placed_in_a_collection_of_the_same_name(self): - bpy.ops.bim.create_project() + def test_spatial_structure_elements_are_placed_in_a_collection_of_the_same_name(self): space_obj = bpy.data.objects.new("IfcSpace/Name", None) space_element = tool.Ifc.get().createIfcSpace() tool.Ifc.link(space_element, space_obj) @@ -71,11 +68,10 @@ class TestAssign(NewFile): products=[space_element], ) subject.assign(space_obj) - assert len(space_obj.users_collection) == 1 + assert len(space_obj.users_collection) == 2 assert space_obj.users_collection[0].name == space_obj.name - def test_in_decomposition_mode_multiple_assigns_do_not_create_duplicate_spatial_structure_collections(self): - bpy.ops.bim.create_project() + def test_multiple_assigns_do_not_create_duplicate_spatial_structure_collections(self): space_obj = bpy.data.objects.new("IfcSpace/Name", None) space_element = tool.Ifc.get().createIfcSpace() tool.Ifc.link(space_element, space_obj) @@ -93,8 +89,7 @@ class TestAssign(NewFile): assert not bpy.data.collections.get("IfcSpace/Name.001") assert not bpy.data.collections.get("IfcSite/My Site.001") - def test_in_decomposition_mode_spatial_zone_elements_are_not_placed_in_a_collection_of_the_same_name(self): - bpy.ops.bim.create_project() + def test_spatial_zone_elements_are_not_placed_in_a_collection_of_the_same_name(self): space_obj = bpy.data.objects.new("IfcSpaceZone/Name", None) space_element = tool.Ifc.get().createIfcSpatialZone() tool.Ifc.link(space_element, space_obj) @@ -106,38 +101,48 @@ class TestAssign(NewFile): products=[space_element], ) subject.assign(space_obj) - assert len(space_obj.users_collection) == 1 + assert len(space_obj.users_collection) == 2 assert space_obj.users_collection[0].name != space_obj.name - def test_in_decomposition_mode_aggregates_are_placed_in_a_collection_of_the_same_name(self): - bpy.ops.bim.create_project() + def test_aggregates_are_also_placed_in_their_container(self): element_obj = bpy.data.objects.new("IfcElementAssembly/Name", None) element = tool.Ifc.get().createIfcElementAssembly() subelement_obj = bpy.data.objects.new("IfcBeam/Name", None) subelement = tool.Ifc.get().createIfcBeam() tool.Ifc.link(element, element_obj) + tool.Ifc.link(subelement, subelement_obj) bpy.context.scene.collection.objects.link(element_obj) + bpy.context.scene.collection.objects.link(subelement_obj) ifcopenshell.api.run( "aggregate.assign_object", tool.Ifc.get(), relating_object=element, products=[subelement], ) + ifcopenshell.api.run( + "spatial.assign_container", + tool.Ifc.get(), + products=[element], + relating_structure=tool.Ifc.get().by_type("IfcSite")[0], + ) subject.assign(element_obj) - assert len(element_obj.users_collection) == 1 - assert element_obj.users_collection[0].name == element_obj.name + subject.assign(subelement_obj) + assert len(element_obj.users_collection) == 2 + assert len(subelement_obj.users_collection) == 2 + assert "IfcSite" in element_obj.users_collection[0].name + assert "IfcSite" in subelement_obj.users_collection[0].name - def test_in_decomposition_mode_projects_are_placed_in_a_collection_of_the_same_name(self): + def test_projects_are_placed_in_a_collection_of_the_same_name(self): tool.Ifc.set(ifcopenshell.file()) element_obj = bpy.data.objects.new("IfcProject/Name", None) element = tool.Ifc.get().createIfcProject() tool.Ifc.link(element, element_obj) bpy.context.scene.collection.objects.link(element_obj) subject.assign(element_obj) - assert len(element_obj.users_collection) == 1 + assert len(element_obj.users_collection) == 2 assert element_obj.users_collection[0].name == element_obj.name - def test_in_decomposition_mode_multiple_assigns_do_not_create_duplicate_collections(self): + def test_multiple_assigns_do_not_create_duplicate_collections(self): tool.Ifc.set(ifcopenshell.file()) element_obj = bpy.data.objects.new("IfcProject/Name", None) element = tool.Ifc.get().createIfcProject() @@ -148,12 +153,11 @@ class TestAssign(NewFile): assert bpy.data.collections.get("IfcProject/Name") assert not bpy.data.collections.get("IfcProject/Name.001") - def test_in_decomposition_mode_existing_collections_are_reassigned_to_the_correct_place_in_the_hierarchy(self): - bpy.ops.bim.create_project() + def test_own_collections_are_retained_and_name_synced(self): space_obj = bpy.data.objects.new("IfcSpace/Name", None) space_element = tool.Ifc.get().createIfcSpace() tool.Ifc.link(space_element, space_obj) - space_collection = bpy.data.collections.new("IfcSpace/Name") + space_collection = bpy.data.collections.new("Foobar") bpy.context.scene.collection.children.link(space_collection) space_obj.BIMObjectProperties.collection = space_collection space_collection.objects.link(space_obj) @@ -164,72 +168,32 @@ class TestAssign(NewFile): products=[space_element], ) subject.assign(space_obj) - assert bpy.context.scene.collection.children.find(space_collection.name) == -1 - assert bpy.data.collections.get("IfcSite/My Site").children.find(space_collection.name) != -1 + assert bpy.context.scene.collection.children.find(space_collection.name) != -1 + assert bpy.data.collections.get("IfcSite/My Site").children.find(space_collection.name) == -1 + assert bpy.data.collections.get("IfcProject/My Project").children.find(space_collection.name) == -1 + assert bpy.context.scene.collection.children.find(space_collection.name) != -1 + assert space_collection.objects.find(space_obj.name) != -1 + assert space_collection.name == "IfcSpace/Name" - def test_in_decomposition_mode_aggregates_subelements_are_placed_in_the_aggregates_collection(self): - bpy.ops.bim.create_project() - element_obj = bpy.data.objects.new("IfcElementAssembly/Name", None) - element = tool.Ifc.get().createIfcElementAssembly() - subelement_obj = bpy.data.objects.new("IfcBeam/Name", None) - subelement = tool.Ifc.get().createIfcBeam() - tool.Ifc.link(element, element_obj) - tool.Ifc.link(subelement, subelement_obj) - bpy.context.scene.collection.objects.link(element_obj) - ifcopenshell.api.run( - "aggregate.assign_object", - tool.Ifc.get(), - relating_object=element, - products=[subelement], - ) - subject.assign(element_obj) - subject.assign(subelement_obj) - assert subelement_obj.users_collection[0].name == element_obj.name - - def test_in_decomposition_mode_aggregates_subelements_are_placed_in_the_spatial_collection_as_a_fallback(self): - # The aggregate object may not exist in all scenarios, such as when it is filtered out - bpy.ops.bim.create_project() - element = tool.Ifc.get().createIfcElementAssembly() - subelement_obj = bpy.data.objects.new("IfcBeam/Name", None) - subelement = tool.Ifc.get().createIfcBeam() - tool.Ifc.link(subelement, subelement_obj) - ifcopenshell.api.run( - "spatial.assign_container", - tool.Ifc.get(), - products=[element], - relating_structure=tool.Ifc.get().by_type("IfcSite")[0], - ) - ifcopenshell.api.run( - "aggregate.assign_object", - tool.Ifc.get(), - relating_object=element, - products=[subelement], - ) - subject.assign(subelement_obj) - assert subelement_obj.users_collection[0].name == "IfcSite/My Site" - - def test_in_decomposition_mode_types_are_placed_in_the_types_collection(self): - bpy.ops.bim.create_project() + def test_types_are_placed_in_the_types_collection(self): element_obj = bpy.data.objects.new("IfcWallType/Name", None) element = tool.Ifc.get().createIfcWallType() tool.Ifc.link(element, element_obj) bpy.context.scene.collection.objects.link(element_obj) subject.assign(element_obj) - assert element_obj.users_collection[0].name == "Types" - assert bpy.data.collections.get("IfcProject/My Project").children.get("Types") + assert element_obj.users_collection[0].name == "IfcTypeProduct" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcTypeProduct") - def test_in_decomposition_mode_openings_are_placed_in_the_openings_collection(self): - bpy.ops.bim.create_project() + def test_openings_are_placed_in_the_openings_collection(self): element_obj = bpy.data.objects.new("IfcOpeningElement/Name", None) element = tool.Ifc.get().createIfcOpeningElement() tool.Ifc.link(element, element_obj) bpy.context.scene.collection.objects.link(element_obj) subject.assign(element_obj) - assert element_obj.users_collection[0].name == "IfcOpeningElements" - assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcOpeningElements") + assert element_obj.users_collection[0].name == "IfcOpeningElement" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcOpeningElement") - def test_in_decomposition_mode_grids_are_placed_in_their_own_collection(self): - bpy.ops.bim.create_project() + def test_grids_are_placed_in_their_container(self): element_obj = bpy.data.objects.new("IfcGrid/Name", None) element = tool.Ifc.get().createIfcGrid() tool.Ifc.link(element, element_obj) @@ -241,11 +205,9 @@ class TestAssign(NewFile): ) bpy.context.scene.collection.objects.link(element_obj) subject.assign(element_obj) - assert element_obj.users_collection[0].name == "IfcGrid/Name" - assert bpy.data.collections.get("IfcSite/My Site").children.get("IfcGrid/Name") + assert element_obj.users_collection[0].name == "IfcSite/My Site" - def test_in_decomposition_mode_grids_axes_are_placed_in_an_axis_collection_of_the_grid(self): - bpy.ops.bim.create_project() + def test_grids_axes_are_placed_in_the_grids_container(self): element_obj = bpy.data.objects.new("IfcGrid/Name", None) axis_obj = bpy.data.objects.new("IfcGrid/Name", None) axis = tool.Ifc.get().createIfcGridAxis() @@ -261,11 +223,9 @@ class TestAssign(NewFile): bpy.context.scene.collection.objects.link(element_obj) subject.assign(element_obj) subject.assign(axis_obj) - assert axis_obj.users_collection[0].name == "UAxes" - assert bpy.data.collections.get("IfcGrid/Name").children.get("UAxes") + assert axis_obj.users_collection[0].name == "IfcSite/My Site" - def test_in_decomposition_mode_drawings_are_placed_in_a_group_in_a_views_collection(self): - bpy.ops.bim.create_project() + def test_drawings_are_placed_in_their_own_collection(self): element_obj = bpy.data.objects.new("IfcAnnotation/DRAWING", None) element = tool.Ifc.get().createIfcAnnotation(ObjectType="DRAWING") tool.Ifc.link(element, element_obj) @@ -276,11 +236,10 @@ class TestAssign(NewFile): subject.assign(element_obj) assert element_obj.users_collection[0].name == "IfcAnnotation/DRAWING" - assert bpy.data.collections.get("Views").children.get("IfcAnnotation/DRAWING") - assert bpy.data.collections.get("IfcProject/My Project").children.get("Views") + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcAnnotation/DRAWING") - def test_in_decomposition_mode_annotations_are_placed_in_a_group_in_a_views_collection(self): - self.test_in_decomposition_mode_drawings_are_placed_in_a_group_in_a_views_collection() + def test_annotations_are_placed_in_their_drawings_collection(self): + self.test_drawings_are_placed_in_their_own_collection() ifc_file = tool.Ifc.get() element_obj = bpy.data.objects.new("IfcAnnotation/Name", None) @@ -293,63 +252,36 @@ class TestAssign(NewFile): subject.assign(element_obj) assert element_obj.users_collection[0].name == "IfcAnnotation/DRAWING" - def test_in_decomposition_mode_structural_members_are_placed_in_a_members_collection(self): - bpy.ops.bim.create_project() + def test_structural_members_are_placed_in_a_members_collection(self): element_obj = bpy.data.objects.new("IfcStructuralCurveMember/Name", None) element = tool.Ifc.get().createIfcStructuralCurveMember() tool.Ifc.link(element, element_obj) subject.assign(element_obj) - assert element_obj.users_collection[0].name == "Members" - assert bpy.data.collections.get("StructuralItems").children.get("Members") - assert bpy.data.collections.get("IfcProject/My Project").children.get("StructuralItems") + assert element_obj.users_collection[0].name == "IfcStructuralItem" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcStructuralItem") - def test_in_decomposition_mode_structural_connections_are_placed_in_a_connections_collection(self): - bpy.ops.bim.create_project() + def test_structural_connections_are_placed_in_a_connections_collection(self): element_obj = bpy.data.objects.new("IfcStructuralCurveConnection/Name", None) element = tool.Ifc.get().createIfcStructuralCurveConnection() tool.Ifc.link(element, element_obj) subject.assign(element_obj) - assert element_obj.users_collection[0].name == "Connections" - assert bpy.data.collections.get("StructuralItems").children.get("Connections") - assert bpy.data.collections.get("IfcProject/My Project").children.get("StructuralItems") + assert element_obj.users_collection[0].name == "IfcStructuralItem" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcStructuralItem") -class TestSync(NewFile): - def test_doing_nothing_if_the_object_has_no_collection(self): - bpy.ops.bim.create_project() - wall_obj = bpy.data.objects.new("Object", None) - wall_element = tool.Ifc.get().createIfcWall() - tool.Ifc.link(wall_element, wall_obj) - subject.sync(wall_obj) - assert not wall_obj.users_collection +class TestAssignIFC4X3(NewIfc4X3): + def test_linear_positioning_elements_are_placed_in_a_special_collection(self): + element_obj = bpy.data.objects.new("Name", None) + element = tool.Ifc.get().createIfcAlignment() + tool.Ifc.link(element, element_obj) + subject.assign(element_obj) + assert element_obj.users_collection[0].name == "IfcLinearPositioningElement" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcLinearPositioningElement") - def test_in_decomposition_mode_elements_can_be_in_spatial_containers(self): - bpy.ops.bim.create_project() - wall_obj = bpy.data.objects.new("Object", None) - wall_element = tool.Ifc.get().createIfcWall() - tool.Ifc.link(wall_element, wall_obj) - bpy.data.collections.get("IfcSite/My Site").objects.link(wall_obj) - subject.sync(wall_obj) - assert ifcopenshell.util.element.get_container(wall_element).is_a("IfcSite") - - def test_in_decomposition_mode_elements_can_aggregate(self): - bpy.ops.bim.create_project() - obj = bpy.data.objects.new("IfcBuildingStorey/Name", None) - col = bpy.data.collections.new("IfcBuildingStorey/Name") - obj.BIMObjectProperties.collection = col - col.BIMCollectionProperties.obj = obj - element = tool.Ifc.get().createIfcBuildingStorey(Name="Name") - tool.Ifc.link(element, obj) - bpy.data.collections.get("IfcBuilding/My Building").children.link(col) - col.objects.link(obj) - subject.sync(obj) - assert ifcopenshell.util.element.get_aggregate(element).is_a("IfcBuilding") - - def test_openings_are_never_contained(self): - bpy.ops.bim.create_project() - obj = bpy.data.objects.new("Object", None) - element = tool.Ifc.get().createIfcOpeningElement() - tool.Ifc.link(element, obj) - bpy.data.collections.get("IfcSite/My Site").objects.link(obj) - subject.sync(obj) - assert ifcopenshell.util.element.get_container(element) is None + def test_referents_are_placed_in_a_special_collection(self): + element_obj = bpy.data.objects.new("Name", None) + element = tool.Ifc.get().createIfcReferent() + tool.Ifc.link(element, element_obj) + subject.assign(element_obj) + assert element_obj.users_collection[0].name == "IfcReferent" + assert bpy.data.collections.get("IfcProject/My Project").children.get("IfcReferent")