From c14f5eeca042232025797990396232e4feab53e7 Mon Sep 17 00:00:00 2001 From: htlcnn Date: Sun, 1 Nov 2020 19:22:49 +0700 Subject: [PATCH] black ifcbimtester --- src/ifcbimtester/bimtester.py | 205 ++++++++--------- src/ifcbimtester/features/environment.py | 2 + .../features/steps/classification.py | 48 ++-- .../features/steps/element_classes.py | 27 ++- src/ifcbimtester/features/steps/geocoding.py | 58 ++--- .../features/steps/geolocation.py | 207 +++++++++--------- .../features/steps/geometric_detail.py | 17 +- .../features/steps/model_federation.py | 72 +++--- .../features/steps/project_setup.py | 58 ++--- src/ifcbimtester/features/steps/steps.py | 66 +++--- src/ifcbimtester/features/steps/utils.py | 43 ++-- 11 files changed, 420 insertions(+), 383 deletions(-) diff --git a/src/ifcbimtester/bimtester.py b/src/ifcbimtester/bimtester.py index 892e727720..6a3246cee6 100755 --- a/src/ifcbimtester/bimtester.py +++ b/src/ifcbimtester/bimtester.py @@ -5,7 +5,7 @@ # $ pyinstaller --onefile --clean --icon=icon.ico --add-data "features;features" bimtester.py` from behave.__main__ import main as behave_main -import behave.formatter.pretty # Needed for pyinstaller to package it +import behave.formatter.pretty # Needed for pyinstaller to package it import ifcopenshell import pystache import os @@ -32,103 +32,105 @@ def get_resource_path(relative_path): def run_tests(args): if not get_features(args): - print('No features could be found to check.') + print("No features could be found to check.") return False - behave_args = [get_resource_path('features')] - if args['advanced_arguments']: - behave_args.extend(args['advanced_arguments'].split()) - elif not args['console']: - behave_args.extend(['--format', 'json.pretty', '--outfile', 'report/report.json']) + behave_args = [get_resource_path("features")] + if args["advanced_arguments"]: + behave_args.extend(args["advanced_arguments"].split()) + elif not args["console"]: + behave_args.extend(["--format", "json.pretty", "--outfile", "report/report.json"]) behave_main(behave_args) - print('# All tests are finished.') + print("# All tests are finished.") return True def get_features(args): current_path = os.path.abspath(".") - features_dir = get_resource_path('features') + features_dir = get_resource_path("features") for f in os.listdir(features_dir): - if f.endswith('.feature'): + if f.endswith(".feature"): os.remove(os.path.join(features_dir, f)) - if args['feature']: - shutil.copyfile(args['feature'], os.path.join( - get_resource_path('features'), - os.path.basename(args['feature']))) + if args["feature"]: + shutil.copyfile(args["feature"], os.path.join(get_resource_path("features"), os.path.basename(args["feature"]))) return True - if os.path.exists('features'): - shutil.copytree('features', get_resource_path('features')) + if os.path.exists("features"): + shutil.copytree("features", get_resource_path("features")) return True has_features = False - for f in os.listdir('.'): - if not f.endswith('.feature'): + for f in os.listdir("."): + if not f.endswith(".feature"): continue - if args['feature'] and args['feature'] != f: + if args["feature"] and args["feature"] != f: continue has_features = True - shutil.copyfile(f, os.path.join( - get_resource_path('features'), - os.path.basename(f))) + shutil.copyfile(f, os.path.join(get_resource_path("features"), os.path.basename(f))) return has_features def generate_report(): - print('# Generating HTML reports now.') - if not os.path.exists('report'): - os.mkdir('report') - report_path = 'report/report.json' + print("# Generating HTML reports now.") + if not os.path.exists("report"): + os.mkdir("report") + report_path = "report/report.json" if not os.path.exists(report_path): - return print('No report data was found.') + return print("No report data was found.") report = json.loads(open(report_path).read()) for feature in report: - file_name = os.path.basename(feature['location']).split(':')[0] + file_name = os.path.basename(feature["location"]).split(":")[0] data = { - 'file_name': file_name, - 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'name': feature['name'], - 'description': feature['description'], - 'is_success': feature['status'] == 'passed', - 'scenarios': [] - } - for scenario in feature['elements']: + "file_name": file_name, + "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "name": feature["name"], + "description": feature["description"], + "is_success": feature["status"] == "passed", + "scenarios": [], + } + for scenario in feature["elements"]: steps = [] total_duration = 0 - for step in scenario['steps']: - if 'result' in step: - total_duration += step['result']['duration'] - name = step['name'] - if 'match' in step and 'arguments' in step['match']: - for a in step['match']['arguments']: - name = name.replace(a['value'], '' + a['value'] + '') - if 'result' not in step or step['result']['status'] == 'undefined': - step['result'] = {} - step['result']['status'] = 'undefined' - step['result']['duration'] = 0 - step['result']['error_message'] = 'This requirement has not yet been specified.' - steps.append({ - 'name': name, - 'time': round(step['result']['duration'], 2), - 'is_success': step['result']['status'] == 'passed', - 'is_unspecified': 'result' not in step or step['result']['status'] == 'undefined', - 'error_message': None if step['result']['status'] == 'passed' else step['result']['error_message'] - }) - total_passes = len([s for s in steps if s['is_success'] == True]) + for step in scenario["steps"]: + if "result" in step: + total_duration += step["result"]["duration"] + name = step["name"] + if "match" in step and "arguments" in step["match"]: + for a in step["match"]["arguments"]: + name = name.replace(a["value"], "" + a["value"] + "") + if "result" not in step or step["result"]["status"] == "undefined": + step["result"] = {} + step["result"]["status"] = "undefined" + step["result"]["duration"] = 0 + step["result"]["error_message"] = "This requirement has not yet been specified." + steps.append( + { + "name": name, + "time": round(step["result"]["duration"], 2), + "is_success": step["result"]["status"] == "passed", + "is_unspecified": "result" not in step or step["result"]["status"] == "undefined", + "error_message": None + if step["result"]["status"] == "passed" + else step["result"]["error_message"], + } + ) + total_passes = len([s for s in steps if s["is_success"] == True]) total_steps = len(steps) pass_rate = round((total_passes / total_steps) * 100) - data['scenarios'].append({ - 'name': scenario['name'], - 'is_success': scenario['status'] == 'passed', - 'time': round(total_duration, 2), - 'steps': steps, - 'total_passes': total_passes, - 'total_steps': total_steps, - 'pass_rate': pass_rate - }) - 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['pass_rate'] = round((data['total_passes'] / data['total_steps']) * 100) + data["scenarios"].append( + { + "name": scenario["name"], + "is_success": scenario["status"] == "passed", + "time": round(total_duration, 2), + "steps": steps, + "total_passes": total_passes, + "total_steps": total_steps, + "pass_rate": pass_rate, + } + ) + 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["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100) - with open('report/{}.html'.format(file_name), 'w') as out: - with open(get_resource_path('features/template.html')) as template: + with open("report/{}.html".format(file_name), "w") as out: + with open(get_resource_path("features/template.html")) as template: out.write(pystache.render(template.read(), data)) @@ -138,35 +140,35 @@ class TestPurger: def purge(self): filenames = [] - if os.path.exists('features'): - for filename in Path('features/').glob('*.feature'): + if os.path.exists("features"): + for filename in Path("features/").glob("*.feature"): filenames.append(filename) - for f in os.listdir('.'): - if f.endswith('.feature'): + for f in os.listdir("."): + if f.endswith(".feature"): filenames.append(f) for filename in filenames: - with open(filename, 'r') as feature_file: + with open(filename, "r") as feature_file: old_file = feature_file.readlines() - with open(filename, 'w') as new_file: + with open(filename, "w") as new_file: for line in old_file: is_purged = False if 'The IFC file "' in line and '" must be provided' in line: filename = line.split('"')[1] - print('Loading file {} ...'.format(filename)) + print("Loading file {} ...".format(filename)) self.file = ifcopenshell.open(filename) - if line.strip()[0:2] == '* ': + if line.strip()[0:2] == "* ": words = line.strip().split() for word in words: if self.is_a_global_id(word): if not self.does_global_id_exist(word): - print('Test for {} purged ...'.format(word)) + print("Test for {} purged ...".format(word)) is_purged = True if not is_purged: new_file.write(line) def is_a_global_id(self, word): - return word[0] in ['0', '1', '2', '3'] and len(word) == 22 + return word[0] in ["0", "1", "2", "3"] and len(word) == 22 def does_global_id_exist(self, global_id): try: @@ -176,42 +178,21 @@ class TestPurger: return False -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='Runs unit tests for BIM data') +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Runs unit tests for BIM data") + parser.add_argument("-p", "--purge", action="store_true", help="Purge tests of deleted elements") + parser.add_argument("-r", "--report", action="store_true", help="Generate a HTML report") + parser.add_argument("-c", "--console", action="store_true", help="Show results in the console") + parser.add_argument("-f", "--feature", type=str, help="Specify a feature file to test", default="") parser.add_argument( - '-p', - '--purge', - action='store_true', - help='Purge tests of deleted elements') - parser.add_argument( - '-r', - '--report', - action='store_true', - help='Generate a HTML report') - parser.add_argument( - '-c', - '--console', - action='store_true', - help='Show results in the console') - parser.add_argument( - '-f', - '--feature', - type=str, - help='Specify a feature file to test', - default='') - parser.add_argument( - '-a', - '--advanced-arguments', - type=str, - help='Specify your own arguments to Python\'s Behave', - default='') + "-a", "--advanced-arguments", type=str, help="Specify your own arguments to Python's Behave", default="" + ) args = vars(parser.parse_args()) - if args['purge']: + if args["purge"]: TestPurger().purge() - elif args['report']: + elif args["report"]: generate_report() else: run_tests(args) - print('# All tasks are complete :-)') + print("# All tasks are complete :-)") diff --git a/src/ifcbimtester/features/environment.py b/src/ifcbimtester/features/environment.py index 9ee093267e..167171f90a 100644 --- a/src/ifcbimtester/features/environment.py +++ b/src/ifcbimtester/features/environment.py @@ -1,4 +1,6 @@ from behave.model import Scenario + + def before_all(context): userdata = context.config.userdata continue_after_failed = True diff --git a/src/ifcbimtester/features/steps/classification.py b/src/ifcbimtester/features/steps/classification.py index a4b103e356..40f2201252 100644 --- a/src/ifcbimtester/features/steps/classification.py +++ b/src/ifcbimtester/features/steps/classification.py @@ -4,63 +4,67 @@ from utils import IfcFile, assert_attribute, assert_type def get_classification(name): - classifications = [c for c in IfcFile.get().by_type('IfcClassification') if c.Name == name] + classifications = [c for c in IfcFile.get().by_type("IfcClassification") if c.Name == name] if len(classifications) != 1: assert False, f'The classification "{name}" was not found' return classifications[0] -@step(u'The classification {name} must be used') +@step("The classification {name} must be used") def step_impl(context, name): get_classification(name) -@step(u'The classification {name} is published by {source}') +@step("The classification {name} is published by {source}") def step_impl(context, name, source): - assert_attribute(get_classification(name), 'Source', source) + assert_attribute(get_classification(name), "Source", source) -@step(u'The classification {name} is the edition {edition} on {edition_date}') +@step("The classification {name} is the edition {edition} on {edition_date}") def step_impl(context, name, edition, edition_date): element = get_classification(name) - assert_attribute(element, 'Edition', edition) - assert_attribute(element, 'EditionDate', edition_date) + assert_attribute(element, "Edition", edition) + assert_attribute(element, "EditionDate", edition_date) -@step(u'The classification {name} has the description "{description}"') +@step('The classification {name} has the description "{description}"') def step_impl(context, name, description): - assert_attribute(get_classification(name), 'Description', description) + assert_attribute(get_classification(name), "Description", description) -@step(u'The classification {name} is referenced by the website {location}') +@step("The classification {name} is referenced by the website {location}") def step_impl(context, name, location): - assert_attribute(get_classification(name), 'Location', location) + assert_attribute(get_classification(name), "Location", location) -@step(u'The classification {name} has a hierarchy denoted by the tokens {tokens}') +@step("The classification {name} has a hierarchy denoted by the tokens {tokens}") def step_impl(context, name, tokens): try: tokens = json.loads(tokens) except: - assert False, f'Tokens {tokens} are not specified as a JSON list' - assert_attribute(get_classification(name), 'ReferenceTokens', tokens) + assert False, f"Tokens {tokens} are not specified as a JSON list" + assert_attribute(get_classification(name), "ReferenceTokens", tokens) -@step(u'The element {guid} is classified as a "{identification}" with name "{reference_name}"') +@step('The element {guid} is classified as a "{identification}" with name "{reference_name}"') def step_impl(context, guid, identification, reference_name): element = IfcFile.by_guid(guid) - if not hasattr(element, 'HasAssociations') or not element.HasAssociations: - assert False, f'The element {element} has no associations.' - references = [a.RelatingClassification for a in element.HasAssociations if a.is_a('IfcRelAssociatesClassification')] + if not hasattr(element, "HasAssociations") or not element.HasAssociations: + assert False, f"The element {element} has no associations." + references = [a.RelatingClassification for a in element.HasAssociations if a.is_a("IfcRelAssociatesClassification")] if not references: - assert False, f'The element {element} has no associated classification references.' + assert False, f"The element {element} has no associated classification references." is_success = False for reference in references: try: - assert_attribute(reference, 'Identification', identification) - assert_attribute(reference, 'Name', reference_name) + assert_attribute(reference, "Identification", identification) + assert_attribute(reference, "Name", reference_name) is_success = True except: pass if not is_success: - assert False, 'No classification references met the requirement for an identification {} and name {} for the element {}. The references we found were: {}'.format(identification, reference_name, element, references) + assert ( + False + ), "No classification references met the requirement for an identification {} and name {} for the element {}. The references we found were: {}".format( + identification, reference_name, element, references + ) diff --git a/src/ifcbimtester/features/steps/element_classes.py b/src/ifcbimtester/features/steps/element_classes.py index 7b14bc06ab..a3908ce2b6 100644 --- a/src/ifcbimtester/features/steps/element_classes.py +++ b/src/ifcbimtester/features/steps/element_classes.py @@ -1,35 +1,38 @@ from behave import step from utils import IfcFile, assert_attribute, assert_type -@step('The element {guid} is an {ifc_class} only') + +@step("The element {guid} is an {ifc_class} only") def step_impl(context, guid, ifc_class): element = IfcFile.by_guid(guid) assert_type(element, ifc_class, is_exact=True) -@step('The element {guid} is an {ifc_class}') +@step("The element {guid} is an {ifc_class}") def step_impl(context, guid, ifc_class): element = IfcFile.by_guid(guid) assert_type(element, ifc_class) -@step('The element {guid} is further defined as a {predefined_type}') +@step("The element {guid} is further defined as a {predefined_type}") def step_impl(context, guid, predefined_type): element = IfcFile.by_guid(guid) - if hasattr(element, 'PredefinedType') \ - and element.PredefinedType == 'USERDEFINED' \ - and hasattr(element,'ObjectType'): - assert_attribute(element, 'ObjectType', predefined_type) - elif hasattr(element, 'PredefinedType'): - assert_attribute(element, 'PredefinedType', predefined_type) + if ( + hasattr(element, "PredefinedType") + and element.PredefinedType == "USERDEFINED" + and hasattr(element, "ObjectType") + ): + assert_attribute(element, "ObjectType", predefined_type) + elif hasattr(element, "PredefinedType"): + assert_attribute(element, "PredefinedType", predefined_type) else: - assert False, 'The element {} does not have a PredefinedType or ObjectType attribute'.format(element) + assert False, "The element {} does not have a PredefinedType or ObjectType attribute".format(element) -@step('The element {guid} should not exist because {reason}') +@step("The element {guid} should not exist because {reason}") def step_impl(context, guid, reason): try: element = IfcFile.get().by_id(guid) except: return - assert False, 'This element {} should be reevaluated.'.format(element) + assert False, "This element {} should be reevaluated.".format(element) diff --git a/src/ifcbimtester/features/steps/geocoding.py b/src/ifcbimtester/features/steps/geocoding.py index 40d62b0345..1b003743ea 100644 --- a/src/ifcbimtester/features/steps/geocoding.py +++ b/src/ifcbimtester/features/steps/geocoding.py @@ -3,11 +3,11 @@ from utils import IfcFile, assert_attribute, assert_type def get_ifc_class_from_spatial_type(spatial_type): - if spatial_type == 'site': - return 'IfcSite' - elif spatial_type == 'building': - return 'IfcBuilding' - return 'IfcFacility' + if spatial_type == "site": + return "IfcSite" + elif spatial_type == "building": + return "IfcBuilding" + return "IfcFacility" def check_geocode_attribute(guid, spatial_type, name, value): @@ -20,58 +20,62 @@ def check_geocode_address(guid, spatial_type, name, value): element = IfcFile.by_guid(guid) ifc_class = get_ifc_class_from_spatial_type(spatial_type) assert_type(element, ifc_class) - if ifc_class == 'IfcSite': - address_name = 'SiteAddress' - elif ifc_class == 'IfcBuilding': - address_name = 'BuildingAddress' + if ifc_class == "IfcSite": + address_name = "SiteAddress" + elif ifc_class == "IfcBuilding": + address_name = "BuildingAddress" assert_attribute(element, address_name) assert_attribute(getattr(element, address_name), name, value) -use_step_matcher('re') -@step('The (site|building|facility) (?P.*) has a name of (?P.*)') +use_step_matcher("re") + + +@step("The (site|building|facility) (?P.*) has a name of (?P.*)") def step_impl(context, spatial_type, guid, name): - check_geocode_attribute(guid, spatial_type, 'Name', name) + check_geocode_attribute(guid, spatial_type, "Name", name) @step('The (site|building|facility) (?P.*) has a description of "(?P.*)"') def step_impl(context, spatial_type, guid, description): - check_geocode_attribute(guid, spatial_type, 'Description', description) + check_geocode_attribute(guid, spatial_type, "Description", description) -@step('The site (?P.*) has a land title number of (?P.*)') + +@step("The site (?P.*) has a land title number of (?P.*)") def step_impl(context, guid, land_title_number): - check_geocode_attribute(guid, 'site', 'LandTitleNumber', land_title_number) + check_geocode_attribute(guid, "site", "LandTitleNumber", land_title_number) + @step('The (site|building) (?P.*) has the address "(?P.*)"') def step_impl(context, spatial_type, guid, address_lines): - check_geocode_address(guid, spatial_type, 'AddressLines', address_lines.split('\\n')) + check_geocode_address(guid, spatial_type, "AddressLines", address_lines.split("\\n")) -@step('The (site|building) (?P.*) has a postal box of (?P.*)') +@step("The (site|building) (?P.*) has a postal box of (?P.*)") def step_impl(context, spatial_type, guid, postal_box): - check_geocode_address(guid, spatial_type, 'PostalBox', postal_box) + check_geocode_address(guid, spatial_type, "PostalBox", postal_box) -@step('The (site|building) (?P.*) is in the town (?P.*)') +@step("The (site|building) (?P.*) is in the town (?P.*)") def step_impl(context, spatial_type, guid, town): - check_geocode_address(guid, spatial_type, 'Town', town) + check_geocode_address(guid, spatial_type, "Town", town) -@step('The (site|building) (?P.*) is in the region (?P.*)') +@step("The (site|building) (?P.*) is in the region (?P.*)") def step_impl(context, spatial_type, guid, region): - check_geocode_address(guid, spatial_type, 'Region', region) + check_geocode_address(guid, spatial_type, "Region", region) -@step('The (site|building) (?P.*) has a post code of (?P.*)') +@step("The (site|building) (?P.*) has a post code of (?P.*)") def step_impl(context, spatial_type, guid, post_code): - check_geocode_address(guid, spatial_type, 'PostalCode', post_code) + check_geocode_address(guid, spatial_type, "PostalCode", post_code) -@step('The (site|building) (?P.*) is in the country (?P.*)') +@step("The (site|building) (?P.*) is in the country (?P.*)") def step_impl(context, spatial_type, guid, country): - check_geocode_address(guid, spatial_type, 'Country', country) + check_geocode_address(guid, spatial_type, "Country", country) @step('The (site|building) (?P.*) has an address description of "(?P.*)"') def step_impl(context, spatial_type, guid, description): - check_geocode_address(guid, spatial_type, 'Description', description) + check_geocode_address(guid, spatial_type, "Description", description) diff --git a/src/ifcbimtester/features/steps/geolocation.py b/src/ifcbimtester/features/steps/geolocation.py index 2a0fe18dae..917e9814c5 100644 --- a/src/ifcbimtester/features/steps/geolocation.py +++ b/src/ifcbimtester/features/steps/geolocation.py @@ -5,31 +5,36 @@ import ifcopenshell.util import ifcopenshell.util.element import ifcopenshell.util.geolocation -@step(u'There must be at least one {ifc_class} element') + +@step(u"There must be at least one {ifc_class} element") def step_impl(context, ifc_class): - assert len(IfcFile.get().by_type(ifc_class)) >= 1, 'An element of {} could not be found'.format(ifc_class) + assert len(IfcFile.get().by_type(ifc_class)) >= 1, "An element of {} could not be found".format(ifc_class) def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_assert=True): if entity_name not in IfcFile.bookmarks: has_entity = False - project = IfcFile.get().by_type('IfcProject')[0] + project = IfcFile.get().by_type("IfcProject")[0] for context in project.RepresentationContexts: - if entity_name == 'IfcMapConversion': - if context.is_a('IfcGeometricRepresentationContext') \ - and context.ContextType == 'Model' \ - and context.HasCoordinateOperation: + if entity_name == "IfcMapConversion": + if ( + context.is_a("IfcGeometricRepresentationContext") + and context.ContextType == "Model" + and context.HasCoordinateOperation + ): IfcFile.bookmarks[entity_name] = context.HasCoordinateOperation[0] has_entity = True - elif entity_name == 'IfcProjectedCRS': - if context.is_a('IfcGeometricRepresentationContext') \ - and context.ContextType == 'Model' \ - and context.HasCoordinateOperation \ - and context.HasCoordinateOperation[0].TargetCRS: + elif entity_name == "IfcProjectedCRS": + if ( + context.is_a("IfcGeometricRepresentationContext") + and context.ContextType == "Model" + and context.HasCoordinateOperation + and context.HasCoordinateOperation[0].TargetCRS + ): IfcFile.bookmarks[entity_name] = context.HasCoordinateOperation[0].TargetCRS has_entity = True if not has_entity: - assert False, 'No model geometric representation contexts refer to an {}'.format(entity_name) + assert False, "No model geometric representation contexts refer to an {}".format(entity_name) if not prop_name: return actual_value = getattr(IfcFile.bookmarks[entity_name], prop_name) @@ -39,172 +44,172 @@ def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_asser return actual_value -@step(u'The project must have coordinate reference system data') +@step(u"The project must have coordinate reference system data") def step_impl(context): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS') + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS") return - check_ifc4_geolocation('IfcProjectedCRS') + check_ifc4_geolocation("IfcProjectedCRS") -@step(u'The name of the CRS must be {coordinate_reference_name}') +@step(u"The name of the CRS must be {coordinate_reference_name}") def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'Name', coordinate_reference_name) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "Name", coordinate_reference_name) return - check_ifc4_geolocation('IfcProjectedCRS', 'Name', coordinate_reference_name) + check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name) -@step(u'The description of the CRS must be {value}') +@step(u"The description of the CRS must be {value}") def step_impl(context, value): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'Description', value) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "Description", value) return - check_ifc4_geolocation('IfcProjectedCRS', 'Description', value) + check_ifc4_geolocation("IfcProjectedCRS", "Description", value) -@step(u'The geodetic datum must be {coordinate_reference_name}') +@step(u"The geodetic datum must be {coordinate_reference_name}") def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'GeodeticDatum', coordinate_reference_name) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "GeodeticDatum", coordinate_reference_name) return - check_ifc4_geolocation('IfcProjectedCRS', 'GeodeticDatum', coordinate_reference_name) + check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name) -@step(u'The vertical datum must be {coordinate_reference_name}') +@step(u"The vertical datum must be {coordinate_reference_name}") def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'VerticalDatum', coordinate_reference_name) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "VerticalDatum", coordinate_reference_name) return - check_ifc4_geolocation('IfcProjectedCRS', 'VerticalDatum', coordinate_reference_name) + check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name) -@step(u'The map projection must be {coordinate_reference_name}') +@step(u"The map projection must be {coordinate_reference_name}") def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'MapProjection', coordinate_reference_name) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "MapProjection", coordinate_reference_name) return - check_ifc4_geolocation('IfcProjectedCRS', 'MapProjection', coordinate_reference_name) + check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name) -@step(u'The map zone must be {coordinate_reference_name}') +@step(u"The map zone must be {coordinate_reference_name}") def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'MapZone', coordinate_reference_name) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "MapZone", coordinate_reference_name) return - check_ifc4_geolocation('IfcProjectedCRS', 'MapZone', coordinate_reference_name) + check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name) -@step(u'The map unit must be {unit}') +@step(u"The map unit must be {unit}") def step_impl(context, unit): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_ProjectedCRS', 'MapUnit', unit) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_ProjectedCRS", "MapUnit", unit) return - actual_value = check_ifc4_geolocation('IfcProjectedCRS', 'MapUnit', should_assert=False) + actual_value = check_ifc4_geolocation("IfcProjectedCRS", "MapUnit", should_assert=False) if not actual_value: - assert False, 'A unit was not provided in the projected CRS' - if actual_value.is_a('IfcSIUnit'): - prefix = actual_value.Prefix if actual_value.Prefix else '' + assert False, "A unit was not provided in the projected CRS" + if actual_value.is_a("IfcSIUnit"): + prefix = actual_value.Prefix if actual_value.Prefix else "" actual_value = prefix + actual_value.Name - elif actual_value.is_a('IfcConversionBasedUnit'): + elif actual_value.is_a("IfcConversionBasedUnit"): actual_value = actual_value.Name assert actual_value == unit, 'We expected a value of "{}" but instead got "{}"'.format(unit, actual_value) -@step(u'The project must have coordinate transformations to convert from local to global coordinates') +@step(u"The project must have coordinate transformations to convert from local to global coordinates") def step_impl(context): - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_MapConversion') - check_ifc4_geolocation('IfcMapConversion') + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_MapConversion") + check_ifc4_geolocation("IfcMapConversion") -@step(u'The eastings of the model must be offset by {number} to derive its global coordinates') +@step(u"The eastings of the model must be offset by {number} to derive its global coordinates") def step_impl(context, number): number = assert_number(number) - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_MapConversion', 'Eastings', number) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_MapConversion", "Eastings", number) return - check_ifc4_geolocation('IfcMapConversion', 'Eastings', number) + check_ifc4_geolocation("IfcMapConversion", "Eastings", number) -@step(u'The northings of the model must be offset by {number} to derive its global coordinates') +@step(u"The northings of the model must be offset by {number} to derive its global coordinates") def step_impl(context, number): number = assert_number(number) - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_MapConversion', 'Northings', number) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_MapConversion", "Northings", number) return - check_ifc4_geolocation('IfcMapConversion', 'Northings', number) + check_ifc4_geolocation("IfcMapConversion", "Northings", number) -@step(u'The height of the model must be offset by {number} to derive its global coordinates') +@step(u"The height of the model must be offset by {number} to derive its global coordinates") def step_impl(context, number): number = assert_number(number) - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_MapConversion', 'OrthogonalHeight', number) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_MapConversion", "OrthogonalHeight", number) return - check_ifc4_geolocation('IfcMapConversion', 'OrthogonalHeight', number) + check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number) -@step(u'The model must be rotated clockwise by {number} to derive its global coordinates') +@step(u"The model must be rotated clockwise by {number} to derive its global coordinates") def step_impl(context, number): number = assert_number(number) - if IfcFile.get().schema == 'IFC2X3': - return check_ifc2x3_geolocation('EPset_MapConversion', 'Height', number) - abscissa = check_ifc4_geolocation('IfcMapConversion', 'XAxisAbscissa', should_assert=False) - ordinate = check_ifc4_geolocation('IfcMapConversion', 'XAxisOrdinate', should_assert=False) + if IfcFile.get().schema == "IFC2X3": + return check_ifc2x3_geolocation("EPset_MapConversion", "Height", number) + abscissa = check_ifc4_geolocation("IfcMapConversion", "XAxisAbscissa", should_assert=False) + ordinate = check_ifc4_geolocation("IfcMapConversion", "XAxisOrdinate", should_assert=False) actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3) value = round(number, 3) assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) -@step(u'The model must be scaled along the horizontal axis by {number} to derive its global coordinates') +@step(u"The model must be scaled along the horizontal axis by {number} to derive its global coordinates") def step_impl(context, number): number = assert_number(number) - if IfcFile.get().schema == 'IFC2X3': - for site in IfcFile.get().by_type('IfcSite'): - assert_pset(site, 'EPset_MapConversion', 'Scale', number) + if IfcFile.get().schema == "IFC2X3": + for site in IfcFile.get().by_type("IfcSite"): + assert_pset(site, "EPset_MapConversion", "Scale", number) return - check_ifc4_geolocation('IfcMapConversion', 'Scale', number) + check_ifc4_geolocation("IfcMapConversion", "Scale", number) -@step(u'The site {guid} has a longitude of {number}') +@step(u"The site {guid} has a longitude of {number}") def step_impl(context, guid, number): number = assert_number(number) site = IfcFile.by_guid(guid) - if not site.is_a('IfcSite'): - assert False, 'The element {} is not an IfcSite'.format(site) - ref = assert_attribute(site, 'RefLongitude') + if not site.is_a("IfcSite"): + assert False, "The element {} is not an IfcSite".format(site) + ref = assert_attribute(site, "RefLongitude") number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) - assert_attribute(site, 'RefLongitude', number) + assert_attribute(site, "RefLongitude", number) -@step(u'The site {guid} has a latitude of {number}') +@step(u"The site {guid} has a latitude of {number}") def step_impl(context, guid, number): number = assert_number(number) site = IfcFile.by_guid(guid) - if not site.is_a('IfcSite'): - assert False, 'The element {} is not an IfcSite'.format(site) - ref = assert_attribute(site, 'RefLatitude') + if not site.is_a("IfcSite"): + assert False, "The element {} is not an IfcSite".format(site) + ref = assert_attribute(site, "RefLatitude") number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) - assert_attribute(site, 'RefLatitude', number) + assert_attribute(site, "RefLatitude", number) -@step(u'The site {guid} has an elevation of {number}') +@step(u"The site {guid} has an elevation of {number}") def step_impl(context, guid, number): number = assert_number(number) site = IfcFile.by_guid(guid) - if not site.is_a('IfcSite'): - assert False, 'The element {} is not an IfcSite'.format(site) - assert_attribute(site, 'RefElevation', number) + if not site.is_a("IfcSite"): + assert False, "The element {} is not an IfcSite".format(site) + assert_attribute(site, "RefElevation", number) diff --git a/src/ifcbimtester/features/steps/geometric_detail.py b/src/ifcbimtester/features/steps/geometric_detail.py index 0fbbe4e8fe..ffb930d08a 100644 --- a/src/ifcbimtester/features/steps/geometric_detail.py +++ b/src/ifcbimtester/features/steps/geometric_detail.py @@ -2,26 +2,27 @@ from behave import step from utils import IfcFile from utils import IfcFile, assert_attribute -@step('All elements must be under {number} polygons') + +@step("All elements must be under {number} polygons") def step_impl(context, number): number = int(number) errors = [] - for element in IfcFile.get().by_type('IfcElement'): + for element in IfcFile.get().by_type("IfcElement"): if not element.Representation: continue total_polygons = 0 tree = IfcFile.get().traverse(element.Representation) for e in tree: - if e.is_a('IfcFace'): + if e.is_a("IfcFace"): total_polygons += 1 - elif e.is_a('IfcPolygonalFaceSet'): + elif e.is_a("IfcPolygonalFaceSet"): total_polygons += len(e.Faces) - elif e.is_a('IfcTriangulatedFaceSet'): + elif e.is_a("IfcTriangulatedFaceSet"): total_polygons += len(e.CoordIndex) if total_polygons > number: errors.append((total_polygons, element)) if errors: - message = 'The following {} elements are over 500 polygons:\n'.format(len(errors)) + message = "The following {} elements are over 500 polygons:\n".format(len(errors)) for error in errors: - message += 'Polygons: {} - {}\n'.format(error[0], error[1]) - assert False, message + message += "Polygons: {} - {}\n".format(error[0], error[1]) + assert False, message diff --git a/src/ifcbimtester/features/steps/model_federation.py b/src/ifcbimtester/features/steps/model_federation.py index b6fd095ff7..18c49c4696 100644 --- a/src/ifcbimtester/features/steps/model_federation.py +++ b/src/ifcbimtester/features/steps/model_federation.py @@ -8,16 +8,16 @@ from utils import IfcFile, assert_number, assert_type def a2p(o, z, x): y = np.cross(z, x) r = np.eye(4) - r[:-1,:-1] = x,y,z - r[-1,:-1] = o + r[:-1, :-1] = x, y, z + r[-1, :-1] = o return r.T def get_axis2placement(plc): - z = np.array(plc.Axis.DirectionRatios if plc.Axis else (0,0,1)) - x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1,0,0)) + z = np.array(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1)) + x = np.array(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0)) o = plc.Location.Coordinates - return a2p(o,z,x) + return a2p(o, z, x) def get_local_placement(plc): @@ -32,82 +32,88 @@ def get_local_placement(plc): def get_decimal_points(value): try: - return len(value.split('.')[1]) + return len(value.split(".")[1]) except: return 0 def get_containing_spatial_elements(element): results = [] - if element.is_a('IfcSpatialElement'): + if element.is_a("IfcSpatialElement"): results.append(element) for rel in element.Decomposes: - if rel.is_a('IfcRelAggregates'): + if rel.is_a("IfcRelAggregates"): results.append(get_containing_spatial_elements(rel.RelatingObject)) - elif element.is_a('IfcElement'): + elif element.is_a("IfcElement"): for rel in element.ContainedInStructure: - if rel.is_a('ifcRelContainedInSpatialStructure'): + if rel.is_a("ifcRelContainedInSpatialStructure"): results.append(get_containing_spatial_elements(rel.RelatingStructure)) return results -@step('There is a datum element {guid} as an {ifc_class}') +@step("There is a datum element {guid} as an {ifc_class}") def step_impl(context, guid, ifc_class): element = IfcFile.by_guid(guid) assert_type(element, ifc_class) -@step('The element {guid} has a global easting, northing, and elevation of {easting}, {northing}, and {elevation} respectively') +@step( + "The element {guid} has a global easting, northing, and elevation of {easting}, {northing}, and {elevation} respectively" +) def step_impl(context, guid, easting, northing, elevation): - if IfcFile.get().schema == 'IFC2X3': - if element.is_a('IfcSite'): + if IfcFile.get().schema == "IFC2X3": + if element.is_a("IfcSite"): site = element else: - potential_sites = [s for s in get_containing_spatial_elements(element) if s.is_a('IfcSite')] + potential_sites = [s for s in get_containing_spatial_elements(element) if s.is_a("IfcSite")] if potential_sites: site = potential_sites[0] else: - assert False, 'The datum element does not belong to a geolocated site' - map_conversion = assert_pset(site, 'EPset_MapConversion') + assert False, "The datum element does not belong to a geolocated site" + map_conversion = assert_pset(site, "EPset_MapConversion") else: - map_conversion = IfcFile.get().by_type('IfcMapConversion') + map_conversion = IfcFile.get().by_type("IfcMapConversion") if map_conversion: map_conversion = map_conversion[0].get_info() else: - assert False, 'No map conversion was found in the file' + assert False, "No map conversion was found in the file" element = IfcFile.by_guid(guid) if not element.ObjectPlacement: - assert False, 'The element does not have an object placement: {}'.format(element) + assert False, "The element does not have an object placement: {}".format(element) m = get_local_placement(element.ObjectPlacement) e, n, h = ifcopenshell.util.geolocation.xyz2enh( - m[0][3], m[1][3], m[2][3], - float(map_conversion['Eastings']), - float(map_conversion['Northings']), - float(map_conversion['OrthogonalHeight']), - float(map_conversion['XAxisAbscissa']), - float(map_conversion['XAxisOrdinate']), - float(map_conversion['Scale']), + m[0][3], + m[1][3], + m[2][3], + float(map_conversion["Eastings"]), + float(map_conversion["Northings"]), + float(map_conversion["OrthogonalHeight"]), + float(map_conversion["XAxisAbscissa"]), + float(map_conversion["XAxisOrdinate"]), + float(map_conversion["Scale"]), ) element_x = round(e, get_decimal_points(easting)) element_y = round(n, get_decimal_points(northing)) element_z = round(h, get_decimal_points(elevation)) expected_placement = (assert_number(easting), assert_number(northing), assert_number(elevation)) if (element_x, element_y, element_z) != expected_placement: - assert False, 'The element {} is meant to have a location of {} but instead we found {}'.format( - element, expected_placement, (element_x, element_y, element_z)) + assert False, "The element {} is meant to have a location of {} but instead we found {}".format( + element, expected_placement, (element_x, element_y, element_z) + ) -@step('The element {guid} has a local X, Y, and Z coordinate of {x}, {y}, and {z} respectively') +@step("The element {guid} has a local X, Y, and Z coordinate of {x}, {y}, and {z} respectively") def step_impl(context, guid, x, y, z): element = IfcFile.by_guid(guid) if not element.ObjectPlacement: - assert False, 'The element does not have an object placement: {}'.format(element) + assert False, "The element does not have an object placement: {}".format(element) m = get_local_placement(element.ObjectPlacement) element_x = round(m[0][3], get_decimal_points(x)) element_y = round(m[1][3], get_decimal_points(y)) element_z = round(m[2][3], get_decimal_points(z)) expected_placement = (assert_number(x), assert_number(y), assert_number(z)) if (element_x, element_y, element_z) != expected_placement: - assert False, 'The element {} is meant to have a location of {} but instead we found {}'.format( - element, expected_placement, (element_x, element_y, element_z)) + assert False, "The element {} is meant to have a location of {} but instead we found {}".format( + element, expected_placement, (element_x, element_y, element_z) + ) diff --git a/src/ifcbimtester/features/steps/project_setup.py b/src/ifcbimtester/features/steps/project_setup.py index eb627033a2..c8abb39ca2 100644 --- a/src/ifcbimtester/features/steps/project_setup.py +++ b/src/ifcbimtester/features/steps/project_setup.py @@ -2,19 +2,20 @@ from behave import step from utils import IfcFile from utils import IfcFile, assert_attribute + @step('The IFC file "{file}" must be provided') def step_impl(context, file): try: IfcFile.load(file) except: - assert False, f'The file {file} could not be loaded' + assert False, f"The file {file} could not be loaded" -@step('IFC data must use the {schema} schema') +@step("IFC data must use the {schema} schema") def step_impl(context, schema): - assert IfcFile.get().schema == schema, \ - 'We expected a schema of {} but instead got {}'.format( - schema, IfcFile.get().schema) + assert IfcFile.get().schema == schema, "We expected a schema of {} but instead got {}".format( + schema, IfcFile.get().schema + ) @step('The IFC file "{file}" is exempt from being provided') @@ -22,68 +23,71 @@ def step_impl(context, file): pass -@step('No further requirements are specified because {reason}') +@step("No further requirements are specified because {reason}") def step_impl(context, reason): pass -@step('The project must have an identifier of {guid}') +@step("The project must have an identifier of {guid}") def step_impl(context, guid): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'GlobalId', guid) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid) @step('The project name, code, or short identifier must be "{value}"') def step_impl(context, value): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'Name', value) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value) @step('The project must have a longer form name of "{value}"') def step_impl(context, value): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'LongName', value) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "LongName", value) @step('The project must be described as "{value}"') def step_impl(context, value): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'Description', value) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Description", value) @step('The project must be categorised under "{value}"') def step_impl(context, value): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'ObjectType', value) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "ObjectType", value) @step('The project must contain information about the "{value}" phase') def step_impl(context, value): - assert_attribute(IfcFile.get().by_type('IfcProject')[0], 'Phase', value) + assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Phase", value) -@step('The project must contain 3D geometry representing the shape of objects') +@step("The project must contain 3D geometry representing the shape of objects") def step_impl(context): - assert get_subcontext('Body', 'Model', 'MODEL_VIEW') + assert get_subcontext("Body", "Model", "MODEL_VIEW") -@step('The project must contain 3D geometry representing clearance zones') +@step("The project must contain 3D geometry representing clearance zones") def step_impl(context): - assert get_subcontext('Clearance', 'Model', 'MODEL_VIEW') + assert get_subcontext("Clearance", "Model", "MODEL_VIEW") -@step('The project must contain 3D geometry representing the center of gravity of objects') +@step("The project must contain 3D geometry representing the center of gravity of objects") def step_impl(context): - assert get_subcontext('CoG', 'Model', 'MODEL_VIEW') + assert get_subcontext("CoG", "Model", "MODEL_VIEW") -@step('The project must contain 3D geometry representing the object bounding boxes') +@step("The project must contain 3D geometry representing the object bounding boxes") def step_impl(context): - assert get_subcontext('Box', 'Model', 'MODEL_VIEW') + assert get_subcontext("Box", "Model", "MODEL_VIEW") def get_subcontext(identifier, type, target_view): - project = IfcFile.get().by_type('IfcProject')[0] + project = IfcFile.get().by_type("IfcProject")[0] for rep_context in project.RepresentationContexts: for subcontext in rep_context.HasSubContexts: - if subcontext.ContextIdentifier == identifier \ - and subcontext.ContextType == type \ - and subcontext.TargetView == target_view: + if ( + subcontext.ContextIdentifier == identifier + and subcontext.ContextType == type + and subcontext.TargetView == target_view + ): return True - assert False, 'The subcontext with identifier {}, type {}, and target view {} could not be found'.format( - identifier, type, target_view) + assert False, "The subcontext with identifier {}, type {}, and target view {} could not be found".format( + identifier, type, target_view + ) diff --git a/src/ifcbimtester/features/steps/steps.py b/src/ifcbimtester/features/steps/steps.py index 64a7ed522b..4d48bde68e 100644 --- a/src/ifcbimtester/features/steps/steps.py +++ b/src/ifcbimtester/features/steps/steps.py @@ -1,7 +1,8 @@ from behave import step from utils import IfcFile -@step(u'there are no {ifc_class} elements because {reason}') + +@step("there are no {ifc_class} elements because {reason}") def step_impl(context, ifc_class, reason): assert len(IfcFile.get().by_type(ifc_class)) == 0 @@ -9,17 +10,18 @@ def step_impl(context, ifc_class, reason): @step('all {ifc_class} elements have a name matching the pattern "{pattern}"') def step_impl(context, ifc_class, pattern): import re + elements = IfcFile.get().by_type(ifc_class) for element in elements: if not re.search(pattern, element.Name): assert False -@step('all {ifc_class} elements have an {representation_class} representation') +@step("all {ifc_class} elements have an {representation_class} representation") def step_impl(context, ifc_class, representation_class): def is_item_a_representation(item, representation): - if '/' in representation: - for cls in representation.split('/'): + if "/" in representation: + for cls in representation.split("/"): if item.is_a(cls): return True elif item.is_a(representation): @@ -32,7 +34,7 @@ def step_impl(context, ifc_class, representation_class): has_representation = False for representation in element.Representation.Representations: for item in representation.Items: - if item.is_a('IfcMappedItem'): + if item.is_a("IfcMappedItem"): # We only check one more level deep. for item2 in item.MappingSource.MappedRepresentation.Items: if is_item_a_representation(item2, representation_class): @@ -43,8 +45,11 @@ def step_impl(context, ifc_class, representation_class): if not has_representation: assert False -use_step_matcher('re') -@step('all (?P.*) elements have an? (?P.*) attribute') + +use_step_matcher("re") + + +@step("all (?P.*) elements have an? (?P.*) attribute") def step_impl(context, ifc_class, attribute): elements = IfcFile.get().by_type(ifc_class) for element in elements: @@ -52,34 +57,37 @@ def step_impl(context, ifc_class, attribute): assert False -@step('all (?P.*) elements have an? (?P.*\..*) property') +@step("all (?P.*) elements have an? (?P.*\..*) property") def step_impl(context, ifc_class, property_path): - pset_name, property_name = property_path.split('.') + pset_name, property_name = property_path.split(".") elements = IfcFile.get().by_type(ifc_class) for element in elements: if not IfcFile.get_property(element, pset_name, property_name): assert False -@step('all (?P.*) elements have an? (?P.*\..*) property value matching the pattern "(?P.*)"') +@step( + 'all (?P.*) elements have an? (?P.*\..*) property value matching the pattern "(?P.*)"' +) def step_impl(context, ifc_class, property_path, pattern): import re - pset_name, property_name = property_path.split('.') + + pset_name, property_name = property_path.split(".") elements = IfcFile.get().by_type(ifc_class) for element in elements: prop = IfcFile.get_property(element, pset_name, property_name) if not prop: assert False # For now, we only check single values - if prop.is_a('IfcPropertySingleValue'): - if not (prop.NominalValue \ - and re.search(pattern, prop.NominalValue.wrappedValue)): + if prop.is_a("IfcPropertySingleValue"): + if not (prop.NominalValue and re.search(pattern, prop.NominalValue.wrappedValue)): assert False @step('all (?P.*) elements have an? (?P.*) matching the pattern "(?P.*)"') def step_impl(context, ifc_class, attribute, pattern): import re + elements = IfcFile.get().by_type(ifc_class) for element in elements: value = getattr(element, attribute) @@ -90,6 +98,7 @@ def step_impl(context, ifc_class, attribute, pattern): @step('all (?P.*) elements have an? (?P.*) taken from the list in "(?P.*)"') def step_impl(context, ifc_class, attributes, list_file): import csv + values = [] with open(list_file) as csvfile: reader = csv.reader(csvfile) @@ -98,16 +107,18 @@ def step_impl(context, ifc_class, attributes, list_file): elements = IfcFile.get().by_type(ifc_class) for element in elements: attribute_values = [] - for attribute in attributes.split(','): + for attribute in attributes.split(","): if not hasattr(element, attribute): - assert False, f'Failed at element {element.GlobalId}' + assert False, f"Failed at element {element.GlobalId}" attribute_values.append(getattr(element, attribute)) if attribute_values not in values: - assert False, f'Failed at element {element.GlobalId}' + assert False, f"Failed at element {element.GlobalId}" -use_step_matcher('parse') -@step('all {ifc_class} elements have a {qto_name}.{quantity_name} quantity') +use_step_matcher("parse") + + +@step("all {ifc_class} elements have a {qto_name}.{quantity_name} quantity") def step_impl(context, ifc_class, qto_name, quantity_name): elements = IfcFile.get().by_type(ifc_class) for element in elements: @@ -123,25 +134,26 @@ def step_impl(context, ifc_class, qto_name, quantity_name): assert False -use_step_matcher('parse') -@step(u'the project has a {attribute_name} attribute with a value of "{attribute_value}"') +use_step_matcher("parse") + + +@step('the project has a {attribute_name} attribute with a value of "{attribute_value}"') def step_impl(context, attribute_name, attribute_value): - project = IfcFile.get().by_type('IfcProject')[0] + project = IfcFile.get().by_type("IfcProject")[0] assert getattr(project, attribute_name) == attribute_value -@step(u'there is an {ifc_class} element with a {attribute_name} attribute with a value of "{attribute_value}"') +@step('there is an {ifc_class} element with a {attribute_name} attribute with a value of "{attribute_value}"') def step_impl(context, ifc_class, attribute_name, attribute_value): elements = IfcFile.get().by_type(ifc_class) for element in elements: - if hasattr(element, attribute_name) \ - and getattr(element, attribute_name) == attribute_value: + if hasattr(element, attribute_name) and getattr(element, attribute_name) == attribute_value: return assert False -@step(u'all buildings have an address') +@step("all buildings have an address") def step_impl(context): - for building in IfcFile.get().by_type('IfcBuilding'): + for building in IfcFile.get().by_type("IfcBuilding"): if not building.BuildingAddress: assert False, f'The building "{building.Name}" has no address.' diff --git a/src/ifcbimtester/features/steps/utils.py b/src/ifcbimtester/features/steps/utils.py index 787d21de88..038b0c4149 100644 --- a/src/ifcbimtester/features/steps/utils.py +++ b/src/ifcbimtester/features/steps/utils.py @@ -2,6 +2,7 @@ import ifcopenshell import ifcopenshell.util import ifcopenshell.util.element + class IfcFile(object): file = None bookmarks = {} @@ -13,7 +14,7 @@ class IfcFile(object): @classmethod def get(cls): if not cls.file: - assert False, 'No file was loaded, so this requirement cannot be checked' + assert False, "No file was loaded, so this requirement cannot be checked" return cls.file @classmethod @@ -21,45 +22,59 @@ class IfcFile(object): try: return cls.get().by_guid(guid) except: - assert False, 'An element with the ID {} could not be found.'.format(guid) + assert False, "An element with the ID {} could not be found.".format(guid) + def assert_number(number): try: return float(number) except ValueError: - assert False, 'A number should be specified, not {}'.format(number) + assert False, "A number should be specified, not {}".format(number) -def assert_type(element, ifc_class, is_exact = False): + +def assert_type(element, ifc_class, is_exact=False): if is_exact: - assert element.is_a() == ifc_class, 'The element {} is an {} instead of {}.'.format(element, element.is_a(), ifc_class) + assert element.is_a() == ifc_class, "The element {} is an {} instead of {}.".format( + element, element.is_a(), ifc_class + ) else: - assert element.is_a(ifc_class), 'The element {} is an {} instead of {}.'.format(element, element.is_a(), ifc_class) + assert element.is_a(ifc_class), "The element {} is an {} instead of {}.".format( + element, element.is_a(), ifc_class + ) + def assert_attribute(element, name, value=None): if not hasattr(element, name): - assert False, 'The element {} does not have the attribute {}'.format(element, name) + assert False, "The element {} does not have the attribute {}".format(element, name) if not value: if getattr(element, name) is None: - assert False, 'The element {} does not have a value for the attribute {}'.format(element, name) + assert False, "The element {} does not have a value for the attribute {}".format(element, name) return getattr(element, name) - if value == 'NULL': + if value == "NULL": value = None actual_value = getattr(element, name) if isinstance(value, list) and actual_value: actual_value = list(actual_value) - assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format(value, actual_value, element) + assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format( + value, actual_value, element + ) + def assert_pset(element, pset_name, prop_name=None, value=None): - if value == 'NULL': + if value == "NULL": value = None psets = ifcopenshell.util.element.get_psets(site) if pset_name not in psets: - assert False, 'The element {} does not have a property set named {}'.format(element, pset_name) + assert False, "The element {} does not have a property set named {}".format(element, pset_name) if prop_name is None: return psets[pset_name] if prop_name not in psets[pset_name]: - assert False, 'The element {} does not have a property named "{}" in the pset "{}"'.format(element, prop_name, pset_name) + assert False, 'The element {} does not have a property named "{}" in the pset "{}"'.format( + element, prop_name, pset_name + ) if value is None: return psets[pset_name][prop_name] actual_value = psets[pset_name][prop_name] - assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format(value, actual_value, element) + assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format( + value, actual_value, element + )