Spaces:
Building
Building
# Copyright 2024 The etils Authors. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
"""Pytest utils.""" | |
from __future__ import annotations | |
import contextlib | |
import dataclasses | |
from typing import Any, Iterator | |
import pytest | |
# ==== Hermetic tests === | |
_SKIP_NON_HERMETIC = False | |
# Non hermetic tests are explicitly marked and skipped if `_SKIP_NON_HERMETIC` | |
# is True. | |
non_hermetic = pytest.mark.skipif( | |
_SKIP_NON_HERMETIC, | |
reason='Non-hermetic test skipped.', | |
) | |
# ==== Subtests === | |
_curr_context = None | |
class _SubtestContext: | |
"""Context of a test using `subtests` fixture. | |
Attributes: | |
subtests: Reference to the original `subtests` fixture output | |
names: Stack of current nested `subtests.test` | |
""" | |
subtests: Any | |
names: list[str] = dataclasses.field(default_factory=list) | |
def subtest(name: str) -> Iterator[None]: | |
"""Contextmanager for a new subtest. To use with `with_subtests` fixture.""" | |
if not _curr_context: | |
raise AssertionError( | |
'`epy.testing.subtest` can only be called inside a ' | |
'`with_subtests` context.' | |
) | |
name = str(name) | |
_curr_context.names.append(name) | |
subtest_name = '/'.join(_curr_context.names) | |
try: | |
with _curr_context.subtests.test(msg=subtest_name): | |
yield | |
finally: | |
out_name = _curr_context.names.pop() | |
assert out_name == name # Sanity check | |
def with_subtests(subtests) -> Iterator[None]: | |
"""Fixture which activate subtests for global usage. | |
This fixture is a small wrapper around `subtests` pytest extension fixing | |
2 issues: | |
* Global usage: https://github.com/pytest-dev/pytest-subtests/issues/44 | |
* Nested report: https://github.com/pytest-dev/pytest-subtests/issues/45 | |
Usage: | |
```python | |
with_subtests = epy.testing.with_subtests # Required to register the fixture | |
@pytest.mark.usefixtures('with_subtests') | |
def my_test(): | |
with epy.testing.subtest('a'): | |
with epy.testing.subtest('b'): | |
assert False | |
``` | |
Args: | |
subtests: Subtest fixture | |
Yields: | |
None | |
""" | |
global _curr_context | |
if _curr_context is not None: | |
raise AssertionError('Conflicting `subtests` context.') | |
new_context = _SubtestContext(subtests=subtests) | |
try: | |
_curr_context = new_context | |
yield | |
finally: | |
_curr_context = None | |