pybuilder-integration 108__tar.gz → 109__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pybuilder_integration-108 → pybuilder_integration-109}/PKG-INFO +1 -1
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/directory_utility.py +1 -2
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/properties.py +2 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/tasks.py +278 -42
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/PKG-INFO +1 -1
- {pybuilder_integration-108 → pybuilder_integration-109}/setup.py +1 -1
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/__init__.py +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/artifact_manager.py +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/cloudwatchlogs_utility.py +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/exec_utility.py +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/tool_utility.py +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/SOURCES.txt +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/dependency_links.txt +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/namespace_packages.txt +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/requires.txt +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/top_level.txt +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/zip-safe +0 -0
- {pybuilder_integration-108 → pybuilder_integration-109}/setup.cfg +0 -0
|
@@ -20,5 +20,7 @@ CONSOLIDATE_TESTS = "consolidate_tavern"
|
|
|
20
20
|
SKIP_NON_MATCHING_TAVERN_ARTIFACTS = "skip_non_matching_tavern_artifacts"
|
|
21
21
|
TESTING_SCOPE = "testing_scope"
|
|
22
22
|
PARALLEL_VERIFY_ENVIRONMENT = "parallel_verify_environment"
|
|
23
|
+
PARALLEL_TAVERN_SUITES = "parallel_tavern_suites"
|
|
24
|
+
PARALLEL_TAVERN_SUITES_WORKERS = "parallel_tavern_suites_workers"
|
|
23
25
|
SUBPROCESS_PROPERTIES_FILE_ENV = "PYBUILDER_INTEGRATION_PROPERTIES_FILE"
|
|
24
26
|
CYPRESS_CACHE_FOLDER = "cypress_cache_folder"
|
|
@@ -7,6 +7,7 @@ import shutil
|
|
|
7
7
|
import subprocess
|
|
8
8
|
import tempfile
|
|
9
9
|
import threading
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
10
11
|
|
|
11
12
|
from defusedxml import ElementTree as ET
|
|
12
13
|
from defusedxml.common import DefusedXmlException
|
|
@@ -48,11 +49,16 @@ _VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES = [
|
|
|
48
49
|
CONSOLIDATE_TESTS,
|
|
49
50
|
SKIP_NON_MATCHING_TAVERN_ARTIFACTS,
|
|
50
51
|
TESTING_SCOPE,
|
|
52
|
+
PARALLEL_TAVERN_SUITES,
|
|
53
|
+
PARALLEL_TAVERN_SUITES_WORKERS,
|
|
51
54
|
"abort_upload",
|
|
52
55
|
"record_cypress",
|
|
53
56
|
"verbose",
|
|
54
57
|
]
|
|
55
58
|
|
|
59
|
+
_TAVERN_INSTALL_LOCK = threading.Lock()
|
|
60
|
+
_DEFAULT_PARALLEL_TAVERN_SUITE_WORKERS = 8
|
|
61
|
+
|
|
56
62
|
def integration_artifact_push(project: Project, logger: Logger, reactor: Reactor):
|
|
57
63
|
logger.info("Starting upload of integration artifacts")
|
|
58
64
|
manager = get_artifact_manager(project)
|
|
@@ -886,15 +892,17 @@ def _run_tavern_tests_in_dist_dir(dist_directory, latest, logger, project, react
|
|
|
886
892
|
logger.debug(f"Run tavern tests in directory: {tavern_test_path} ")
|
|
887
893
|
logger.debug(f"Run tavern tests in directory files: {os.listdir(tavern_test_path)} ")
|
|
888
894
|
if latest:
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
895
|
+
suite_dirs = list(_iter_latest_tavern_suite_dirs(tavern_test_path, project))
|
|
896
|
+
if _get_bool_property(project, PARALLEL_TAVERN_SUITES, False) and len(suite_dirs) > 1:
|
|
897
|
+
_run_tavern_suites_parallel(suite_dirs, logger, project, reactor)
|
|
898
|
+
else:
|
|
899
|
+
for tavern_test_directory in suite_dirs:
|
|
900
|
+
logger.info(f"Running {tavern_test_directory}")
|
|
901
|
+
_run_tavern_tests_in_dir(test_dir=tavern_test_directory,
|
|
894
902
|
logger=logger,
|
|
895
903
|
project=project,
|
|
896
904
|
reactor=reactor,
|
|
897
|
-
role=os.path.basename(
|
|
905
|
+
role=os.path.basename(tavern_test_directory))
|
|
898
906
|
else:
|
|
899
907
|
_run_tavern_tests_in_dir(test_dir=f"{tavern_test_path}",
|
|
900
908
|
logger=logger,
|
|
@@ -904,6 +912,92 @@ def _run_tavern_tests_in_dist_dir(dist_directory, latest, logger, project, react
|
|
|
904
912
|
logger.info(f"Ran Tavern tests: {total_time.get_millis()}")
|
|
905
913
|
|
|
906
914
|
|
|
915
|
+
def _iter_latest_tavern_suite_dirs(tavern_test_path, project):
|
|
916
|
+
for tavern_dir in sorted(os.listdir(tavern_test_path)):
|
|
917
|
+
tavern_test_directory = os.path.join(tavern_test_path, tavern_dir)
|
|
918
|
+
if os.path.isdir(tavern_test_directory) and _should_run_latest(tavern_dir, project):
|
|
919
|
+
yield tavern_test_directory
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
def _parallel_tavern_suite_workers(project, suite_count):
|
|
923
|
+
configured = project.get_property(PARALLEL_TAVERN_SUITES_WORKERS, None)
|
|
924
|
+
if configured is None or configured == "":
|
|
925
|
+
return max(1, min(_DEFAULT_PARALLEL_TAVERN_SUITE_WORKERS, suite_count))
|
|
926
|
+
try:
|
|
927
|
+
workers = int(configured)
|
|
928
|
+
except (TypeError, ValueError):
|
|
929
|
+
workers = _DEFAULT_PARALLEL_TAVERN_SUITE_WORKERS
|
|
930
|
+
return max(1, min(workers, suite_count))
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def _run_tavern_suites_parallel(suite_dirs, logger, project, reactor):
|
|
934
|
+
prepare_reports_directory(project)
|
|
935
|
+
runnable = []
|
|
936
|
+
for tavern_test_directory in suite_dirs:
|
|
937
|
+
_, requested_marker, declared_markers, skip_non_matching = _tavern_marker_context(
|
|
938
|
+
tavern_test_directory, project
|
|
939
|
+
)
|
|
940
|
+
if _should_skip_nonmatching_tavern(requested_marker, declared_markers, skip_non_matching):
|
|
941
|
+
logger.info(
|
|
942
|
+
f"Skipping Tavern tests: artifact markers {sorted(declared_markers)} do not match {requested_marker}"
|
|
943
|
+
)
|
|
944
|
+
continue
|
|
945
|
+
runnable.append(tavern_test_directory)
|
|
946
|
+
if not runnable:
|
|
947
|
+
return
|
|
948
|
+
if len(runnable) == 1:
|
|
949
|
+
tavern_test_directory = runnable[0]
|
|
950
|
+
logger.info(f"Running {tavern_test_directory}")
|
|
951
|
+
_run_tavern_tests_in_dir(
|
|
952
|
+
test_dir=tavern_test_directory,
|
|
953
|
+
logger=logger,
|
|
954
|
+
project=project,
|
|
955
|
+
reactor=reactor,
|
|
956
|
+
role=os.path.basename(tavern_test_directory),
|
|
957
|
+
)
|
|
958
|
+
return
|
|
959
|
+
|
|
960
|
+
suite_sites = {}
|
|
961
|
+
for tavern_test_directory in runnable:
|
|
962
|
+
suite_sites[tavern_test_directory] = _install_tavern_suite_site(
|
|
963
|
+
tavern_test_directory, logger, project, reactor
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
workers = _parallel_tavern_suite_workers(project, len(runnable))
|
|
967
|
+
logger.info(f"Running {len(runnable)} tavern suites in parallel (workers={workers})")
|
|
968
|
+
failures = []
|
|
969
|
+
|
|
970
|
+
def run_suite(tavern_test_directory):
|
|
971
|
+
role = os.path.basename(tavern_test_directory)
|
|
972
|
+
logger.info(f"Running {tavern_test_directory}")
|
|
973
|
+
_run_tavern_tests_in_dir(
|
|
974
|
+
test_dir=tavern_test_directory,
|
|
975
|
+
logger=logger,
|
|
976
|
+
project=project,
|
|
977
|
+
reactor=reactor,
|
|
978
|
+
role=role,
|
|
979
|
+
isolated=True,
|
|
980
|
+
skip_install=True,
|
|
981
|
+
extra_pythonpath=suite_sites[tavern_test_directory],
|
|
982
|
+
)
|
|
983
|
+
return role
|
|
984
|
+
|
|
985
|
+
with ThreadPoolExecutor(max_workers=workers) as executor:
|
|
986
|
+
futures = {executor.submit(run_suite, suite_dir): suite_dir for suite_dir in runnable}
|
|
987
|
+
for future in as_completed(futures):
|
|
988
|
+
suite_dir = futures[future]
|
|
989
|
+
role = os.path.basename(suite_dir)
|
|
990
|
+
try:
|
|
991
|
+
future.result()
|
|
992
|
+
except Exception as error:
|
|
993
|
+
failures.append(f"{role}: {error}")
|
|
994
|
+
|
|
995
|
+
if failures:
|
|
996
|
+
raise BuildFailedException(
|
|
997
|
+
"Parallel tavern suites failed: " + "; ".join(sorted(failures))
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
|
|
907
1001
|
def verify_cypress(project: Project, logger: Logger, reactor: Reactor):
|
|
908
1002
|
# Get directories with test and cypress executable
|
|
909
1003
|
work_dir = project.expand_path(f"${CYPRESS_TEST_DIR}")
|
|
@@ -1087,7 +1181,101 @@ def verify_tavern(project: Project, logger: Logger, reactor: Reactor):
|
|
|
1087
1181
|
package_artifacts(project, test_dir, "tavern", project.get_property(ROLE))
|
|
1088
1182
|
|
|
1089
1183
|
|
|
1090
|
-
def
|
|
1184
|
+
def _tavern_marker_context(test_dir, project):
|
|
1185
|
+
extra_args = [project.expand(prop) for prop in project.get_property(TAVERN_ADDITIONAL_ARGS, [])]
|
|
1186
|
+
requested_marker = _extract_tavern_marker_filter(extra_args)
|
|
1187
|
+
declared_markers = read_tavern_service_markers(test_dir)
|
|
1188
|
+
skip_non_matching = _get_bool_property(project, SKIP_NON_MATCHING_TAVERN_ARTIFACTS, False)
|
|
1189
|
+
return extra_args, requested_marker, declared_markers, skip_non_matching
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def _should_skip_nonmatching_tavern(requested_marker, declared_markers, skip_non_matching):
|
|
1193
|
+
return bool(skip_non_matching and requested_marker and declared_markers and requested_marker not in declared_markers)
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
def _install_tavern_requirements(test_dir, logger, project, reactor):
|
|
1197
|
+
requirements_file = os.path.join(test_dir, "requirements.txt")
|
|
1198
|
+
if not os.path.exists(requirements_file):
|
|
1199
|
+
return
|
|
1200
|
+
dependency = RequirementsFile(requirements_file)
|
|
1201
|
+
with _TAVERN_INSTALL_LOCK:
|
|
1202
|
+
install_dependencies(logger, project, dependency, reactor.pybuilder_venv,
|
|
1203
|
+
f"{prepare_logs_directory(project)}/install_tavern_pip_dependencies.log")
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
def _plugin_venv_executable_path(executable):
|
|
1207
|
+
if executable is None:
|
|
1208
|
+
return None
|
|
1209
|
+
if isinstance(executable, (list, tuple)):
|
|
1210
|
+
if not executable:
|
|
1211
|
+
return None
|
|
1212
|
+
return _plugin_venv_executable_path(executable[0])
|
|
1213
|
+
try:
|
|
1214
|
+
path = os.fspath(executable)
|
|
1215
|
+
except TypeError:
|
|
1216
|
+
return None
|
|
1217
|
+
if isinstance(path, bytes):
|
|
1218
|
+
path = os.fsdecode(path)
|
|
1219
|
+
return path
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
def _plugin_venv_python(reactor):
|
|
1223
|
+
venv = getattr(reactor, "pybuilder_venv", None)
|
|
1224
|
+
path = _plugin_venv_executable_path(getattr(venv, "executable", None))
|
|
1225
|
+
if path and os.path.isfile(path) and os.access(path, os.X_OK):
|
|
1226
|
+
return path
|
|
1227
|
+
raise BuildFailedException("plugin venv python is missing or not executable")
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def _tavern_suite_site_directory(project, role):
|
|
1231
|
+
root = project.expand_path("$dir_target/integration/tavern-sites")
|
|
1232
|
+
directory_utility._ensure_directory_exists(root)
|
|
1233
|
+
site = os.path.join(root, role)
|
|
1234
|
+
if os.path.islink(site) or os.path.isfile(site):
|
|
1235
|
+
os.unlink(site)
|
|
1236
|
+
elif os.path.isdir(site):
|
|
1237
|
+
shutil.rmtree(site)
|
|
1238
|
+
os.makedirs(site, mode=0o700)
|
|
1239
|
+
return site
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
def _isolated_suite_subprocess_env(extra_pythonpath=None):
|
|
1243
|
+
env = os.environ.copy()
|
|
1244
|
+
env.pop("GITHUB_STEP_SUMMARY", None)
|
|
1245
|
+
env.pop("PYTHONHOME", None)
|
|
1246
|
+
env.pop("VIRTUAL_ENV", None)
|
|
1247
|
+
env.pop("PYTHONPATH", None)
|
|
1248
|
+
if extra_pythonpath:
|
|
1249
|
+
env["PYTHONPATH"] = extra_pythonpath
|
|
1250
|
+
return env
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
def _install_tavern_suite_site(test_dir, logger, project, reactor):
|
|
1254
|
+
requirements_file = os.path.join(test_dir, "requirements.txt")
|
|
1255
|
+
if not os.path.exists(requirements_file):
|
|
1256
|
+
return None
|
|
1257
|
+
role = os.path.basename(test_dir)
|
|
1258
|
+
site = _tavern_suite_site_directory(project, role)
|
|
1259
|
+
command = [_plugin_venv_python(reactor), "-m", "pip", "install", "-r", requirements_file, "-t", site]
|
|
1260
|
+
logger.info(f"Installing tavern requirements for {role} into {site}")
|
|
1261
|
+
process = subprocess.Popen(
|
|
1262
|
+
command,
|
|
1263
|
+
stdout=subprocess.PIPE,
|
|
1264
|
+
stderr=subprocess.STDOUT,
|
|
1265
|
+
cwd=test_dir,
|
|
1266
|
+
env=_isolated_suite_subprocess_env(),
|
|
1267
|
+
shell=False,
|
|
1268
|
+
)
|
|
1269
|
+
returncode = _stream_subprocess_output(process, logger, role)
|
|
1270
|
+
if returncode != 0:
|
|
1271
|
+
raise BuildFailedException(
|
|
1272
|
+
f"Failed to install tavern requirements for {role} into isolated site {site}"
|
|
1273
|
+
)
|
|
1274
|
+
return site
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, reactor: Reactor, role=None,
|
|
1278
|
+
isolated=False, skip_install=False, extra_pythonpath=None):
|
|
1091
1279
|
logger.info("Running tavern tests: {}".format(test_dir))
|
|
1092
1280
|
if not os.path.exists(test_dir):
|
|
1093
1281
|
logger.info("Skipping tavern run: no tests")
|
|
@@ -1095,51 +1283,51 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
|
|
|
1095
1283
|
logger.info(f"Found {len(os.listdir(test_dir))} files in tavern test directory")
|
|
1096
1284
|
# todo is this unique enough for each run?
|
|
1097
1285
|
output_file, run_name = get_test_report_file(project, test_dir)
|
|
1098
|
-
extra_args
|
|
1099
|
-
requested_marker
|
|
1100
|
-
declared_markers = read_tavern_service_markers(test_dir)
|
|
1101
|
-
skip_non_matching_artifacts = _get_bool_property(project, SKIP_NON_MATCHING_TAVERN_ARTIFACTS, False)
|
|
1102
|
-
if skip_non_matching_artifacts and requested_marker and declared_markers and requested_marker not in declared_markers:
|
|
1286
|
+
extra_args, requested_marker, declared_markers, skip_non_matching = _tavern_marker_context(test_dir, project)
|
|
1287
|
+
if _should_skip_nonmatching_tavern(requested_marker, declared_markers, skip_non_matching):
|
|
1103
1288
|
logger.info(
|
|
1104
1289
|
f"Skipping Tavern tests: artifact markers {sorted(declared_markers)} do not match {requested_marker}"
|
|
1105
1290
|
)
|
|
1106
1291
|
return True
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
dependency = RequirementsFile(requirements_file)
|
|
1113
|
-
install_dependencies(logger, project, dependency, reactor.pybuilder_venv,
|
|
1114
|
-
f"{prepare_logs_directory(project)}/install_tavern_pip_dependencies.log")
|
|
1292
|
+
if not isolated:
|
|
1293
|
+
from sys import path as syspath
|
|
1294
|
+
syspath.insert(0, test_dir)
|
|
1295
|
+
if not skip_install:
|
|
1296
|
+
_install_tavern_requirements(test_dir, logger, project, reactor)
|
|
1115
1297
|
args = ["--junit-xml", f"{output_file}", test_dir] + extra_args
|
|
1116
1298
|
if project.get_property("verbose"):
|
|
1117
1299
|
args.append("-s")
|
|
1118
1300
|
args.append("-v")
|
|
1119
|
-
if _get_bool_property(project, RUN_PARALLEL, False):
|
|
1301
|
+
if not isolated and _get_bool_property(project, RUN_PARALLEL, False):
|
|
1120
1302
|
args.extend(['-n', 'auto'])
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1303
|
+
target_url = project.get_property(INTEGRATION_TARGET_URL)
|
|
1304
|
+
public_target_url = project.get_property(INTEGRATION_PUBLIC_TARGET_URL)
|
|
1305
|
+
environment = project.get_property(ENVIRONMENT)
|
|
1306
|
+
pytest_env = _isolated_suite_subprocess_env(extra_pythonpath) if isolated else os.environ.copy()
|
|
1307
|
+
pytest_env['TARGET'] = target_url
|
|
1308
|
+
pytest_env['PUBLIC_TARGET'] = public_target_url
|
|
1309
|
+
pytest_env[ENVIRONMENT] = environment
|
|
1310
|
+
if not isolated:
|
|
1311
|
+
os.environ['TARGET'] = target_url
|
|
1312
|
+
os.environ['PUBLIC_TARGET'] = public_target_url
|
|
1313
|
+
os.environ[ENVIRONMENT] = environment
|
|
1314
|
+
logger.info(f"Running against target: {target_url} and "
|
|
1315
|
+
f"public target: {public_target_url}")
|
|
1316
|
+
logger.debug(f"Running args: {args} ")
|
|
1317
|
+
label = role or os.path.basename(test_dir)
|
|
1318
|
+
ret = _invoke_pytest(args, test_dir, pytest_env, logger, label, isolated, reactor)
|
|
1319
|
+
if _is_pytest_no_tests_collected(ret):
|
|
1320
|
+
fallback_args = _without_tavern_marker_filter(args)
|
|
1321
|
+
if fallback_args != args and (not skip_non_matching or not declared_markers):
|
|
1322
|
+
logger.warn(
|
|
1323
|
+
"No Tavern tests matched the marker filter; rerunning the full suite"
|
|
1324
|
+
)
|
|
1325
|
+
logger.debug(f"Running fallback args: {fallback_args} ")
|
|
1326
|
+
ret = _invoke_pytest(
|
|
1327
|
+
fallback_args, test_dir, pytest_env, logger, label, isolated, reactor
|
|
1328
|
+
)
|
|
1141
1329
|
|
|
1142
|
-
if ret
|
|
1330
|
+
if _is_pytest_failure(ret):
|
|
1143
1331
|
if role:
|
|
1144
1332
|
roles = []
|
|
1145
1333
|
if project.get_property(CONSOLIDATE_TESTS, False):
|
|
@@ -1156,6 +1344,54 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
|
|
|
1156
1344
|
return True
|
|
1157
1345
|
|
|
1158
1346
|
|
|
1347
|
+
def _is_pytest_no_tests_collected(ret):
|
|
1348
|
+
if ret is None:
|
|
1349
|
+
return False
|
|
1350
|
+
return int(ret) == int(pytest.ExitCode.NO_TESTS_COLLECTED)
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def _is_pytest_failure(ret):
|
|
1354
|
+
if ret is None:
|
|
1355
|
+
return True
|
|
1356
|
+
return int(ret) != 0
|
|
1357
|
+
|
|
1358
|
+
|
|
1359
|
+
def _invoke_pytest(args, test_dir, env, logger, label, isolated, reactor=None):
|
|
1360
|
+
if not isolated:
|
|
1361
|
+
cache_wd = os.getcwd()
|
|
1362
|
+
try:
|
|
1363
|
+
os.chdir(test_dir)
|
|
1364
|
+
return pytest.main(args)
|
|
1365
|
+
finally:
|
|
1366
|
+
os.chdir(cache_wd)
|
|
1367
|
+
command = [_plugin_venv_python(reactor), "-u", "-m", "pytest"] + args
|
|
1368
|
+
process = subprocess.Popen(
|
|
1369
|
+
command,
|
|
1370
|
+
stdout=subprocess.PIPE,
|
|
1371
|
+
stderr=subprocess.STDOUT,
|
|
1372
|
+
cwd=test_dir,
|
|
1373
|
+
env=env,
|
|
1374
|
+
shell=False,
|
|
1375
|
+
)
|
|
1376
|
+
return _stream_subprocess_output(process, logger, label)
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
def _stream_subprocess_output(process, logger, label):
|
|
1380
|
+
stdout = process.stdout
|
|
1381
|
+
if stdout is None:
|
|
1382
|
+
return process.wait()
|
|
1383
|
+
try:
|
|
1384
|
+
while True:
|
|
1385
|
+
chunk = stdout.readline()
|
|
1386
|
+
if not chunk:
|
|
1387
|
+
break
|
|
1388
|
+
redacted = _redact_secret_log_line(_decode_subprocess_stdout_line(chunk))
|
|
1389
|
+
logger.info(f"[{label}] {redacted}")
|
|
1390
|
+
finally:
|
|
1391
|
+
stdout.close()
|
|
1392
|
+
return process.wait()
|
|
1393
|
+
|
|
1394
|
+
|
|
1159
1395
|
def _without_tavern_marker_filter(args):
|
|
1160
1396
|
marker_options = {"-m", "--markexpr"}
|
|
1161
1397
|
fallback_args = []
|
|
@@ -21,7 +21,7 @@ class install(_install):
|
|
|
21
21
|
if __name__ == '__main__':
|
|
22
22
|
setup(
|
|
23
23
|
name = 'pybuilder-integration',
|
|
24
|
-
version = '
|
|
24
|
+
version = '109',
|
|
25
25
|
description = 'A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.',
|
|
26
26
|
long_description = 'A pybuilder plugin that runs integration tests against a target. This is intended to be a broader scope than unit-tests encompassing dependant functionality.',
|
|
27
27
|
long_description_content_type = None,
|
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/artifact_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/exec_utility.py
RENAMED
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration/tool_utility.py
RENAMED
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/requires.txt
RENAMED
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/top_level.txt
RENAMED
|
File without changes
|
{pybuilder_integration-108 → pybuilder_integration-109}/pybuilder_integration.egg-info/zip-safe
RENAMED
|
File without changes
|
|
File without changes
|