forked from d2iq-archive/dcos-commons
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conftest.py
157 lines (130 loc) · 5.33 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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
""" This file configures python logging for the pytest framework
integration tests
Note: pytest must be invoked with this file in the working directory
E.G. py.test frameworks/<your-frameworks>/tests
"""
import logging
import os.path
import pytest
import retrying
import sys
import time
import sdk_diag
import sdk_external_volumes
import sdk_repository
import sdk_package_registry
import sdk_utils
import teamcity
log_level = os.getenv("TEST_LOG_LEVEL", "INFO").upper()
log_levels = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "EXCEPTION")
assert log_level in log_levels, "{} is not a valid log level. Use one of: {}".format(
log_level, ", ".join(log_levels)
)
# write everything to stdout due to the following circumstances:
# - other libraries may use print()/stdout directly
# - teamcity splits out stdout vs stderr into separate outputs, we'd want them combined
# Erase all existing root handlers to ensure that the following basicConfig call isn't ignored as a default handler
# may have been configured automatically via ANY interaction with the logging lib
# TODO(takirala): Replace this with `force=True` once we bump to python 3.7+
rootlog = logging.getLogger()
for h in rootlog.handlers[:]:
rootlog.removeHandler(h)
h.close()
logging.basicConfig(
format="[%(asctime)s|%(name)s-%(funcName)s(%(lineno)d)|%(levelname)s]: %(message)s",
level=log_level,
stream=sys.stdout,
)
# reduce excessive DEBUG/INFO noise produced by some underlying libraries:
for noise_source in [
"dcos.http",
"dcos.marathon",
"dcos.util",
"paramiko.transport",
"urllib3.connectionpool",
]:
logging.getLogger(noise_source).setLevel("WARNING")
log = logging.getLogger(__name__)
start_time = 0
def is_env_var_set(key: str, default: str) -> bool:
return str(os.environ.get(key, default)).lower() in ["true", "1"]
# The following environment variable allows for log collection to be turned off.
# This is useful, for example in testing.
INTEGRATION_TEST_LOG_COLLECTION = is_env_var_set(
"INTEGRATION_TEST_LOG_COLLECTION", default=str(True)
)
@pytest.fixture(scope="session", autouse=True)
def configure_universe(tmpdir_factory):
if is_env_var_set("PACKAGE_REGISTRY_ENABLED", default=""):
yield from sdk_package_registry.package_registry_session(tmpdir_factory)
else:
yield from sdk_repository.universe_session()
@pytest.fixture(scope="session", autouse=True)
def configure_external_volumes():
if is_env_var_set("ENABLE_EXTERNAL_VOLUMES", default=str(False)):
yield from sdk_external_volumes.external_volumes_session()
else:
yield
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item: pytest.Item, call): # _pytest.runner.CallInfo
"""Hook to run after every test, before any other post-test hooks.
See also: https://docs.pytest.org/en/latest/example/simple.html\
#making-test-result-information-available-in-fixtures
"""
# Execute all other hooks to obtain the report object.
outcome = yield
# Handle failures. Must be done here and not in a fixture in order to properly handle post-yield
# fixture teardown failures.
if INTEGRATION_TEST_LOG_COLLECTION:
@retrying.retry(
# It is possible that test-related timeouts are triggered while diagnostics-fetching
# runs, instead of when the actual test runs. That manifests as an "INTERNALERROR>
# Failed: Timeout" that crashes the test process. That is undesirable because then we
# get no diagnostics artifacts which would allow us to investigate why the test timed
# out in the first place.
#
# Here, we retry the diagnostics-fetching method in case of pytest exceptions.
#
# Check DCOS-51362 for more context.
stop_max_attempt_number=3,
retry_on_exception=lambda e: isinstance(e, pytest.fail.Exception),
)
def _create_diagnostics_bundle():
sdk_diag.handle_test_report(item, outcome.get_result())
_create_diagnostics_bundle()
else:
print("INTEGRATION_TEST_LOG_COLLECTION==False. Skipping log collection")
def pytest_runtest_teardown(item: pytest.Item):
"""Hook to run after every test."""
# Inject footer at end of test, may be followed by additional teardown.
# Don't do this when running in teamcity, where it's redundant.
if not teamcity.is_running_under_teamcity():
global start_time
duration = time.time() - start_time
start_time = 0
print(
"""
==========
======= END: {}::{} ({})
==========""".format(
sdk_diag.get_test_suite_name(item), item.name, sdk_utils.pretty_duration(duration)
)
)
def pytest_runtest_setup(item: pytest.Item):
"""Hook to run before every test."""
# Inject header at start of test, following automatic "path/to/test_file.py::test_name":
# Don't do this when running in teamcity, where it's redundant.
if not teamcity.is_running_under_teamcity():
global start_time
start_time = time.time()
print(
"""
==========
======= START: {}::{}
==========""".format(
sdk_diag.get_test_suite_name(item), item.name
)
)
if INTEGRATION_TEST_LOG_COLLECTION:
sdk_diag.handle_test_setup(item)
sdk_utils.check_dcos_min_version_mark(item)