Skip to content

Commit

Permalink
List pytest execution times
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p committed Nov 7, 2024
1 parent 7d3c6b9 commit 917273a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,9 @@ jobs:
-p no:sugar \
$(sed -n "${{ matrix.group }},1p" pytest_buckets.txt) \
2>&1 | tee pytest-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Rename time report
run: |
mv pytest-time-report.csv pytest-time-report-${{ matrix.python-version }}-${{ matrix.group }}.txt
- name: Upload pytest output
if: success() || failure() && steps.pytest-full.conclusion == 'failure'
uses: actions/[email protected]
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,5 @@ tmp_cache
.ropeproject

# Will be created from script/split_tests.py
pytest_buckets.txt
pytest_buckets.txt
pytest_time_report.csv
48 changes: 48 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
import asyncio
from collections.abc import AsyncGenerator, Callable, Coroutine, Generator
from contextlib import AsyncExitStack, asynccontextmanager, contextmanager
import csv
import datetime
import functools
import gc
import itertools
import logging
import os
from pathlib import Path
import reprlib
from shutil import rmtree
import sqlite3
import ssl
import threading
import time
from typing import TYPE_CHECKING, Any, cast
from unittest.mock import AsyncMock, MagicMock, Mock, _patch, patch

from _pytest.terminal import TerminalReporter
from aiohttp import client
from aiohttp.test_utils import (
BaseTestServer,
Expand Down Expand Up @@ -142,6 +146,48 @@ def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption("--dburl", action="store", default="sqlite://")


class PytestExecutionTimer: # noqa: D101
def __init__(self) -> None: # noqa: D107
self.durations: dict[tuple[str, str], list[float]] = {}

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_logstart( # noqa: D102
self, nodeid: str, location: tuple[str, int | None, str]
):
self.durations.setdefault((location[0], location[2]), []).append(
time.monotonic()
)
yield

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_logfinish( # noqa: D102
self, nodeid: str, location: tuple[str, int | None, str]
):
yield
self.durations[(location[0], location[2])].append(time.monotonic())

def pytest_terminal_summary( # noqa: D102
self,
terminalreporter: TerminalReporter,
exitstatus: pytest.ExitCode,
config: pytest.Config,
) -> None:
if config.option.collectonly:
return
terminalreporter.section("pytest-execution-timer")
data: dict[str, list[float]] = {}
for key, value in self.durations.items():
data.setdefault(key[0], []).append(value[1] - value[0])
lines = [(f"{sum(values):.3f}", key) for key, values in sorted(data.items())]
for line in lines:
terminalreporter.write_line("\t".join(line))
file = Path(__file__).parents[1].joinpath("pytest-time-report.csv")
with open(file, "w", encoding="utf-8") as fp:
writer = csv.writer(fp)
writer.writerow(("Time", "Path"))
writer.writerows(lines)


def pytest_configure(config: pytest.Config) -> None:
"""Register marker for tests that log exceptions."""
config.addinivalue_line(
Expand All @@ -155,6 +201,8 @@ def pytest_configure(config: pytest.Config) -> None:
# See https://github.com/syrupy-project/syrupy/pull/901
SnapshotSession.finish = override_syrupy_finish

config.pluginmanager.register(PytestExecutionTimer())


def pytest_runtest_setup() -> None:
"""Prepare pytest_socket and freezegun.
Expand Down

0 comments on commit 917273a

Please sign in to comment.