sflkit 0.2.19__tar.gz → 0.2.20__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.
- {sflkit-0.2.19/src/sflkit.egg-info → sflkit-0.2.20}/PKG-INFO +1 -1
- {sflkit-0.2.19 → sflkit-0.2.20}/pyproject.toml +1 -1
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/python/factory.py +9 -6
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/runners/run.py +126 -23
- {sflkit-0.2.19 → sflkit-0.2.20/src/sflkit.egg-info}/PKG-INFO +1 -1
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit.egg-info/SOURCES.txt +1 -2
- sflkit-0.2.20/tests/test_runner_pytest_versions.py +121 -0
- sflkit-0.2.19/src/sflkit/runners/resources/__init__.py +0 -0
- sflkit-0.2.19/src/sflkit/runners/resources/pytest_main.py +0 -82
- {sflkit-0.2.19 → sflkit-0.2.20}/LICENSE +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/README.md +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/setup.cfg +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/analysis_type.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/analyzer.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/factory.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/mapping.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/predicate.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/similarity.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/spectra.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/analysis/suggestion.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/cli.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/color.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/config.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/instrumentation/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/instrumentation/dir_instrumentation.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/extract.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/finder.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/language.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/meta.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/python/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/python/extract.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/python/finder.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/python/visitor.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/language/visitor.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/logger.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/mapping.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/model/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/model/event_file.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/model/model.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/model/scope.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit/runners/__init__.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit.egg-info/dependency_links.txt +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit.egg-info/entry_points.txt +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit.egg-info/requires.txt +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/src/sflkit.egg-info/top_level.txt +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_analysis_objects.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_cli.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_color.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_config.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_coverage.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_events.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_execution.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_instrumentation.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_language.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_predicates.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_runner.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_scope.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_spectra.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_suggestions.py +0 -0
- {sflkit-0.2.19 → sflkit-0.2.20}/tests/test_visitors.py +0 -0
|
@@ -849,14 +849,17 @@ class LenEventFactory(DefEventFactory):
|
|
|
849
849
|
keywords=[],
|
|
850
850
|
)
|
|
851
851
|
)
|
|
852
|
-
return
|
|
853
|
-
test=Call(
|
|
854
|
-
func=Name(id="hasattr"),
|
|
855
|
-
args=[Name(id=event.var), Constant(value="__len__")],
|
|
856
|
-
keywords=[],
|
|
857
|
-
),
|
|
852
|
+
return Try(
|
|
858
853
|
body=[call],
|
|
854
|
+
handlers=[
|
|
855
|
+
ExceptHandler(
|
|
856
|
+
type=Name(id="TypeError"),
|
|
857
|
+
name=None,
|
|
858
|
+
body=[Pass()],
|
|
859
|
+
)
|
|
860
|
+
],
|
|
859
861
|
orelse=[],
|
|
862
|
+
finalbody=[],
|
|
860
863
|
)
|
|
861
864
|
|
|
862
865
|
def get_event_call(self, event: LenEvent):
|
|
@@ -53,6 +53,7 @@ class Runner(abc.ABC):
|
|
|
53
53
|
files: Optional[List[os.PathLike] | os.PathLike] = None,
|
|
54
54
|
base: Optional[os.PathLike] = None,
|
|
55
55
|
environ: Environment = None,
|
|
56
|
+
k: str = None,
|
|
56
57
|
) -> List[str]:
|
|
57
58
|
return []
|
|
58
59
|
|
|
@@ -101,6 +102,7 @@ class Runner(abc.ABC):
|
|
|
101
102
|
files: Optional[List[os.PathLike] | os.PathLike] = None,
|
|
102
103
|
base: Optional[os.PathLike] = None,
|
|
103
104
|
environ: Environment = None,
|
|
105
|
+
k: str = None,
|
|
104
106
|
):
|
|
105
107
|
self.passing_tests.clear()
|
|
106
108
|
self.failing_tests.clear()
|
|
@@ -109,7 +111,7 @@ class Runner(abc.ABC):
|
|
|
109
111
|
directory,
|
|
110
112
|
output,
|
|
111
113
|
self.filter_tests(
|
|
112
|
-
self.get_tests(directory, files=files, base=base, environ=environ)
|
|
114
|
+
self.get_tests(directory, files=files, base=base, environ=environ, k=k)
|
|
113
115
|
),
|
|
114
116
|
environ=environ,
|
|
115
117
|
)
|
|
@@ -119,6 +121,120 @@ class VoidRunner(Runner):
|
|
|
119
121
|
pass
|
|
120
122
|
|
|
121
123
|
|
|
124
|
+
class PytestStructure:
|
|
125
|
+
def __init__(self, name: str, parent: Optional["PytestStructure"] = None):
|
|
126
|
+
self.name = name
|
|
127
|
+
self.children = []
|
|
128
|
+
self.parent = parent
|
|
129
|
+
self.depth = 0
|
|
130
|
+
|
|
131
|
+
def add_child(self, child):
|
|
132
|
+
self.children.append(child)
|
|
133
|
+
|
|
134
|
+
def set_parent(self, parent):
|
|
135
|
+
self.parent = parent
|
|
136
|
+
|
|
137
|
+
def get_seperator(self):
|
|
138
|
+
return ""
|
|
139
|
+
|
|
140
|
+
def get_tests(self) -> List[str]:
|
|
141
|
+
if self.children:
|
|
142
|
+
return [
|
|
143
|
+
self.name + self.get_seperator() + test
|
|
144
|
+
for child in self.children
|
|
145
|
+
for test in child.get_tests()
|
|
146
|
+
]
|
|
147
|
+
else:
|
|
148
|
+
return [self.name]
|
|
149
|
+
|
|
150
|
+
DIR = "Dir"
|
|
151
|
+
PACKAGE = "Package"
|
|
152
|
+
MODULE = "Module"
|
|
153
|
+
CLASS = "Class"
|
|
154
|
+
FUNCTION = "Function"
|
|
155
|
+
UNIT_TEST_CASE = "UnitTestCase"
|
|
156
|
+
TEST_CASE_FUNCTION = "TestCaseFunction"
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def get_pattern(obj):
|
|
160
|
+
return re.compile(rf"<{obj} ['\"]?(?P<name>[^>'\"]*)['\"]?>")
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def parse_tests(output: str) -> List[str]:
|
|
164
|
+
trees = []
|
|
165
|
+
current = None
|
|
166
|
+
for original_line in output.split("\n"):
|
|
167
|
+
line = original_line.lstrip()
|
|
168
|
+
level = len(original_line) - len(line)
|
|
169
|
+
if f"<{PytestStructure.DIR}" in line:
|
|
170
|
+
structure = Directory
|
|
171
|
+
pattern = PytestStructure.get_pattern(PytestStructure.DIR)
|
|
172
|
+
elif f"<{PytestStructure.PACKAGE}" in line:
|
|
173
|
+
structure = Directory
|
|
174
|
+
pattern = PytestStructure.get_pattern(PytestStructure.PACKAGE)
|
|
175
|
+
elif f"<{PytestStructure.MODULE}" in line:
|
|
176
|
+
structure = Module
|
|
177
|
+
pattern = PytestStructure.get_pattern(PytestStructure.MODULE)
|
|
178
|
+
elif f"<{PytestStructure.CLASS}" in line:
|
|
179
|
+
structure = Class
|
|
180
|
+
pattern = PytestStructure.get_pattern(PytestStructure.CLASS)
|
|
181
|
+
elif f"<{PytestStructure.FUNCTION}" in line:
|
|
182
|
+
structure = Function
|
|
183
|
+
pattern = PytestStructure.get_pattern(PytestStructure.FUNCTION)
|
|
184
|
+
elif f"<{PytestStructure.UNIT_TEST_CASE}" in line:
|
|
185
|
+
structure = Class
|
|
186
|
+
pattern = PytestStructure.get_pattern(PytestStructure.UNIT_TEST_CASE)
|
|
187
|
+
elif f"<{PytestStructure.TEST_CASE_FUNCTION}" in line:
|
|
188
|
+
structure = Function
|
|
189
|
+
pattern = PytestStructure.get_pattern(
|
|
190
|
+
PytestStructure.TEST_CASE_FUNCTION
|
|
191
|
+
)
|
|
192
|
+
else:
|
|
193
|
+
continue
|
|
194
|
+
match = pattern.search(line)
|
|
195
|
+
if match:
|
|
196
|
+
obj = structure(match.group("name"))
|
|
197
|
+
obj.depth = level
|
|
198
|
+
if current is None:
|
|
199
|
+
trees.append(obj)
|
|
200
|
+
elif current.depth < obj.depth:
|
|
201
|
+
obj.set_parent(current)
|
|
202
|
+
current.add_child(obj)
|
|
203
|
+
elif current.depth >= obj.depth:
|
|
204
|
+
while current.depth >= obj.depth and current:
|
|
205
|
+
current = current.parent
|
|
206
|
+
if current is None:
|
|
207
|
+
trees.append(obj)
|
|
208
|
+
else:
|
|
209
|
+
obj.set_parent(current)
|
|
210
|
+
current.add_child(obj)
|
|
211
|
+
current = obj
|
|
212
|
+
return [test for tree in trees for test in tree.get_tests()]
|
|
213
|
+
|
|
214
|
+
def __repr__(self):
|
|
215
|
+
return f"<{self.__class__.__name__} {self.name}>"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class Directory(PytestStructure):
|
|
219
|
+
def get_seperator(self):
|
|
220
|
+
return os.sep
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class Module(PytestStructure):
|
|
224
|
+
def get_seperator(self):
|
|
225
|
+
return "::"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class Class(PytestStructure):
|
|
229
|
+
def get_seperator(self):
|
|
230
|
+
return "::"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class Function(PytestStructure):
|
|
234
|
+
def get_seperator(self):
|
|
235
|
+
return "::"
|
|
236
|
+
|
|
237
|
+
|
|
122
238
|
class PytestRunner(Runner):
|
|
123
239
|
def __init__(
|
|
124
240
|
self,
|
|
@@ -171,9 +287,13 @@ class PytestRunner(Runner):
|
|
|
171
287
|
files: Optional[List[os.PathLike] | os.PathLike] = None,
|
|
172
288
|
base: Optional[os.PathLike] = None,
|
|
173
289
|
environ: Environment = None,
|
|
290
|
+
k: str = None,
|
|
174
291
|
) -> List[str]:
|
|
175
292
|
c = []
|
|
176
293
|
directory = directory.absolute()
|
|
294
|
+
if k:
|
|
295
|
+
c.append("-k")
|
|
296
|
+
c.append(k)
|
|
177
297
|
if files:
|
|
178
298
|
if isinstance(files, (str, os.PathLike)):
|
|
179
299
|
c.append(str(files))
|
|
@@ -185,21 +305,11 @@ class PytestRunner(Runner):
|
|
|
185
305
|
root_dir = directory / base
|
|
186
306
|
else:
|
|
187
307
|
root_dir = directory
|
|
188
|
-
tmp = Path(f"tmp_sflkit_pytest_tmp_{id(self)}").absolute()
|
|
189
|
-
pytest_main = (
|
|
190
|
-
Path(__file__).parent.resolve() / "resources" / "pytest_main.py"
|
|
191
|
-
).absolute()
|
|
192
|
-
environ = dict(environ or os.environ)
|
|
193
|
-
environ["SFLKIT_PYTEST_COLLECTION_FINISH_FILE"] = str(tmp)
|
|
194
|
-
if self.set_python_path:
|
|
195
|
-
if "PYTHONPATH" in environ:
|
|
196
|
-
environ["PYTHONPATH"] = str(directory) + ":" + environ["PYTHONPATH"]
|
|
197
|
-
else:
|
|
198
|
-
environ["PYTHONPATH"] = str(directory)
|
|
199
308
|
process = subprocess.run(
|
|
200
309
|
[
|
|
201
310
|
"python3",
|
|
202
|
-
|
|
311
|
+
"-m",
|
|
312
|
+
"pytest",
|
|
203
313
|
"--collect-only",
|
|
204
314
|
]
|
|
205
315
|
+ c,
|
|
@@ -209,16 +319,8 @@ class PytestRunner(Runner):
|
|
|
209
319
|
cwd=directory,
|
|
210
320
|
)
|
|
211
321
|
LOGGER.info(f"pytest collection finished with {process.returncode}")
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if tmp.exists():
|
|
215
|
-
with open(tmp, "r") as f:
|
|
216
|
-
tests = [line.strip() for line in f.readlines()]
|
|
217
|
-
os.remove(tmp)
|
|
218
|
-
return self._normalize_paths(tests, directory, root_dir)
|
|
219
|
-
else:
|
|
220
|
-
LOGGER.warn(f"Could not find {tmp}")
|
|
221
|
-
return []
|
|
322
|
+
tests = PytestStructure.parse_tests(process.stdout.decode("utf8"))
|
|
323
|
+
return self._normalize_paths(tests, directory, root_dir)
|
|
222
324
|
|
|
223
325
|
@staticmethod
|
|
224
326
|
def __get_pytest_result__(
|
|
@@ -309,6 +411,7 @@ class InputRunner(Runner):
|
|
|
309
411
|
files: Optional[List[os.PathLike] | os.PathLike] = None,
|
|
310
412
|
base: Optional[os.PathLike] = None,
|
|
311
413
|
environ: Environment = None,
|
|
414
|
+
k: str = None,
|
|
312
415
|
) -> List[str]:
|
|
313
416
|
return list(self.passing.keys()) + list(self.failing.keys())
|
|
314
417
|
|
|
@@ -42,8 +42,6 @@ src/sflkit/model/model.py
|
|
|
42
42
|
src/sflkit/model/scope.py
|
|
43
43
|
src/sflkit/runners/__init__.py
|
|
44
44
|
src/sflkit/runners/run.py
|
|
45
|
-
src/sflkit/runners/resources/__init__.py
|
|
46
|
-
src/sflkit/runners/resources/pytest_main.py
|
|
47
45
|
tests/test_analysis_objects.py
|
|
48
46
|
tests/test_cli.py
|
|
49
47
|
tests/test_color.py
|
|
@@ -55,6 +53,7 @@ tests/test_instrumentation.py
|
|
|
55
53
|
tests/test_language.py
|
|
56
54
|
tests/test_predicates.py
|
|
57
55
|
tests/test_runner.py
|
|
56
|
+
tests/test_runner_pytest_versions.py
|
|
58
57
|
tests/test_scope.py
|
|
59
58
|
tests/test_spectra.py
|
|
60
59
|
tests/test_suggestions.py
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import unittest
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from sflkit import Config, instrument_config, Analyzer
|
|
9
|
+
from sflkit.analysis.analysis_type import AnalysisType
|
|
10
|
+
from sflkit.analysis.suggestion import Location
|
|
11
|
+
from sflkit.mapping import EventMapping
|
|
12
|
+
from sflkit.model import EventFile
|
|
13
|
+
from sflkit.runners.run import (
|
|
14
|
+
PytestRunner,
|
|
15
|
+
)
|
|
16
|
+
from tests.utils import BaseTest
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@unittest.skip
|
|
20
|
+
class RunnerTests(BaseTest):
|
|
21
|
+
VENV = Path("test_venv")
|
|
22
|
+
PYTHON_BASE = os.path.join(
|
|
23
|
+
os.path.expanduser("~"), ".pyenv", "versions", "3.8.4", "bin", "python3"
|
|
24
|
+
)
|
|
25
|
+
PYTHON = "python3"
|
|
26
|
+
|
|
27
|
+
def assertPathExists(self, path: os.PathLike):
|
|
28
|
+
self.assertTrue(os.path.exists(path), f"{path} does not exists.")
|
|
29
|
+
|
|
30
|
+
def create_venv(self, environ):
|
|
31
|
+
shutil.rmtree(self.VENV, ignore_errors=True)
|
|
32
|
+
subprocess.check_call([self.PYTHON_BASE, "-m", "venv", self.VENV], env=environ)
|
|
33
|
+
|
|
34
|
+
def activate_venv(self, environ):
|
|
35
|
+
environ["VIRTUAL_ENV"] = str(self.VENV.absolute())
|
|
36
|
+
environ["_OLD_VIRTUAL_PATH"] = environ["PATH"]
|
|
37
|
+
if sys.platform.startswith("win"):
|
|
38
|
+
environ["PATH"] = f'{(self.VENV / "Scripts").absolute()};{environ["PATH"]}'
|
|
39
|
+
else:
|
|
40
|
+
environ["PATH"] = f'{(self.VENV / "bin").absolute()}:{environ["PATH"]}'
|
|
41
|
+
if "PYTHONHOME" in environ:
|
|
42
|
+
environ["_OLD_VIRTUAL_PYTHONHOME"] = environ["PYTHONHOME"]
|
|
43
|
+
del environ["PYTHONHOME"]
|
|
44
|
+
if "VIRTUAL_ENV_DISABLE_PROMPT" not in environ:
|
|
45
|
+
environ["_OLD_VIRTUAL_PS1"] = environ["PS1"] if "PS1" in environ else ""
|
|
46
|
+
environ[
|
|
47
|
+
"PS1"
|
|
48
|
+
] = f'({self.VENV}) {environ["PS1"] if "PS1" in environ else ""}'
|
|
49
|
+
environ["VIRTUAL_ENV_PROMPT"] = f"({self.VENV})"
|
|
50
|
+
return environ
|
|
51
|
+
|
|
52
|
+
def install_pytest(self, environ, pytest_version):
|
|
53
|
+
subprocess.check_call(
|
|
54
|
+
[self.PYTHON, "-m", "pip", "install", f"pytest=={pytest_version}"],
|
|
55
|
+
env=environ,
|
|
56
|
+
)
|
|
57
|
+
subprocess.check_call(
|
|
58
|
+
[self.PYTHON, "-m", "pip", "install", f"sflkitlib"],
|
|
59
|
+
env=environ,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def tearDown(self):
|
|
63
|
+
shutil.rmtree(self.VENV, ignore_errors=True)
|
|
64
|
+
|
|
65
|
+
def _runner_test(self, pytest_version):
|
|
66
|
+
environ = os.environ.copy()
|
|
67
|
+
self.create_venv(environ)
|
|
68
|
+
environ = self.activate_venv(environ)
|
|
69
|
+
self.install_pytest(environ, pytest_version)
|
|
70
|
+
config = Config.create(
|
|
71
|
+
path=os.path.join(self.TEST_RESOURCES, BaseTest.TEST_RUNNER),
|
|
72
|
+
language="python",
|
|
73
|
+
events="line",
|
|
74
|
+
predicates="line",
|
|
75
|
+
working=BaseTest.TEST_DIR,
|
|
76
|
+
exclude="tests",
|
|
77
|
+
)
|
|
78
|
+
instrument_config(config)
|
|
79
|
+
runner = PytestRunner(set_python_path=True)
|
|
80
|
+
output = Path(BaseTest.TEST_DIR, "events").absolute()
|
|
81
|
+
runner.run(
|
|
82
|
+
Path(BaseTest.TEST_DIR),
|
|
83
|
+
output,
|
|
84
|
+
files=[Path("tests", "test_middle.py")],
|
|
85
|
+
environ=environ,
|
|
86
|
+
)
|
|
87
|
+
self.assertPathExists(output)
|
|
88
|
+
self.assertPathExists(output / "passing")
|
|
89
|
+
self.assertPathExists(output / "failing")
|
|
90
|
+
self.assertPathExists(output / "undefined")
|
|
91
|
+
mapping = EventMapping.load(config)
|
|
92
|
+
analyzer = Analyzer(
|
|
93
|
+
[
|
|
94
|
+
EventFile(
|
|
95
|
+
output / "failing" / os.listdir(output / "failing")[0],
|
|
96
|
+
0,
|
|
97
|
+
mapping,
|
|
98
|
+
failing=True,
|
|
99
|
+
)
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
EventFile(output / "passing" / path, run_id, mapping)
|
|
103
|
+
for run_id, path in enumerate(os.listdir(output / "passing"), start=1)
|
|
104
|
+
],
|
|
105
|
+
config.factory,
|
|
106
|
+
)
|
|
107
|
+
analyzer.analyze()
|
|
108
|
+
predicates = analyzer.get_analysis_by_type(AnalysisType.LINE)
|
|
109
|
+
suggestions = sorted(map(lambda p: p.get_suggestion(), predicates))
|
|
110
|
+
self.assertEqual(1, suggestions[-1].suspiciousness)
|
|
111
|
+
self.assertEqual(1, len(suggestions[-1].lines))
|
|
112
|
+
self.assertEqual(Location("middle.py", 7), suggestions[-1].lines[0])
|
|
113
|
+
|
|
114
|
+
def test_pytest_3_10_1(self):
|
|
115
|
+
self._runner_test("3.10.1")
|
|
116
|
+
|
|
117
|
+
def test_pytest_5_4_3(self):
|
|
118
|
+
self._runner_test("5.4.3")
|
|
119
|
+
|
|
120
|
+
def test_pytest_7_0_1(self):
|
|
121
|
+
self._runner_test("7.0.1")
|
|
File without changes
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
from typing import Optional, Union, List, Sequence
|
|
4
|
-
|
|
5
|
-
# noinspection PyPackageRequirements
|
|
6
|
-
from _pytest._code import ExceptionInfo
|
|
7
|
-
|
|
8
|
-
# noinspection PyPackageRequirements
|
|
9
|
-
from _pytest._io import TerminalWriter
|
|
10
|
-
|
|
11
|
-
# noinspection PyPackageRequirements
|
|
12
|
-
from _pytest.config import (
|
|
13
|
-
ExitCode,
|
|
14
|
-
_prepareconfig,
|
|
15
|
-
ConftestImportFailure,
|
|
16
|
-
UsageError,
|
|
17
|
-
filter_traceback_for_conftest_import_failure,
|
|
18
|
-
_PluggyPlugin,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def pytest_collection_finish(session):
|
|
23
|
-
"""
|
|
24
|
-
Called after collection has been performed and modified.
|
|
25
|
-
"""
|
|
26
|
-
file = os.getenv("SFLKIT_PYTEST_COLLECTION_FINISH_FILE", "tmp_sflkit_pytest")
|
|
27
|
-
with open(file, "w") as f:
|
|
28
|
-
for item in session.items:
|
|
29
|
-
f.write(item.nodeid + "\n")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def main(
|
|
33
|
-
args: Optional[Union[List[str], "os.PathLike[str]"]] = None,
|
|
34
|
-
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
|
35
|
-
) -> Union[int, ExitCode]:
|
|
36
|
-
"""Perform an in-process test run.
|
|
37
|
-
|
|
38
|
-
:param args: List of command line arguments.
|
|
39
|
-
:param plugins: List of plugin objects to be auto-registered during initialization.
|
|
40
|
-
|
|
41
|
-
:returns: An exit code.
|
|
42
|
-
"""
|
|
43
|
-
try:
|
|
44
|
-
try:
|
|
45
|
-
config = _prepareconfig(args, plugins)
|
|
46
|
-
except ConftestImportFailure as e:
|
|
47
|
-
exc_info = ExceptionInfo.from_exc_info(e.excinfo)
|
|
48
|
-
tw = TerminalWriter(sys.stderr)
|
|
49
|
-
tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)
|
|
50
|
-
exc_info.traceback = exc_info.traceback.filter(
|
|
51
|
-
filter_traceback_for_conftest_import_failure
|
|
52
|
-
)
|
|
53
|
-
exc_repr = (
|
|
54
|
-
exc_info.getrepr(style="short", chain=False)
|
|
55
|
-
if exc_info.traceback
|
|
56
|
-
else exc_info.exconly()
|
|
57
|
-
)
|
|
58
|
-
formatted_tb = str(exc_repr)
|
|
59
|
-
for line in formatted_tb.splitlines():
|
|
60
|
-
tw.line(line.rstrip(), red=True)
|
|
61
|
-
return ExitCode.USAGE_ERROR
|
|
62
|
-
else:
|
|
63
|
-
try:
|
|
64
|
-
config.hook.pytest_collection_finish = pytest_collection_finish
|
|
65
|
-
ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
|
|
66
|
-
config=config
|
|
67
|
-
)
|
|
68
|
-
try:
|
|
69
|
-
return ExitCode(ret)
|
|
70
|
-
except ValueError:
|
|
71
|
-
return ret
|
|
72
|
-
finally:
|
|
73
|
-
config._ensure_unconfigure()
|
|
74
|
-
except UsageError as e:
|
|
75
|
-
tw = TerminalWriter(sys.stderr)
|
|
76
|
-
for msg in e.args:
|
|
77
|
-
tw.line(f"ERROR: {msg}\n", red=True)
|
|
78
|
-
return ExitCode.USAGE_ERROR
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if __name__ == "__main__":
|
|
82
|
-
main(sys.argv[1:])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|