Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Global test configuration."""

import pytest
from firedrake.petsc import get_external_packages
from firedrake.petsc import PETSc, get_external_packages


def pytest_configure(config):
Expand Down Expand Up @@ -122,3 +122,34 @@ def fin():
assert len(tape.get_blocks()) == 0

request.addfinalizer(fin)


class _petsc_raises:
"""Context manager for catching PETSc-raised exceptions.

The usual `pytest.raises` exception handler is not suitable for errors
raised inside a callback to PETSc because the error is wrapped inside a
`PETSc.Error` object and so this context manager unpacks this to access
the actual internal error.

Parameters
----------
exc_type :
The exception type that is expected to be raised inside a PETSc callback.

"""
def __init__(self, exc_type):
self.exc_type = exc_type

def __enter__(self):
pass

def __exit__(self, exc_type, exc_val, traceback):
if exc_type is PETSc.Error and isinstance(exc_val.__cause__, self.exc_type):
return True


@pytest.fixture
def petsc_raises():
# This function is needed because pytest does not support classes as fixtures.
return _petsc_raises
8 changes: 4 additions & 4 deletions tests/macro/test_macro_multigrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def test_macro_grid_transfer(hierarchy, space, degrees, variant, transfer_type):


@pytest.mark.parametrize("degree", (1,))
def test_macro_multigrid_poisson(hierarchy, degree, variant):
def test_macro_multigrid_poisson(hierarchy, degree, variant, petsc_raises):
mesh = hierarchy[-1]
V = FunctionSpace(mesh, "CG", degree, variant=variant)
u = TrialFunction(V)
Expand All @@ -153,7 +153,7 @@ def test_macro_multigrid_poisson(hierarchy, degree, variant):
problem = LinearVariationalProblem(a, L, uh, bcs=bcs)
solver = LinearVariationalSolver(problem, solver_parameters=mg_params)
if complex_mode and variant == "alfeld":
with pytest.raises(NotImplementedError):
with petsc_raises(NotImplementedError):
solver.solve()
else:
solver.solve()
Expand All @@ -172,7 +172,7 @@ def square_hierarchy():


@pytest.mark.parametrize("family", ("HCT-red", "HCT"))
def test_macro_multigrid_biharmonic(square_hierarchy, family):
def test_macro_multigrid_biharmonic(square_hierarchy, family, petsc_raises):
mesh = square_hierarchy[-1]
V = FunctionSpace(mesh, family, 3)
u = TrialFunction(V)
Expand All @@ -185,7 +185,7 @@ def test_macro_multigrid_biharmonic(square_hierarchy, family):
problem = LinearVariationalProblem(a, L, uh, bcs=bcs)
solver = LinearVariationalSolver(problem, solver_parameters=mg_params)
if complex_mode:
with pytest.raises(NotImplementedError):
with petsc_raises(NotImplementedError):
solver.solve()
else:
solver.solve()
Expand Down
15 changes: 3 additions & 12 deletions tests/slate/test_slate_hybridization.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def test_slate_hybridization(degree, hdiv_family, quadrilateral):
assert u_err < 1e-11


def test_slate_hybridization_wrong_option(setup_poisson):
def test_slate_hybridization_wrong_option(setup_poisson, petsc_raises):
a, L, W = setup_poisson

w = Function(W)
Expand All @@ -145,18 +145,9 @@ def test_slate_hybridization_wrong_option(setup_poisson):
'pc_fieldsplit_type': 'frog'}}}
problem = LinearVariationalProblem(a, L, w)
solver = LinearVariationalSolver(problem, solver_parameters=params)
with pytest.raises(ValueError):
# HybridizationPC isn't called directly from the Python interpreter,
# it's a callback that PETSc calls. This means that the call stack from pytest
# down to HybridizationPC goes via PETSc C code, which interferes with the exception
# before it is observed outside. Hence removing PETSc's error handler
# makes the problem go away, because PETSc stops interfering.
# We need to repush the error handler because popErrorHandler globally changes
# the system state for all future tests.
from firedrake.petsc import PETSc
PETSc.Sys.pushErrorHandler("ignore")

with petsc_raises(ValueError):
solver.solve()
PETSc.Sys.popErrorHandler("ignore")


def test_slate_hybridization_nested_schur(setup_poisson):
Expand Down