Repository: pytest-dev/pytest
File: src/_pytest/timing.py (Python)

Code before:

    # Creation time of this instant, using time.time(), to measure actual time.
    # Use a `lambda` to initialize the default to correctly get the mocked time via `MockTiming`.
    time: float = dataclasses.field(default_factory=lambda: time(), init=False)

    # Performance counter tick of the instant, used to measure precise elapsed time.
    # Use a `lambda` to initialize the default to correctly get the mocked time via `MockTiming`.
    perf_count: float = dataclasses.field(
        default_factory=lambda: perf_counter(), init=False
    )

    def duration(self) -> Duration:
        """Measure the duration since `Instant` was created."""
        return Duration(start=self, stop=Instant())

    def as_utc(self) -> datetime:
        """Instant as UTC datetime."""
        return datetime.fromtimestamp(self.time, timezone.utc)


@dataclasses.dataclass(frozen=True)
class Duration:
    """A span of time as measured by `Instant.duration()`."""

    start: Instant
    stop: Instant

    @property
    def elapsed_s(self) -> float:
        """Elapsed time of the duration, in seconds, measured using a performance counter for precise timing."""
        return self.stop.perf_count - self.start.perf_count


@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


Review comment (suggestion):
im tempted to suggest
```suggestion
    stop: Instant = dataclasses.field(default_factory=Instant)
```

but it may be best left to the explicit method in the instant

Code after:

    # Creation time of this instant, using time.time(), to measure actual time.
    # Note: using a `lambda` to correctly get the mocked time via `MockTiming`.
    time: float = dataclasses.field(default_factory=lambda: time(), init=False)

    # Performance counter tick of the instant, used to measure precise elapsed time.
    # Note: using a `lambda` to correctly get the mocked time via `MockTiming`.
    perf_count: float = dataclasses.field(
        default_factory=lambda: perf_counter(), init=False
    )

    def elapsed(self) -> Duration:
        """Measure the duration since `Instant` was created."""
        return Duration(start=self, stop=Instant())

    def as_utc(self) -> datetime:
        """Instant as UTC datetime."""
        return datetime.fromtimestamp(self.time, timezone.utc)


@dataclasses.dataclass(frozen=True)
class Duration:
    """A span of time as measured by `Instant.elapsed()`."""

    start: Instant
    stop: Instant

    @property
    def seconds(self) -> float:
        """Elapsed time of the duration in seconds, measured using a performance counter for precise timing."""
        return self.stop.perf_count - self.start.perf_count


@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