From: nialov Date: Mon, 4 Dec 2023 17:33:46 +0000 (+0200) Subject: test: mark flaky ref count tests X-Git-Tag: 3.1.15~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=70ef364324ba3448ef9ac0e29329c9d802380e4b;p=thirdparty%2Fpsycopg.git test: mark flaky ref count tests All tests that have the 'gc' fixture are now marked with the refcount mark. The reasoning is that they demonstrate flaky behaviour and disabling them in certain CI is necessary to ensure reliable testing. See #692 --- diff --git a/tests/conftest.py b/tests/conftest.py index f5d162357..6647cfd9e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,7 @@ -import gc -import sys import asyncio import selectors -from typing import Any, Dict, List, Tuple +import sys +from typing import Any, Dict, List import pytest @@ -14,6 +13,7 @@ pytest_plugins = ( "tests.fix_proxy", "tests.fix_psycopg", "tests.fix_crdb", + "tests.fix_gc", "tests.pool.fix_pool", ) @@ -98,70 +98,3 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config): terminalreporter.section("failed tests ignored") for msg in allow_fail_messages: terminalreporter.line(msg) - - -NO_COUNT_TYPES: Tuple[type, ...] = () - -if sys.version_info[:2] == (3, 10): - # On my laptop there are occasional creations of a single one of these objects - # with empty content, which might be some Decimal caching. - # Keeping the guard as strict as possible, to be extended if other types - # or versions are necessary. - try: - from _contextvars import Context # type: ignore - except ImportError: - pass - else: - NO_COUNT_TYPES += (Context,) - - -class GCFixture: - __slots__ = () - - @staticmethod - def collect() -> None: - """ - gc.collect(), but more insisting. - """ - for i in range(3): - gc.collect() - - @staticmethod - def count() -> int: - """ - len(gc.get_objects()), with subtleties. - """ - - if not NO_COUNT_TYPES: - return len(gc.get_objects()) - - # Note: not using a list comprehension because it pollutes the objects list. - rv = 0 - for obj in gc.get_objects(): - if isinstance(obj, NO_COUNT_TYPES): - continue - rv += 1 - - return rv - - -@pytest.fixture(name="gc") -def fixture_gc(): - """ - Provides a consistent way to run garbage collection and count references. - - **Note:** This will skip tests on PyPy. - """ - if sys.implementation.name == "pypy": - pytest.skip(reason="depends on refcount semantics") - return GCFixture() - - -@pytest.fixture -def gc_collect(): - """ - Provides a consistent way to run garbage collection. - - **Note:** This will *not* skip tests on PyPy. - """ - return GCFixture.collect diff --git a/tests/fix_gc.py b/tests/fix_gc.py new file mode 100644 index 000000000..ead6c6b21 --- /dev/null +++ b/tests/fix_gc.py @@ -0,0 +1,85 @@ +import gc +import sys +from typing import Tuple + +import pytest + + +def pytest_collection_modifyitems(items): + for item in items: + if "gc" in item.fixturenames: + item.add_marker(pytest.mark.refcount) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "refcount: the test checks ref counts which is sometimes flaky", + ) + + +NO_COUNT_TYPES: Tuple[type, ...] = () + +if sys.version_info[:2] == (3, 10): + # On my laptop there are occasional creations of a single one of these objects + # with empty content, which might be some Decimal caching. + # Keeping the guard as strict as possible, to be extended if other types + # or versions are necessary. + try: + from _contextvars import Context # type: ignore + except ImportError: + pass + else: + NO_COUNT_TYPES += (Context,) + + +class GCFixture: + __slots__ = () + + @staticmethod + def collect() -> None: + """ + gc.collect(), but more insisting. + """ + for i in range(3): + gc.collect() + + @staticmethod + def count() -> int: + """ + len(gc.get_objects()), with subtleties. + """ + + if not NO_COUNT_TYPES: + return len(gc.get_objects()) + + # Note: not using a list comprehension because it pollutes the objects list. + rv = 0 + for obj in gc.get_objects(): + if isinstance(obj, NO_COUNT_TYPES): + continue + rv += 1 + + return rv + + +@pytest.fixture(name="gc") +def fixture_gc(): + """ + Provides a consistent way to run garbage collection and count references. + + **Note:** This will skip tests on PyPy. + """ + if sys.implementation.name == "pypy": + pytest.skip(reason="depends on refcount semantics") + return GCFixture() + + +@pytest.fixture +def gc_collect(): + """ + Provides a consistent way to run garbage collection. + + **Note:** This will *not* skip tests on PyPy. + """ + return GCFixture.collect