Skip to content

Commit 7a795d4

Browse files
committed
Add comprehensive test suite
- Add conftest.py with shared pytest fixtures - Add test_module.py for module attributes and exports - Add test_solvers.py for solver discovery and constants - Add test_simulation.py for grid and simulation functions - Add test_output.py for VTK and CSV output - Add test_errors.py for error handling - Add test_integration.py for end-to-end workflows Tests cover: - Dynamic solver registration - Grid creation and validation - Simulation execution - Output file generation - Error conditions and edge cases
1 parent 5689d6a commit 7a795d4

7 files changed

Lines changed: 697 additions & 0 deletions

File tree

tests/conftest.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Pytest configuration and shared fixtures for CFD Python tests
3+
"""
4+
import pytest
5+
import sys
6+
import os
7+
8+
# Add the build directory to the path for testing
9+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
10+
11+
try:
12+
import cfd_python
13+
except ImportError:
14+
pytest.skip("CFD Python module not built yet", allow_module_level=True)
15+
16+
17+
@pytest.fixture
18+
def cfd():
19+
"""Provide the cfd_python module"""
20+
return cfd_python
21+
22+
23+
@pytest.fixture
24+
def small_grid_params():
25+
"""Small grid parameters for quick tests"""
26+
return {
27+
'nx': 5,
28+
'ny': 5,
29+
'xmin': 0.0,
30+
'xmax': 1.0,
31+
'ymin': 0.0,
32+
'ymax': 1.0,
33+
}
34+
35+
36+
@pytest.fixture
37+
def default_solver_params(cfd):
38+
"""Default solver parameters"""
39+
return cfd.get_default_solver_params()

tests/test_errors.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Tests for error handling
3+
"""
4+
import pytest
5+
import cfd_python
6+
7+
8+
class TestErrorHandling:
9+
"""Test error handling"""
10+
11+
def test_run_simulation_invalid_nx(self):
12+
"""Test error handling for invalid nx type"""
13+
with pytest.raises(TypeError):
14+
cfd_python.run_simulation("invalid", 5, steps=3)
15+
16+
def test_run_simulation_invalid_ny(self):
17+
"""Test error handling for invalid ny type"""
18+
with pytest.raises(TypeError):
19+
cfd_python.run_simulation(5, "invalid", steps=3)
20+
21+
def test_create_grid_invalid_params(self):
22+
"""Test error handling for invalid grid parameters"""
23+
with pytest.raises(TypeError):
24+
cfd_python.create_grid("invalid", 10, 0.0, 1.0, 0.0, 1.0)
25+
26+
def test_write_vtk_scalar_invalid_data_type(self, tmp_path):
27+
"""Test error handling for invalid data type"""
28+
output_file = tmp_path / "invalid.vtk"
29+
with pytest.raises(TypeError):
30+
cfd_python.write_vtk_scalar(
31+
str(output_file), "test", "not a list",
32+
5, 5, 0.0, 1.0, 0.0, 1.0
33+
)
34+
35+
def test_has_solver_invalid_type(self):
36+
"""Test error handling for invalid solver type argument"""
37+
with pytest.raises(TypeError):
38+
cfd_python.has_solver(123) # Should be string
39+
40+
def test_get_solver_info_empty_string(self):
41+
"""Test error handling for empty solver name"""
42+
with pytest.raises(ValueError):
43+
cfd_python.get_solver_info('')
44+
45+
def test_create_grid_zero_dimensions(self):
46+
"""Test error handling for zero grid dimensions"""
47+
with pytest.raises(ValueError):
48+
cfd_python.create_grid(0, 10, 0.0, 1.0, 0.0, 1.0)
49+
50+
def test_create_grid_negative_dimensions(self):
51+
"""Test error handling for negative grid dimensions"""
52+
with pytest.raises(ValueError):
53+
cfd_python.create_grid(-5, 10, 0.0, 1.0, 0.0, 1.0)
54+
55+
def test_run_simulation_zero_steps(self):
56+
"""Test error handling for zero steps"""
57+
with pytest.raises(ValueError):
58+
cfd_python.run_simulation(5, 5, steps=0)

