sflkit 0.2.18__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.
Files changed (62) hide show
  1. {sflkit-0.2.18/src/sflkit.egg-info → sflkit-0.2.20}/PKG-INFO +1 -1
  2. {sflkit-0.2.18 → sflkit-0.2.20}/pyproject.toml +1 -1
  3. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/python/factory.py +9 -6
  4. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/runners/run.py +150 -12
  5. {sflkit-0.2.18 → sflkit-0.2.20/src/sflkit.egg-info}/PKG-INFO +1 -1
  6. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit.egg-info/SOURCES.txt +1 -0
  7. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_instrumentation.py +56 -6
  8. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_runner.py +1 -1
  9. sflkit-0.2.20/tests/test_runner_pytest_versions.py +121 -0
  10. {sflkit-0.2.18 → sflkit-0.2.20}/LICENSE +0 -0
  11. {sflkit-0.2.18 → sflkit-0.2.20}/README.md +0 -0
  12. {sflkit-0.2.18 → sflkit-0.2.20}/setup.cfg +0 -0
  13. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/__init__.py +0 -0
  14. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/__init__.py +0 -0
  15. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/analysis_type.py +0 -0
  16. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/analyzer.py +0 -0
  17. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/factory.py +0 -0
  18. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/mapping.py +0 -0
  19. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/predicate.py +0 -0
  20. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/similarity.py +0 -0
  21. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/spectra.py +0 -0
  22. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/analysis/suggestion.py +0 -0
  23. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/cli.py +0 -0
  24. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/color.py +0 -0
  25. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/config.py +0 -0
  26. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/instrumentation/__init__.py +0 -0
  27. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/instrumentation/dir_instrumentation.py +0 -0
  28. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
  29. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/__init__.py +0 -0
  30. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/extract.py +0 -0
  31. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/finder.py +0 -0
  32. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/language.py +0 -0
  33. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/meta.py +0 -0
  34. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/python/__init__.py +0 -0
  35. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/python/extract.py +0 -0
  36. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/python/finder.py +0 -0
  37. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/python/visitor.py +0 -0
  38. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/language/visitor.py +0 -0
  39. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/logger.py +0 -0
  40. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/mapping.py +0 -0
  41. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/model/__init__.py +0 -0
  42. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/model/event_file.py +0 -0
  43. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/model/model.py +0 -0
  44. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/model/scope.py +0 -0
  45. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit/runners/__init__.py +0 -0
  46. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit.egg-info/dependency_links.txt +0 -0
  47. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit.egg-info/entry_points.txt +0 -0
  48. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit.egg-info/requires.txt +0 -0
  49. {sflkit-0.2.18 → sflkit-0.2.20}/src/sflkit.egg-info/top_level.txt +0 -0
  50. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_analysis_objects.py +0 -0
  51. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_cli.py +0 -0
  52. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_color.py +0 -0
  53. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_config.py +0 -0
  54. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_coverage.py +0 -0
  55. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_events.py +0 -0
  56. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_execution.py +0 -0
  57. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_language.py +0 -0
  58. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_predicates.py +0 -0
  59. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_scope.py +0 -0
  60. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_spectra.py +0 -0
  61. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_suggestions.py +0 -0
  62. {sflkit-0.2.18 → sflkit-0.2.20}/tests/test_visitors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sflkit
3
- Version: 0.2.18
3
+ Version: 0.2.20
4
4
  Summary: SFLKit: : A Workbench for Statistical Fault Localization
5
5
  Author-email: Marius Smytzek <marius.smytzek@cispa.de>
6
6
  Project-URL: Homepage, https://github.com/uds-se/sflkit/
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "sflkit"
10
- version = "0.2.18"
10
+ version = "0.2.20"
11
11
  authors = [
12
12
  { name = "Marius Smytzek", email = "marius.smytzek@cispa.de" },
13
13
  ]
@@ -849,14 +849,17 @@ class LenEventFactory(DefEventFactory):
849
849
  keywords=[],
850
850
  )
851
851
  )
852
- return If(
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):
@@ -9,6 +9,8 @@ import subprocess
9
9
  from pathlib import Path
10
10
  from typing import List, Dict, Optional, Tuple
11
11
 
12
+ from sflkit.logger import LOGGER
13
+
12
14
  Environment = Dict[str, str]
13
15
 
