Source code for turbo_turtle.tests.test_system

import os
import shlex
import string
import typing
import pathlib
import tempfile
import subprocess
from importlib.metadata import version, PackageNotFoundError

import pytest
import numpy

from turbo_turtle import _settings
from turbo_turtle import _utilities
from turbo_turtle._main import get_parser
from turbo_turtle.conftest import missing_display


parser = get_parser()
subcommand_list = parser._subparsers._group_actions[0].choices.keys()
env = os.environ.copy()
turbo_turtle_command = "turbo-turtle"

try:
    version("turbo_turtle")
    installed = True
except PackageNotFoundError:
    # TODO: Recover from the SCons task definition?
    build_directory = _settings._project_root_abspath.parent / "build" / "systemtests"
    build_directory.mkdir(parents=True, exist_ok=True)
    installed = False

# If executing in repository, add package to PYTHONPATH and change the root command
if not installed:
    turbo_turtle_command = "python -m turbo_turtle._main"
    package_parent_path = _settings._project_root_abspath.parent
    key = "PYTHONPATH"
    if key in env:
        env[key] = f"{package_parent_path}:{env[key]}"
    else:
        env[key] = f"{package_parent_path}"


[docs] def setup_sphere_commands( model, inner_radius, outer_radius, angle, y_offset, quadrant, element_type, element_replacement, backend, output_type, ) -> typing.List[string.Template]: """Return the sphere/partition/mesh commands for system testing :returns: list of string or string template commands """ model = pathlib.Path(model).with_suffix(".cae") image = model.with_suffix(".png") # TODO: Merge Gmsh sphere system tests when partition/mesh/image subcommands are implemented # https://re-git.lanl.gov/aea/python-projects/turbo-turtle/-/issues/217 if backend == "cubit": model = model.with_suffix(".cub") image = image.parent / f"{image.stem}-cubit{image.suffix}" assembly = model.stem + "_assembly.inp" center = f"0. {y_offset} 0." xvector = _utilities.character_delimited_list([1.0, 0.0, 0.0]) zvector = _utilities.character_delimited_list([0.0, 0.0, 1.0]) backend_option = f"--backend {backend}" if backend is not None else "" commands = [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--inner-radius {inner_radius} --outer-radius {outer_radius} --output-file {model} " f"--model-name {model.stem} --part-name {model.stem} --quadrant {quadrant} " f"--revolution-angle {angle} --y-offset {y_offset} {backend_option}" ), string.Template( "${turbo_turtle_command} partition --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --output-file {model} " f"--model-name {model.stem} --part-name {model.stem} --center {center} " f"--xvector {xvector} --zvector {zvector} {backend_option}" ), string.Template( "${turbo_turtle_command} mesh --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --output-file {model} " f"--model-name {model.stem} --part-name {model.stem} --global-seed 0.15 " f"--element-type {element_type} {backend_option}" ), string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --output-file {image} " f"--model-name {model.stem} --part-name {model.stem} {backend_option}" ), string.Template( "${turbo_turtle_command} export --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --model-name {model.stem} --part-name {model.stem} " f"--element-type {element_replacement} --destination . " f"--assembly {assembly} --output-type {output_type} {backend_option}" ), ] # Skip the image subcommand when DISPLAY is not found # Skip the image subcommand when running the Genesis variations. We don't need duplicate images of the cubit meshes if (backend == "cubit" and missing_display) or (backend == "cubit" and output_type.lower() == "genesis"): commands.pop(3) # Skip the partition/mesh/image/export if inner_radius == 0: commands = [commands[0]] return pytest.param(commands, marks=getattr(pytest.mark, backend))
def setup_geometry_xyplot_commands(model, input_file, backend) -> typing.List[string.Template]: part_name = _utilities.character_delimited_list(csv.stem for csv in input_file) input_file = _utilities.character_delimited_list(input_file) commands = [ string.Template( "${turbo_turtle_command} geometry-xyplot " "--abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {input_file} --output-file {model}.png --part-name {part_name} " f"--backend {backend}" ) ] return pytest.param(commands, marks=getattr(pytest.mark, backend)) def setup_geometry_commands( model, input_file, revolution_angle, y_offset, backend, ) -> typing.List[string.Template]: model = pathlib.Path(model).with_suffix(".cae") if backend == "cubit": model = model.with_suffix(".cub") if backend == "gmsh": model = model.with_suffix(".step") part_name = " ".join(csv.stem for csv in input_file) input_file = _utilities.character_delimited_list(input_file) backend_option = f"--backend {backend}" if backend is not None else "" commands = [ string.Template( "${turbo_turtle_command} geometry --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {input_file} --model-name {model.stem} " f"--part-name {part_name} --output-file {model} --revolution-angle {revolution_angle} " f"--y-offset {y_offset} {backend_option}" ) ] return pytest.param(commands, marks=getattr(pytest.mark, backend)) def setup_sets_commands( model, input_file, revolution_angle, face_sets, edge_sets, edge_seeds, element_type, backend, ) -> typing.List[string.Template]: model = pathlib.Path(model).with_suffix(".cae") if backend == "cubit": model = model.with_suffix(".cub") part_name = " ".join(csv.stem for csv in input_file) commands = setup_geometry_commands(model, input_file, revolution_angle, 0.0, backend).values[0] if face_sets is not None: face_sets = _utilities.construct_append_options("--face-set", face_sets) else: face_sets = "" if edge_sets is not None: edge_sets = _utilities.construct_append_options("--edge-set", edge_sets) else: edge_sets = "" if edge_seeds is not None: edge_seeds = _utilities.construct_append_options("--edge-seed", edge_seeds) else: edge_seeds = "" backend_option = f"--backend {backend}" if backend is not None else "" sets_commands = [ string.Template( "${turbo_turtle_command} sets --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --model-name {model.stem} " f"--part-name {part_name} --output-file {model} {face_sets} {edge_sets} {backend_option}" ), string.Template( "${turbo_turtle_command} mesh --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {model} --model-name {model.stem} " f"--part-name {part_name} --output-file {model} --global-seed 1. --element-type {element_type} " f"{edge_seeds} {backend_option}" ), ] commands.extend(sets_commands) return pytest.param(commands, marks=getattr(pytest.mark, backend)) def setup_cylinder_commands(model, revolution_angle, backend) -> typing.List[string.Template]: model = pathlib.Path(model).with_suffix(".cae") if backend == "cubit": model = model.with_suffix(".cub") if backend == "gmsh": model = model.with_suffix(".step") backend_option = f"--backend {backend}" if backend is not None else "" commands = [ string.Template( "${turbo_turtle_command} cylinder --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--model-name {model.stem} --part-name {model.stem} " f"--output-file {model} --revolution-angle {revolution_angle} " f"--inner-radius 1 --outer-radius 2 --height 1 {backend_option}" ) ] return pytest.param(commands, marks=getattr(pytest.mark, backend)) def setup_merge_commands(part_name, backend) -> typing.List[string.Template]: commands = [] sphere_model = pathlib.Path("merge-sphere.cae") sphere_element_type = "C3D8" sphere_element_replacement = "C3D8R" geometry_model = pathlib.Path("merge-multi-part") output_file = pathlib.Path("merge.cae") if backend == "cubit": sphere_model = sphere_model.with_suffix(".cub") sphere_element_type = None sphere_element_replacement = "HEX20" geometry_model = geometry_model.with_suffix(".cub") output_file = output_file.with_suffix(".cub") # Create sphere file sphere_options = ( str(sphere_model), 1.0, 2.0, 360.0, 0.0, "both", sphere_element_type, sphere_element_replacement, backend, "abaqus", ) commands.append(setup_sphere_commands(*sphere_options).values[0][0]) # Create washer/vase combined file geometry_options = ( str(geometry_model), [ _settings._project_root_abspath / "tests" / "washer.csv", _settings._project_root_abspath / "tests" / "vase.csv", ], 360.0, 0.0, backend, ) commands.extend(setup_geometry_commands(*geometry_options).values[0]) # Run the actual merge command part_name = f"--part-name {part_name}" if part_name else "" backend_option = f"--backend {backend}" if backend is not None else "" merge_command = string.Template( "${turbo_turtle_command} merge --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {sphere_model} {geometry_model} " f"--output-file {output_file} --merged-model-name merge " f"--model-name merge-multi-part merge-sphere {part_name} {backend_option}" ) commands.append(merge_command) return pytest.param(commands, marks=getattr(pytest.mark, backend)) commands_list = [] # Legacy geometry system tests requires a series of commands before the temp directory is removed # TODO: Decide if we should package or drop the legacy geometry tests name = "Turbo-Turtle-Tests" legacy_geometry_file = _settings._project_root_abspath / "tests" / "legacy_geometry.py" commands_list.append( pytest.param( [ string.Template(f"${{abaqus_command}} cae -noGui {legacy_geometry_file}"), string.Template( "${turbo_turtle_command} partition --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {name}.cae --output-file {name}.cae --model-name {name} " f"--part-name seveneigths-sphere --center 0 0 0 --xvector 1 0 0 --zvector 0 0 1" ), string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {name}.cae --model-name {name} " f"--output-file seveneigths-sphere.png --part-name seveneigths-sphere" ), string.Template( "${turbo_turtle_command} partition --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {name}.cae --output-file {name}.cae --model-name {name} --part-name swiss-cheese " "--center 0 0 0 --xvector 1 0 0 --zvector 0 0 1" ), string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " f"--input-file {name}.cae --model-name {name} --output-file swiss-cheese.png --part-name swiss-cheese" ), ], marks=pytest.mark.abaqus, ) ) # Sphere/partition/mesh system_tests = ( # model/part, inner_radius, outer_radius, angle, y-offset, quadrant, element_type, element_replacement, backend, output_type # noqa: E501 # Abaqus CAE ("sphere.cae", 1., 2., 360., 0., "both", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("solid-sphere.cae", 0., 2., 360., 0., "both", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("axisymmetric.cae", 1., 2., 0., 0., "both", "CAX4", "CAX4R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("quarter-sphere.cae", 1., 2., 90., 0., "both", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("offset-sphere.cae", 1., 2., 360., 1., "both", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("eigth-sphere.cae", 1., 2., 90., 0., "upper", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 ("half-sphere.cae", 1., 2., 360., 0., "upper", "C3D8", "C3D8R", "abaqus", "abaqus"), # fmt: skip # noqa: E241,E501 # Cubit: for Abaqus INP ("sphere.cae", 1., 2., 360., 0., "both", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("solid-sphere.cae", 0., 2., 360., 0., "both", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("axisymmetric.cae", 1., 2., 0., 0., "both", None, "CAX4R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("quarter-sphere.cae", 1., 2., 90., 0., "both", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("offset-sphere.cae", 1., 2., 360., 1., "both", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("eigth-sphere.cae", 1., 2., 90., 0., "upper", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("half-sphere.cae", 1., 2., 360., 0., "upper", None, "C3D8R", "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 # Cubit "element type" is really a "meshing scheme" ("sphere-tets.cae", 1., 2., 360., 0., "both", "tetmesh", None, "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 ("axisymmetric-tri.cae", 1., 2., 0., 0., "both", "trimesh", None, "cubit", "abaqus"), # fmt: skip # noqa: E241,E501 # Cubit: for Genesis INP ("sphere-genesis.cae", 1., 2., 360., 0., "both", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("solid-sphere-genesis.cae", 0., 2., 360., 0., "both", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("axisymmetric-genesis.cae", 1., 2., 0., 0., "both", None, "QUAD", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("quarter-sphere-genesis.cae", 1., 2., 90., 0., "both", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("offset-sphere-genesis.cae", 1., 2., 360., 1., "both", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("eigth-sphere-genesis.cae", 1., 2., 90., 0., "upper", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("half-sphere-genesis.cae", 1., 2., 360., 0., "upper", None, "HEX", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 # Cubit "element type" is really a "meshing scheme" ("sphere-tets-genesis.cae", 1., 2., 360., 0., "both", "tetmesh", "TRI", "cubit", "genesis"), # fmt: skip # noqa: E241,E501 ("axisymmetric-tri-genesis.cae", 1., 2., 0., 0., "both", "trimesh", "TETRA", "cubit", "genesis"), # fmt: skip # noqa: E241,E231,E501 ) for test in system_tests: commands_list.append(setup_sphere_commands(*test)) # Geometry XY Plot tests system_tests = ( # model/part, input_file, backend ("washer", [_settings._project_root_abspath / "tests" / "washer.csv"], "abaqus"), # fmt: skip # noqa: E241 ("vase", [_settings._project_root_abspath / "tests" / "vase.csv"], "abaqus"), # fmt: skip # noqa: E241 ("multi-part", [_settings._project_root_abspath / "tests" / "washer.csv", _settings._project_root_abspath / "tests" / "vase.csv"], "abaqus"), # fmt: skip # noqa: E241 ) for test in system_tests: commands_list.append(setup_geometry_xyplot_commands(*test)) # Geometry tests system_tests = ( # model/part, input_file, angle, y-offset, backend # Abaqus ("washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 ("offset-washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 1., "abaqus"), # fmt: skip # noqa: E241,E501 ("washer-axisymmetric", [_settings._project_root_abspath / "tests" / "washer.csv"], 0.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 ("vase", [_settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 ("vase-axisymmetric", [_settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 ("multi-part-3D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 ("multi-part-2D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "abaqus"), # fmt: skip # noqa: E241,E501 # Cubit ("washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 ("offset-washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 1., "cubit"), # fmt: skip # noqa: E241,E501 ("washer-axisymmetric", [_settings._project_root_abspath / "tests" / "washer.csv"], 0.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 ("vase", [_settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 ("vase-axisymmetric", [_settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 ("multi-part-3D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 ("multi-part-2D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "cubit"), # fmt: skip # noqa: E241,E501 # Gmsh ("washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ("offset-washer", [_settings._project_root_abspath / "tests" / "washer.csv"], 360.0, 1., "gmsh"), # fmt: skip # noqa: E241,E501 ("washer-axisymmetric", [_settings._project_root_abspath / "tests" / "washer.csv"], 0.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ("vase", [_settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ("vase-axisymmetric", [_settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ("multi-part-3D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 360.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ("multi-part-2D", [_settings._project_root_abspath / "tests" / "washer.csv", # fmt: skip # noqa: E241 _settings._project_root_abspath / "tests" / "vase.csv"], 0.0, 0., "gmsh"), # fmt: skip # noqa: E241,E501 ) for test in system_tests: commands_list.append(setup_geometry_commands(*test)) # Sets/mesh tests system_tests = ( # model/part, input_file, angle, face_sets, edge_sets, edge seeds, element_type, backend # Abaqus # TODO: Pick some Abaqus edge sets for the system tests ( "vase", [_settings._project_root_abspath / "tests" / "vase.csv"], 360.0, [["top", "'[#4 ]'"], ["bottom", "'[#40 ]'"]], None, None, "C3D8R", "abaqus", ), ( "vase-axisymmetric", [_settings._project_root_abspath / "tests" / "vase.csv"], 0.0, None, [["top", "'[#10 ]'"], ["bottom", "'[#1 ]'"]], [["top", "5"], ["bottom", "0.5"]], "CAX4", "abaqus", ), # Cubit ( "vase", [_settings._project_root_abspath / "tests" / "vase.csv"], 360.0, [["top", "4"], ["bottom", "7"], ["outer", "'2 3 8'"]], [["top_outer", "13"]], [["top_outer", "1.000001"]], "C3D8R", "cubit", ), ( "vase-axisymmetric", [_settings._project_root_abspath / "tests" / "vase.csv"], 0.0, None, [["top", "2"], ["bottom", "6"], ["outer", "'1 7 8'"]], [["top", "1.000001"]], "CAX4", "cubit", ), ) for test in system_tests: commands_list.append(setup_sets_commands(*test)) # Cylinder tests system_tests = ( # model/part, angle, backend ("cylinder_3d", 360., "abaqus"), # fmt: skip # noqa: E241 ("cylinder_2d", 0., "abaqus"), # fmt: skip # noqa: E241 ("cylinder_3d", 360., "cubit"), # fmt: skip # noqa: E241 ("cylinder_2d", 0., "cubit"), # fmt: skip # noqa: E241 ("cylinder_3d", 360., "gmsh"), # fmt: skip # noqa: E241 ("cylinder_2d", 0., "gmsh"), # fmt: skip # noqa: E241 ) for test in system_tests: commands_list.append(setup_cylinder_commands(*test)) # TODO: Merge Gmsh sphere system tests when partition/mesh/image subcommands are implemented # https://re-git.lanl.gov/aea/python-projects/turbo-turtle/-/issues/217 gmsh_sphere_2D = [ [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=0. --backend gmsh" ) ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=0. " "--backend gmsh --quadrant upper" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=0. " "--backend gmsh --quadrant lower" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=0. --backend gmsh" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=0. " "--backend gmsh --quadrant upper" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=0. " "--backend gmsh --quadrant lower" ), ], ] for test in gmsh_sphere_2D: test.append( string.Template( "${turbo_turtle_command} mesh --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.step --output-file sphere.msh --global-seed 1. --element-type unused " "--backend gmsh" ) ) if not missing_display: test.append( string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.step --output-file sphere.step.png --x-angle 0 --y-angle 0 --backend gmsh" ) ) test.append( string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.msh --output-file sphere.msh.png --x-angle 0 --y-angle 0 --backend gmsh" ) ) commands_list.append(pytest.param(test, marks=pytest.mark.gmsh)) gmsh_sphere_3D = [ [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=360. " "--backend gmsh" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=360. " "--backend gmsh --quadrant upper" ), ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 1. --outer-radius 2. --output-file sphere.step --revolution-angle=360. " "--backend gmsh --quadrant lower" ), ], # TODO: Fix solid sphere revolve # https://re-git.lanl.gov/aea/python-projects/turbo-turtle/-/issues/218 # [ # fmt: skip # noqa: E265 # string.Template( # "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " # "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=360. " # "--backend gmsh" # ) # ], # fmt: skip # noqa: E265 [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=360. " "--backend gmsh --quadrant upper" ) ], [ string.Template( "${turbo_turtle_command} sphere --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--inner-radius 0. --outer-radius 1. --output-file sphere.step --revolution-angle=360. " "--backend gmsh --quadrant lower" ) ], ] for test in gmsh_sphere_3D: test.append( string.Template( "${turbo_turtle_command} mesh --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.step --output-file sphere.msh --global-seed 1. --element-type unused " "--backend gmsh" ) ) if not missing_display: test.append( string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.step --output-file sphere.step.png --x-angle 45 --y-angle -45 " "--backend gmsh" ) ) test.append( string.Template( "${turbo_turtle_command} image --abaqus-command ${abaqus_command} --cubit-command ${cubit_command} " "--input-file sphere.msh --output-file sphere.msh.png --x-angle 45 --y-angle -45 --backend gmsh" ) ) commands_list.append(pytest.param(test, marks=pytest.mark.gmsh)) # Merge tests for part_name in ("washer vase merge-sphere", ""): commands_list.append(setup_merge_commands(part_name, backend="abaqus")) commands_list.append(setup_merge_commands(part_name, backend="cubit")) # SCons extensions tests # System tests as SCons tasks # TODO: Decide how to handle this system test which requires both Abaqus and Cubit. # Separate the SConstruct file into Abaqus/Cubit halves? Dedicated, non-matrixed construction environment? sconstruct = _settings._project_root_abspath / "tests/SConstruct" commands_list.append( pytest.param( [ string.Template( f"scons . --sconstruct={sconstruct} --turbo-turtle-command='${{turbo_turtle_command}}' " "--abaqus-command=${abaqus_command} --backend=abaqus" ) ], marks=pytest.mark.abaqus, ) ) commands_list.append( pytest.param( [ string.Template( f"scons . --sconstruct={sconstruct} --turbo-turtle-command='${{turbo_turtle_command}}' " "--cubit-command=${cubit_command} --backend=cubit" ) ], marks=pytest.mark.cubit, ) ) # User manual example SCons tasks sconstruct_files = [ [ _settings._project_root_abspath / "tutorials/SConstruct", _settings._project_root_abspath / "tutorials/SConscript", ] ] for files in sconstruct_files: space_delimited_files = " ".join([str(path) for path in files]) scons_test_commands = [ string.Template("${turbo_turtle_command} fetch SConstruct SConscript"), # FIXME: Figure out why this command fails on the CI server, but not in local user tests # https://re-git.lanl.gov/aea/python-projects/turbo-turtle/-/issues/159 # f"scons . --turbo-turtle-command="{turbo_turtle_command}"" # fmt: skip # noqa: E265 ] commands_list.append(scons_test_commands)
[docs] @pytest.mark.systemtest @pytest.mark.require_third_party @pytest.mark.parametrize("commands", commands_list) def test_require_third_party(abaqus_command, cubit_command, commands: list) -> None: """Run system tests that require third-party software Executes with a temporary directory that is cleaned up after each test execution. Accepts custom pytest CLI options to specify abaqus and cubit commands .. code-block:: pytest --abaqus-command /my/system/abaqus --cubit-command /my/system/cubit :param abaqus_command: string absolute path to Abaqus executable :param cubit_command: string absolute path to Cubit executable :param command: the full list of command string(s) for the system test """ test_project_shell_commands(abaqus_command, cubit_command, commands)
def run_commands(commands, build_directory, template_substitution={}) -> None: for command in commands: if isinstance(command, string.Template): command = command.substitute(template_substitution) command = shlex.split(command) subprocess.check_output(command, env=env, cwd=build_directory).decode("utf-8") # Help/Usage sign-of-life project_only_commands_list = [string.Template("${turbo_turtle_command} -h")] project_only_commands_list.extend( [string.Template(f"${{turbo_turtle_command}} {subcommand} -h") for subcommand in subcommand_list] ) project_only_commands_list.append(string.Template("${turbo_turtle_command} fetch"))
[docs] @pytest.mark.systemtest @pytest.mark.parametrize("commands", project_only_commands_list) def test_project_shell_commands(abaqus_command, cubit_command, commands: list) -> None: """Run the system tests. Executes with a temporary directory that is cleaned up after each test execution. Accepts custom pytest CLI options to specify abaqus and cubit commands .. code-block:: pytest --abaqus-command /my/system/abaqus --cubit-command /my/system/cubit :param abaqus_command: string absolute path to Abaqus executable :param cubit_command: string absolute path to Cubit executable :param command: the full list of command string(s) for the system test """ template_substitution = { "turbo_turtle_command": turbo_turtle_command, "abaqus_command": abaqus_command, "cubit_command": cubit_command, } if isinstance(commands, str) or isinstance(commands, string.Template): commands = [commands] if installed: with tempfile.TemporaryDirectory() as temp_directory: run_commands(commands, temp_directory, template_substitution=template_substitution) else: with tempfile.TemporaryDirectory(dir=build_directory, ignore_cleanup_errors=True) as temp_directory: run_commands(commands, temp_directory, template_substitution=template_substitution)