tests/test_integration.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""
2+
Integration tests for complete workflows
3+
"""
4+
import pytest
5+
import cfd_python
6+
7+
8+
class TestIntegration:
9+
"""Integration tests for complete workflows"""
10+
11+
def test_full_simulation_workflow(self, tmp_path):
12+
"""Test complete simulation workflow"""
13+
# 1. List available solvers
14+
solvers = cfd_python.list_solvers()
15+
assert len(solvers) > 0
16+
17+
# 2. Get solver info
18+
solver_info = cfd_python.get_solver_info(solvers[0])
19+
assert 'name' in solver_info
20+
21+
# 3. Get default parameters
22+
params = cfd_python.get_default_solver_params()
23+
assert 'dt' in params
24+
25+
# 4. Create grid
26+
grid = cfd_python.create_grid(10, 10, 0.0, 1.0, 0.0, 1.0)
27+
assert grid['nx'] == 10
28+
29+
# 5. Run simulation
30+
vtk_file = tmp_path / "workflow_test.vtk"
31+
result = cfd_python.run_simulation_with_params(
32+
10, 10, 0.0, 1.0, 0.0, 1.0,
33+
steps=5,
34+
solver_type=solvers[0],
35+
output_file=str(vtk_file)
36+
)
37+
38+
# 6. Verify results
39+
assert 'velocity_magnitude' in result
40+
assert len(result['velocity_magnitude']) == 100
41+
assert vtk_file.exists()
42+
43+
def test_solver_constant_matches_list(self):
44+
"""Test that solver constants match list_solvers output"""
45+
solvers = cfd_python.list_solvers()
46+
47+
for solver_name in solvers:
48+
# Use the constant to run a simulation
49+
const_name = 'SOLVER_' + solver_name.upper()
50+
if hasattr(cfd_python, const_name):
51+
solver_type = getattr(cfd_python, const_name)
52+
# Should be able to use this to check solver exists
53+
assert cfd_python.has_solver(solver_type)
54+
55+
def test_multiple_simulations_same_grid(self):
56+
"""Test running multiple simulations with same grid"""
57+
grid = cfd_python.create_grid(8, 8, 0.0, 1.0, 0.0, 1.0)
58+
59+
results = []
60+
for i in range(3):
61+
result = cfd_python.run_simulation(
62+
grid['nx'], grid['ny'], steps=2
63+
)
64+
results.append(result)
65+
66+
# All results should have same size
67+
for r in results:
68+
assert len(r) == 64
69+
70+
def test_different_solvers_same_problem(self):
71+
"""Test running same problem with different solvers"""
72+
solvers = cfd_python.list_solvers()
73+
74+
results = {}
75+
for solver_name in solvers[:2]: # Test first two solvers
76+
result = cfd_python.run_simulation(
77+
5, 5, steps=3, solver_type=solver_name
78+
)
79+
results[solver_name] = result
80+
81+
# All results should have correct size
82+
for name, result in results.items():
83+
assert len(result) == 25, f"Solver {name} returned wrong size"
84+
85+
def test_output_workflow(self, tmp_path):
86+
"""Test complete output workflow"""
87+
# Set output directory
88+
cfd_python.set_output_dir(str(tmp_path))
89+
90+
# Run simulation
91+
nx, ny = 6, 6
92+
result = cfd_python.run_simulation(nx, ny, steps=3)
93+
94+
# Write VTK scalar output
95+
vtk_file = tmp_path / "velocity_mag.vtk"
96+
cfd_python.write_vtk_scalar(
97+
str(vtk_file), "velocity_magnitude", result,
98+
nx, ny, 0.0, 1.0, 0.0, 1.0
99+
)
100+
assert vtk_file.exists()
101+
102+
# Write VTK vector output
103+
u_data = [0.1] * (nx * ny)
104+
v_data = [0.05] * (nx * ny)
105+
vtk_vector_file = tmp_path / "velocity.vtk"
106+
cfd_python.write_vtk_vector(
107+
str(vtk_vector_file), "velocity", u_data, v_data,
108+
nx, ny, 0.0, 1.0, 0.0, 1.0
109+
)
110+
assert vtk_vector_file.exists()
111+
112+
# Write CSV timeseries
113+
csv_file = tmp_path / "timeseries.csv"
114+
p_data = [1.0] * (nx * ny)
115+
cfd_python.write_csv_timeseries(
116+
str(csv_file), step=0, time=0.0,
117+
u_data=u_data, v_data=v_data, p_data=p_data,
118+
nx=nx, ny=ny, dt=0.001, iterations=10,
119+
create_new=True
120+
)
121+
assert csv_file.exists()

tests/test_module.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""
2+
Tests for module-level attributes and exports
3+
"""
4+
import pytest
5+
import cfd_python
6+
7+
8+
class TestModuleAttributes:
9+
"""Test module-level attributes and constants"""
10+
11+
def test_version(self):
12+
"""Test version string exists"""
13+
assert hasattr(cfd_python, '__version__')
14+
assert isinstance(cfd_python.__version__, str)
15+
assert cfd_python.__version__ == "0.3.0"
16+
17+
def test_output_constants_exist(self):
18+
"""Test OUTPUT_* constants are defined"""
19+
assert hasattr(cfd_python, 'OUTPUT_PRESSURE')
20+
assert hasattr(cfd_python, 'OUTPUT_VELOCITY')
21+
assert hasattr(cfd_python, 'OUTPUT_FULL_FIELD')
22+
assert hasattr(cfd_python, 'OUTPUT_CSV_TIMESERIES')
23+
assert hasattr(cfd_python, 'OUTPUT_CSV_CENTERLINE')
24+
assert hasattr(cfd_python, 'OUTPUT_CSV_STATISTICS')
25+
26+
def test_output_constants_are_integers(self):
27+
"""Test OUTPUT_* constants are integers"""
28+
assert isinstance(cfd_python.OUTPUT_PRESSURE, int)
29+
assert isinstance(cfd_python.OUTPUT_VELOCITY, int)
30+
assert isinstance(cfd_python.OUTPUT_FULL_FIELD, int)
31+
assert isinstance(cfd_python.OUTPUT_CSV_TIMESERIES, int)
32+
assert isinstance(cfd_python.OUTPUT_CSV_CENTERLINE, int)
33+
assert isinstance(cfd_python.OUTPUT_CSV_STATISTICS, int)
34+
35+
def test_output_constants_unique(self):
36+
"""Test OUTPUT_* constants have unique values"""
37+
values = [
38+
cfd_python.OUTPUT_PRESSURE,
39+
cfd_python.OUTPUT_VELOCITY,
40+
cfd_python.OUTPUT_FULL_FIELD,
41+
cfd_python.OUTPUT_CSV_TIMESERIES,
42+
cfd_python.OUTPUT_CSV_CENTERLINE,
43+
cfd_python.OUTPUT_CSV_STATISTICS,
44+
]
45+
assert len(values) == len(set(values)), "OUTPUT_* constants should have unique values"
46+
47+
48+
class TestAllExports:
49+
"""Test that __all__ exports are accessible"""
50+
51+
def test_core_functions_exported(self):
52+
"""Test core functions are in __all__ and accessible"""
53+
core_functions = [
54+
'run_simulation',
55+
'create_grid',
56+
'get_default_solver_params',
57+
'run_simulation_with_params',
58+
'list_solvers',
59+
'has_solver',
60+
'get_solver_info',
61+
'set_output_dir',
62+
'write_vtk_scalar',
63+
'write_vtk_vector',
64+
'write_csv_timeseries',
65+
]
66+
for func_name in core_functions:
67+
assert hasattr(cfd_python, func_name), f"Missing function: {func_name}"
68+
assert callable(getattr(cfd_python, func_name)), f"{func_name} should be callable"
69+
70+
def test_output_constants_in_all(self):
71+
"""Test OUTPUT_* constants are exported"""
72+
output_constants = [
73+
'OUTPUT_PRESSURE',
74+
'OUTPUT_VELOCITY',
75+
'OUTPUT_FULL_FIELD',
76+
'OUTPUT_CSV_TIMESERIES',
77+
'OUTPUT_CSV_CENTERLINE',
78+
'OUTPUT_CSV_STATISTICS',
79+
]
80+
for const_name in output_constants:
81+
assert const_name in cfd_python.__all__, f"{const_name} should be in __all__"

0 commit comments

Comments
 (0)