14
16
  PYTEST_RESULT_PATTERN = re.compile(
@@ -51,6 +53,7 @@ class Runner(abc.ABC):
51
53
  files: Optional[List[os.PathLike] | os.PathLike] = None,
52
54
  base: Optional[os.PathLike] = None,
53
55
  environ: Environment = None,
56
+ k: str = None,
54
57
  ) -> List[str]:
55
58
  return []
56
59
 
@@ -99,6 +102,7 @@ class Runner(abc.ABC):
99
102
  files: Optional[List[os.PathLike] | os.PathLike] = None,
100
103
  base: Optional[os.PathLike] = None,
101
104
  environ: Environment = None,
105
+ k: str = None,
102
106
  ):
103
107
  self.passing_tests.clear()
104
108
  self.failing_tests.clear()
@@ -107,7 +111,7 @@ class Runner(abc.ABC):
107
111
  directory,
108
112
  output,
109
113
  self.filter_tests(
110
- self.get_tests(directory, files=files, base=base, environ=environ)
114
+ self.get_tests(directory, files=files, base=base, environ=environ, k=k)
111
115
  ),
112
116
  environ=environ,
113
117
  )
@@ -117,7 +121,130 @@ class VoidRunner(Runner):
117
121
  pass
118
122
 
119
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
+
120
238
  class PytestRunner(Runner):
239
+ def __init__(
240
+ self,
241
+ re_filter: str = r".*",
242
+ timeout=DEFAULT_TIMEOUT,
243
+ set_python_path: bool = False,
244
+ ):
245
+ super().__init__(re_filter, timeout)
246
+ self.set_python_path = set_python_path
247
+
121
248
  @staticmethod
122
249
  def _common_base(directory: Path, tests: List[str]) -> Path:
123
250
  parts = directory.parts
@@ -160,29 +287,39 @@ class PytestRunner(Runner):
160
287
  files: Optional[List[os.PathLike] | os.PathLike] = None,
161
288
  base: Optional[os.PathLike] = None,
162
289
  environ: Environment = None,
290
+ k: str = None,
163
291
  ) -> List[str]:
164
292
  c = []
165
293
  directory = directory.absolute()
294
+ if k:
295
+ c.append("-k")
296
+ c.append(k)
166
297
  if files:
167
298
  if isinstance(files, (str, os.PathLike)):
168
- c.append(files)
299
+ c.append(str(files))
169
300
  else:
170
- c += files
301
+ c += [str(f) for f in files]
171
302
  if base:
172
303
  if not files:
173
- c.append(base)
304
+ c.append(str(base))
174
305
  root_dir = directory / base
175
306
  else:
176
307
  root_dir = directory
177
- output = subprocess.run(
178
- ["python", "-m", "pytest", "--collect-only", "-q"] + c,
308
+ process = subprocess.run(
309
+ [
310
+ "python3",
311
+ "-m",
312
+ "pytest",
313
+ "--collect-only",
314
+ ]
315
+ + c,
179
316
  stdout=subprocess.PIPE,
317
+ stderr=subprocess.PIPE,
180
318
  env=environ,
181
319
  cwd=directory,
182
- ).stdout.decode("utf-8")
183
- tests = output.split("\n\n", 1)[0].split("\n")
184
- if tests[-1] == "":
185
- tests = tests[:-1]
320
+ )
321
+ LOGGER.info(f"pytest collection finished with {process.returncode}")
322
+ tests = PytestStructure.parse_tests(process.stdout.decode("utf8"))
186
323
  return self._normalize_paths(tests, directory, root_dir)
187
324
 
188
325
  @staticmethod
@@ -207,7 +344,7 @@ class PytestRunner(Runner):
207
344
  ) -> TestResult:
208
345
  try:
209
346
  output = subprocess.run(
210
- ["python", "-m", "pytest", test],
347
+ ["python3", "-m", "pytest", test],
211
348
  stdout=subprocess.PIPE,
212
349
  env=environ,
213
350
  cwd=directory,
@@ -274,6 +411,7 @@ class InputRunner(Runner):
274
411
  files: Optional[List[os.PathLike] | os.PathLike] = None,
275
412
  base: Optional[os.PathLike] = None,
276
413
  environ: Environment = None,
414
+ k: str = None,
277
415
  ) -> List[str]:
278
416
  return list(self.passing.keys()) + list(self.failing.keys())
279
417
 
@@ -288,7 +426,7 @@ class InputRunner(Runner):
288
426
  result = TestResult.FAILING
289
427
  try:
