Skip to content

Commit f7e8d3f

Browse files
syscordanpyansys-ci-botRobPasMue
authored
test: adding test coverage for nurbs, design, and geometry commands (#2260)
Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: Roberto Pastor Muela <[email protected]>
1 parent 4b76c44 commit f7e8d3f

File tree

6 files changed

+274
-0
lines changed

6 files changed

+274
-0
lines changed

doc/changelog.d/2260.test.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Adding test coverage for nurbs, design, and geometry commands

tests/_incompatible_tests.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ backends:
5858
- tests/integration/test_spaceclaim_tutorial_examples.py::test_combine_example
5959
- tests/integration/test_spaceclaim_tutorial_examples.py::test_pull_example
6060
- tests/integration/test_spaceclaim_tutorial_examples.py::test_intersect_example
61+
- tests/integration/test_design.py::test_legacy_export_download
62+
- tests/integration/test_design.py::test_updating_design_from_tracker
63+
- tests/integration/test_design.py::test_failure_for_export
6164
# Opening large files through streaming is only available from 25.2 onwards
6265
- tests/integration/test_design.py::test_modeler_open_files
6366
# Named selections elements are only consistent from 25.2 onwards
@@ -83,6 +86,8 @@ backends:
8386
- tests/integration/test_design_import.py::test_design_import_solid_edge2025
8487
- tests/integration/test_design_import.py::test_design_import_nx2412
8588
- tests/integration/test_design_import.py::test_design_import_inventor2026
89+
- tests/integration/test_design.py::test_legacy_export_download
90+
- tests/integration/test_design.py::test_failure_for_export
8691
# Bug fix included from 25.2 onwards
8792
- tests/integration/test_issues.py::test_issue_1192_temp_body_on_empty_intersect
8893
- tests/integration/test_issues.py::test_issue_2251_double_import_crash
@@ -169,6 +174,9 @@ backends:
169174
- tests/integration/test_spaceclaim_tutorial_examples.py::test_combine_example
170175
- tests/integration/test_spaceclaim_tutorial_examples.py::test_pull_example
171176
- tests/integration/test_spaceclaim_tutorial_examples.py::test_intersect_example
177+
- tests/integration/test_design.py::test_legacy_export_download
178+
- tests/integration/test_design.py::test_updating_design_from_tracker
179+
- tests/integration/test_design.py::test_failure_for_export
172180
# Opening large files through streaming is only available from 25.2 onwards
173181
- tests/integration/test_design.py::test_modeler_open_files
174182
# Named selections elements are only consistent from 25.2 onwards
@@ -203,6 +211,8 @@ backends:
203211
- tests/integration/test_design_import.py::test_design_import_solid_edge2025
204212
- tests/integration/test_design_import.py::test_design_import_nx2412
205213
- tests/integration/test_design_import.py::test_design_import_inventor2026
214+
- tests/integration/test_design.py::test_legacy_export_download
215+
- tests/integration/test_design.py::test_failure_for_export
206216
# Insert file naming convention changed in 26.1
207217
- tests/integration/test_design_import.py::test_design_insert
208218
# Bug fix included from 25.2 onwards
@@ -230,6 +240,9 @@ backends:
230240
- tests/integration/test_spaceclaim_tutorial_examples.py::test_combine_example
231241
- tests/integration/test_spaceclaim_tutorial_examples.py::test_pull_example
232242
- tests/integration/test_spaceclaim_tutorial_examples.py::test_intersect_example
243+
- tests/integration/test_design.py::test_legacy_export_download
244+
- tests/integration/test_design.py::test_updating_design_from_tracker
245+
- tests/integration/test_design.py::test_failure_for_export
233246
# Opening large files through streaming is only available from 25.2 onwards
234247
- tests/integration/test_design.py::test_modeler_open_files
235248
# Named selections elements are only consistent from 25.2 onwards
@@ -264,6 +277,8 @@ backends:
264277
- tests/integration/test_design_import.py::test_design_import_jt
265278
- tests/integration/test_design_import.py::test_design_import_solid_edge2025
266279
- tests/integration/test_design_import.py::test_design_import_nx2412
280+
- tests/integration/test_design.py::test_legacy_export_download
281+
- tests/integration/test_design.py::test_failure_for_export
267282
# Insert file naming convention changed in 26.1
268283
- tests/integration/test_design_import.py::test_design_insert
269284
# Importing named selections from design was not available prior to 26.1
@@ -315,6 +330,8 @@ backends:
315330
- tests/integration/test_design_export.py::test_import_export_open_file_design[PARASOLID_TEXT-x_t-rci_std.x_t-2-1]
316331
- tests/integration/test_design_export.py::test_import_export_open_file_design[SCDOCX-scdocx-reactorWNS.scdocx-1-3]
317332
- tests/integration/test_design_import.py::test_design_insert_id_bug
333+
- tests/integration/test_design.py::test_legacy_export_download
334+
- tests/integration/test_design.py::test_failure_for_export
318335
# Insert file naming convention changed in 26.1
319336
- tests/integration/test_design_import.py::test_design_insert
320337
# Importing named selections from design was not available prior to 26.1
56.6 KB
Binary file not shown.

tests/integration/test_design.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from ansys.geometry.core.connection import BackendType
3535
import ansys.geometry.core.connection.defaults as pygeom_defaults
3636
from ansys.geometry.core.designer import (
37+
Component,
3738
CurveType,
3839
DesignFileFormat,
3940
MidSurfaceOffsetType,
@@ -3911,6 +3912,118 @@ def test_write_body_facets_on_save(
39113912
assert not missing
39123913

39133914

3915+
def test_updating_design_from_tracker(modeler: Modeler):
3916+
# Adding coverage for handling the tracker response when updating the design
3917+
design = modeler.open_file(
3918+
Path(FILES_DIR, "boxes_w_mat.scdocx")
3919+
) # ,read_existing_design=True))
3920+
# Creating the tracker response that modifies, deletes and creates bodies
3921+
tracker_response = {
3922+
"modified_bodies": [
3923+
{"id": "body1", "name": "ModifiedBody1", "is_surface": True},
3924+
{"id": "7", "name": "Fake_Mat", "is_surface": False},
3925+
{"id": "10", "name": "Steel", "is_surface": False},
3926+
],
3927+
"deleted_bodies": [
3928+
{"id": "11:1", "name": "DeletedBody2"},
3929+
{"id": "7:515/7:518", "name": "DeletedBody2"},
3930+
{"id": "test", "name": "SubComponentBody"},
3931+
],
3932+
"created_bodies": [
3933+
{
3934+
"id": "body100",
3935+
"name": "ExistingBody",
3936+
"is_surface": False,
3937+
"parent_id": "Component1",
3938+
},
3939+
],
3940+
}
3941+
# Adding needed bodies and components that are modified
3942+
design.bodies.append(
3943+
MasterBody("body100", "ExistingBody", design._grpc_client, is_surface=False)
3944+
)
3945+
design.components[2].components.append(
3946+
Component("SubComponent", design.components[2], design._grpc_client, preexisting_id="sub1")
3947+
)
3948+
design.components[2].components[0].components.append(
3949+
Component(
3950+
"SubComponent2",
3951+
design.components[2].components[0],
3952+
design._grpc_client,
3953+
preexisting_id="sub2",
3954+
)
3955+
)
3956+
design.components[2].components[0].bodies.append(
3957+
MasterBody("test", "SubComponentBody", design._grpc_client, is_surface=True)
3958+
)
3959+
# Changing the ids of the bodies to match those in the tracker response
3960+
design.components[1].bodies[0]._id = "7"
3961+
design.bodies[0]._id = "10"
3962+
result = design._update_from_tracker(tracker_response)
3963+
assert result is None
3964+
# Then we create particular body_into statements to hit lines not hit when calling
3965+
# the _update_from_tracker method
3966+
body_info = {
3967+
"id": "body1",
3968+
"name": "UpdatedSubComponentBody",
3969+
"is_surface": False,
3970+
"parent_id": "sub1",
3971+
}
3972+
design._find_and_update_body(body_info, design.components[2])
3973+
top_component = Component("TopComponent", design, design._grpc_client, preexisting_id="top1")
3974+
design.components.append(top_component)
3975+
body_info = {
3976+
"id": "body1",
3977+
"name": "NewBody",
3978+
"is_surface": True,
3979+
"parent_id": "top1",
3980+
}
3981+
result = design._find_and_add_body(body_info, design.components)
3982+
3983+
3984+
def test_legacy_export_download(
3985+
modeler: Modeler, tmp_path_factory: pytest.TempPathFactory, use_grpc_client_old_backend: Modeler
3986+
):
3987+
# Test is meant to add test coverage for using an old backend to export and download
3988+
# Creating the directory and file to export
3989+
working_directory = tmp_path_factory.mktemp("test_import_export_reimport")
3990+
original_file = Path(FILES_DIR, "reactorWNS.scdocx")
3991+
reexported_file = Path(working_directory, "reexported.scdocx")
3992+
design = modeler.create_design("Assembly")
3993+
design.insert_file(original_file)
3994+
# The following lines export the file to scdocx and parasolid
3995+
design.download(reexported_file, format=DesignFileFormat.SCDOCX)
3996+
reexported_file = Path(working_directory, "reexported.x_t")
3997+
design.download(reexported_file, format=DesignFileFormat.PARASOLID_TEXT)
3998+
# This is testing out exporting out to a non-supported format
3999+
reexported_file = Path(working_directory, "reexported.stride")
4000+
with pytest.raises(
4001+
TypeError, match="memoryview: a bytes-like object is required, not 'NoneType'"
4002+
):
4003+
design.download(reexported_file, format=DesignFileFormat.STRIDE)
4004+
4005+
4006+
def test_failure_for_export(modeler: Modeler, tmp_path_factory: pytest.TempPathFactory):
4007+
# # Creating the directory and file to export
4008+
working_directory = tmp_path_factory.mktemp("test_import_export_reimport")
4009+
original_file = Path(FILES_DIR, "reactorWNS.scdocx")
4010+
reexported_file = Path(working_directory, "reexported.scdocx")
4011+
design = modeler.create_design("Assembly")
4012+
design.insert_file(original_file)
4013+
# Giving the download an incorrect file extension in the file path for the chosen format
4014+
reexported_file = Path(working_directory, "reexported.x_t")
4015+
with pytest.raises(
4016+
GeometryExitedError,
4017+
match="Geometry service connection terminated: The method or operation is not implemented.",
4018+
):
4019+
design.download(file_location=reexported_file, format=DesignFileFormat.STEP)
4020+
# Exporting to the invalid type to get an error
4021+
with pytest.raises(
4022+
TypeError, match="memoryview: a bytes-like object is required, not 'NoneType'"
4023+
):
4024+
design.download(file_location=reexported_file, format=DesignFileFormat.INVALID)
4025+
4026+
39144027
def test_combine_merge(modeler: Modeler):
39154028
design = modeler.create_design("combine_merge")
39164029
box1 = design.extrude_sketch("box1", Sketch().box(Point2D([0, 0]), 1, 1), 1)

tests/integration/test_geometry_commands.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
DraftSide,
3030
ExtrudeType,
3131
FillPatternType,
32+
GeometryCommands,
3233
OffsetMode,
3334
)
3435
from ansys.geometry.core.math import Plane, Point2D, Point3D, UnitVector3D
@@ -1407,6 +1408,99 @@ def test_thicken_surface_body(modeler: Modeler):
14071408
)
14081409

14091410

1411+
def test_failures_to_extrude(modeler: Modeler):
1412+
"""Add test that covers code for failing to extrude in different ways."""
1413+
# First need to create surface to extrude from
1414+
sketch = Sketch()
1415+
p1 = Point2D([0, 0])
1416+
p2 = Point2D([10, 0])
1417+
p3 = Point2D([10, 5])
1418+
p4 = Point2D([0, 5])
1419+
sketch.segment(p1, p2)
1420+
sketch.segment(p2, p3)
1421+
sketch.segment(p3, p4)
1422+
sketch.segment(p4, p1)
1423+
design = modeler.create_design("SimpleFaceDesign")
1424+
surface = design.create_surface("SimpleFace", sketch)
1425+
grpc_client = modeler.client
1426+
geometry_commands = GeometryCommands(grpc_client, _internal_use=True)
1427+
1428+
# Failing to extrude single face
1429+
results = geometry_commands.extrude_faces(
1430+
faces=surface.faces[0],
1431+
distance=10,
1432+
direction=UnitVector3D([1, 0, 0]),
1433+
extrude_type=ExtrudeType.ADD,
1434+
pull_symmetric=False,
1435+
)
1436+
assert results == []
1437+
# Then adding a check to ensure no new faces were made
1438+
assert len(design.bodies[0].faces) == 1
1439+
# For the rest of the failures, we need a 2nd surface so here is creating the circle
1440+
circle_sketch = Sketch()
1441+
circle_sketch.circle(Point2D([20, 20]), 5.0)
1442+
circle_surface = design.create_surface("CircleSurface", circle_sketch)
1443+
# The square surface needs to be rotate to make some of the extrustion not valid
1444+
surface.rotate(Point3D([0, 0, 0]), UnitVector3D([0, 1, 0]), np.pi / 2)
1445+
# Failing to extrude edge
1446+
results = geometry_commands.extrude_edges(
1447+
edges=circle_surface.edges[0],
1448+
distance=10,
1449+
from_face=surface.faces[0],
1450+
)
1451+
assert results == []
1452+
assert len(design.bodies[0].faces) == 1
1453+
assert len(design.bodies[1].faces) == 1
1454+
# Failing to extrude edge up to
1455+
results = geometry_commands.extrude_edges_up_to(
1456+
circle_surface.edges[0],
1457+
surface.faces[0],
1458+
Point3D([0, 0, 0]),
1459+
UnitVector3D([1, 0, 0]),
1460+
)
1461+
assert results == []
1462+
assert len(design.bodies[0].faces) == 1
1463+
assert len(design.bodies[1].faces) == 1
1464+
# Failing to revolve face
1465+
results = geometry_commands.revolve_faces(
1466+
selection=circle_surface.faces[0],
1467+
axis=Line([0, 0, 1], [0, 0, 1]),
1468+
angle=-np.pi * 3 / 2,
1469+
)
1470+
assert results == []
1471+
assert len(design.bodies[0].faces) == 1
1472+
assert len(design.bodies[1].faces) == 1
1473+
# Failing to revolve face up to
1474+
results = modeler.geometry_commands.revolve_faces_up_to(
1475+
circle_surface.faces[0],
1476+
surface.faces[0],
1477+
Line([0.5, 0.5, 0], [0, 0, 1]),
1478+
UnitVector3D([1, 0, 0]),
1479+
ExtrudeType.FORCE_ADD,
1480+
)
1481+
assert results == []
1482+
assert len(design.bodies[0].faces) == 1
1483+
assert len(design.bodies[1].faces) == 1
1484+
# Failing to revolve face by helix
1485+
# Commenting out test due to it causing a segfault inside the container
1486+
# modeler.geometry_commands.revolve_faces_by_helix(
1487+
# circle_surface.faces[0],
1488+
# Line([0.5, 0.5, 0], [0, 0, 1]),
1489+
# UnitVector3D([1, 0, 0]),
1490+
# 5,
1491+
# 1,
1492+
# np.pi / 4,
1493+
# True,
1494+
# True,
1495+
# )
1496+
# assert len(design.bodies[0].faces) == 1
1497+
# # Only in 252 does the revolve not fail. This is reproducible in 252 SpaceClaim and latest
1498+
# if modeler._grpc_client.backend_version == "25.2.0":
1499+
# assert len(design.bodies[1].faces) == 4
1500+
# else:
1501+
# assert len(design.bodies[1].faces) == 1
1502+
1503+
14101504
def test_offset_faces(modeler: Modeler):
14111505
"""Test offsetting faces."""
14121506
design = modeler.create_design("offset_faces")

tests/test_primitives.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,3 +1803,52 @@ def test_nurbs_surface_simple_evaluation():
18031803
assert isinstance(evaluation.vv_derivative, Vector3D)
18041804
assert isinstance(evaluation.normal, UnitVector3D)
18051805
assert isinstance(evaluation.surface, NURBSSurface)
1806+
1807+
1808+
def test_nurbs_surface_not_implemented():
1809+
"""Code coverage for methods not implemented for NURBS surfaces"""
1810+
# Creating the NURBS surface and parameters
1811+
matrix = Matrix44([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 0.8, 2], [0, 0, 0, 1]])
1812+
param = ParamUV(0.5, 0.5)
1813+
point = Point3D([1.0, 1.0, 1.0])
1814+
degree_u = 2
1815+
degree_v = 2
1816+
knots_u = [0, 0, 0, 1, 1, 1]
1817+
knots_v = [0, 0, 0, 1, 1, 1]
1818+
control_points = [
1819+
Point3D([0, 0, 0]),
1820+
Point3D([0, 1, 1]),
1821+
Point3D([0, 2, 0]),
1822+
Point3D([1, 0, 1]),
1823+
Point3D([1, 1, 2]),
1824+
Point3D([1, 2, 1]),
1825+
Point3D([2, 0, 0]),
1826+
Point3D([2, 1, 1]),
1827+
Point3D([2, 2, 0]),
1828+
]
1829+
nurbs_surface = NURBSSurface.from_control_points(
1830+
degree_u=degree_u,
1831+
degree_v=degree_v,
1832+
knots_u=knots_u,
1833+
knots_v=knots_v,
1834+
control_points=control_points,
1835+
)
1836+
# Code for the not implemented methods under NURBSSurface
1837+
with pytest.raises(NotImplementedError):
1838+
nurbs_surface.transformed_copy(matrix)
1839+
with pytest.raises(NotImplementedError):
1840+
nurbs_surface.contains_param(param)
1841+
with pytest.raises(NotImplementedError):
1842+
nurbs_surface.contains_point(point)
1843+
with pytest.raises(NotImplementedError):
1844+
nurbs_surface.project_point(point)
1845+
# Code for the not implemented methods under NURBSSurfaceEvaluation
1846+
evaluation = NURBSSurfaceEvaluation(nurbs_surface, param)
1847+
with pytest.raises(NotImplementedError):
1848+
evaluation.min_curvature()
1849+
with pytest.raises(NotImplementedError):
1850+
evaluation.min_curvature_direction()
1851+
with pytest.raises(NotImplementedError):
1852+
evaluation.max_curvature()
1853+
with pytest.raises(NotImplementedError):
1854+
evaluation.max_curvature_direction()

0 commit comments

Comments
 (0)