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
7 changes: 7 additions & 0 deletions changelog/12017.contrib.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Mixed internal improvements:

* Migrate formatting to f-strings in some tests.
* Use type-safe constructs in JUnitXML tests.
* Moved`` MockTiming`` into ``_pytest.timing``.

-- by :user:`RonnyPfannschmidt`
10 changes: 3 additions & 7 deletions src/_pytest/setuponly.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,9 @@ def _show_fixture_action(
# Use smaller indentation the higher the scope: Session = 0, Package = 1, etc.
scope_indent = list(reversed(Scope)).index(fixturedef._scope)
tw.write(" " * 2 * scope_indent)
tw.write(
"{step} {scope} {fixture}".format( # noqa: UP032 (Readability)
step=msg.ljust(8), # align the output to TEARDOWN
scope=fixturedef.scope[0].upper(),
fixture=fixturedef.argname,
)
)

scopename = fixturedef.scope[0].upper()
tw.write(f"{msg:<8} {scopename} {fixturedef.argname}")

if msg == "SETUP":
deps = sorted(arg for arg in fixturedef.argnames if arg != "request")
Expand Down
36 changes: 36 additions & 0 deletions src/_pytest/timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,45 @@

from __future__ import annotations

import dataclasses
from datetime import datetime
from time import perf_counter
from time import sleep
from time import time
from typing import TYPE_CHECKING


if TYPE_CHECKING:
from pytest import MonkeyPatch


@dataclasses.dataclass
class MockTiming:
"""Mocks _pytest.timing with a known object that can be used to control timing in tests
deterministically.

pytest itself should always use functions from `_pytest.timing` instead of `time` directly.

This then allows us more control over time during testing, if testing code also
uses `_pytest.timing` functions.

Time is static, and only advances through `sleep` calls, thus tests might sleep over large
numbers and obtain accurate time() calls at the end, making tests reliable and instant."""

_current_time: float = datetime(2020, 5, 22, 14, 20, 50).timestamp()

def sleep(self, seconds: float) -> None:
self._current_time += seconds

def time(self) -> float:
return self._current_time

def patch(self, monkeypatch: MonkeyPatch) -> None:
from _pytest import timing # noqa: PLW0406

monkeypatch.setattr(timing, "sleep", self.sleep)
monkeypatch.setattr(timing, "time", self.time)
monkeypatch.setattr(timing, "perf_counter", self.time)


__all__ = ["perf_counter", "sleep", "time"]
21 changes: 2 additions & 19 deletions testing/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from collections.abc import Generator
import dataclasses
import importlib.metadata
import re
import sys
Expand Down Expand Up @@ -233,24 +232,8 @@ def mock_timing(monkeypatch: MonkeyPatch):
Time is static, and only advances through `sleep` calls, thus tests might sleep over large
numbers and obtain accurate time() calls at the end, making tests reliable and instant.
"""

@dataclasses.dataclass
class MockTiming:
_current_time: float = 1590150050.0

def sleep(self, seconds: float) -> None:
self._current_time += seconds

def time(self) -> float:
return self._current_time

def patch(self) -> None:
from _pytest import timing

monkeypatch.setattr(timing, "sleep", self.sleep)
monkeypatch.setattr(timing, "time", self.time)
monkeypatch.setattr(timing, "perf_counter", self.time)
from _pytest.timing import MockTiming

result = MockTiming()
result.patch()
result.patch(monkeypatch)
return result
42 changes: 19 additions & 23 deletions testing/python/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2315,14 +2315,14 @@ def test_ordering_dependencies_torndown_first(
) -> None:
"""#226"""
pytester.makepyfile(
"""
f"""
import pytest
values = []
@pytest.fixture(%(param1)s)
@pytest.fixture({param1})
def arg1(request):
request.addfinalizer(lambda: values.append("fin1"))
values.append("new1")
@pytest.fixture(%(param2)s)
@pytest.fixture({param2})
def arg2(request, arg1):
request.addfinalizer(lambda: values.append("fin2"))
values.append("new2")
Expand All @@ -2331,8 +2331,7 @@ def test_arg(arg2):
pass
def test_check():
assert values == ["new1", "new2", "fin2", "fin1"]
""" # noqa: UP031 (python syntax issues)
% locals()
"""
)
reprec = pytester.inline_run("-s")
reprec.assertoutcome(passed=2)
Expand Down Expand Up @@ -3212,21 +3211,21 @@ def test_finalizer_order_on_parametrization(
) -> None:
"""#246"""
pytester.makepyfile(
"""
f"""
import pytest
values = []

@pytest.fixture(scope=%(scope)r, params=["1"])
@pytest.fixture(scope={scope!r}, params=["1"])
def fix1(request):
return request.param

@pytest.fixture(scope=%(scope)r)
@pytest.fixture(scope={scope!r})
def fix2(request, base):
def cleanup_fix2():
assert not values, "base should not have been finalized"
request.addfinalizer(cleanup_fix2)

@pytest.fixture(scope=%(scope)r)
@pytest.fixture(scope={scope!r})
def base(request, fix1):
def cleanup_base():
values.append("fin_base")
Expand All @@ -3239,8 +3238,7 @@ def test_baz(base, fix2):
pass
def test_other():
pass
""" # noqa: UP031 (python syntax issues)
% {"scope": scope}
"""
)
reprec = pytester.inline_run("-lvs")
reprec.assertoutcome(passed=3)
Expand Down Expand Up @@ -3426,42 +3424,40 @@ class TestRequestScopeAccess:

def test_setup(self, pytester: Pytester, scope, ok, error) -> None:
pytester.makepyfile(
"""
f"""
import pytest
@pytest.fixture(scope=%r, autouse=True)
@pytest.fixture(scope={scope!r}, autouse=True)
def myscoped(request):
for x in %r:
for x in {ok.split()}:
assert hasattr(request, x)
for x in %r:
for x in {error.split()}:
pytest.raises(AttributeError, lambda:
getattr(request, x))
assert request.session
assert request.config
def test_func():
pass
""" # noqa: UP031 (python syntax issues)
% (scope, ok.split(), error.split())
"""
)
reprec = pytester.inline_run("-l")
reprec.assertoutcome(passed=1)

def test_funcarg(self, pytester: Pytester, scope, ok, error) -> None:
pytester.makepyfile(
"""
f"""
import pytest
@pytest.fixture(scope=%r)
@pytest.fixture(scope={scope!r})
def arg(request):
for x in %r:
for x in {ok.split()!r}:
assert hasattr(request, x)
for x in %r:
for x in {error.split()!r}:
pytest.raises(AttributeError, lambda:
getattr(request, x))
assert request.session
assert request.config
def test_func(arg):
pass
""" # noqa: UP031 (python syntax issues)
% (scope, ok.split(), error.split())
"""
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=1)
Expand Down
Loading