turbo_turtle._tests.test_wrappers — turbo-turtle 1.2.9 documentation

Skip to main content
`Ctrl`+`K`

#

# Source code for turbo_turtle._tests.test_wrappers

    """Test the Python 3 wrappers of third-party interfaces."""

    import argparse
    import copy
    import typing
    from unittest.mock import patch

    import pytest

    from turbo_turtle import _abaqus_wrappers, _gmsh_wrappers

    command = "/dummy/command"

    geometry_namespace_sparse = {
        "input_file": ["input_file"],
        "output_file": "output_file",
        "unit_conversion": 1.0,
        "euclidean_distance": 1.0,
        "planar": None,
        "model_name": "model_name",
        "part_name": [None],
        "delimiter": ",",
        "header_lines": 0,
        "revolution_angle": 360.0,
        "y_offset": 0.0,
        "rtol": None,
        "atol": None,
    }
    geometry_namespace_full = copy.deepcopy(geometry_namespace_sparse)
    (geometry_namespace_full.update({"planar": True, "part_name": ["part_name"], "rtol": 1.0e-9, "atol": 1.0e-9}),)
    geometry_expected_options_sparse = [
        "--input-file",
        "--output-file",
        "--unit-conversion",
        "--euclidean-distance",
        "--model-name",
        "--delimiter",
        "--header-lines",
        "--revolution-angle",
        "--y-offset",
    ]
    geometry_unexpected_options_sparse = ["--planar", "--part-name", "--atol", "--rtol"]

    cylinder_namespace = {
        "inner_radius": 1.0,
        "outer_radius": 2.0,
        "height": 1.0,
        "output_file": "output_file",
        "model_name": "model_name",
        "part_name": "part_name",
        "revolution_angle": 360.0,
        "y_offset": 0.0,
    }
    cylinder_expected_options = [
        command,
        "--inner-radius",
        "--outer-radius",
        "--height",
        "--output-file",
        "--model-name",
        "--part-name",
        "--revolution-angle",
        "--y-offset",
    ]

    sphere_namespace_sparse = {
        "inner_radius": 1.0,
        "outer_radius": 2.0,
        "output_file": "output_file",
        "input_file": None,
        "quadrant": "both",
        "revolution_angle": 360.0,
        "y_offset": 0.0,
        "model_name": "model_name",
        "part_name": "part_name",
    }
    sphere_namespace_full = copy.deepcopy(sphere_namespace_sparse)
    (sphere_namespace_full.update({"input_file": "input_file"}),)
    sphere_expected_options_sparse = [
        command,
        "--inner-radius",
        "--outer-radius",
        "--output-file",
        "--quadrant",
        "--revolution-angle",
        "--y-offset",
        "--model-name",
        "--part-name",
    ]
    sphere_unexpected_options_sparse = ["--input-file"]

    partition_namespace_sparse = {
        "input_file": "input_file",
        "output_file": None,
        "center": (0.0, 0.0, 0.0),
        "xvector": (0.0, 0.0, 0.0),
        "zvector": (0.0, 0.0, 0.0),
        "model_name": "model_name",
        "part_name": ["part_name"],
        "big_number": 0.0,
    }
    partition_namespace_full = copy.deepcopy(partition_namespace_sparse)
    (partition_namespace_full.update({"output_file": "output_file"}),)
    partition_expected_options_sparse = [
        command,
        "--input-file",
        "--center",
        "--xvector",
        "--zvector",
        "--model-name",
        "--part-name",
        "--big-number",
    ]
    partition_unexpected_options_sparse = ["--output-file"]

    sets_namespace_sparse = {
        "input_file": "input_file",
        "output_file": None,
        "model_name": "model_name",
        "part_name": "part_name",
        "face_sets": None,
        "edge_sets": None,
        "vertex_sets": None,
    }
    sets_namespace_full = copy.deepcopy(sets_namespace_sparse)
    sets_namespace_full.update(
        {
            "output_file": "output_file",
            "face_sets": [["name1", "2"]],
            "edge_sets": [["name2", "4"]],
            "vertex_sets": [["name3", "6"]],
        }
    )
    sets_expected_options_sparse = [command, "--input-file", "--model-name", "--part-name"]
    sets_unexpected_options_sparse = ["--output-file", "--face-set", "--edge-set", "--vertex-set"]

    mesh_namespace_sparse = {
        "input_file": "input_file",
        "element_type": "element_type",
        "output_file": None,
        "model_name": "model_name",
        "part_name": "part_name",
        "global_seed": "global_seed",
        "edge_seeds": None,
    }
    mesh_namespace_full = copy.deepcopy(mesh_namespace_sparse)
    (mesh_namespace_full.update({"output_file": "output_file", "edge_seeds": [["name", "1"]]}),)
    mesh_expected_options_sparse = [
        command,
        "--input-file",
        "--element-type",
        "--model-name",
        "--part-name",
        "--global-seed",
    ]
    mesh_unexpected_options_sparse = ["--output-file", "--edge-seed"]

    merge_namespace_sparse = {
        "input_file": ["input_file"],
        "output_file": "output_file",
        "merged_model_name": "merged_model_name",
        "model_name": [None],
        "part_name": [None],
    }
    merge_namespace_full = copy.deepcopy(merge_namespace_sparse)
    (
        merge_namespace_full.update(
            {
                "model_name": ["model_name"],
                "part_name": ["part_name"],
            }
        ),
    )
    merge_expected_options_sparse = [
        "--input-file",
        "--output-file",
        "--merged-model-name",
    ]
    merge_unexpected_options_sparse = ["--model-name", "--part-name"]

    export_namespace_sparse = {
        "input_file": "input_file",
        "model_name": "model_name",
        "part_name": ["part_name"],
        "element_type": [None],
        "destination": ".",
        "assembly": None,
    }
    export_namespace_full = copy.deepcopy(export_namespace_sparse)
    (
        export_namespace_full.update(
            {
                "element_type": ["element_type"],
                "assembly": True,
            }
        ),
    )
    export_expected_options_sparse = ["--input-file", "--model-name", "--part-name", "--destination"]
    export_unexpected_options_sparse = ["--element-type", "--assembly"]

    image_namespace_sparse = {
        "input_file": "input_file",
        "output_file": "output_file",
        "x_angle": 0.0,
        "y_angle": 0.0,
        "z_angle": 0.0,
        "image_size": [1, 2],
        "model_name": "model_name",
        "part_name": None,
        "color_map": "color_map",
    }
    image_namespace_full = copy.deepcopy(image_namespace_sparse)
    (image_namespace_full.update({"part_name": "part_name"}),)
    image_expected_options_sparse = [
        command,
        "--input-file",
        "--output-file",
        "--x-angle",
        "--y-angle",
        "--z-angle",
        "--image-size",
        "--model-name",
        "--color-map",
    ]
    image_unexpected_options_sparse = ["--part-name"]

    wrapper_tests = {
        "cylinder": ("cylinder", cylinder_namespace, cylinder_expected_options, []),
        "geometry: sparse": (
            "geometry",
            geometry_namespace_sparse,
            geometry_expected_options_sparse,
            geometry_unexpected_options_sparse,
        ),
        "geometry: full": (
            "geometry",
            geometry_namespace_full,
            geometry_expected_options_sparse + geometry_unexpected_options_sparse,
            [],
        ),
        "sphere: sparse": (
            "sphere",
            sphere_namespace_sparse,
            sphere_expected_options_sparse,
            sphere_unexpected_options_sparse,
        ),
        "sphere: full": (
            "sphere",
            sphere_namespace_full,
            sphere_expected_options_sparse + sphere_unexpected_options_sparse,
            [],
        ),
        "partition: sparse": (
            "partition",
            partition_namespace_sparse,
            partition_expected_options_sparse,
            partition_unexpected_options_sparse,
        ),
        "partition: full": (
            "partition",
            partition_namespace_full,
            partition_expected_options_sparse + partition_unexpected_options_sparse,
            [],
        ),
        "sets: sparse": (
            "sets",
            sets_namespace_sparse,
            sets_expected_options_sparse,
            sets_unexpected_options_sparse,
        ),
        "sets: full": (
            "sets",
            sets_namespace_full,
            sets_expected_options_sparse + sets_unexpected_options_sparse,
            [],
        ),
        "mesh: sparse": (
            "mesh",
            mesh_namespace_sparse,
            mesh_expected_options_sparse,
            mesh_unexpected_options_sparse,
        ),
        "mesh: full": (
            "mesh",
            mesh_namespace_full,
            mesh_expected_options_sparse + mesh_unexpected_options_sparse,
            [],
        ),
        "merge: sparse": (
            "merge",
            merge_namespace_sparse,
            merge_expected_options_sparse,
            merge_unexpected_options_sparse,
        ),
        "merge: full": (
            "merge",
            merge_namespace_full,
            merge_expected_options_sparse + merge_unexpected_options_sparse,
            [],
        ),
        "export: sparse": (
            "export",
            export_namespace_sparse,
            export_expected_options_sparse,
            export_unexpected_options_sparse,
        ),
        "export: full": (
            "export",
            export_namespace_full,
            export_expected_options_sparse + export_unexpected_options_sparse,
            [],
        ),
        "image: sparse": (
            "image",
            image_namespace_sparse,
            image_expected_options_sparse,
            image_unexpected_options_sparse,
        ),
        "image: full": (
            "image",
            image_namespace_full,
            image_expected_options_sparse + image_unexpected_options_sparse,
            [],
        ),
    }

    @pytest.mark.parametrize(
        "subcommand, namespace, expected_options, unexpected_options",
        wrapper_tests.values(),
        ids=wrapper_tests.keys(),
    )
    def test_abaqus_wrappers(
        subcommand: str, namespace: dict[str, typing.Any], expected_options: list[str], unexpected_options: list[str]
    ) -> None:
        """Test the :mod:`turbo_turtle._abaqus_wrappers` module."""
        args = argparse.Namespace(**namespace)
        with patch("turbo_turtle._utilities.run_command") as mock_run:
            subcommand_wrapper = getattr(_abaqus_wrappers, subcommand)
            subcommand_wrapper(args, command)
        mock_run.assert_called_once()
        command_string = mock_run.call_args[0][0]
        for option in expected_options:
            assert option in command_string
        for option in unexpected_options:
            assert option not in command_string

    def trim_namespace(original: dict, pop_keys: typing.Sequence[str]) -> dict:
        """Create a modified dictionary deepcopy by removing the provided keys.

        :returns: Modified dictionary deepcopy with pop keys removed
        :rtype: dict
        """
        modified = copy.deepcopy(original)
        for key in pop_keys:
            if key in modified:
                modified.pop(key)
        return modified

    def test_trim_namespace() -> None:
        """Test :func:`.trim_namespace` function."""
        original = {"keep": "keep", "pop": "pop"}
        modified = trim_namespace(original, ("pop",))
        assert modified == {"keep": "keep"}

    geometry_positional = ("input_file", "output_file")
    geometry_unused = ("model_name",)
    geometry_keywords = trim_namespace(geometry_namespace_sparse, geometry_positional + geometry_unused)

    cylinder_positional = ("inner_radius", "outer_radius", "height", "output_file")
    cylinder_unused = ("model_name",)
    cylinder_keywords = trim_namespace(cylinder_namespace, cylinder_positional + cylinder_unused)

    sphere_positional = ("inner_radius", "outer_radius", "output_file")
    sphere_unused = ("model_name",)
    sphere_keywords = trim_namespace(sphere_namespace_sparse, sphere_positional + sphere_unused)

    partition_positional = ("input_file",)
    partition_unused = ("model_name",)
    partition_keywords = trim_namespace(partition_namespace_sparse, partition_positional + partition_unused)

    mesh_positional = ("input_file", "element_type")
    mesh_unused = ("model_name",)
    mesh_keywords = trim_namespace(mesh_namespace_sparse, mesh_positional + mesh_unused)

    merge_positional = ("input_file", "output_file")
    merge_unused = ("model_name", "merged_model_name", "part_name")
    merge_keywords = trim_namespace(merge_namespace_sparse, merge_positional + merge_unused)

    export_namespace_cubit = copy.deepcopy(export_namespace_sparse)
    export_namespace_cubit["output_type"] = "output_type"
    export_positional = ("input_file",)
    export_unused = ("model_name", "assembly")
    export_keywords = trim_namespace(export_namespace_cubit, export_positional + export_unused)

    image_positional = ("input_file", "output_file", "command")
    image_unused = ("model_name", "part_name", "color_map")
    image_keywords = trim_namespace(image_namespace_sparse, image_positional + image_unused)

    cubit_wrapper_tests = {
        "geometry": ("geometry", geometry_namespace_sparse, (["input_file"], "output_file"), geometry_keywords),
        "cylinder": ("cylinder", cylinder_namespace, (1.0, 2.0, 1.0, "output_file"), cylinder_keywords),
        "sphere": ("sphere", sphere_namespace_sparse, (1.0, 2.0, "output_file"), sphere_keywords),
        "partition": ("partition", partition_namespace_sparse, ("input_file",), partition_keywords),
        "mesh": ("mesh", mesh_namespace_sparse, ("input_file", "element_type"), mesh_keywords),
        "merge": ("merge", merge_namespace_sparse, (["input_file"], "output_file"), merge_keywords),
        "export": ("export", export_namespace_cubit, ("input_file",), export_keywords),
        "image": ("image", image_namespace_sparse, ("input_file", "output_file", command), image_keywords),
    }

    @pytest.mark.parametrize(
        "subcommand, namespace, positional, keywords",
        cubit_wrapper_tests.values(),
        ids=cubit_wrapper_tests.keys(),
    )
    def test_cubit_wrappers(
        subcommand: str, namespace: dict[str, typing.Any], positional: tuple[str], keywords: dict[str, typing.Any]
    ) -> None:
        """Test the :mod:`turbo_turtle._cubit_wrappers` module."""
        args = argparse.Namespace(**namespace)
        with (
            patch("turbo_turtle._utilities.import_cubit"),
            patch(f"turbo_turtle._cubit_python.{subcommand}") as mock_function,
        ):
            # Third-party module requires Cubit imports, which require special handling.
            from turbo_turtle import _cubit_wrappers  # noqa: PLC0415

            subcommand_wrapper = getattr(_cubit_wrappers, subcommand)
            subcommand_wrapper(args, command)
        mock_function.assert_called_once()
        call_positional = mock_function.call_args[0]
        call_keywords = mock_function.call_args[1]
        assert call_positional == positional
        assert call_keywords == keywords

    geometry_keywords = trim_namespace(geometry_namespace_sparse, geometry_positional)
    cylinder_keywords = trim_namespace(cylinder_namespace, cylinder_positional)
    sphere_keywords = trim_namespace(sphere_namespace_sparse, sphere_positional)
    partition_keywords = trim_namespace(partition_namespace_sparse, partition_positional)
    mesh_keywords = trim_namespace(mesh_namespace_sparse, mesh_positional)
    merge_keywords = trim_namespace(merge_namespace_sparse, merge_positional)
    export_keywords = trim_namespace(export_namespace_cubit, export_positional)
    image_keywords = trim_namespace(image_namespace_sparse, image_positional + image_unused)
    gmsh_wrapper_tests = {
        "geometry": (
            "geometry",
            geometry_namespace_sparse,
            (["input_file"], "output_file"),
            geometry_keywords,
        ),
        "cylinder": (
            "cylinder",
            cylinder_namespace,
            (1.0, 2.0, 1.0, "output_file"),
            cylinder_keywords,
        ),
        "sphere": (
            "sphere",
            sphere_namespace_sparse,
            (1.0, 2.0, "output_file"),
            sphere_keywords,
        ),
        "partition": (
            "partition",
            partition_namespace_sparse,
            ("input_file",),
            partition_keywords,
        ),
        "mesh": (
            "mesh",
            mesh_namespace_sparse,
            ("input_file", "element_type"),
            mesh_keywords,
        ),
        "merge": (
            "merge",
            merge_namespace_sparse,
            (["input_file"], "output_file"),
            merge_keywords,
        ),
        "export": (
            "export",
            export_namespace_cubit,
            ("input_file",),
            export_keywords,
        ),
        "image": (
            "image",
            image_namespace_sparse,
            ("input_file", "output_file"),
            image_keywords,
        ),
    }

    @pytest.mark.parametrize(
        "subcommand, namespace, positional, keywords",
        gmsh_wrapper_tests.values(),
        ids=gmsh_wrapper_tests.keys(),
    )
    def test_gmsh_wrappers(
        subcommand: str, namespace: dict[str, typing.Any], positional: tuple[str], keywords: dict[str, typing.Any]
    ) -> None:
        """Test the :mod:`turbo_turtle._gmsh_wrappers` module."""
        args = argparse.Namespace(**namespace)
        implemented = ["geometry", "cylinder", "sphere", "mesh", "image"]
        if subcommand in implemented:
            with (
                patch("turbo_turtle._utilities.import_gmsh"),
                patch(f"turbo_turtle._gmsh_python.{subcommand}") as mock_function,
            ):
                subcommand_wrapper = getattr(_gmsh_wrappers, subcommand)
                subcommand_wrapper(args, command)
            mock_function.assert_called_once()
            call_positional = mock_function.call_args[0]
            call_keywords = mock_function.call_args[1]
            assert call_positional == positional
            assert call_keywords == keywords
        else:
            with (
                patch("turbo_turtle._utilities.import_gmsh"),
                patch(f"turbo_turtle._gmsh_python.{subcommand}") as mock_function,
                pytest.raises(RuntimeError),
            ):
                subcommand_wrapper = getattr(_gmsh_wrappers, subcommand)
                subcommand_wrapper(args, command)