diff --git a/src/blenderbim/blenderbim/bim/operator.py b/src/blenderbim/blenderbim/bim/operator.py index d812b09bac..9c6fe239cc 100644 --- a/src/blenderbim/blenderbim/bim/operator.py +++ b/src/blenderbim/blenderbim/bim/operator.py @@ -650,7 +650,7 @@ class OverrideDelete(bpy.types.Operator): @classmethod def poll(cls, context): - return context.active_object is not None + return len(context.selected_objects) > 0 def execute(self, context): if IfcStore.get_file(): diff --git a/src/blenderbim/pytest.ini b/src/blenderbim/pytest.ini index 4f33eaa399..1d2f3021f7 100644 --- a/src/blenderbim/pytest.ini +++ b/src/blenderbim/pytest.ini @@ -2,3 +2,4 @@ markers = context owner + void diff --git a/src/blenderbim/test/bim/feature/void.feature b/src/blenderbim/test/bim/feature/void.feature new file mode 100644 index 0000000000..5d272779b6 --- /dev/null +++ b/src/blenderbim/test/bim/feature/void.feature @@ -0,0 +1,313 @@ +@void +Feature: Void + Covers voids and fillings. + +Scenario: Add an opening + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube + And the object "IfcWall/Cube" is selected + And additionally the object "Cube" is selected + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube" should display as "WIRE" + And the object "IfcWall/Cube" has a boolean difference by "IfcOpeningElement/Cube" + And the object "IfcWall/Cube" is voided by "Cube" + +Scenario: Add an opening on an opening + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And the object "IfcOpeningElement/Cube" is selected + And additionally the object "IfcOpeningElement/Cube.001" is selected + And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='IfcOpeningElement/Cube.001')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube" should display as "WIRE" + And the object "IfcOpeningElement/Cube" is not a void + Then the object "IfcOpeningElement/Cube.001" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube.001" should display as "WIRE" + Then the object "IfcOpeningElement/Cube.001" has no boolean difference by "IfcOpeningElement/Cube" + And the object "IfcOpeningElement/Cube.001" is not voided by "Cube" + +Scenario: Add an opening on a null object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I deselect all objects + And I press "bim.add_opening(opening='IfcOpeningElement/Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube" should display as "WIRE" + And the object "IfcOpeningElement/Cube" is not a void + +Scenario: Add an opening on a non IFC object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube" should display as "WIRE" + And the object "IfcOpeningElement/Cube" is not a void + And the object "Cube" is not an IFC element + +Scenario: Add an opening with a null object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I deselect all objects + And I press "bim.add_opening(obj='IfcWall/Cube')" + Then the object "IfcWall/Cube" is an "IfcWall" + And the object "IfcWall/Cube" is not voided + +Scenario: Add an opening to Element B with a void that already voids Element A + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='IfcWall/Cube.001')" + Then the object "IfcWall/Cube" is not voided + And the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" + And the object "IfcWall/Cube.001" is voided by "Cube" + And the object "IfcWall/Cube.001" has a boolean difference by "IfcOpeningElement/Cube" + +Scenario: Remove an opening + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube of size "1" at "1,0,0" + And the object "Cube" is selected + And additionally the object "IfcWall/Cube" is selected + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + And the variable "opening_id" is "IfcStore.get_file().by_type('IfcOpeningElement')[0].id()" + And I press "bim.remove_opening(opening_id={opening_id}, obj='IfcWall/Cube')" + Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" + And the object "IfcWall/Cube" is not voided by "Cube" + And the object "Cube" is not an IFC element + +Scenario: Remove a non dynamic opening manually + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube of size "1" at "1,0,0" + And the object "IfcWall/Cube" is selected + And additionally the object "Cube" is selected + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + And the object "IfcWall/Cube" is selected + And I press "bim.switch_representation(ifc_definition_id=86, should_reload=True)" + Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" + And the object "IfcWall/Cube" has "16" vertices + And the variable "opening_id" is "IfcStore.get_file().by_type('IfcOpeningElement')[0].id()" + When I press "bim.remove_opening(opening_id={opening_id}, obj='IfcWall/Cube')" + Then the object "IfcWall/Cube" has "8" vertices + And the object "Cube" is not an IFC element + +Scenario: Remove an opening using deletion + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube of size "1" at "1,0,0" + And the object "IfcWall/Cube" is selected + And additionally the object "Cube" is selected + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + And the object "IfcOpeningElement/Cube" is selected + And I delete the selected objects + Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" + And the object "IfcWall/Cube" is not voided by "Cube" + +Scenario: Remove and opening indirectly by deleting its building Element + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a cube of size "1" at "1,0,0" + And the object "IfcWall/Cube" is selected + And additionally the object "Cube" is selected + And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" + And the object "IfcWall/Cube" is selected + And I delete the selected objects + Then the object "Cube" is not an IFC element + +Scenario: Add a filling + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And the object "IfcOpeningElement/Cube" is selected + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcOpeningElement/Cube" should display as "WIRE" + And the object "IfcDoor/Cube" is an "IfcDoor" + And the void "IfcOpeningElement/Cube" is filled by "Cube" + +Scenario: Add a filling on a null object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And the object "IfcDoor/Cube" is selected + And I press "bim.add_filling(obj='IfcDoor/Cube')" + Then the object "IfcDoor/Cube" is an "IfcDoor" + And the object "IfcDoor/Cube" is not a filling + +Scenario: Add a filling on itself + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And the object "IfcOpeningElement/Cube" is selected + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcOpeningElement/Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the void "IfcOpeningElement/Cube" is not filled by "Cube" + And the object "IfcOpeningElement/Cube" is not a filling + +Scenario: Add a filling with a non IFC object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "IfcOpeningElement/Cube" is selected + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='Cube')" + Then the object "Cube" is not an IFC element + And the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the void "IfcOpeningElement/Cube" is not filled by "Cube" + +Scenario: Add a filling on a non IFC object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I press "bim.add_filling(opening='Cube', obj='IfcDoor/Cube')" + Then the object "Cube" is not an IFC element + And the object "IfcDoor/Cube" is an "IfcDoor" + +Scenario: Add a filling on Opening B with a filling that already fills Opening A + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I press "bim.add_filling(opening='IfcOpeningElement/Cube.001', obj='IfcDoor/Cube')" + Then the void "IfcOpeningElement/Cube" is not filled by "Cube" + And the void "IfcOpeningElement/Cube.001" is filled by "Cube" + +Scenario: Remove a filling + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" + And I press "bim.remove_filling(obj='IfcDoor/Cube')" + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the object "IfcDoor/Cube" is an "IfcDoor" + And the void "IfcOpeningElement/Cube" is not filled by "Cube" + +Scenario: Remove a filling which is not an IFC object + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I press "bim.remove_filling(obj='Cube')" + Then the object "Cube" is not an IFC element + +Scenario: Remove a filling which is not a filling + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And the object "IfcDoor/Cube" is not a filling + And I press "bim.remove_filling(obj='IfcDoor/Cube')" + Then the object "IfcDoor/Cube" is an "IfcDoor" + And the object "IfcDoor/Cube" is not a filling + +Scenario: Remove a filling which is null + Given an empty IFC project + When I press "bim.remove_filling()" + Then nothing happens + +Scenario: Remove a filling using deletion on the filling + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" + And the object "IfcDoor/Cube" is selected + And I delete the selected objects + Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" + And the void "IfcOpeningElement/Cube" is not filled by "Cube" + +Scenario: Remove a filling using deletion on the opening + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" + And I press "bim.assign_class" + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" + And I press "bim.assign_class" + And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" + And the object "IfcOpeningElement/Cube" is selected + And I delete the selected objects + Then the object "IfcDoor/Cube" is an "IfcDoor" + And the object "IfcDoor/Cube" is not a filling \ No newline at end of file diff --git a/src/blenderbim/test/bim/module/void/__init__.py b/src/blenderbim/test/bim/module/void/__init__.py deleted file mode 100644 index fb33323db9..0000000000 --- a/src/blenderbim/test/bim/module/void/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# BlenderBIM Add-on - OpenBIM Blender Add-on -# Copyright (C) 2021 Dion Moult -# -# This file is part of BlenderBIM Add-on. -# -# BlenderBIM Add-on is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BlenderBIM Add-on is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BlenderBIM Add-on. If not, see . diff --git a/src/blenderbim/test/bim/module/void/test_operator.py b/src/blenderbim/test/bim/module/void/test_operator.py deleted file mode 100644 index b5aa4b7863..0000000000 --- a/src/blenderbim/test/bim/module/void/test_operator.py +++ /dev/null @@ -1,404 +0,0 @@ -# BlenderBIM Add-on - OpenBIM Blender Add-on -# Copyright (C) 2021 Dion Moult -# -# This file is part of BlenderBIM Add-on. -# -# BlenderBIM Add-on is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BlenderBIM Add-on is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BlenderBIM Add-on. If not, see . - -import test.bim.bootstrap - - -class TestAddOpening(test.bim.bootstrap.NewFile): - @test.bim.bootstrap.scenario - def test_adding_an_opening(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube - And the object "IfcWall/Cube" is selected - And additionally the object "Cube" is selected - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube" should display as "WIRE" - And the object "IfcWall/Cube" has a boolean difference by "IfcOpeningElement/Cube" - And the object "IfcWall/Cube" is voided by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_adding_an_opening_on_an_opening(self): - """An opening can't legally be voided by another opening""" - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And the object "IfcOpeningElement/Cube" is selected - And additionally the object "IfcOpeningElement/Cube.001" is selected - And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='IfcOpeningElement/Cube.001')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube" should display as "WIRE" - And the object "IfcOpeningElement/Cube" is not a void - Then the object "IfcOpeningElement/Cube.001" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube.001" should display as "WIRE" - Then the object "IfcOpeningElement/Cube.001" has no boolean difference by "IfcOpeningElement/Cube" - And the object "IfcOpeningElement/Cube.001" is not voided by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_adding_an_opening_on_a_null_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I deselect all objects - And I press "bim.add_opening(opening='IfcOpeningElement/Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube" should display as "WIRE" - And the object "IfcOpeningElement/Cube" is not a void - """ - - @test.bim.bootstrap.scenario - def test_adding_an_opening_on_a_non_ifc_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube" should display as "WIRE" - And the object "IfcOpeningElement/Cube" is not a void - And the object "Cube" is not an IFC element - """ - - @test.bim.bootstrap.scenario - def test_adding_an_opening_with_a_null_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I deselect all objects - And I press "bim.add_opening(obj='IfcWall/Cube')" - Then the object "IfcWall/Cube" is an "IfcWall" - And the object "IfcWall/Cube" is not voided - """ - - @test.bim.bootstrap.scenario - def test_adding_an_opening_to_element_b_with_a_void_that_already_voids_element_a(self): - """An opening can legally void at most one element""" - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I press "bim.add_opening(opening='IfcOpeningElement/Cube', obj='IfcWall/Cube.001')" - Then the object "IfcWall/Cube" is not voided - And the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" - And the object "IfcWall/Cube.001" is voided by "Cube" - And the object "IfcWall/Cube.001" has a boolean difference by "IfcOpeningElement/Cube" - """ - - -class TestRemoveOpening(test.bim.bootstrap.NewFile): - @test.bim.bootstrap.scenario - def test_removing_an_opening_manually(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube of size "1" at "1,0,0" - And the object "Cube" is selected - And additionally the object "IfcWall/Cube" is selected - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - And the variable "opening_id" is "IfcStore.get_file().by_type('IfcOpeningElement')[0].id()" - And I press "bim.remove_opening(opening_id={opening_id}, obj='IfcWall/Cube')" - Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" - And the object "IfcWall/Cube" is not voided by "Cube" - And the object "Cube" is not an IFC element - """ - - @test.bim.bootstrap.scenario - def test_removing_a_non_dynamic_opening_manually(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube of size "1" at "1,0,0" - And the object "IfcWall/Cube" is selected - And additionally the object "Cube" is selected - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - And the object "IfcWall/Cube" is selected - And I press "bim.switch_representation(ifc_definition_id=86, should_reload=True)" - Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" - And the object "IfcWall/Cube" has "16" vertices - And the variable "opening_id" is "IfcStore.get_file().by_type('IfcOpeningElement')[0].id()" - When I press "bim.remove_opening(opening_id={opening_id}, obj='IfcWall/Cube')" - Then the object "IfcWall/Cube" has "8" vertices - And the object "Cube" is not an IFC element - """ - - @test.bim.bootstrap.scenario - def test_removing_an_opening_using_deletion(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube of size "1" at "1,0,0" - And the object "IfcWall/Cube" is selected - And additionally the object "Cube" is selected - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - And the object "IfcOpeningElement/Cube" is selected - And I delete the selected objects - Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube" - And the object "IfcWall/Cube" is not voided by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_removing_an_opening_indirectly_by_deleting_its_building_element(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" - And I press "bim.assign_class" - And I add a cube of size "1" at "1,0,0" - And the object "IfcWall/Cube" is selected - And additionally the object "Cube" is selected - And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')" - And the object "IfcWall/Cube" is selected - And I delete the selected objects - Then the object "Cube" is not an IFC element - """ - - -class TestAddFilling(test.bim.bootstrap.NewFile): - @test.bim.bootstrap.scenario - def test_adding_a_filling(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And the object "IfcOpeningElement/Cube" is selected - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcOpeningElement/Cube" should display as "WIRE" - And the object "IfcDoor/Cube" is an "IfcDoor" - And the void "IfcOpeningElement/Cube" is filled by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_adding_a_filling_on_null_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And the object "IfcDoor/Cube" is selected - And I press "bim.add_filling(obj='IfcDoor/Cube')" - Then the object "IfcDoor/Cube" is an "IfcDoor" - And the object "IfcDoor/Cube" is not a filling - """ - - @test.bim.bootstrap.scenario - def test_adding_a_filling_on_itself(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And the object "IfcOpeningElement/Cube" is selected - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcOpeningElement/Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the void "IfcOpeningElement/Cube" is not filled by "Cube" - And the object "IfcOpeningElement/Cube" is not a filling - """ - - @test.bim.bootstrap.scenario - def test_adding_a_filling_with_a_non_ifc_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "IfcOpeningElement/Cube" is selected - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='Cube')" - Then the object "Cube" is not an IFC element - And the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the void "IfcOpeningElement/Cube" is not filled by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_adding_a_filling_on_a_non_ifc_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I press "bim.add_filling(opening='Cube', obj='IfcDoor/Cube')" - Then the object "Cube" is not an IFC element - And the object "IfcDoor/Cube" is an "IfcDoor" - """ - - @test.bim.bootstrap.scenario - def test_adding_a_filling_on_opening_b_when_the_filling_alread_fills_element_a(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I press "bim.add_filling(opening='IfcOpeningElement/Cube.001', obj='IfcDoor/Cube')" - Then the void "IfcOpeningElement/Cube" is not filled by "Cube" - And the void "IfcOpeningElement/Cube.001" is filled by "Cube" - """ - - -class TestRemoveFilling(test.bim.bootstrap.NewFile): - @test.bim.bootstrap.scenario - def test_removing_a_filling(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" - And I press "bim.remove_filling(obj='IfcDoor/Cube')" - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the object "IfcDoor/Cube" is an "IfcDoor" - And the void "IfcOpeningElement/Cube" is not filled by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_removing_a_filling_which_is_not_an_ifc_object(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I press "bim.remove_filling(obj='Cube')" - Then the object "Cube" is not an IFC element - """ - - @test.bim.bootstrap.scenario - def test_removing_a_filling_which_is_not_a_filling(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I press "bim.remove_filling(obj='IfcDoor/Cube')" - Then the object "IfcDoor/Cube" is an "IfcDoor" - And the object "IfcDoor/Cube" is not a filling - """ - - @test.bim.bootstrap.scenario - def test_removing_a_filling_which_is_null(self): - return """ - Given an empty IFC project - And I press "bim.remove_filling()" - Then nothing interesting happens - """ - - @test.bim.bootstrap.scenario - def test_removing_a_filling_using_deletion_on_the_filling(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" - And the object "IfcDoor/Cube" is selected - And I delete the selected objects - Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" - And the void "IfcOpeningElement/Cube" is not filled by "Cube" - """ - - @test.bim.bootstrap.scenario - def test_removing_a_filling_using_deletion_on_the_opening(self): - return """ - Given an empty IFC project - And I add a cube - When the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement" - And I press "bim.assign_class" - And I add a cube - And the object "Cube" is selected - And I set "scene.BIMRootProperties.ifc_class" to "IfcDoor" - And I press "bim.assign_class" - And I press "bim.add_filling(opening='IfcOpeningElement/Cube', obj='IfcDoor/Cube')" - And the object "IfcOpeningElement/Cube" is selected - And I delete the selected objects - Then the object "IfcDoor/Cube" is an "IfcDoor" - And the object "IfcDoor/Cube" is not a filling - """ diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 944bc51110..6261d35bfe 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -42,6 +42,17 @@ def an_empty_ifc_project(): bpy.ops.bim.create_project() +@when("I add a cube") +@given("I add a cube") +def i_add_a_cube(): + bpy.ops.mesh.primitive_cube_add() + + +@when(parsers.parse('I add a cube of size "{size}" at "{location}"')) +def i_add_a_cube_of_size_size_at_location(size, location): + bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")]) + + @when(parsers.parse('I press "{operator}"')) def i_press_operator(operator): operator = replace_variables(operator) @@ -51,6 +62,45 @@ def i_press_operator(operator): exec(f"bpy.ops.{operator}()") +@when("I deselect all objects") +def i_deselect_all_objects(): + bpy.context.view_layer.objects.active = None + bpy.ops.object.select_all(action="DESELECT") + + +@when(parsers.parse('the object "{name}" is selected')) +def the_object_name_is_selected(name): + i_deselect_all_objects() + additionally_the_object_name_is_selected(name) + + +@when(parsers.parse('additionally the object "{name}" is selected')) +def additionally_the_object_name_is_selected(name): + obj = bpy.context.scene.objects.get(name) + if not obj: + assert False, 'The object "{name}" could not be selected' + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + + +@when(parsers.parse('I set "{prop}" to "{value}"')) +def i_set_prop_to_value(prop, value): + try: + eval(f"bpy.context.{prop}") + except: + assert False, "Property does not exist" + try: + exec(f'bpy.context.{prop} = "{value}"') + except: + exec(f"bpy.context.{prop} = {value}") + + +@when("I delete the selected objects") +def i_delete_the_selected_objects(): + bpy.ops.object.delete() + + +@then(parsers.parse('the variable "{key}" is "{value}"')) @when(parsers.parse('the variable "{key}" is "{value}"')) def the_variable_key_is_value(key, value): variables[key] = eval(replace_variables(value)) @@ -61,6 +111,127 @@ def nothing_happens(): pass +@then('the object "{name}" exists') +def the_object_name_exists(name) -> bpy.types.Object: + obj = bpy.data.objects.get(name) + if not obj: + assert False, f'The object "{name}" does not exist' + return obj + + +@then('an IFC file exists') +def an_ifc_file_exists(): + ifc = IfcStore.get_file() + if not ifc: + assert False, "No IFC file is available" + return ifc + + +@then(parsers.parse('the object "{name}" should display as "{mode}"')) +def the_object_name_should_display_as_mode(name, mode): + obj = the_object_name_exists(name) + assert obj.display_type == mode + + +@then(parsers.parse('the object "{name1}" has a boolean difference by "{name2}"')) +def the_object_name1_has_a_boolean_difference_by_name2(name1, name2): + obj = the_object_name_exists(name1) + has_mod = any((m for m in obj.modifiers if m.type == "BOOLEAN" and m.object and m.object.name == name2)) + assert has_mod, "No boolean found" + + +@then(parsers.parse('the object "{name1}" has no boolean difference by "{name2}"')) +def the_object_name1_has_no_boolean_difference_by_name2(name1, name2): + try: + the_object_name1_has_a_boolean_difference_by_name2(name1, name2) + except AssertionError: + return + assert False, "A boolean was found" + + +@then(parsers.parse('the object "{name}" is voided by "{void}"')) +def the_object_name_is_voided_by_void(name, void): + ifc = IfcStore.get_file() + element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) + assert any((rel for rel in element.HasOpenings if rel.RelatedOpeningElement.Name == void)), "No void found" + + +@then(parsers.parse('the object "{name}" is not voided by "{void}"')) +def the_object_name_is_not_voided_by_void(name, void): + try: + the_object_name_is_voided_by_void(name, void) + except AssertionError: + return + assert False, "A void was found" + + +@then(parsers.parse('the object "{name}" is not voided')) +def the_object_name_is_not_voided(name): + ifc = IfcStore.get_file() + element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) + assert not element.HasOpenings, "A void was found" + + +@then(parsers.parse('the object "{name}" is not a void')) +def the_object_name_is_a_void(name): + ifc = IfcStore.get_file() + obj = the_object_name_exists(name) + element = ifc.by_id(obj.BIMObjectProperties.ifc_definition_id) + assert any((element.VoidsElements)), "No void was found" + + +@then(parsers.parse('the object "{name}" is not a void')) +def the_object_name_is_not_a_void(name): + try: + the_object_name_is_a_void(name) + except AssertionError: + return + assert False, "A void was found" + + +@then(parsers.parse('the object "{name}" is an "{ifc_class}"')) +def the_object_name_is_an_ifc_class(name, ifc_class): + ifc = an_ifc_file_exists() + element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) + assert element.is_a(ifc_class), f'Object "{name}" is a {element.is_a()}' + + +@then(parsers.parse('the object "{name}" is not an IFC element')) +def the_object_name_is_not_an_ifc_element(name): + id = the_object_name_exists(name).BIMObjectProperties.ifc_definition_id + assert id == 0, f"The ID is {id}" + + +@then(parsers.parse('the object "{name}" has "{number}" vertices')) +def the_object_name_has_number_vertices(name, number): + total = len(the_object_name_exists(name).data.vertices) + assert total == int(number), f"We found {total} vertices" + + +@then(parsers.parse('the void "{name}" is filled by "{filling}"')) +def the_void_name_is_filled_by_filling(name, filling): + ifc = IfcStore.get_file() + element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) + assert any((rel.RelatedBuildingElement.Name == filling for rel in element.HasFillings)), "No filling found" + + +@then(parsers.parse('the void "{name}" is not filled by "{filling}"')) +def the_void_name_is_not_filled_by_filling(name, filling): + try: + the_void_name_is_filled_by_filling(name, filling) + except AssertionError: + return + assert False, "A filling was found" + + +@when(parsers.parse('the object "{name}" is not a filling')) +@then(parsers.parse('the object "{name}" is not a filling')) +def the_object_name_is_not_a_filling(name): + ifc = IfcStore.get_file() + element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id) + assert not any(element.FillsVoids), "A filling was found" + + @then(parsers.parse('"{prop}" is "{value}"')) def prop_is_value(prop, value): value = replace_variables(value)