Skip to content
Open
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
48 changes: 48 additions & 0 deletions pyomo/contrib/solver/test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import re
from typing import List, Optional, Type, Union
from pyomo.common import unittest
from pyomo.contrib.solver.common.base import SolverBase
from .registry import SolverTestFilter, SolverTestRegistry
from .builder import SolverTestBuilder
from . import base
from . import linear
from . import dual
from . import quadratic
from . import nonlinear


def add_tests(
test_case_cls: type[unittest.TestCase],
opt_cls: Type[SolverBase],
*,
include: Optional[List[Union[str, re.Pattern]]] = None,
exclude: Optional[List[Union[str, re.Pattern]]] = None,
include_tags: Optional[List[str]] = None,
exclude_tags: Optional[List[str]] = None,
warn_unsupported: bool = False,
warn_unavailable: bool = False,
Comment on lines +18 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you describe each of these in a docstring?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Particularly the first two. They are not obvious to me.

) -> None:

if not issubclass(test_case_cls, unittest.TestCase):
raise TypeError(f"{test_case_cls} must be a TestCase subclass")

if not issubclass(opt_cls, SolverBase):
raise TypeError(f"{opt_cls} must be a SolverBase subclass")

test_builder = SolverTestBuilder(warn_unsupported, warn_unavailable)

test_filter = SolverTestFilter(
include=include,
exclude=exclude,
include_tags=include_tags,
exclude_tags=exclude_tags,
)

filtered_tests = SolverTestRegistry.get_filtered_tests(test_filter)

for base_test_name, test_meta in filtered_tests.items():

test_method = test_builder.build(opt_cls, test_meta)
if test_method is not None:
test_name = f"{base_test_name}_{opt_cls.name}"
setattr(test_case_cls, test_name, test_method)
Empty file.
46 changes: 46 additions & 0 deletions pyomo/contrib/solver/test/builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from collections.abc import Callable
from typing import Optional, Type

from pyomo.common import unittest
from pyomo.contrib.solver.common.base import SolverBase
from .registry import SolverTestMeta


class SolverTestBuilder:
def __init__(self, warn_unsupported: bool, warn_unavailable: bool):
self.warn_unsupported = warn_unsupported
self.warn_unavailable = warn_unavailable

def build(
self, opt_cls: Type[SolverBase], test_meta: SolverTestMeta
) -> Optional[Callable[[unittest.TestCase], None]]:
try:
avail = bool(opt_cls().available())
except Exception as e:
avail = False

can_run, skip_reason = test_meta.can_run_on(opt_cls)

if can_run and avail:
return self._build_runnable_test(opt_cls, test_meta)
elif can_run and not avail and self.warn_unavailable:
return self._build_skip_test(f"Solver {opt_cls.name} is not available")
elif not can_run and self.warn_unsupported:
return self._build_skip_test(f"Solver {opt_cls.name} {skip_reason}")

else:
return None

def _build_runnable_test(
self, opt_cls: Type[SolverBase], test_meta: SolverTestMeta
):
def test_method(self: unittest.TestCase):
test_meta.func(self, opt_cls)

return test_method

def _build_skip_test(self, reason: str):
def test_method(self: unittest.TestCase):
self.skipTest(reason)

return test_method
Loading