diff --git a/doc/changelog.d/2046.test.md b/doc/changelog.d/2046.test.md new file mode 100644 index 0000000000..8b866c300f --- /dev/null +++ b/doc/changelog.d/2046.test.md @@ -0,0 +1 @@ +Add more tests and update some tests \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py index 372d0868be..3545a131d2 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py @@ -359,10 +359,14 @@ def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102 from ansys.api.geometry.v0.repairtools_pb2 import FindStitchFacesRequest + from ..base.conversions import from_measurement_to_server_length + # Create the request - assumes all inputs are valid and of the proper type request = FindStitchFacesRequest( faces=kwargs["body_ids"], - maximum_distance=DoubleValue(value=kwargs["max_distance"]) + maximum_distance=DoubleValue( + value=from_measurement_to_server_length(kwargs["max_distance"]) + ) if kwargs["max_distance"] is not None else None, allow_multiple_bodies=BoolValue(value=kwargs["allow_multiple_bodies"]), diff --git a/tests/integration/files/MissingFaces_AngleDistance.scdocx b/tests/integration/files/MissingFaces_AngleDistance.scdocx index 5ed58d9995..019a45176d 100644 Binary files a/tests/integration/files/MissingFaces_AngleDistance.scdocx and b/tests/integration/files/MissingFaces_AngleDistance.scdocx differ diff --git a/tests/integration/files/Nonmanifold_CheckGeometry.scdocx b/tests/integration/files/Nonmanifold_CheckGeometry.scdocx new file mode 100644 index 0000000000..582e35a140 Binary files /dev/null and b/tests/integration/files/Nonmanifold_CheckGeometry.scdocx differ diff --git a/tests/integration/files/stitch_1200_bodies.dsco b/tests/integration/files/stitch_1200_bodies.dsco deleted file mode 100644 index 08b757fed9..0000000000 Binary files a/tests/integration/files/stitch_1200_bodies.dsco and /dev/null differ diff --git a/tests/integration/files/stitch_300_bodies.dsco b/tests/integration/files/stitch_300_bodies.dsco new file mode 100644 index 0000000000..a5ca0fe9b1 Binary files /dev/null and b/tests/integration/files/stitch_300_bodies.dsco differ diff --git a/tests/integration/test_prepare_tools.py b/tests/integration/test_prepare_tools.py index de197d5148..dd06fa1e65 100644 --- a/tests/integration/test_prepare_tools.py +++ b/tests/integration/test_prepare_tools.py @@ -71,6 +71,11 @@ def test_remove_rounds(modeler: Modeler): result = modeler.prepare_tools.remove_rounds(roundfaces) assert len(design.bodies[0].faces) == 6 assert result is True + result = modeler.prepare_tools.remove_rounds([design.bodies[0].faces[0]]) + # test removing a face that is not a round + assert result is True # remove round always returns True for success + result = modeler.prepare_tools.remove_rounds(None) + assert result == [] def test_share_topology(modeler: Modeler): @@ -97,6 +102,8 @@ def test_share_topology(modeler: Modeler): edges += len(body.edges) assert faces == 13 assert edges == 27 + result = modeler.prepare_tools.share_topology(None) + assert result is False def test_enhanced_share_topology(modeler: Modeler): @@ -113,6 +120,8 @@ def test_enhanced_share_topology(modeler: Modeler): result = modeler.prepare_tools.enhanced_share_topology(design.bodies, 0.000554167, True) assert result.found == 14 assert result.repaired == 14 + result = modeler.prepare_tools.enhanced_share_topology(None, 0, False) + assert result.found == 0 def test_detect_logos(modeler: Modeler): @@ -132,6 +141,12 @@ def test_detect_logos(modeler: Modeler): success = modeler.prepare_tools.find_and_remove_logos(max_height=0.005) assert success is True assert len(body.faces) == 42 + result = modeler.prepare_tools.find_and_remove_logos(None, min_height=0.001, max_height=0.005) + assert result is False + result = modeler.prepare_tools.find_and_remove_logos( + design.components[0].bodies, min_height=0.001, max_height=0.005 + ) + assert result is False def test_detect_and_fix_logo_as_problem_area(modeler: Modeler): @@ -155,3 +170,38 @@ def test_detect_and_fix_logo_as_problem_area(modeler: Modeler): result.fix() assert success is False assert len(design.components[0].bodies[2].faces) == 42 + + +def test_volume_extract_bad_faces(modeler: Modeler): + """Test a volume extract with bad faces.""" + design = modeler.open_file(FILES_DIR / "BoxWithRound.scdocx") + + body = design.bodies[0] + inside_faces = [] + sealing_faces = [body.faces[1], body.faces[4]] + created_bodies = modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + assert len(created_bodies) == 0 + inside_faces = [body.faces[6]] + sealing_faces = [] + created_bodies = modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + assert len(created_bodies) == 0 + inside_faces = [body.faces[0]] + sealing_faces = [body.faces[1]] + created_bodies = modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces) + assert len(created_bodies) == 0 + + +def test_volume_extract_bad_edges(modeler: Modeler): + """Test a volume extract with bad edges.""" + design = modeler.open_file(FILES_DIR / "BoxWithRound.scdocx") + body = design.bodies[0] + sealing_edges = [] + created_bodies = modeler.prepare_tools.extract_volume_from_edge_loops( + sealing_edges, + ) + assert len(created_bodies) == 0 + sealing_edges = [body.edges[0], body.edges[1]] + created_bodies = modeler.prepare_tools.extract_volume_from_edge_loops( + sealing_edges, + ) + assert len(created_bodies) == 0 diff --git a/tests/integration/test_repair_tools.py b/tests/integration/test_repair_tools.py index 1e063d1549..2e0720c2c2 100644 --- a/tests/integration/test_repair_tools.py +++ b/tests/integration/test_repair_tools.py @@ -21,7 +21,24 @@ # SOFTWARE. """ "Testing of repair tools.""" +import pytest + from ansys.geometry.core.modeler import Modeler +from ansys.geometry.core.tools.check_geometry import InspectResult +from ansys.geometry.core.tools.problem_areas import ( + DuplicateFaceProblemAreas, + ExtraEdgeProblemAreas, + InterferenceProblemAreas, + MissingFaceProblemAreas, + ProblemArea, + ShortEdgeProblemAreas, + SmallFaceProblemAreas, + SplitEdgeProblemAreas, + StitchFaceProblemAreas, + UnsimplifiedFaceProblemAreas, +) +from ansys.geometry.core.tools.repair_tool_message import RepairToolMessage +from ansys.geometry.core.tools.repair_tools import RepairTools from .conftest import FILES_DIR @@ -219,7 +236,7 @@ def test_fix_small_face(modeler: Modeler): def test_find_stitch_faces(modeler: Modeler): """Test to read geometry and find it's stitch face problem areas.""" design = modeler.open_file(FILES_DIR / "stitch_before.scdocx") - problem_areas = modeler.repair_tools.find_stitch_faces(design.bodies) + problem_areas = modeler.repair_tools.find_stitch_faces(design.bodies, 0.0001) assert len(problem_areas) == 1 @@ -283,28 +300,28 @@ def test_fix_interference(modeler: Modeler): def test_find_and_fix_stitch_faces(modeler: Modeler): """Test to find and fix stitch faces and validate that we get a solid.""" - design = modeler.open_file(FILES_DIR / "stitch_1200_bodies.dsco") - assert len(design.bodies) == 3600 + design = modeler.open_file(FILES_DIR / "stitch_300_bodies.dsco") + assert len(design.bodies) == 900 - stitch_faces = modeler.repair_tools.find_and_fix_stitch_faces(design.bodies) + stitch_faces = modeler.repair_tools.find_and_fix_stitch_faces(design.bodies, 0.0001) assert stitch_faces.found == 1 assert stitch_faces.repaired == 1 - assert len(design.bodies) == 1200 + assert len(design.bodies) == 300 def test_find_and_fix_stitch_faces_comprehensive(modeler: Modeler): """Test to find and fix stitch faces and validate that we get a solid.""" - design = modeler.open_file(FILES_DIR / "stitch_1200_bodies.dsco") - assert len(design.bodies) == 3600 + design = modeler.open_file(FILES_DIR / "stitch_300_bodies.dsco") + assert len(design.bodies) == 900 stitch_faces = modeler.repair_tools.find_and_fix_stitch_faces( design.bodies, comprehensive_result=True ) - assert stitch_faces.found == 1200 - assert stitch_faces.repaired == 1200 + assert stitch_faces.found == 300 + assert stitch_faces.repaired == 300 - assert len(design.bodies) == 1200 + assert len(design.bodies) == 300 def test_find_and_fix_duplicate_faces(modeler: Modeler): @@ -393,14 +410,16 @@ def test_find_and_fix_missing_faces(modeler: Modeler): def test_find_and_fix_missing_faces_angle_distance(modeler: Modeler): """Test to read geometry, find and fix missing faces specify angle and distance.""" design = modeler.open_file(FILES_DIR / "MissingFaces_AngleDistance.scdocx") - assert len(design.bodies) == 1 - assert len(design.bodies[0].faces) == 11 - missing_faces = modeler.repair_tools.find_missing_faces(design.bodies, 0.785398, 0.0005) + assert len(design.bodies) == 4 + total_faces = sum(len(body.faces) for body in design.bodies) + assert total_faces == 22 + missing_faces = modeler.repair_tools.find_missing_faces(design.bodies, 0.698131, 0.015) assert len(missing_faces) == 4 for face in missing_faces: face.fix() - assert len(design.bodies) == 1 - assert len(design.bodies[0].faces) == 15 + assert len(design.bodies) == 4 + total_faces = sum(len(body.faces) for body in design.bodies) + assert total_faces == 26 def test_find_and_fix_short_edges_problem_areas(modeler: Modeler): @@ -560,3 +579,190 @@ def test_find_and_fix_simplify(modeler: Modeler): assert result assert result.found == 23 assert result.repaired == 23 # There is a SC bug where success is always true + + +def test_design_import_check_geometry(modeler: Modeler): + """Test importing a design with check geometry.""" + # Open the design + design = modeler.open_file(FILES_DIR / "Nonmanifold_CheckGeometry.scdocx") + inspect_results = modeler.repair_tools.inspect_geometry(design.bodies) + + # Assert the number of inspect results and issues + assert len(inspect_results) == 1 + issues = inspect_results[0].issues + assert len(issues) == 5 + + # Expected messages, message IDs, and message types + expected_data = [ + { + "message": "Geometry intersects itself.", + "message_id": 26, + "message_type": 3, + "faces": 0, + "edges": 0, + }, + { + "message": "Geometry intersects itself.", + "message_id": 26, + "message_type": 3, + "faces": 0, + "edges": 0, + }, + { + "message": "Geometry intersects itself.", + "message_id": 26, + "message_type": 3, + "faces": 0, + "edges": 0, + }, + { + "message": "Geometry intersects itself.", + "message_id": 26, + "message_type": 3, + "faces": 0, + "edges": 0, + }, + { + "message": "Face illegally intersects or abuts another face.", + "message_id": 25, + "message_type": 3, + "faces": 2, + "edges": 0, + }, + ] + + # Convert issues and expected data to sets of tuples for comparison + actual_data = { + ( + issue.message, + issue.message_id, + issue.message_type, + len(issue.faces), + len(issue.edges), + ) + for issue in issues + } + + expected_data_set = { + ( + item["message"], + item["message_id"], + item["message_type"], + item["faces"], + item["edges"], + ) + for item in expected_data + } + + # Assert that the actual data matches the expected data regardless of order + assert actual_data == expected_data_set + + # Test repair functionality + repair_message = inspect_results[0].repair() + assert repair_message.success is True + assert repair_message.created_bodies == [] + assert repair_message.modified_bodies == [] + + +def test_repair_no_body(modeler: Modeler): + """Test the repair method when body is None.""" + grpc_client = modeler.client + # Create an instance of InspectResult with body set to None + inspect_result = InspectResult(grpc_client=grpc_client, body=None, issues=[]) + # Call the repair method + repair_message = inspect_result.repair() + # Assert the returned RepairToolMessage + assert isinstance(repair_message, RepairToolMessage) + assert repair_message.success is False + assert repair_message.created_bodies == [] + assert repair_message.modified_bodies == [] + + +def test_problem_area_fix_not_implemented(modeler: Modeler): + """Test that the fix method in the ProblemArea base class raises NotImplementedError.""" + grpc_client = modeler.client + # Create an instance of ProblemArea + problem_area = ProblemArea(id="123", grpc_client=grpc_client) + # Assert that calling fix raises NotImplementedError + with pytest.raises( + NotImplementedError, match="Fix method is not implemented in the base class." + ): + problem_area.fix() + + +@pytest.mark.parametrize( + "problem_area_class, kwargs", + [ + (DuplicateFaceProblemAreas, {"faces": []}), + (MissingFaceProblemAreas, {"edges": []}), + (ExtraEdgeProblemAreas, {"edges": []}), + (ShortEdgeProblemAreas, {"edges": []}), + (SmallFaceProblemAreas, {"faces": []}), + (SplitEdgeProblemAreas, {"edges": []}), + (StitchFaceProblemAreas, {"bodies": []}), + (UnsimplifiedFaceProblemAreas, {"faces": []}), + (InterferenceProblemAreas, {"bodies": []}), + ], +) +def test_problem_area_fix_no_data(modeler: Modeler, problem_area_class, kwargs): + """Test the fix method for various ProblemArea subclasses when required attributes are empty.""" + grpc_client = modeler.client + problem_area = problem_area_class(id="123", grpc_client=grpc_client, **kwargs) + repair_message = problem_area.fix() + assert isinstance(repair_message, RepairToolMessage) + assert repair_message.success is False + assert repair_message.created_bodies == [] + assert repair_message.modified_bodies == [] + + +@pytest.mark.parametrize( + "method_name", + [ + "find_split_edges", + "find_extra_edges", + "find_inexact_edges", + "find_short_edges", + "find_duplicate_faces", + "find_missing_faces", + "find_small_faces", + "find_interferences", + ], +) +def test_repair_tools_no_bodies(modeler: Modeler, method_name): + """Test RepairTools methods when bodies is empty or None.""" + grpc_client = modeler.client + repair_tools = RepairTools(grpc_client, modeler) + method = getattr(repair_tools, method_name) + + # Test with an empty list of bodies + result = method(bodies=[]) + assert result == [] + + # Test with None as bodies + result = method(bodies=None) + assert result == [] + + +@pytest.mark.parametrize( + "method_name", + [ + "find_and_fix_short_edges", + "find_and_fix_extra_edges", + "find_and_fix_split_edges", + "find_and_fix_simplify", + ], +) +def test_repair_tools_find_and_fix_no_bodies(modeler: Modeler, method_name): + """Test RepairTools find_and_fix methods when bodies is empty or None.""" + grpc_client = modeler.client + repair_tools = RepairTools(grpc_client, modeler) + method = getattr(repair_tools, method_name) + + # Test with an empty list of bodies + result = method(bodies=[]) + assert isinstance(result, RepairToolMessage) + assert result.success is False + assert result.created_bodies == [] + assert result.modified_bodies == [] + assert result.found == 0 + assert result.repaired == 0