-
Notifications
You must be signed in to change notification settings - Fork 9
/
conftest.py
127 lines (95 loc) · 3.58 KB
/
conftest.py
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
"""Pytest fixtures and configuration."""
from pathlib import Path
import subprocess
import pytest
from pytest import Session
EXAMPLES_DIR = Path(__file__).parent / "examples"
class ExampleFailedException(Exception):
"""Raised when an example fails."""
def __init__(self, message: str, exit_status: int):
"""Initialize ExampleFailedException."""
super().__init__(message)
self.exit_status = exit_status
class ExampleRunner:
"""Run the docker-compose of a given example."""
def __init__(self, compose_file: str):
"""Initialize ExampleRunner."""
self.compose_file = compose_file
def compose(self, *command: str) -> int:
"""Runs docker-compose using subprocess with the given command.
Returns exit status and output.
"""
try:
subprocess.run(
["docker-compose", "-f", self.compose_file, *command],
check=True,
)
return 0
except subprocess.CalledProcessError as e:
return e.returncode
def cleanup(self):
"""Runs docker-compose down -v for cleanup."""
exit_status = self.compose("down", "-v")
if exit_status != 0:
raise ExampleFailedException(
f"Cleanup failed with exit status {exit_status}", exit_status
)
def handle_run(self, *command: str):
"""Handles the run of docker-compose/.
raises exception if exit status is non-zero.
"""
try:
exit_status = self.compose(*command)
if exit_status != 0:
raise ExampleFailedException(
f"Command failed with exit status: {exit_status}",
exit_status=exit_status,
)
finally:
self.cleanup()
def pytest_collect_file(parent: Session, path):
"""Pytest collection hook.
This will collect the docker-compose.yml files from the examples and create
pytest items to run them.
"""
file = Path(str(path))
# Skip certain examples
if (file.parent / "__skip__").exists():
return
if file.suffix == ".yml" and file.parent.parent == EXAMPLES_DIR:
return ExampleFile.from_parent(parent, path=file.parent)
class ExampleFile(pytest.File):
"""Pytest file for example."""
def collect(self):
"""Collect tests from example file."""
path = Path(self.fspath)
item = ExmapleItem.from_parent(
self, name=path.name, compose_file=str(path / "docker-compose.yml")
)
item.add_marker(pytest.mark.examples)
yield item
class ExmapleItem(pytest.Item):
"""Example item.
Runs the docker-compose.yml file of the example and reports failure if the
exit status is non-zero.
"""
def __init__(self, name: str, parent: pytest.File, compose_file: str):
"""Initialize ExampleItem."""
super().__init__(name, parent)
self.compose_file = compose_file
def runtest(self) -> None:
"""Run the test."""
ExampleRunner(self.compose_file).handle_run("run", "example")
def repr_failure(self, excinfo):
"""Called when self.runtest() raises an exception."""
if isinstance(excinfo.value, ExampleFailedException):
return "\n".join(
[
"Example failed!",
f" {excinfo.value}",
]
)
return f"Some other exectpion happened: {excinfo.value}"
def reportinfo(self):
"""Report info about the example."""
return self.fspath, 0, f"example: {self.name}"