Fix #1727. BIMTester now runs within the BlenderBIM Add-on, implement attribute checking in IDS, and fix some minor IDS bugs.

This commit is contained in:
Dion Moult
2021-09-08 20:16:39 +10:00
parent b9084ad0f5
commit d0eb716738
10 changed files with 129 additions and 61 deletions
+13
View File
@@ -33,11 +33,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py37h5f1835d_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py37h5f1835d_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/30/c0/d0526314971fc661b083ab135747dc68446a3022686da8c16d25fcf6ef07/lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl
endif endif
ifeq ($(PYVERSION), py39) ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
endif endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2 ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2 OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2
@@ -49,11 +51,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py37h2d7f23a_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py37h2d7f23a_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/1e/3e/f0abc15d5dac50939bccc589aae336d5ead4c72e7ad1039a2e0f3630ea92/lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl
endif endif
ifeq ($(PYVERSION), py39) ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
endif endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2 ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2 OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2
@@ -65,11 +69,13 @@ ifeq ($(PYVERSION), py37)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py37h839d6b1_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py37h839d6b1_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/9e/5e/171ee9d40a600f565fe691ec5bf7596247ec62cfb2edc00c91afe8ea837b/lxml-4.6.3-cp37-cp37m-win_amd64.whl
endif endif
ifeq ($(PYVERSION), py39) ifeq ($(PYVERSION), py39)
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2 HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2 EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2 BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
endif endif
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2 ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2 OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2
@@ -350,6 +356,13 @@ endif
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
rm -rf dist/working rm -rf dist/working
# Required by ids
mkdir dist/working
cd dist/working && wget $(LXML_URL)
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
cp -r dist/working/lxml dist/blenderbim/libs/site/packages/
rm -rf dist/working
# Required by behave # Required by behave
mkdir dist/working mkdir dist/working
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
@@ -21,7 +21,6 @@ import os
import bpy import bpy
import tempfile import tempfile
import webbrowser import webbrowser
import ifcopenshell
try: try:
import bimtester import bimtester
@@ -31,7 +30,6 @@ except:
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.") print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
from pathlib import Path from pathlib import Path
from itertools import cycle
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -42,7 +40,7 @@ class ExecuteBIMTester(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BimTesterProperties props = context.scene.BimTesterProperties
return props.ifc_file and props.feature return (props.ifc_file or props.should_load_from_memory) and props.feature
def execute(self, context): def execute(self, context):
props = context.scene.BimTesterProperties props = context.scene.BimTesterProperties
@@ -243,19 +241,3 @@ class QAHelper:
is_in_scenario = False is_in_scenario = False
destination.write(source_line) destination.write(source_line)
os.remove(filename + "~") os.remove(filename + "~")
colour_list = [
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
(0.699, 0.876, 0.54, 1),
(0.199, 0.629, 0.174, 1),
(0.983, 0.605, 0.602, 1),
(0.89, 0.101, 0.112, 1),
(0.989, 0.751, 0.427, 1),
(0.986, 0.497, 0.1, 1),
(0.792, 0.699, 0.839, 1),
(0.414, 0.239, 0.603, 1),
(0.993, 0.999, 0.6, 1),
(0.693, 0.349, 0.157, 1),
]
+23 -3
View File
@@ -17,14 +17,19 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import re import re
import bpy import bpy
import pytest import pytest
import webbrowser
import blenderbim import blenderbim
import ifcopenshell import ifcopenshell
import ifcopenshell.util.representation import ifcopenshell.util.representation
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
# Monkey-patch webbrowser opening since we want to test headlessly
webbrowser.open = lambda x: True
class NewFile: class NewFile:
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -64,8 +69,15 @@ def the_object_name_is_selected(name):
obj.select_set(True) obj.select_set(True)
def i_select_value_in_prop(value, prop): def i_set_prop_to_value(prop, value):
exec(f'bpy.context.{prop} = "{value}"') try:
exec(f'bpy.context.{prop} = "{value}"')
except:
exec(f"bpy.context.{prop} = {value}")
def i_enable_prop(prop):
exec(f"bpy.context.{prop} = True")
def i_press_operator(operator): def i_press_operator(operator):
@@ -136,11 +148,17 @@ def the_object_name1_and_name2_are_different_elements(name1, name2):
assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}" assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}"
def the_file_name_should_contain_value(name, value):
with open(name, "r") as f:
assert value in f.read()
definitions = { definitions = {
"an empty IFC project": an_empty_ifc_project, "an empty IFC project": an_empty_ifc_project,
"I add a cube": i_add_a_cube, "I add a cube": i_add_a_cube,
'the object "(.*)" is selected': the_object_name_is_selected, 'the object "(.*)" is selected': the_object_name_is_selected,
'I select "(.*)" in "(.*)"': i_select_value_in_prop, 'I set "(.*)" to "(.*)"': i_set_prop_to_value,
'I enable "(.*)"': i_enable_prop,
'I press "(.*)"': i_press_operator, 'I press "(.*)"': i_press_operator,
'the object "(.*)" is an "(.*)"': the_object_name_is_an_ifc_class, 'the object "(.*)" is an "(.*)"': the_object_name_is_an_ifc_class,
'the object "(.*)" is in the collection "(.*)"': the_object_name_is_in_the_collection_collection, 'the object "(.*)" is in the collection "(.*)"': the_object_name_is_in_the_collection_collection,
@@ -151,6 +169,7 @@ definitions = {
'the object "(.*)" is contained in "(.*)"': the_object_name_is_contained_in_container_name, 'the object "(.*)" is contained in "(.*)"': the_object_name_is_contained_in_container_name,
"I duplicate the selected objects": i_duplicate_the_selected_objects, "I duplicate the selected objects": i_duplicate_the_selected_objects,
'the object "(.*)" and "(.*)" are different elements': the_object_name1_and_name2_are_different_elements, 'the object "(.*)" and "(.*)" are different elements': the_object_name1_and_name2_are_different_elements,
'the file "(.*)" should contain "(.*)"': the_file_name_should_contain_value,
} }
@@ -158,6 +177,7 @@ definitions = {
def run(scenario): def run(scenario):
for line in scenario.split("\n"): for line in scenario.split("\n"):
line = line.strip() line = line.strip()
line = line.replace("{cwd}", os.getcwd())
if not line: if not line:
continue continue
match = None match = None
@@ -0,0 +1,31 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
import test.bim.bootstrap
class TestExecuteBIMTester(test.bim.bootstrap.NewFile):
@test.bim.bootstrap.scenario
def test_executing_bimtester(self):
return """
Given an empty IFC project
When I enable "scene.BimTesterProperties.should_load_from_memory"
And I set "scene.BimTesterProperties.feature" to "{cwd}/test/files/sample-ids.xml"
And I press "bim.execute_bim_tester"
Then the file "{cwd}/test/files/sample-ids.xml.html" should contain "Tests passed: <strong>1 / 1</strong> (100%)"
"""
@@ -26,7 +26,7 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
Given an empty IFC project Given an empty IFC project
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class" And I press "bim.assign_class"
Then the object "IfcWall/Cube" is an "IfcWall" Then the object "IfcWall/Cube" is an "IfcWall"
And the object "IfcWall/Cube" is in the collection "Collection" And the object "IfcWall/Cube" is in the collection "Collection"
@@ -39,8 +39,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
Given an empty IFC project Given an empty IFC project
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And I select "IfcElementType" in "scene.BIMRootProperties.ifc_product" And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I select "IfcWallType" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class" And I press "bim.assign_class"
Then the object "IfcWallType/Cube" is an "IfcWallType" Then the object "IfcWallType/Cube" is an "IfcWallType"
And the object "IfcWallType/Cube" is in the collection "Types" And the object "IfcWallType/Cube" is in the collection "Types"
@@ -53,8 +53,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
Given an empty IFC project Given an empty IFC project
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And I select "IfcSpatialElement" in "scene.BIMRootProperties.ifc_product" And I set "scene.BIMRootProperties.ifc_product" to "IfcSpatialElement"
And I select "IfcBuilding" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcBuilding"
And I press "bim.assign_class" And I press "bim.assign_class"
Then the object "IfcBuilding/Cube" is an "IfcBuilding" Then the object "IfcBuilding/Cube" is an "IfcBuilding"
And the object "IfcBuilding/Cube" is in the collection "IfcBuilding/Cube" And the object "IfcBuilding/Cube" is in the collection "IfcBuilding/Cube"
@@ -67,8 +67,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
Given an empty IFC project Given an empty IFC project
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And I select "IfcElement" in "scene.BIMRootProperties.ifc_product" And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I select "IfcOpeningElement" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement"
And I press "bim.assign_class" And I press "bim.assign_class"
Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement" Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement"
And the object "IfcOpeningElement/Cube" is in the collection "IfcOpeningElements" And the object "IfcOpeningElement/Cube" is in the collection "IfcOpeningElements"
@@ -82,7 +82,7 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And the object "Cube" is placed in the collection "IfcBuildingStorey/My Storey" And the object "Cube" is placed in the collection "IfcBuildingStorey/My Storey"
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class" And I press "bim.assign_class"
Then the object "IfcWall/Cube" is contained in "My Storey" Then the object "IfcWall/Cube" is contained in "My Storey"
""" """
@@ -95,7 +95,7 @@ class TestCopyClass(test.bim.bootstrap.NewFile):
Given an empty IFC project Given an empty IFC project
When I add a cube When I add a cube
And the object "Cube" is selected And the object "Cube" is selected
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class" And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class" And I press "bim.assign_class"
And I duplicate the selected objects And I duplicate the selected objects
Then the object "IfcWall/Cube" and "IfcWall/Cube.001" are different elements Then the object "IfcWall/Cube" and "IfcWall/Cube.001" are different elements
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ids xmlns="http://standards.buildingsmart.org/IDS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://standards.buildingsmart.org/IDS ids_04.xsd">
<specification name="Sample IDS" necessity="required">
<applicability>
<entity>
<name><simpleValue>IfcBuilding</simpleValue></name>
</entity>
</applicability>
<requirements>
<property location="any">
<propertyset><simpleValue>attribute</simpleValue></propertyset>
<name><simpleValue>name</simpleValue></name>
<value><simpleValue>My Building</simpleValue></value>
</property>
</requirements>
</specification>
<info/>
</ids>
+6 -3
View File
@@ -41,13 +41,16 @@ class ReportGenerator:
return return
for scenario in feature["elements"]: for scenario in feature["elements"]:
scenario_data = self.process_scenario(scenario) scenario_data = self.process_scenario(scenario, feature)
if scenario_data: if scenario_data:
data["scenarios"].append(scenario_data) data["scenarios"].append(scenario_data)
data["total_passes"] = sum([s["total_passes"] for s in data["scenarios"]]) data["total_passes"] = sum([s["total_passes"] for s in data["scenarios"]])
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]]) data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100) try:
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
except ZeroDivisionError:
data["pass_rate"] = 0
data.update(self.get_template_strings()) data.update(self.get_template_strings())
@@ -57,7 +60,7 @@ class ReportGenerator:
) as template: ) as template:
out.write(pystache.render(template.read(), data)) out.write(pystache.render(template.read(), data))
def process_scenario(self, scenario): def process_scenario(self, scenario, feature):
if len(scenario["steps"]) == 0: if len(scenario["steps"]) == 0:
print("Scenario '{}' in feature '{}' has no steps.".format(scenario["name"], feature["name"])) print("Scenario '{}' in feature '{}' has no steps.".format(scenario["name"], feature["name"]))
return return
+1 -1
View File
@@ -88,7 +88,7 @@ class TestRunner:
logging.basicConfig(level=logging.INFO, format="%(message)s") logging.basicConfig(level=logging.INFO, format="%(message)s")
ids_handler = IDSHandler() ids_handler = IDSHandler()
logger.addHandler(ids_handler) logger.addHandler(ids_handler)
ids_file = ifcopenshell.ids.ids(args["feature"]) ids_file = ifcopenshell.ids.ids.open(args["feature"])
ids_file.validate(IfcStore.file, logger) ids_file.validate(IfcStore.file, logger)
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
+27 -26
View File
@@ -16,11 +16,9 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import os
import re import re
import logging import logging
import operator
import os
import csv
import numpy as np import numpy as np
from datetime import date from datetime import date
@@ -30,13 +28,8 @@ import ifcopenshell.util.placement
from bcf.v2.bcfxml import BcfXml from bcf.v2.bcfxml import BcfXml
from bcf.v2 import data as bcf from bcf.v2 import data as bcf
from bcf import bcfxml
from xmlschema import XMLSchema from xmlschema import XMLSchema
from xmlschema import XMLSchemaConverter
from xmlschema import etree_tostring from xmlschema import etree_tostring
from lxml import etree as ElementTree
from xmlschema.validators import facets
from xmlschema.validators import identities from xmlschema.validators import identities
@@ -233,13 +226,18 @@ class ids:
else: else:
logger.debug("No applicable elements found. None required.") logger.debug("No applicable elements found. None required.")
try:
percentage = self.ifc_passed / self.ifc_applicable * 100
except ZeroDivisionError:
percentage = 0
logger.debug( logger.debug(
"Out of %s IFC elements, %s were applicable and %s of them passed (%s)." "Out of %s IFC elements, %s were applicable and %s of them passed (%s)."
% ( % (
len(ifc_file.by_type("IfcProduct")), len(ifc_file.by_type("IfcProduct")),
self.ifc_applicable, self.ifc_applicable,
self.ifc_passed, self.ifc_passed,
str(self.ifc_passed / self.ifc_applicable * 100) + "%", str(percentage) + "%",
) )
) )
for h in logger.handlers: for h in logger.handlers:
@@ -686,25 +684,28 @@ class property(facet):
self.location = self.node["@location"] self.location = self.node["@location"]
# TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue' if self.propertyset == "attribute":
instance_props = ifcopenshell.util.element.get_psets(inst) val = {k.lower(): v for k, v in inst.get_info().items()}.get(self.name, None)
if ifcopenshell.util.element.get_type(inst):
type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst))
else: else:
type_props = {} # TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue'
instance_props = ifcopenshell.util.element.get_psets(inst)
if self.location == "instance": if ifcopenshell.util.element.get_type(inst):
props = instance_props type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst))
elif self.location == "type" and type_props: else:
props = type_props type_props = {}
elif self.location == "any" and (instance_props or type_props):
props = {**instance_props, **type_props}
else:
props = {}
pset = props.get(self.propertyset) if self.location == "instance":
val = pset.get(self.name) if pset else None props = instance_props
elif self.location == "type" and type_props:
props = type_props
elif self.location == "any" and (instance_props or type_props):
props = {**instance_props, **type_props}
else:
props = {}
pset = props.get(self.propertyset)
val = pset.get(self.name) if pset else None
self.location_msg = location[self.location] self.location_msg = location[self.location]
di = {"name": self.name, "propertyset": self.propertyset, "value": "'%s'" % val, "location": self.location_msg} di = {"name": self.name, "propertyset": self.propertyset, "value": "'%s'" % val, "location": self.location_msg}
@@ -913,7 +914,7 @@ class restriction:
elif n[3:6] == "max": elif n[3:6] == "max":
r.options.append("<=") r.options.append("<=")
else: else:
self.options.append("==") r.options.append("==")
r.options[-1] += str(ids_dict[n]["@value"]) r.options[-1] += str(ids_dict[n]["@value"])
elif n == "pattern": elif n == "pattern":
r.type = "pattern" r.type = "pattern"