Marco Ricci commited on 2025-05-23 00:03:24
Zeige 2 geänderte Dateien mit 56 Einfügungen und 0 Löschungen.
* the-annoying-os: Exclude unused test function xfail_on_the_annoying_os from coverage Document the changes due to The Annoying OS in the changelog Add changes necessary to appease The Annoying OS
... | ... |
@@@ -1603,68 -1603,64 +1603,123 @@@ A cached pytest mark to skip this test |
1603 | 1603 |
available. Usually this means that the test targets the |
1604 | 1604 |
`derivepassphrase export vault` subcommand, whose functionality depends |
1605 | 1605 |
on cryptography support being available. |
1606 |
+ """ |
|
1607 |
+ skip_if_on_the_annoying_os = pytest.mark.skipif( |
|
1608 |
+ sys.platform == 'win32', |
|
1609 |
+ reason='The Annoying OS behaves differently.', |
|
1610 |
+ ) |
|
1611 |
+ """ |
|
1612 |
+ A cached pytest mark to skip this test if running on The Annoying |
|
1613 |
+ Operating System, a.k.a. Microsoft Windows. Usually this is due to |
|
1614 |
+ unnecessary and stupid differences in the OS internals, and these |
|
1615 |
+ differences are deemed irreconcilable in the context of the decorated |
|
1616 |
+ test, so the test is to be skipped. |
|
1617 |
+ |
|
1618 |
+ See also: |
|
1619 |
+ [`xfail_on_the_annoying_os`][] |
|
1620 |
+ |
|
1606 | 1621 |
""" |
1607 | 1622 |
+skip_if_no_multiprocessing_support = pytest.mark.skipif( |
1608 | 1623 |
+ importlib.util.find_spec('multiprocessing') is None, |
1609 | 1624 |
+ reason='no "multiprocessing" support', |
1610 | 1625 |
+) |
1611 | 1626 |
+""" |
1612 | 1627 |
+A cached pytest mark to skip this test if multiprocessing support is not |
1613 | 1628 |
+available. Usually this means that the test targets the concurrency |
1614 | 1629 |
+features of `derivepassphrase`, which is generally only possible to test |
1615 | 1630 |
+in separate processes because the testing machinery operates on |
1616 | 1631 |
+process-global state. |
1617 | 1632 |
+""" |
1618 | 1633 |
+ |
1619 | 1634 |
+MIN_CONCURRENCY = 4 |
1620 | 1635 |
+""" |
1621 | 1636 |
+The minimum amount of concurrent threads used for testing. |
1622 | 1637 |
+""" |
1623 | 1638 |
+ |
1624 | 1639 |
+ |
1625 | 1640 |
+def get_concurrency_limit() -> int: |
1626 | 1641 |
+ """Return the imposed limit on the number of concurrent threads. |
1627 | 1642 |
+ |
1628 | 1643 |
+ We use [`os.process_cpu_count`][] as the limit on Python 3.13 and |
1629 | 1644 |
+ higher, and [`os.cpu_count`][] on Python 3.12 and below. On |
1630 | 1645 |
+ Python 3.12 and below, we explicitly support the `PYTHON_CPU_COUNT` |
1631 | 1646 |
+ environment variable. We guarantee at least [`MIN_CONCURRENCY`][] |
1632 | 1647 |
+ many threads in any case. |
1633 | 1648 |
+ |
1634 | 1649 |
+ """ # noqa: RUF002 |
1635 | 1650 |
+ result: int | None = None |
1636 | 1651 |
+ if sys.version_info >= (3, 13): |
1637 | 1652 |
+ result = os.process_cpu_count() |
1638 | 1653 |
+ else: |
1639 | 1654 |
+ try: |
1640 | 1655 |
+ cpus = os.sched_getaffinity(os.getpid()) |
1641 | 1656 |
+ except AttributeError: |
1642 | 1657 |
+ pass |
1643 | 1658 |
+ else: |
1644 | 1659 |
+ result = len(cpus) |
1645 | 1660 |
+ return max(result if result is not None else 0, MIN_CONCURRENCY) |
1646 | 1661 |
+ |
1647 | 1662 |
+ |
1648 | 1663 |
+def get_concurrency_step_count( |
1649 | 1664 |
+ settings: hypothesis.settings | None = None, |
1650 | 1665 |
+) -> int: |
1651 | 1666 |
+ """Return the desired step count for concurrency-related tests. |
1652 | 1667 |
+ |
1653 | 1668 |
+ This is the smaller of the [general concurrency |
1654 | 1669 |
+ limit][tests.get_concurrency_limit] and the step count from the |
1655 | 1670 |
+ current hypothesis settings. |
1656 | 1671 |
+ |
1657 | 1672 |
+ Args: |
1658 | 1673 |
+ settings: |
1659 | 1674 |
+ The hypothesis settings for a specific tests. If not given, |
1660 | 1675 |
+ then the current profile will be queried directly. |
1661 | 1676 |
+ |
1662 | 1677 |
+ """ |
1663 | 1678 |
+ if settings is None: # pragma: no cover |
1664 | 1679 |
+ settings = hypothesis.settings() |
1665 | 1680 |
+ return min(get_concurrency_limit(), settings.stateful_step_count) |
1666 | 1681 |
|
1667 | 1682 |
|
1683 |
+ def xfail_on_the_annoying_os( |
|
1684 |
+ f: Callable | None = None, |
|
1685 |
+ /, |
|
1686 |
+ *, |
|
1687 |
+ reason: str = '', |
|
1688 |
+ ) -> pytest.MarkDecorator | Any: # pragma: no cover |
|
1689 |
+ """Annotate a test which fails on The Annoying OS. |
|
1690 |
+ |
|
1691 |
+ Annotate a test to indicate that it fails on The Annoying Operating |
|
1692 |
+ System, a.k.a. Microsoft Windows. Usually this is due to |
|
1693 |
+ differences in the design of OS internals, and usually, these |
|
1694 |
+ differences are both unnecessary and stupid. |
|
1695 |
+ |
|
1696 |
+ Args: |
|
1697 |
+ f: |
|
1698 |
+ A callable to decorate. If not given, return the pytest |
|
1699 |
+ mark directly. |
|
1700 |
+ reason: |
|
1701 |
+ An optional, more detailed reason stating why this test |
|
1702 |
+ fails on The Annoying OS. |
|
1703 |
+ |
|
1704 |
+ Returns: |
|
1705 |
+ The callable, marked as an expected failure on the Annoying OS, |
|
1706 |
+ or alternatively a suitable pytest mark if no callable was |
|
1707 |
+ passed. The reason will begin with the phrase "The Annoying OS |
|
1708 |
+ behaves differently.", and the optional detailed reason, if not |
|
1709 |
+ empty, will follow. |
|
1710 |
+ |
|
1711 |
+ """ |
|
1712 |
+ base_reason = 'The Annoying OS behaves differently.' |
|
1713 |
+ full_reason = base_reason if not reason else f'{base_reason} {reason}' |
|
1714 |
+ mark = pytest.mark.xfail( |
|
1715 |
+ sys.platform == 'win32', |
|
1716 |
+ reason=full_reason, |
|
1717 |
+ raises=(AssertionError, hypothesis.errors.FailedHealthCheck), |
|
1718 |
+ strict=True, |
|
1719 |
+ ) |
|
1720 |
+ return mark if f is None else mark(f) |
|
1721 |
+ |
|
1722 |
+ |
|
1668 | 1723 |
def list_keys(self: Any = None) -> list[_types.SSHKeyCommentPair]: |
1669 | 1724 |
"""Return a list of all SSH test keys, as key/comment pairs. |
1670 | 1725 |
|