290
428
  process = subprocess.run(
291
- ["python", self.access] + test,
429
+ ["python3", self.access] + test,
292
430
  stdout=subprocess.PIPE,
293
431
  stderr=subprocess.PIPE,
294
432
  env=environ,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sflkit
3
- Version: 0.2.18
3
+ Version: 0.2.20
4
4
  Summary: SFLKit: : A Workbench for Statistical Fault Localization
5
5
  Author-email: Marius Smytzek <marius.smytzek@cispa.de>
6
6
  Project-URL: Homepage, https://github.com/uds-se/sflkit/
@@ -53,6 +53,7 @@ tests/test_instrumentation.py
53
53
  tests/test_language.py
54
54
  tests/test_predicates.py
55
55
  tests/test_runner.py
56
+ tests/test_runner_pytest_versions.py
56
57
  tests/test_scope.py
57
58
  tests/test_spectra.py
58
59
  tests/test_suggestions.py
@@ -2,8 +2,7 @@ import atexit
2
2
  import os
3
3
  from pathlib import Path
4
4
 
5
- from parameterized import parameterized
6
- from sflkitlib.events import event, EventType
5
+ from sflkitlib.events import EventType
7
6
 
8
7
  from sflkit import instrument_config, Config
9
8
  from utils import BaseTest
@@ -121,6 +120,61 @@ class TestInstrumentation(BaseTest):
121
120
  self.assertTrue(mapping.exists())
122
121
  self.assertTrue(mapping.is_file())
123
122
 
123
+ def test_instrument_exclude(self):
124
+ src = Path(BaseTest.TEST_RESOURCES, "test_exclude")
125
+ dst = Path(BaseTest.TEST_DIR)
126
+ instrument_config(
127
+ Config.create(
128
+ path=str(src),
129
+ language="python",
130
+ events="line",
131
+ predicates="line",
132
+ working=BaseTest.TEST_DIR,
133
+ include="included",
134
+ exclude=os.path.join("included", "excluded"),
135
+ )
136
+ )
137
+ included = dst / "included"
138
+ excluded = included / "excluded"
139
+ a = included / "a.py"
140
+ b = excluded / "b.py"
141
+ c = dst / "c.py"
142
+
143
+ included_src = src / "included"
144
+ excluded_src = included_src / "excluded"
145
+ a_src = included_src / "a.py"
146
+ b_src = excluded_src / "b.py"
147
+ c_src = src / "c.py"
148
+
149
+ self.assertTrue(included.exists())
150
+ self.assertTrue(included.is_dir())
151
+ self.assertTrue(excluded.exists())
152
+ self.assertTrue(excluded.is_dir())
153
+ self.assertTrue(a.exists())
154
+ self.assertTrue(a.is_file())
155
+ self.assertTrue(b.exists())
156
+ self.assertTrue(b.is_file())
157
+ self.assertTrue(c.exists())
158
+ self.assertTrue(c.is_file())
159
+
160
+ with open(a, "r") as fp:
161
+ a_content = fp.read()
162
+ with open(a_src, "r") as fp:
163
+ a_src_content = fp.read()
164
+ self.assertNotEqual(a_src_content, a_content)
165
+
166
+ with open(b, "r") as fp:
167
+ b_content = fp.read()
168
+ with open(b_src, "r") as fp:
169
+ b_src_content = fp.read()
170
+ self.assertEqual(b_src_content, b_content)
171
+
172
+ with open(c, "r") as fp:
173
+ c_content = fp.read()
174
+ with open(c_src, "r") as fp:
175
+ c_src_content = fp.read()
176
+ self.assertEqual(c_src_content, c_content)
177
+
124
178
 
125
179
  class TestLib(BaseTest):
126
180
  @classmethod
@@ -146,10 +200,6 @@ class TestLib(BaseTest):
146
200
  def setUp(self) -> None:
147
201
  self.events = None
148
202
 
149
- def load(self) -> None:
150
- self.lib.dump_events()
151
- self.events = event.load(self.TEST_PATH)
152
-
153
203
  def tearDown(self) -> None:
154
204
  # noinspection PyBroadException
155
205
  try:
@@ -37,7 +37,7 @@ class RunnerTests(BaseTest):
37
37
  exclude="tests",
38
38
  )
39
39
  instrument_config(config)
40
- runner = PytestRunner()
40
+ runner = PytestRunner(set_python_path=True)
41
41
  output = Path(BaseTest.TEST_DIR, "events").absolute()
42
42
  runner.run(
43
43
  Path(BaseTest.TEST_DIR), output, files=[Path("tests", "test_middle.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
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