pybuilder-integration 104__tar.gz → 106__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-104 → pybuilder_integration-106}/PKG-INFO +4 -3
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/artifact_manager.py +9 -1
- pybuilder_integration-106/pybuilder_integration/directory_utility.py +168 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/properties.py +1 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/tasks.py +82 -16
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/PKG-INFO +4 -3
- pybuilder_integration-106/pybuilder_integration.egg-info/requires.txt +4 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/setup.py +4 -3
- pybuilder_integration-104/pybuilder_integration/directory_utility.py +0 -70
- pybuilder_integration-104/pybuilder_integration.egg-info/requires.txt +0 -3
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/__init__.py +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/cloudwatchlogs_utility.py +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/exec_utility.py +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/tool_utility.py +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/SOURCES.txt +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/dependency_links.txt +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/namespace_packages.txt +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/top_level.txt +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/zip-safe +0 -0
- {pybuilder_integration-104 → pybuilder_integration-106}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pybuilder-integration
|
|
3
|
-
Version:
|
|
3
|
+
Version: 106
|
|
4
4
|
Summary: A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.
|
|
5
5
|
Home-page: https://github.com/rspitler/pybuilder-integration
|
|
6
6
|
Author:
|
|
@@ -10,9 +10,10 @@ Maintainer-email:
|
|
|
10
10
|
License: Apache 2.0
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Requires-Dist: pytest
|
|
13
|
+
Requires-Dist: pytest==7.4.4
|
|
14
14
|
Requires-Dist: tavern==1.25.2
|
|
15
|
-
Requires-Dist: boto3
|
|
15
|
+
Requires-Dist: boto3==1.43.72
|
|
16
|
+
Requires-Dist: defusedxml==0.7.1
|
|
16
17
|
Dynamic: classifier
|
|
17
18
|
Dynamic: description
|
|
18
19
|
Dynamic: home-page
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/artifact_manager.py
RENAMED
|
@@ -8,7 +8,7 @@ from pybuilder.reactor import Reactor
|
|
|
8
8
|
|
|
9
9
|
from pybuilder_integration import exec_utility
|
|
10
10
|
from pybuilder_integration.directory_utility import get_latest_distribution_directory, \
|
|
11
|
-
get_latest_zipped_distribution_directory
|
|
11
|
+
get_latest_zipped_distribution_directory, read_tavern_service_markers, write_tavern_manifest
|
|
12
12
|
from pybuilder_integration.properties import *
|
|
13
13
|
|
|
14
14
|
|
|
@@ -200,13 +200,21 @@ def _unzip_downloaded_artifacts(dir_with_zips: str, destination: str, logger: Lo
|
|
|
200
200
|
os.makedirs(consolidated_folder, exist_ok=True)
|
|
201
201
|
logger.debug(f"Creating role file for log retrieval {consolidated_folder}/roles")
|
|
202
202
|
open(f"{consolidated_folder}/roles", 'w').close()
|
|
203
|
+
consolidated_markers = set()
|
|
204
|
+
has_tavern_manifest = False
|
|
203
205
|
for dirn in os.listdir(f"{destination}/tavern"):
|
|
204
206
|
if "consolidated" != dirn:
|
|
205
207
|
logger.debug(f"Consolidating directory: {dirn}")
|
|
208
|
+
markers = read_tavern_service_markers(f"{destination}/tavern/{dirn}")
|
|
209
|
+
if markers is not None:
|
|
210
|
+
has_tavern_manifest = True
|
|
211
|
+
consolidated_markers.update(markers)
|
|
206
212
|
shutil.copytree(f"{destination}/tavern/{dirn}", consolidated_folder, dirs_exist_ok=True)
|
|
207
213
|
shutil.rmtree(f"{destination}/tavern/{dirn}")
|
|
208
214
|
with open(f"{consolidated_folder}/roles", "a") as fp:
|
|
209
215
|
fp.write(f"{dirn}\n")
|
|
216
|
+
if has_tavern_manifest:
|
|
217
|
+
write_tavern_manifest(consolidated_folder, consolidated_markers)
|
|
210
218
|
return destination
|
|
211
219
|
|
|
212
220
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
TAVERN_MANIFEST_DIRECTORY = ".pybuilder-integration"
|
|
9
|
+
TAVERN_MANIFEST_FILENAME = "tavern-manifest.json"
|
|
10
|
+
_TAVERN_MARKER_EXCLUSIONS = {"parametrize", "skip", "skipif", "usefixtures", "xfail"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def prepare_reports_directory(project):
|
|
14
|
+
return prepare_directory("$dir_reports", project)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def prepare_logs_directory(project):
|
|
18
|
+
return prepare_directory("$dir_logs", project)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def prepare_dist_directory(project):
|
|
22
|
+
return prepare_directory("$dir_dist", project)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def prepare_directory(dir_variable, project):
|
|
26
|
+
package__format = f"{dir_variable}/integration"
|
|
27
|
+
reports_dir = project.expand_path(package__format)
|
|
28
|
+
return _ensure_directory_exists(reports_dir)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _ensure_directory_exists(path):
|
|
32
|
+
if not os.path.exists(path):
|
|
33
|
+
os.makedirs(path)
|
|
34
|
+
return path
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_working_distribution_directory(project):
|
|
38
|
+
dist_directory = prepare_dist_directory(project)
|
|
39
|
+
return _ensure_directory_exists(f"{dist_directory}/working")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_latest_distribution_directory(project):
|
|
43
|
+
dist_directory = prepare_dist_directory(project)
|
|
44
|
+
from pybuilder_integration import ENVIRONMENT
|
|
45
|
+
environment = project.get_mandatory_property(ENVIRONMENT)
|
|
46
|
+
return _ensure_directory_exists(f"{dist_directory}/LATEST-{environment}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_latest_zipped_distribution_directory(project):
|
|
50
|
+
dist_directory = prepare_dist_directory(project)
|
|
51
|
+
from pybuilder_integration import ENVIRONMENT
|
|
52
|
+
environment = project.get_mandatory_property(ENVIRONMENT)
|
|
53
|
+
return _ensure_directory_exists(f"{dist_directory}/LATEST-{environment}/zipped")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_local_zip_artifact_path(tool, project, include_ending=False):
|
|
57
|
+
artifact = f"{prepare_dist_directory(project)}/{tool}-{project.name}"
|
|
58
|
+
if include_ending:
|
|
59
|
+
return f"{artifact}.zip"
|
|
60
|
+
return artifact
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_tavern_manifest_path(test_dir):
|
|
64
|
+
return os.path.join(test_dir, TAVERN_MANIFEST_DIRECTORY, TAVERN_MANIFEST_FILENAME)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def collect_tavern_service_markers(test_dir):
|
|
68
|
+
markers = set()
|
|
69
|
+
for root, _, files in os.walk(test_dir):
|
|
70
|
+
for filename in files:
|
|
71
|
+
if filename.endswith(".tavern.yaml"):
|
|
72
|
+
markers.update(_collect_markers_from_tavern_file(os.path.join(root, filename)))
|
|
73
|
+
return sorted(markers)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def write_tavern_manifest(test_dir, service_markers=None):
|
|
77
|
+
manifest_path = get_tavern_manifest_path(test_dir)
|
|
78
|
+
manifest_directory = os.path.dirname(manifest_path)
|
|
79
|
+
os.makedirs(manifest_directory, exist_ok=True)
|
|
80
|
+
if service_markers is None:
|
|
81
|
+
service_markers = collect_tavern_service_markers(test_dir)
|
|
82
|
+
manifest_content = json.dumps({"service_markers": sorted(service_markers)}).encode("utf-8")
|
|
83
|
+
descriptor, temporary_path = tempfile.mkstemp(prefix=".tavern-manifest-", dir=manifest_directory)
|
|
84
|
+
try:
|
|
85
|
+
bytes_written = 0
|
|
86
|
+
while bytes_written < len(manifest_content):
|
|
87
|
+
written = os.write(descriptor, manifest_content[bytes_written:])
|
|
88
|
+
if written == 0:
|
|
89
|
+
raise OSError("Could not write Tavern marker manifest")
|
|
90
|
+
bytes_written += written
|
|
91
|
+
os.close(descriptor)
|
|
92
|
+
descriptor = None
|
|
93
|
+
os.replace(temporary_path, manifest_path)
|
|
94
|
+
finally:
|
|
95
|
+
if descriptor is not None:
|
|
96
|
+
os.close(descriptor)
|
|
97
|
+
if os.path.exists(temporary_path):
|
|
98
|
+
os.unlink(temporary_path)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def read_tavern_service_markers(test_dir):
|
|
102
|
+
manifest_path = get_tavern_manifest_path(test_dir)
|
|
103
|
+
if not os.path.exists(manifest_path):
|
|
104
|
+
return None
|
|
105
|
+
try:
|
|
106
|
+
with open(manifest_path) as manifest_file:
|
|
107
|
+
manifest = json.load(manifest_file)
|
|
108
|
+
except (OSError, ValueError):
|
|
109
|
+
return None
|
|
110
|
+
markers = manifest.get("service_markers", [])
|
|
111
|
+
if not isinstance(markers, list) or not all(isinstance(marker, str) for marker in markers):
|
|
112
|
+
return None
|
|
113
|
+
return set(markers)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _collect_markers_from_tavern_file(path):
|
|
117
|
+
markers = set()
|
|
118
|
+
in_marks = False
|
|
119
|
+
marks_indent = 0
|
|
120
|
+
with open(path) as tavern_file:
|
|
121
|
+
for line in tavern_file:
|
|
122
|
+
match = re.match(r"^(\s*)marks:\s*(.*)$", line)
|
|
123
|
+
if match:
|
|
124
|
+
in_marks = True
|
|
125
|
+
marks_indent = len(match.group(1))
|
|
126
|
+
inline_markers = match.group(2).strip()
|
|
127
|
+
if inline_markers.startswith("[") and inline_markers.endswith("]"):
|
|
128
|
+
for marker in inline_markers[1:-1].split(","):
|
|
129
|
+
_add_tavern_marker(markers, marker)
|
|
130
|
+
continue
|
|
131
|
+
if not in_marks:
|
|
132
|
+
continue
|
|
133
|
+
if not line.strip() or line.lstrip().startswith("#"):
|
|
134
|
+
continue
|
|
135
|
+
indent = len(line) - len(line.lstrip())
|
|
136
|
+
if indent <= marks_indent:
|
|
137
|
+
in_marks = False
|
|
138
|
+
continue
|
|
139
|
+
marker = re.match(r"^\s*-\s*(.+?)\s*(?:#.*)?$", line)
|
|
140
|
+
if marker:
|
|
141
|
+
_add_tavern_marker(markers, marker.group(1))
|
|
142
|
+
return markers
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _add_tavern_marker(markers, value):
|
|
146
|
+
marker = value.strip().strip("'\"")
|
|
147
|
+
if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", marker) and marker not in _TAVERN_MARKER_EXCLUSIONS:
|
|
148
|
+
markers.add(marker)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def package_artifacts(project, test_dir, tool, role):
|
|
152
|
+
# Make a copy for easy access in environment validation
|
|
153
|
+
working_dir = get_working_distribution_directory(project)
|
|
154
|
+
shutil.copytree(test_dir, f"{working_dir}/{tool}",dirs_exist_ok=True)
|
|
155
|
+
if tool == "tavern":
|
|
156
|
+
write_tavern_manifest(f"{working_dir}/{tool}")
|
|
157
|
+
with tempfile.TemporaryDirectory() as tempdir:
|
|
158
|
+
shutil.copytree(test_dir, f"{tempdir}/{role}",dirs_exist_ok=True)
|
|
159
|
+
if tool == "tavern":
|
|
160
|
+
write_tavern_manifest(f"{tempdir}/{role}")
|
|
161
|
+
# package a copy for distribution
|
|
162
|
+
# zip up the test and add them to the integration test dist directory
|
|
163
|
+
base_name = get_local_zip_artifact_path(tool=tool, project=project)
|
|
164
|
+
shutil.make_archive(base_name=base_name,
|
|
165
|
+
base_dir=role,
|
|
166
|
+
format="zip",
|
|
167
|
+
root_dir=tempdir)
|
|
168
|
+
|
|
@@ -17,6 +17,7 @@ APPLICATION = "application"
|
|
|
17
17
|
APPLICATION_GROUP = "application_group"
|
|
18
18
|
RUN_PARALLEL = "pytest_parallel"
|
|
19
19
|
CONSOLIDATE_TESTS = "consolidate_tavern"
|
|
20
|
+
SKIP_NON_MATCHING_TAVERN_ARTIFACTS = "skip_non_matching_tavern_artifacts"
|
|
20
21
|
TESTING_SCOPE = "testing_scope"
|
|
21
22
|
PARALLEL_VERIFY_ENVIRONMENT = "parallel_verify_environment"
|
|
22
23
|
SUBPROCESS_PROPERTIES_FILE_ENV = "PYBUILDER_INTEGRATION_PROPERTIES_FILE"
|
|
@@ -3,9 +3,10 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
import shutil
|
|
5
5
|
import subprocess
|
|
6
|
-
import sys
|
|
7
6
|
import tempfile
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
from defusedxml import ElementTree as ET
|
|
9
|
+
from defusedxml.common import DefusedXmlException
|
|
9
10
|
|
|
10
11
|
import pytest
|
|
11
12
|
from pybuilder.core import Project, Logger, RequirementsFile
|
|
@@ -18,7 +19,8 @@ from pybuilder_integration import exec_utility, tool_utility, directory_utility
|
|
|
18
19
|
from pybuilder_integration.artifact_manager import get_artifact_manager
|
|
19
20
|
from pybuilder_integration.cloudwatchlogs_utility import CloudwatchLogs
|
|
20
21
|
from pybuilder_integration.directory_utility import get_working_distribution_directory, \
|
|
21
|
-
package_artifacts, prepare_reports_directory, get_local_zip_artifact_path, prepare_logs_directory
|
|
22
|
+
package_artifacts, prepare_reports_directory, get_local_zip_artifact_path, prepare_logs_directory, \
|
|
23
|
+
read_tavern_service_markers
|
|
22
24
|
from pybuilder_integration.properties import *
|
|
23
25
|
from pybuilder_integration.tool_utility import install_cypress
|
|
24
26
|
|
|
@@ -26,6 +28,7 @@ from pybuilder_integration.tool_utility import install_cypress
|
|
|
26
28
|
_VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES = [
|
|
27
29
|
INTEGRATION_ARTIFACT_BUCKET,
|
|
28
30
|
ENVIRONMENT,
|
|
31
|
+
ENVIRONMENT_VARIABLES,
|
|
29
32
|
ARTIFACT_MANAGER,
|
|
30
33
|
CYPRESS_TEST_DIR,
|
|
31
34
|
CYPRESS_CONFIG_FILE,
|
|
@@ -40,6 +43,7 @@ _VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES = [
|
|
|
40
43
|
APPLICATION_GROUP,
|
|
41
44
|
RUN_PARALLEL,
|
|
42
45
|
CONSOLIDATE_TESTS,
|
|
46
|
+
SKIP_NON_MATCHING_TAVERN_ARTIFACTS,
|
|
43
47
|
TESTING_SCOPE,
|
|
44
48
|
"abort_upload",
|
|
45
49
|
"record_cypress",
|
|
@@ -132,10 +136,54 @@ def _run_verify_environment_passes_in_subprocesses(project: Project, logger: Log
|
|
|
132
136
|
if failures:
|
|
133
137
|
raise BuildFailedException("Parallel verify_environment failed: " + "; ".join(failures))
|
|
134
138
|
finally:
|
|
139
|
+
for _, process, _ in processes:
|
|
140
|
+
poll = getattr(process, "poll", None)
|
|
141
|
+
if callable(poll) and poll() is None:
|
|
142
|
+
process.terminate()
|
|
143
|
+
process.wait()
|
|
144
|
+
log_handle = getattr(process, "_pybuilder_integration_log_handle", None)
|
|
145
|
+
if log_handle and not log_handle.closed:
|
|
146
|
+
log_handle.close()
|
|
147
|
+
_merge_parallel_verify_environment_outputs(project, passes)
|
|
135
148
|
if os.path.exists(properties_file):
|
|
136
149
|
os.remove(properties_file)
|
|
137
150
|
|
|
138
151
|
|
|
152
|
+
def _merge_parallel_verify_environment_outputs(project: Project, passes):
|
|
153
|
+
parent_target = project.expand_path("$dir_target")
|
|
154
|
+
parent_logs_directory = prepare_logs_directory(project)
|
|
155
|
+
parent_output_directories = [
|
|
156
|
+
("logs", parent_logs_directory),
|
|
157
|
+
("reports", prepare_reports_directory(project)),
|
|
158
|
+
]
|
|
159
|
+
parent_cypress_log = os.path.join(parent_logs_directory, "cypress_run.log")
|
|
160
|
+
has_parent_cypress_log = False
|
|
161
|
+
for pass_config in passes:
|
|
162
|
+
stage_target = os.path.join(parent_target, "verify_environment", pass_config["name"])
|
|
163
|
+
for output_directory, destination_directory in parent_output_directories:
|
|
164
|
+
source = os.path.join(stage_target, output_directory, "integration")
|
|
165
|
+
if os.path.exists(source):
|
|
166
|
+
destination = os.path.join(
|
|
167
|
+
destination_directory,
|
|
168
|
+
"parallel",
|
|
169
|
+
pass_config["name"],
|
|
170
|
+
)
|
|
171
|
+
shutil.copytree(source, destination, dirs_exist_ok=True)
|
|
172
|
+
if output_directory == "logs":
|
|
173
|
+
cypress_log = os.path.join(source, "cypress_run.log")
|
|
174
|
+
if os.path.exists(cypress_log):
|
|
175
|
+
_copy_cypress_log(cypress_log, parent_cypress_log, has_parent_cypress_log)
|
|
176
|
+
has_parent_cypress_log = True
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _copy_cypress_log(source, destination, append):
|
|
180
|
+
flags = os.O_WRONLY | os.O_CREAT | (os.O_APPEND if append else os.O_TRUNC)
|
|
181
|
+
mode = "ab" if append else "wb"
|
|
182
|
+
with open(source, "rb") as source_file:
|
|
183
|
+
with os.fdopen(os.open(destination, flags, 0o600), mode) as destination_file:
|
|
184
|
+
shutil.copyfileobj(source_file, destination_file)
|
|
185
|
+
|
|
186
|
+
|
|
139
187
|
def _start_verify_environment_subprocess(project: Project, logger: Logger, pass_config, properties_file):
|
|
140
188
|
log_file = os.path.join(prepare_logs_directory(project), f"verify_environment_{pass_config['name']}.log")
|
|
141
189
|
command = _build_verify_environment_subprocess_command(project, pass_config)
|
|
@@ -164,9 +212,7 @@ def _start_verify_environment_subprocess(project: Project, logger: Logger, pass_
|
|
|
164
212
|
def _build_verify_environment_subprocess_command(project: Project, pass_config):
|
|
165
213
|
stage_target = os.path.join(project.expand_path("$dir_target"), "verify_environment", pass_config["name"])
|
|
166
214
|
return [
|
|
167
|
-
|
|
168
|
-
"-c",
|
|
169
|
-
"from pybuilder.cli import main; main()",
|
|
215
|
+
"pyb",
|
|
170
216
|
pass_config["task"],
|
|
171
217
|
"-P", f"{WORKING_TEST_DIR}={pass_config['test_dir']}",
|
|
172
218
|
"-P", f"{PARALLEL_VERIFY_ENVIRONMENT}=False",
|
|
@@ -189,13 +235,12 @@ def _collect_json_safe_project_properties(project: Project):
|
|
|
189
235
|
safe_properties = {}
|
|
190
236
|
for name in _VERIFY_ENVIRONMENT_SUBPROCESS_PROPERTIES:
|
|
191
237
|
value = project.get_property(name, None)
|
|
192
|
-
if value is None:
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
safe_properties[name] = value
|
|
238
|
+
if value is not None:
|
|
239
|
+
try:
|
|
240
|
+
json.dumps(value)
|
|
241
|
+
except (TypeError, ValueError):
|
|
242
|
+
continue
|
|
243
|
+
safe_properties[name] = value
|
|
199
244
|
return safe_properties
|
|
200
245
|
|
|
201
246
|
|
|
@@ -374,7 +419,7 @@ def _build_cypress_summary(log_path):
|
|
|
374
419
|
name = tc.get("name", "")
|
|
375
420
|
msg = (failure.get("message") or failure.text or "").split("\n")[0][:120]
|
|
376
421
|
failures.append((f"{suite} > {name}", msg))
|
|
377
|
-
except ET.ParseError:
|
|
422
|
+
except (ET.ParseError, DefusedXmlException):
|
|
378
423
|
pass
|
|
379
424
|
|
|
380
425
|
passed = total - failed - skipped
|
|
@@ -449,6 +494,15 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
|
|
|
449
494
|
logger.info(f"Found {len(os.listdir(test_dir))} files in tavern test directory")
|
|
450
495
|
# todo is this unique enough for each run?
|
|
451
496
|
output_file, run_name = get_test_report_file(project, test_dir)
|
|
497
|
+
extra_args = [project.expand(prop) for prop in project.get_property(TAVERN_ADDITIONAL_ARGS, [])]
|
|
498
|
+
requested_marker = _extract_tavern_marker_filter(extra_args)
|
|
499
|
+
declared_markers = read_tavern_service_markers(test_dir)
|
|
500
|
+
skip_non_matching_artifacts = _get_bool_property(project, SKIP_NON_MATCHING_TAVERN_ARTIFACTS, False)
|
|
501
|
+
if skip_non_matching_artifacts and requested_marker and declared_markers and requested_marker not in declared_markers:
|
|
502
|
+
logger.info(
|
|
503
|
+
f"Skipping Tavern tests: artifact markers {sorted(declared_markers)} do not match {requested_marker}"
|
|
504
|
+
)
|
|
505
|
+
return True
|
|
452
506
|
from sys import path as syspath
|
|
453
507
|
syspath.insert(0, test_dir)
|
|
454
508
|
# install any requirements that my exist
|
|
@@ -457,7 +511,6 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
|
|
|
457
511
|
dependency = RequirementsFile(requirements_file)
|
|
458
512
|
install_dependencies(logger, project, dependency, reactor.pybuilder_venv,
|
|
459
513
|
f"{prepare_logs_directory(project)}/install_tavern_pip_dependencies.log")
|
|
460
|
-
extra_args = [project.expand(prop) for prop in project.get_property(TAVERN_ADDITIONAL_ARGS, [])]
|
|
461
514
|
args = ["--junit-xml", f"{output_file}", test_dir] + extra_args
|
|
462
515
|
if project.get_property("verbose"):
|
|
463
516
|
args.append("-s")
|
|
@@ -476,7 +529,7 @@ def _run_tavern_tests_in_dir(test_dir: str, logger: Logger, project: Project, re
|
|
|
476
529
|
ret = pytest.main(args)
|
|
477
530
|
if ret == pytest.ExitCode.NO_TESTS_COLLECTED:
|
|
478
531
|
fallback_args = _without_tavern_marker_filter(args)
|
|
479
|
-
if fallback_args != args:
|
|
532
|
+
if fallback_args != args and (not skip_non_matching_artifacts or not declared_markers):
|
|
480
533
|
logger.warn(
|
|
481
534
|
"No Tavern tests matched the marker filter; rerunning the full suite"
|
|
482
535
|
)
|
|
@@ -519,6 +572,19 @@ def _without_tavern_marker_filter(args):
|
|
|
519
572
|
return fallback_args
|
|
520
573
|
|
|
521
574
|
|
|
575
|
+
def _extract_tavern_marker_filter(args):
|
|
576
|
+
for index, arg in enumerate(args):
|
|
577
|
+
if arg in {"-m", "--markexpr"} and index + 1 < len(args):
|
|
578
|
+
marker = args[index + 1]
|
|
579
|
+
elif arg.startswith("--markexpr="):
|
|
580
|
+
marker = arg.split("=", 1)[1]
|
|
581
|
+
else:
|
|
582
|
+
continue
|
|
583
|
+
if marker.replace("_", "").isalnum() and marker:
|
|
584
|
+
return marker
|
|
585
|
+
return None
|
|
586
|
+
|
|
587
|
+
|
|
522
588
|
def get_test_report_file(project, test_dir, tool="tavern"):
|
|
523
589
|
run_name = os.path.basename(test_dir)
|
|
524
590
|
output_file = os.path.join(prepare_reports_directory(project), f"{tool}-{run_name}.out.xml")
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pybuilder-integration
|
|
3
|
-
Version:
|
|
3
|
+
Version: 106
|
|
4
4
|
Summary: A pybuilder plugin that runs integration tests (Tavern & Cypress) against a target.
|
|
5
5
|
Home-page: https://github.com/rspitler/pybuilder-integration
|
|
6
6
|
Author:
|
|
@@ -10,9 +10,10 @@ Maintainer-email:
|
|
|
10
10
|
License: Apache 2.0
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Requires-Dist: pytest
|
|
13
|
+
Requires-Dist: pytest==7.4.4
|
|
14
14
|
Requires-Dist: tavern==1.25.2
|
|
15
|
-
Requires-Dist: boto3
|
|
15
|
+
Requires-Dist: boto3==1.43.72
|
|
16
|
+
Requires-Dist: defusedxml==0.7.1
|
|
16
17
|
Dynamic: classifier
|
|
17
18
|
Dynamic: description
|
|
18
19
|
Dynamic: home-page
|
|
@@ -21,7 +21,7 @@ class install(_install):
|
|
|
21
21
|
if __name__ == '__main__':
|
|
22
22
|
setup(
|
|
23
23
|
name = 'pybuilder-integration',
|
|
24
|
-
version = '
|
|
24
|
+
version = '106',
|
|
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,
|
|
@@ -49,9 +49,10 @@ if __name__ == '__main__':
|
|
|
49
49
|
data_files = [],
|
|
50
50
|
package_data = {},
|
|
51
51
|
install_requires = [
|
|
52
|
-
'pytest',
|
|
52
|
+
'pytest==7.4.4',
|
|
53
53
|
'tavern==1.25.2',
|
|
54
|
-
'boto3'
|
|
54
|
+
'boto3==1.43.72',
|
|
55
|
+
'defusedxml==0.7.1'
|
|
55
56
|
],
|
|
56
57
|
extras_require = {},
|
|
57
58
|
dependency_links = [],
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import shutil
|
|
3
|
-
import tempfile
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def prepare_reports_directory(project):
|
|
8
|
-
return prepare_directory("$dir_reports", project)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def prepare_logs_directory(project):
|
|
12
|
-
return prepare_directory("$dir_logs", project)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def prepare_dist_directory(project):
|
|
16
|
-
return prepare_directory("$dir_dist", project)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def prepare_directory(dir_variable, project):
|
|
20
|
-
package__format = f"{dir_variable}/integration"
|
|
21
|
-
reports_dir = project.expand_path(package__format)
|
|
22
|
-
return _ensure_directory_exists(reports_dir)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _ensure_directory_exists(path):
|
|
26
|
-
if not os.path.exists(path):
|
|
27
|
-
os.makedirs(path)
|
|
28
|
-
return path
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def get_working_distribution_directory(project):
|
|
32
|
-
dist_directory = prepare_dist_directory(project)
|
|
33
|
-
return _ensure_directory_exists(f"{dist_directory}/working")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def get_latest_distribution_directory(project):
|
|
37
|
-
dist_directory = prepare_dist_directory(project)
|
|
38
|
-
from pybuilder_integration import ENVIRONMENT
|
|
39
|
-
environment = project.get_mandatory_property(ENVIRONMENT)
|
|
40
|
-
return _ensure_directory_exists(f"{dist_directory}/LATEST-{environment}")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def get_latest_zipped_distribution_directory(project):
|
|
44
|
-
dist_directory = prepare_dist_directory(project)
|
|
45
|
-
from pybuilder_integration import ENVIRONMENT
|
|
46
|
-
environment = project.get_mandatory_property(ENVIRONMENT)
|
|
47
|
-
return _ensure_directory_exists(f"{dist_directory}/LATEST-{environment}/zipped")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def get_local_zip_artifact_path(tool, project, include_ending=False):
|
|
51
|
-
artifact = f"{prepare_dist_directory(project)}/{tool}-{project.name}"
|
|
52
|
-
if include_ending:
|
|
53
|
-
return f"{artifact}.zip"
|
|
54
|
-
return artifact
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def package_artifacts(project, test_dir, tool, role):
|
|
58
|
-
# Make a copy for easy access in environment validation
|
|
59
|
-
working_dir = get_working_distribution_directory(project)
|
|
60
|
-
shutil.copytree(test_dir, f"{working_dir}/{tool}",dirs_exist_ok=True)
|
|
61
|
-
with tempfile.TemporaryDirectory() as tempdir:
|
|
62
|
-
shutil.copytree(test_dir, f"{tempdir}/{role}",dirs_exist_ok=True)
|
|
63
|
-
# package a copy for distribution
|
|
64
|
-
# zip up the test and add them to the integration test dist directory
|
|
65
|
-
base_name = get_local_zip_artifact_path(tool=tool, project=project)
|
|
66
|
-
shutil.make_archive(base_name=base_name,
|
|
67
|
-
base_dir=role,
|
|
68
|
-
format="zip",
|
|
69
|
-
root_dir=tempdir)
|
|
70
|
-
|
|
File without changes
|
|
File without changes
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/exec_utility.py
RENAMED
|
File without changes
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration/tool_utility.py
RENAMED
|
File without changes
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/top_level.txt
RENAMED
|
File without changes
|
{pybuilder_integration-104 → pybuilder_integration-106}/pybuilder_integration.egg-info/zip-safe
RENAMED
|
File without changes
|
|
File without changes
|