Skip to content

Commit ab87cef

Browse files
Add first version of pytest infrastructure
Includes first fixtures and tagging approach Also update first pytest test (cosserat curve) to use new approaches
1 parent 43a730a commit ab87cef

File tree

5 files changed

+256
-29
lines changed

5 files changed

+256
-29
lines changed

.github/actions/run_tests/action.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,4 @@ runs:
4949
coverage html
5050
coverage report
5151
coverage-badge -o htmlcov/coverage.svg
52-
coverage run --rcfile=${{ inputs.coverage_config }} -m pytest pytest_testing_cosserat_curve.py
53-
coverage report
52+
pytest

pyproject.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies = [
3939
"numpy-quaternion",
4040
"pre-commit",
4141
"pytest",
42+
"pytest-cov",
4243
"pyvista",
4344
"pyvista_utils@git+https://github.com/isteinbrecher/pyvista_utils.git@main",
4445
"scipy",
@@ -55,3 +56,11 @@ CI-CD = [
5556
"cubitpy@git+https://github.com/imcs-compsim/cubitpy.git@main",
5657
"setuptools" # Needed for coverage-badge
5758
]
59+
60+
[tool.pytest.ini_options]
61+
addopts = "-p pytest_cov --cov-report=term --cov-report=html --cov-fail-under=30 --cov=meshpy/ --cov=tutorial/ --cov-append"
62+
markers = [
63+
"fourc_arborx: tests in combination with 4C and ArborX",
64+
"cubitpy: tests in combination with CubitPy",
65+
"performance: performance tests of MeshPy"
66+
]

tests/conftest.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# -*- coding: utf-8 -*-
2+
# -----------------------------------------------------------------------------
3+
# MeshPy: A beam finite element input generator
4+
#
5+
# MIT License
6+
#
7+
# Copyright (c) 2018-2024
8+
# Ivo Steinbrecher
9+
# Institute for Mathematics and Computer-Based Simulation
10+
# Universitaet der Bundeswehr Muenchen
11+
# https://www.unibw.de/imcs-en
12+
#
13+
# Permission is hereby granted, free of charge, to any person obtaining a copy
14+
# of this software and associated documentation files (the "Software"), to deal
15+
# in the Software without restriction, including without limitation the rights
16+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
# copies of the Software, and to permit persons to whom the Software is
18+
# furnished to do so, subject to the following conditions:
19+
#
20+
# The above copyright notice and this permission notice shall be included in
21+
# all copies or substantial portions of the Software.
22+
#
23+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
# SOFTWARE.
30+
# -----------------------------------------------------------------------------
31+
"""Testing framework infrastructure."""
32+
33+
import os
34+
from pathlib import Path
35+
36+
import pytest
37+
from _pytest.config import Config
38+
from _pytest.config.argparsing import Parser
39+
40+
41+
def pytest_addoption(parser: Parser) -> None:
42+
"""Add custom command line options to pytest.
43+
44+
Args:
45+
parser: Pytest parser
46+
"""
47+
48+
parser.addoption(
49+
"--4C_ArborX",
50+
action="store_true",
51+
default=False,
52+
help="Execute 4C and ArborX based tests.",
53+
)
54+
55+
parser.addoption(
56+
"--CubitPy",
57+
action="store_true",
58+
default=False,
59+
help="Execute CubitPy based tests.",
60+
)
61+
62+
parser.addoption(
63+
"--performance",
64+
action="store_true",
65+
default=False,
66+
help="Execute performance tests.",
67+
)
68+
69+
70+
def pytest_collection_modifyitems(config: Config, items: list) -> None:
71+
"""Filter tests based on their markers and provided command line options.
72+
73+
Currently configured options:
74+
`pytest`: execute tests with no markers
75+
`pytest --4C_ArborX`: execute tests with the `fourc_arborx` marker
76+
`pytest --CubitPy`: execute tests with the `cubitpy` marker
77+
`pytest --performance`: execute tests with the `performance` marker
78+
79+
Args:
80+
config: Pytest config
81+
items: Pytest list of tests
82+
"""
83+
84+
selected_tests = []
85+
86+
# loop over all collected tests
87+
for item in items:
88+
# Get all set markers for current test (e.g. `fourc_arborx`, `cubitpy`, `performance`)
89+
markers = [marker.name for marker in item.iter_markers()]
90+
91+
if config.getoption("--4C_ArborX") and "fourc_arborx" in markers:
92+
selected_tests.append(item)
93+
94+
if config.getoption("--CubitPy") and "cubitpy" in markers:
95+
selected_tests.append(item)
96+
97+
if config.getoption("--performance") and "performance" in markers:
98+
selected_tests.append(item)
99+
100+
if not markers:
101+
selected_tests.append(item)
102+
103+
deselected_tests = list(set(items) - set(selected_tests))
104+
105+
items[:] = selected_tests
106+
config.hook.pytest_deselected(items=deselected_tests)
107+
108+
109+
@pytest.fixture(scope="session")
110+
def reference_file_directory() -> Path:
111+
"""Provide the path to the reference file directory.
112+
113+
Returns:
114+
Path: A Path object representing the full path to the reference file directory.
115+
"""
116+
117+
testing_path = os.path.abspath(os.path.dirname(__file__))
118+
return Path(testing_path) / "reference-files"
119+
120+
121+
@pytest.fixture(scope="function")
122+
def current_test_name(request: pytest.FixtureRequest) -> str:
123+
"""Return the name of the current pytest test.
124+
125+
Args:
126+
request: The pytest request object.
127+
128+
Returns:
129+
str: The name of the current pytest test.
130+
"""
131+
132+
return request.node.name
133+
134+
135+
@pytest.fixture
136+
def compare_result_file(reference, result, rtol, atol):
137+
raise NotImplementedError

tests/pytest_testing_cosserat_curve.py renamed to tests/test_cosserat_curve.py

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import os
3535

3636
import numpy as np
37-
import pytest
3837
import pyvista as pv
3938
import quaternion
4039

@@ -50,30 +49,27 @@
5049
from .utils import (
5150
compare_test_result_pytest,
5251
compare_vtk_pytest,
53-
get_pytest_test_name,
54-
testing_input,
55-
testing_temp,
5652
)
5753

5854

59-
def load_cosserat_curve_from_file():
55+
def load_cosserat_curve_from_file(reference_file_directory):
6056
"""Load the centerline coordinates from the reference files and create the
6157
Cosserat curve."""
6258
coordinates = np.loadtxt(
63-
os.path.join(testing_input, "test_cosserat_curve_centerline.txt"),
59+
os.path.join(reference_file_directory, "test_cosserat_curve_centerline.txt"),
6460
comments="#",
6561
delimiter=",",
6662
unpack=False,
6763
)
6864
return CosseratCurve(coordinates)
6965

7066

71-
def create_beam_solid_input_file():
67+
def create_beam_solid_input_file(reference_file_directory):
7268
"""Create a beam and solid input file for testing purposes."""
7369

7470
mpy.import_mesh_full = True
7571
mesh = InputFile(
76-
dat_file=os.path.join(testing_input, "test_cosserat_curve_mesh.dat")
72+
dat_file=os.path.join(reference_file_directory, "test_cosserat_curve_mesh.dat")
7773
)
7874
create_beam_mesh_helix(
7975
mesh,
@@ -89,10 +85,12 @@ def create_beam_solid_input_file():
8985
return mesh
9086

9187

92-
def test_cosserat_curve_translate_and_rotate():
88+
def test_cosserat_curve_translate_and_rotate(
89+
reference_file_directory, current_test_name
90+
):
9391
"""Test that a curve can be loaded, rotated and transformed."""
9492

95-
curve = load_cosserat_curve_from_file()
93+
curve = load_cosserat_curve_from_file(reference_file_directory)
9694

9795
# Translate the curve so that the start is at the origin
9896
curve.translate(-curve.centerline_interpolation(5.0))
@@ -113,7 +111,7 @@ def test_cosserat_curve_translate_and_rotate():
113111
def load_compare(name):
114112
"""Load the compare files and return a numpy array."""
115113
return np.loadtxt(
116-
os.path.join(testing_input, f"{get_pytest_test_name()}_{name}.txt")
114+
os.path.join(reference_file_directory, f"{current_test_name}_{name}.txt")
117115
)
118116

119117
assert np.allclose(sol_half_pos, load_compare("pos_half_ref"), rtol=1e-14)
@@ -127,25 +125,27 @@ def load_compare(name):
127125
)
128126

129127

130-
def test_cosserat_curve_vtk_representation():
128+
def test_cosserat_curve_vtk_representation(
129+
tmp_path, reference_file_directory, current_test_name
130+
):
131131
"""Test the vtk representation of the Cosserat curve."""
132132

133-
result_name = os.path.join(testing_temp, get_pytest_test_name() + ".vtu")
134-
curve = load_cosserat_curve_from_file()
133+
result_name = os.path.join(tmp_path, current_test_name + ".vtu")
134+
curve = load_cosserat_curve_from_file(reference_file_directory)
135135
pv.UnstructuredGrid(curve.get_pyvista_polyline()).save(result_name)
136136
compare_vtk_pytest(
137-
os.path.join(testing_input, get_pytest_test_name() + ".vtu"),
137+
os.path.join(reference_file_directory, current_test_name + ".vtu"),
138138
result_name,
139139
rtol=1e-8,
140140
atol=1e-8,
141141
)
142142

143143

144-
def test_cosserat_curve_project_point():
144+
def test_cosserat_curve_project_point(reference_file_directory):
145145
"""Test that the project point function works as expected."""
146146

147147
# Load the curve
148-
curve = load_cosserat_curve_from_file()
148+
curve = load_cosserat_curve_from_file(reference_file_directory)
149149

150150
# Translate the curve so that the start is at the origin
151151
curve.translate(-curve.centerline_interpolation(0.0))
@@ -158,16 +158,16 @@ def test_cosserat_curve_project_point():
158158
assert np.allclose(t_ref, curve.project_point([-5, 1, 1], t0=4.0), rtol=rtol)
159159

160160

161-
def test_cosserat_mesh_transformation():
161+
def test_cosserat_mesh_transformation(reference_file_directory, current_test_name):
162162
"""Test that the get_mesh_transformation function works as expected."""
163163

164-
curve = load_cosserat_curve_from_file()
164+
curve = load_cosserat_curve_from_file(reference_file_directory)
165165
pos, rot = curve.get_centerline_position_and_rotation(0)
166166
rot = Rotation.from_quaternion(quaternion.as_float_array(rot))
167167
curve.translate(-pos)
168168
curve.translate([1, 2, 3])
169169

170-
mesh = create_beam_solid_input_file()
170+
mesh = create_beam_solid_input_file(reference_file_directory)
171171
pos, rot = get_mesh_transformation(
172172
curve,
173173
mesh.nodes,
@@ -184,7 +184,8 @@ def test_cosserat_mesh_transformation():
184184

185185
def load_result(name):
186186
with open(
187-
os.path.join(testing_input, f"{get_pytest_test_name()}_{name}.json"), "r"
187+
os.path.join(reference_file_directory, f"{current_test_name}_{name}.json"),
188+
"r",
188189
) as f:
189190
return np.array(json.load(f))
190191

@@ -198,19 +199,19 @@ def load_result(name):
198199
assert np.allclose(rot_ref, rot_np, rtol=1e-14)
199200

200201

201-
def test_cosserat_curve_mesh_warp():
202+
def test_cosserat_curve_mesh_warp(reference_file_directory):
202203
"""Warp a balloon along a centerline."""
203204

204205
# Load the curve
205-
curve = load_cosserat_curve_from_file()
206+
curve = load_cosserat_curve_from_file(reference_file_directory)
206207
pos, rot = curve.get_centerline_position_and_rotation(0)
207208
rot = Rotation.from_quaternion(quaternion.as_float_array(rot))
208209
curve.translate(-pos)
209210
curve.translate([1, 2, 3])
210211

211212
# Warp the mesh. The reference coordinate system is rotated such that z axis is the longitudinal direction,
212213
# and x and y are the first and second cross-section basis vectors respectively.
213-
mesh = create_beam_solid_input_file()
214+
mesh = create_beam_solid_input_file(reference_file_directory)
214215
warp_mesh_along_curve(
215216
mesh,
216217
curve,
@@ -226,18 +227,20 @@ def test_cosserat_curve_mesh_warp():
226227
)
227228

228229

229-
def test_cosserat_curve_mesh_warp_transform_boundary_conditions():
230+
def test_cosserat_curve_mesh_warp_transform_boundary_conditions(
231+
reference_file_directory,
232+
):
230233
"""Test the transform boundary creation function."""
231234

232235
# Load the curve
233-
curve = load_cosserat_curve_from_file()
236+
curve = load_cosserat_curve_from_file(reference_file_directory)
234237
pos, rot = curve.get_centerline_position_and_rotation(0)
235238
rot = Rotation.from_quaternion(quaternion.as_float_array(rot))
236239
curve.translate(-pos)
237240
curve.translate([1, 2, 3])
238241

239242
# Load the mesh
240-
mesh = create_beam_solid_input_file()
243+
mesh = create_beam_solid_input_file(reference_file_directory)
241244

242245
# Apply the transform boundary conditions
243246
create_transform_boundary_conditions(

0 commit comments

Comments
 (0)