Repository navigation
Expand file tree
/
Copy pathconftest.py
More file actions
127 lines (100 loc) · 4.41 KB
/
Copy pathconftest.py
File metadata and controls
127 lines (100 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""Report native coverage when the optional backend is installed."""
from collections import Counter
import json
from pathlib import Path
import pytest
from gix import testtools
from git import _backend
from test.lib import GIT_REPO, TestBase
from test.lib.fixtures import read_only_fixture
def pytest_addoption(parser):
parser.addoption("--backend-report", help="Write native/CLI operation counts as JSON")
parser.addoption("--max-cli-processes", type=int, help="Maximum Git.execute launches in the pytest session")
parser.addoption("--max-cli-test-processes", type=int, help="Maximum Git.execute launches in test call phases")
def cli_processes():
return _backend.statistics().get(("Git.execute", "CLI process"), 0)
def pytest_configure(config):
for option in ("--max-cli-processes", "--max-cli-test-processes"):
limit = config.getoption(option)
if limit is not None and limit < 0:
raise pytest.UsageError(option + " must be nonnegative")
config._cli_process_start = cli_processes()
config._cli_process_phases = Counter()
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(item):
before = cli_processes()
yield
item.config._cli_process_phases["setup"] += cli_processes() - before
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
before = cli_processes()
yield
item.config._cli_process_phases["call"] += cli_processes() - before
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(item):
before = cli_processes()
yield
item.config._cli_process_phases["teardown"] += cli_processes() - before
def pytest_sessionfinish(session, exitstatus):
config = session.config
actual = {
"--max-cli-processes": cli_processes() - config._cli_process_start,
"--max-cli-test-processes": config._cli_process_phases["call"],
}
config._cli_process_failures = [
f"{option}: {count} launches exceed ceiling {config.getoption(option)}"
for option, count in actual.items()
if config.getoption(option) is not None and count > config.getoption(option)
]
if config._cli_process_failures:
session.exitstatus = pytest.ExitCode.TESTS_FAILED
def pytest_report_header(config):
return "GitPython backend: " + _backend.name
def pytest_terminal_summary(terminalreporter, exitstatus, config):
total = cli_processes() - config._cli_process_start
phases = config._cli_process_phases
terminalreporter.section("Git CLI process launches")
terminalreporter.write_line(
f"{total} total: {phases['setup']} setup, {phases['call']} call, "
f"{phases['teardown']} teardown, {total - sum(phases.values())} collection/session"
)
for failure in config._cli_process_failures:
terminalreporter.write_line(failure, red=True)
records = [
{"method": method, "outcome": outcome, "count": count}
for (method, outcome), count in sorted(_backend.statistics().items())
]
if _backend.name == "gix":
terminalreporter.section("GixPython operation coverage")
for item in records:
terminalreporter.write_line("{method}: {outcome} ({count})".format(**item))
records.extend(
{"method": "pytest." + phase, "outcome": "CLI process", "count": count}
for phase, count in sorted({**phases, "session_total": total}.items())
)
path = config.getoption("--backend-report")
if path:
Path(path).parent.mkdir(parents=True, exist_ok=True)
with open(path, "w", encoding="utf-8") as stream:
json.dump(records, stream, indent=2)
stream.write("\n")
@pytest.fixture(scope="session")
def dependency_repo_factory():
"""Prepare each immutable historical clone source only when a test needs it."""
source = Path(GIT_REPO).resolve()
revision = testtools.git(source, "rev-parse HEAD").strip()
paths = {}
def get(name):
if name not in paths:
paths[name] = str(read_only_fixture("dependencies.sh", source, revision, name) / "repo")
return paths[name]
return get
@pytest.fixture(scope="class", autouse=True)
def historical_dependency_sources(request):
if request.cls is not None and issubclass(request.cls, TestBase):
factory = request.getfixturevalue("dependency_repo_factory")
request.cls._dependency_repo_factory = staticmethod(factory)
yield
del request.cls._dependency_repo_factory
else:
yield