sflkit 0.2.16__tar.gz → 0.2.17__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 (61) hide show
  1. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17}/PKG-INFO +1 -1
  2. {sflkit-0.2.16 → sflkit-0.2.17}/pyproject.toml +1 -1
  3. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/analysis_type.py +2 -0
  4. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/analyzer.py +27 -4
  5. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/factory.py +25 -0
  6. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/mapping.py +2 -0
  7. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/predicate.py +51 -12
  8. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/spectra.py +0 -1
  9. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/cli.py +45 -6
  10. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/config.py +11 -5
  11. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/dir_instrumentation.py +1 -1
  12. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/meta.py +1 -1
  13. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/factory.py +1 -1
  14. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/mapping.py +6 -2
  15. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/event_file.py +7 -1
  16. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/model.py +3 -2
  17. {sflkit-0.2.16 → sflkit-0.2.17/src/sflkit.egg-info}/PKG-INFO +1 -1
  18. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/SOURCES.txt +1 -5
  19. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_cli.py +2 -2
  20. sflkit-0.2.17/tests/test_coverage.py +19 -0
  21. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_suggestions.py +28 -0
  22. {sflkit-0.2.16 → sflkit-0.2.17}/LICENSE +0 -0
  23. {sflkit-0.2.16 → sflkit-0.2.17}/README.md +0 -0
  24. {sflkit-0.2.16 → sflkit-0.2.17}/setup.cfg +0 -0
  25. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/__init__.py +0 -0
  26. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/__init__.py +0 -0
  27. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/similarity.py +0 -0
  28. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/suggestion.py +0 -0
  29. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/color.py +0 -0
  30. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/__init__.py +0 -0
  31. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
  32. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/__init__.py +0 -0
  33. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/extract.py +0 -0
  34. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/finder.py +0 -0
  35. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/language.py +0 -0
  36. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/__init__.py +0 -0
  37. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/extract.py +0 -0
  38. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/finder.py +0 -0
  39. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/visitor.py +0 -0
  40. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/visitor.py +0 -0
  41. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/logger.py +0 -0
  42. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/__init__.py +0 -0
  43. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/scope.py +0 -0
  44. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/runners/__init__.py +0 -0
  45. {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/runners/run.py +0 -0
  46. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/dependency_links.txt +0 -0
  47. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/entry_points.txt +0 -0
  48. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/requires.txt +0 -0
  49. {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/top_level.txt +0 -0
  50. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_analysis_objects.py +0 -0
  51. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_color.py +0 -0
  52. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_config.py +0 -0
  53. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_events.py +0 -0
  54. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_execution.py +0 -0
  55. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_instrumentation.py +0 -0
  56. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_language.py +0 -0
  57. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_predicates.py +0 -0
  58. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_runner.py +0 -0
  59. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_scope.py +0 -0
  60. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_spectra.py +0 -0
  61. {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_visitors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sflkit
3
- Version: 0.2.16
3
+ Version: 0.2.17
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.16"
10
+ version = "0.2.17"
11
11
  authors = [
12
12
  { name = "Marius Smytzek", email = "marius.smytzek@cispa.de" },
13
13
  ]
@@ -28,6 +28,7 @@ class AnalysisType(enum.Enum):
28
28
  SPECIAL_STRING = 13
29
29
  EMPTY_BYTES = 14
30
30
  LENGTH = 15
31
+ FUNCTION_ERROR = 16
31
32
 
32
33
  def __str__(self):
33
34
  return self.name
@@ -56,6 +57,7 @@ class AnalysisObject(object):
56
57
  def __init__(self, event):
57
58
  self.suspiciousness: float = 0
58
59
  self.last_evaluation: EvaluationResult = EvaluationResult.UNOBSERVED
60
+ self.hits = dict()
59
61
 
60
62
  def get_last_evaluation(self, id_: int) -> EvaluationResult:
61
63
  return self.last_evaluation
@@ -1,6 +1,7 @@
1
- from typing import List, Callable
1
+ import os
2
+ from typing import List, Callable, Set, Dict
2
3
 
3
- from sflkit.analysis.analysis_type import AnalysisType
4
+ from sflkit.analysis.analysis_type import AnalysisType, AnalysisObject
4
5
  from sflkit.analysis.factory import AnalysisFactory
5
6
  from sflkit.analysis.suggestion import Suggestion
6
7
  from sflkit.model.event_file import EventFile
@@ -17,6 +18,7 @@ class Analyzer(object):
17
18
  self.relevant_event_files = relevant_event_files
18
19
  self.irrelevant_event_files = irrelevant_event_files
19
20
  self.model = Model(factory)
21
+ self.paths: Dict[int, os.PathLike] = dict()
20
22
 
21
23
  def _analyze(self, event_file):
22
24
  self.model.prepare(event_file)
@@ -29,16 +31,17 @@ class Analyzer(object):
29
31
 
30
32
  def analyze(self):
31
33
  for event_file in self.relevant_event_files + self.irrelevant_event_files:
34
+ self.paths[event_file.run_id] = event_file.path
32
35
  self._analyze(event_file)
33
36
  self._finalize()
34
37
 
35
38
  def dump(self, path):
36
39
  pass
37
40
 
38
- def get_analysis(self):
41
+ def get_analysis(self) -> Set[AnalysisObject]:
39
42
  return list(self.model.get_analysis())
40
43
 
41
- def get_analysis_by_type(self, type_: AnalysisType):
44
+ def get_analysis_by_type(self, type_: AnalysisType) -> Set[AnalysisObject]:
42
45
  return list(
43
46
  filter(lambda p: p.analysis_type() == type_, self.model.get_analysis())
44
47
  )
@@ -66,3 +69,23 @@ class Analyzer(object):
66
69
  ],
67
70
  reverse=True,
68
71
  )[:]
72
+
73
+ def get_coverage_per_run(
74
+ self, type_: AnalysisType = None
75
+ ) -> Dict[EventFile, Set[AnalysisObject]]:
76
+ if type_:
77
+ objects = self.get_analysis_by_type(type_)
78
+ else:
79
+ objects = self.get_analysis()
80
+ coverage = dict()
81
+ for obj in objects:
82
+ for run_id in obj.hits:
83
+ if run_id not in coverage:
84
+ coverage[run_id] = {obj}
85
+ else:
86
+ coverage[run_id].add(obj)
87
+ return coverage
88
+
89
+ def get_coverage(self, type_: AnalysisType = None) -> Set[AnalysisObject]:
90
+ coverage = self.get_coverage_per_run(type_)
91
+ return set.union(*coverage.values())
@@ -18,6 +18,7 @@ from sflkit.analysis.predicate import (
18
18
  ContainsDigitPredicate,
19
19
  ContainsSpecialPredicate,
20
20
  EmptyBytesPredicate,
21
+ FunctionErrorPredicate,
21
22
  )
22
23
  from sflkit.analysis.spectra import Line, Function, Loop, DefUse, Length
23
24
  from sflkit.model.scope import Scope
@@ -429,6 +430,29 @@ class LengthFactory(AnalysisFactory):
429
430
  return self.objects[key][:]
430
431
 
431
432
 
433
+ class FunctionErrorFactory(AnalysisFactory):
434
+ def __init__(self):
435
+ super().__init__()
436
+ self.function_mapping = dict()
437
+
438
+ def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
439
+ if event.event_type == EventType.FUNCTION_ENTER:
440
+ self.function_mapping[event.function_id] = event.line
441
+ if event.event_type in (EventType.FUNCTION_ERROR, EventType.FUNCTION_EXIT):
442
+ line = self.function_mapping[event.function_id]
443
+ key = (
444
+ FunctionErrorPredicate.analysis_type(),
445
+ event.file,
446
+ line,
447
+ event.function_id,
448
+ )
449
+ if key not in self.objects:
450
+ self.objects[key] = FunctionErrorPredicate(
451
+ event.file, line, event.function
452
+ )
453
+ return [self.objects[key]]
454
+
455
+
432
456
  analysis_factory_mapping = {
433
457
  AnalysisType.LINE: LineFactory,
434
458
  AnalysisType.BRANCH: BranchFactory,
@@ -446,4 +470,5 @@ analysis_factory_mapping = {
446
470
  AnalysisType.VARIABLE: VariableFactory,
447
471
  AnalysisType.SCALAR_PAIR: ScalarPairFactory,
448
472
  AnalysisType.FUNCTION: FunctionFactory,
473
+ AnalysisType.FUNCTION_ERROR: FunctionErrorFactory,
449
474
  }
@@ -6,6 +6,7 @@ from sflkit.analysis.predicate import (
6
6
  VariablePredicate,
7
7
  ReturnPredicate,
8
8
  NonePredicate,
9
+ FunctionErrorPredicate,
9
10
  )
10
11
  from sflkit.analysis.predicate import (
11
12
  EmptyStringPredicate,
@@ -39,6 +40,7 @@ analysis_mapping[AnalysisType.ASCII_STRING] = IsAsciiPredicate
39
40
  analysis_mapping[AnalysisType.DIGIT_STRING] = ContainsDigitPredicate
40
41
  analysis_mapping[AnalysisType.SPECIAL_STRING] = ContainsSpecialPredicate
41
42
  analysis_mapping[AnalysisType.CONDITION] = Condition
43
+ analysis_mapping[AnalysisType.FUNCTION_ERROR] = FunctionErrorPredicate
42
44
 
43
45
  """
44
46
  If you want to add new spectra or predicates, please register them here and in sdtools/analysis/analysis_type.py
@@ -1,9 +1,15 @@
1
1
  import enum
2
2
  from abc import ABC
3
- from typing import Tuple, Callable, Optional
3
+ from typing import Tuple, Callable, Optional, List, Type
4
4
 
5
5
  from sflkitlib.events import EventType
6
- from sflkitlib.events.event import BranchEvent, FunctionExitEvent, DefEvent
6
+ from sflkitlib.events.event import (
7
+ BranchEvent,
8
+ FunctionExitEvent,
9
+ DefEvent,
10
+ Event,
11
+ ConditionEvent,
12
+ )
7
13
 
8
14
  from sflkit.analysis.analysis_type import AnalysisType, EvaluationResult
9
15
  from sflkit.analysis.spectra import Spectrum
@@ -51,12 +57,14 @@ class Predicate(Spectrum, ABC):
51
57
  else:
52
58
  self.false_relevant_observed()
53
59
 
54
- def hit(self, id_, event, scope_: scope.Scope = None):
55
- super(Predicate, self).hit(id_, event, scope_)
60
+ def hit(self, id_, event: Event, scope_: scope.Scope = None):
56
61
  if id_ not in self.true_hits:
57
62
  self.true_hits[id_] = 0
58
- if self._evaluate_predicate(scope_):
63
+ if id_ not in self.hits:
64
+ self.hits[id_] = 0
65
+ if self._evaluate_predicate(event, scope_):
59
66
  self.true_hits[id_] += 1
67
+ self.hits[id_] += 1
60
68
  self.last_evaluation = EvaluationResult.TRUE
61
69
  else:
62
70
  self.last_evaluation = EvaluationResult.FALSE
@@ -66,7 +74,7 @@ class Predicate(Spectrum, ABC):
66
74
  metric = Predicate.IncreaseTrue
67
75
  return super().get_metric(metric)
68
76
 
69
- def _evaluate_predicate(self, scope_: scope.Scope):
77
+ def _evaluate_predicate(self, event: Event, scope_: scope.Scope):
70
78
  return False
71
79
 
72
80
  def true_relevant_observed(self):
@@ -139,7 +147,7 @@ class Branch(Predicate):
139
147
  def events():
140
148
  return [EventType.BRANCH]
141
149
 
142
- def hit(self, id_, event, scope_: scope.Scope = None):
150
+ def hit(self, id_, event: BranchEvent, scope_: scope.Scope = None):
143
151
  if id_ not in self.true_hits:
144
152
  self.true_hits[id_] = 0
145
153
  if event.then_id == self.then_id:
@@ -206,7 +214,7 @@ class Comparison(Predicate, ABC):
206
214
  EventType.FUNCTION_ERROR,
207
215
  ]
208
216
 
209
- def _evaluate_predicate(self, scope_: scope.Scope) -> bool:
217
+ def _evaluate_predicate(self, event: Event, scope_: scope.Scope) -> bool:
210
218
  return self._compare(self._get_first(scope_), self._get_second(scope_))
211
219
 
212
220
  def _compare(self, first, second) -> bool:
@@ -220,7 +228,7 @@ class Comparison(Predicate, ABC):
220
228
 
221
229
 
222
230
  class ScalarPair(Comparison):
223
- def __init__(self, event, op: Comp, var: str):
231
+ def __init__(self, event: DefEvent, op: Comp, var: str):
224
232
  super().__init__(event.file, event.line, op)
225
233
  self.var1 = event.var
226
234
  self.var2 = var
@@ -240,7 +248,7 @@ class ScalarPair(Comparison):
240
248
 
241
249
 
242
250
  class VariablePredicate(Comparison):
243
- def __init__(self, event, op: Comp):
251
+ def __init__(self, event: DefEvent, op: Comp):
244
252
  super().__init__(event.file, event.line, op)
245
253
  self.var = event.var
246
254
 
@@ -376,7 +384,7 @@ class FunctionPredicate(Predicate, ABC):
376
384
  EventType.DEF,
377
385
  ]
378
386
 
379
- def _evaluate_predicate(self, scope_: scope.Scope):
387
+ def _evaluate_predicate(self, event: Event, scope_: scope.Scope):
380
388
  value = scope_.value(self.var)
381
389
  return isinstance(value, str) and self.predicate(scope_.value(self.var))
382
390
 
@@ -438,7 +446,7 @@ class Condition(Predicate):
438
446
  def events():
439
447
  return [EventType.CONDITION]
440
448
 
441
- def hit(self, id_, event, scope_: scope.Scope = None):
449
+ def hit(self, id_, event: ConditionEvent, scope_: scope.Scope = None):
442
450
  super(Predicate, self).hit(id_, event, scope_)
443
451
  if id_ not in self.true_hits:
444
452
  self.true_hits[id_] = 0
@@ -450,3 +458,34 @@ class Condition(Predicate):
450
458
 
451
459
  def __str__(self):
452
460
  return f"{self.analysis_type()}:{self.file}:{self.line}:{self.condition}"
461
+
462
+
463
+ class FunctionErrorPredicate(Predicate):
464
+ def __init__(self, file, line, function):
465
+ super().__init__(file, line)
466
+ self.function = function
467
+
468
+ @staticmethod
469
+ def analysis_type() -> AnalysisType:
470
+ return AnalysisType.FUNCTION_ERROR
471
+
472
+ @staticmethod
473
+ def events() -> List[Type]:
474
+ return [
475
+ EventType.FUNCTION_ENTER,
476
+ EventType.FUNCTION_ERROR,
477
+ EventType.FUNCTION_EXIT,
478
+ ]
479
+
480
+ def _evaluate_predicate(self, event: Event, scope_: scope.Scope):
481
+ return event.event_type == EventType.FUNCTION_ERROR
482
+
483
+ def get_suggestion(self, metric: Callable = None, base_dir: str = ""):
484
+ finder = self.function_finder(self.file, self.line, self.function)
485
+ return Suggestion(
486
+ [Location(self.file, line) for line in finder.get_locations(base_dir)],
487
+ self.get_metric(metric),
488
+ )
489
+
490
+ def __str__(self):
491
+ return f"{self.analysis_type()}:{self.file}:{self.function}:{self.line}"
@@ -39,7 +39,6 @@ class Spectrum(AnalysisObject, ABC):
39
39
  self.failed = failed_observed + failed_not_observed
40
40
  self.failed_observed = failed_observed
41
41
  self.failed_not_observed = failed_not_observed
42
- self.hits = dict()
43
42
  self.last_evaluation: EvaluationResult = EvaluationResult.FALSE
44
43
 
45
44
  def __str__(self):
@@ -7,11 +7,15 @@ from typing import Any
7
7
 
8
8
  import sflkit
9
9
  from sflkit.analysis.suggestion import Suggestion, Location
10
+ from sflkit.config import hash_identifier
10
11
  from sflkit.logger import LOGGER
12
+ from sflkit.mapping import EventMapping
13
+ from sflkit.model import EventFile
11
14
 
12
15
  INSTRUMENT = "instrument"
13
16
  RUN = "run"
14
17
  ANALYZE = "analyze"
18
+ READ = "read"
15
19
 
16
20
 
17
21
  class ResultEncoder(json.JSONEncoder):
@@ -51,6 +55,14 @@ def main(*args: str, stdout=sys.stdout, stderr=sys.stderr):
51
55
  results = sflkit.analyze(args.config, args.analysis)
52
56
  with open(args.out, "w") as output:
53
57
  json.dump(results, output, cls=ResultEncoder, indent=4)
58
+ elif args.command == READ:
59
+ mapping = EventMapping.load_from_file(hash_identifier(args.target))
60
+ with EventFile(args.event_file, 0, mapping) as event_file:
61
+ for i, event in enumerate(event_file.load()):
62
+ print(f"{i:>5}: {event}")
63
+ if (i + 1) % 10 == 0:
64
+ input("Press Enter to continue... ")
65
+ print("\033[F\033[K", end="")
54
66
 
55
67
 
56
68
  def parse_args(args=None, namespace=None):
@@ -66,9 +78,6 @@ def parse_args(args=None, namespace=None):
66
78
  default=False,
67
79
  help="the debug flag to activate debug information",
68
80
  )
69
- arg_parser.add_argument(
70
- "-c", "--config", dest="config", required=True, help="path to the config file"
71
- )
72
81
  commands = arg_parser.add_subparsers(
73
82
  title="command",
74
83
  description="The framework allows for the execution of various commands.",
@@ -77,12 +86,15 @@ def parse_args(args=None, namespace=None):
77
86
  required=True,
78
87
  )
79
88
 
80
- commands.add_parser(
89
+ instrument_parser = commands.add_parser(
81
90
  INSTRUMENT,
82
91
  description="The instrumentation command instruments a subject to collect "
83
92
  "various predicates during its execution.",
84
93
  help="execute the instrumentation of subject",
85
94
  )
95
+ instrument_parser.add_argument(
96
+ "-c", "--config", dest="config", required=True, help="path to the config file"
97
+ )
86
98
 
87
99
  analyze_parser = commands.add_parser(
88
100
  ANALYZE,
@@ -90,6 +102,9 @@ def parse_args(args=None, namespace=None):
90
102
  "subject to detect the statistically correlating ones.",
91
103
  help="execute the analysis of the collected predicates",
92
104
  )
105
+ analyze_parser.add_argument(
106
+ "-c", "--config", dest="config", required=True, help="path to the config file"
107
+ )
93
108
  analyze_parser.add_argument(
94
109
  "-a",
95
110
  "--analysis",
@@ -106,18 +121,42 @@ def parse_args(args=None, namespace=None):
106
121
  help="The report of the final results, i.e. the suggestions sorted by analysis",
107
122
  )
108
123
 
109
- analyze_parser = commands.add_parser(
124
+ run_parser = commands.add_parser(
110
125
  RUN,
111
126
  description="The run command executes the tests of the subject and creates the event files.",
112
127
  help="execute the tests and collect predicates",
113
128
  )
114
- analyze_parser.add_argument(
129
+ run_parser.add_argument(
130
+ "-c", "--config", dest="config", required=True, help="path to the config file"
131
+ )
132
+ run_parser.add_argument(
115
133
  "-o",
116
134
  "--out",
117
135
  dest="out",
118
136
  default=None,
119
137
  help="The output path of the event files.",
120
138
  )
139
+
140
+ read_parser = commands.add_parser(
141
+ READ,
142
+ description="The read command reads an event file and prints the events.",
143
+ help="read an event file",
144
+ )
145
+ read_parser.add_argument(
146
+ "-e",
147
+ "--events",
148
+ dest="event_file",
149
+ required=True,
150
+ help="The input path of the event file.",
151
+ )
152
+ read_parser.add_argument(
153
+ "-t",
154
+ "--target",
155
+ dest="target",
156
+ required=True,
157
+ help="The path of the target to investigate.",
158
+ )
159
+
121
160
  return arg_parser.parse_args(args=args, namespace=namespace)
122
161
 
123
162
 
@@ -86,9 +86,11 @@ class Config:
86
86
  try:
87
87
  # target section
88
88
  target = config["target"]
89
- self.target_path = Path(target["path"])
90
- self.language = Language[target["language"].upper()]
91
- self.language.setup()
89
+ if "path" in target:
90
+ self.target_path = Path(target["path"])
91
+ if "language" in target:
92
+ self.language = Language[target["language"].upper()]
93
+ self.language.setup()
92
94
 
93
95
  # events section
94
96
  events = config["events"]
@@ -111,7 +113,7 @@ class Config:
111
113
  self.events = {
112
114
  e for p in self.predicates for e in analysis_mapping[p].events()
113
115
  }
114
- else:
116
+ elif "events" in events:
115
117
  self.factory = CombinationFactory(list())
116
118
  # get the events
117
119
  self.events = list(
@@ -331,7 +333,11 @@ class Config:
331
333
  conf.write(fp)
332
334
 
333
335
  def identifier(self):
334
- return hashlib.md5(str(self.target_path).encode("utf-8")).hexdigest()
336
+ return hash_identifier(self.target_path)
337
+
338
+
339
+ def hash_identifier(path: os.PathLike):
340
+ return hashlib.md5(str(path).encode("utf-8")).hexdigest()
335
341
 
336
342
 
337
343
  def parse_config(path: str) -> Config:
@@ -103,5 +103,5 @@ class DirInstrumentation(Instrumentation):
103
103
  continue
104
104
  else:
105
105
  self.handle_element(element, file_queue, src, dst, suffixes, check)
106
- self.events = self.file_instrumentation.events
106
+ self.events = self.file_instrumentation.events
107
107
  LOGGER.info(f"I found {len(self.events)} events in {src}.")
@@ -61,7 +61,7 @@ class TmpGenerator:
61
61
  self.random = random.randbytes(4).hex()
62
62
 
63
63
  def get_var_name(self):
64
- var = f"sd_tmp_{self.random}_{self._tmp_count}"
64
+ var = f"sk_tmp_{self.random}_{self._tmp_count}"
65
65
  self._tmp_count += 1
66
66
  return var
67
67
 
@@ -521,7 +521,7 @@ class FunctionErrorEventFactory(FunctionEventFactory):
521
521
  function_error_event = FunctionErrorEvent(
522
522
  self.file,
523
523
  node.lineno,
524
- self.get_function_event_id(node, self.event_id_generator),
524
+ self.event_id_generator.get_next_id(),
525
525
  node.name,
526
526
  self.get_function_id(node),
527
527
  )
@@ -27,12 +27,16 @@ class EventMapping:
27
27
  def load(config: Any):
28
28
  if not hasattr(config, "identifier"):
29
29
  raise InstrumentationError(f"Argument does not have an identifier")
30
- file = EventMapping.get_path(config.identifier())
30
+ return EventMapping.load_from_file(config.identifier())
31
+
32
+ @staticmethod
33
+ def load_from_file(identifier: str):
34
+ file = EventMapping.get_path(identifier)
31
35
  if file.exists():
32
36
  return EventMapping(load_json(file))
33
37
  else:
34
38
  raise InstrumentationError(
35
- f"Cannot find information about instrumentation of {config.identifier()}"
39
+ f"Cannot find information about instrumentation of {identifier or file}"
36
40
  )
37
41
 
38
42
  def write(self, config):
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from pickle import PickleError
2
3
 
3
4
  from sflkitlib.events import event
@@ -7,7 +8,11 @@ from sflkit.mapping import EventMapping
7
8
 
8
9
  class EventFile(object):
9
10
  def __init__(
10
- self, path: str, run_id: int, mapping: EventMapping, failing: bool = False
11
+ self,
12
+ path: os.PathLike,
13
+ run_id: int,
14
+ mapping: EventMapping,
15
+ failing: bool = False,
11
16
  ):
12
17
  self.path = path
13
18
  self.run_id = run_id
@@ -18,6 +23,7 @@ class EventFile(object):
18
23
 
19
24
  def __enter__(self):
20
25
  self._file_pointer = open(self.path, "rb")
26
+ return self
21
27
 
22
28
  def __exit__(self, exc_type, exc_val, exc_tb):
23
29
  self._file_pointer.close()
@@ -1,4 +1,4 @@
1
- from typing import List
1
+ from typing import Set
2
2
 
3
3
  from sflkit.model.scope import Scope
4
4
 
@@ -68,7 +68,8 @@ class Model(object):
68
68
  def exit_scope(self):
69
69
  self.variables = self.variables.exit()
70
70
 
71
- def get_analysis(self) -> List:
71
+ # noinspection PyUnresolvedReferences
72
+ def get_analysis(self) -> Set["AnalysisObject"]:
72
73
  return self.factory.get_all()
73
74
 
74
75
  def finalize(self, passed, failed):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sflkit
3
- Version: 0.2.16
3
+ Version: 0.2.17
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/
@@ -1,11 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- src/SFLKit.egg-info/PKG-INFO
5
- src/SFLKit.egg-info/SOURCES.txt
6
- src/SFLKit.egg-info/dependency_links.txt
7
- src/SFLKit.egg-info/entry_points.txt
8
- src/SFLKit.egg-info/top_level.txt
9
4
  src/sflkit/__init__.py
10
5
  src/sflkit/cli.py
11
6
  src/sflkit/color.py
@@ -51,6 +46,7 @@ tests/test_analysis_objects.py
51
46
  tests/test_cli.py
52
47
  tests/test_color.py
53
48
  tests/test_config.py
49
+ tests/test_coverage.py
54
50
  tests/test_events.py
55
51
  tests/test_execution.py
56
52
  tests/test_instrumentation.py
@@ -34,9 +34,9 @@ class TestCli(BaseTest):
34
34
  pass
35
35
 
36
36
  def test_instrument_analyze(self):
37
- main("-c", self.config_path, "instrument")
37
+ main("instrument", "-c", self.config_path)
38
38
  self.execute_subject([], 0)
39
- main("-c", self.config_path, "analyze", "-o", self.results_path)
39
+ main("analyze", "-c", self.config_path, "-o", self.results_path)
40
40
  with open(self.results_path, "r") as fp:
41
41
  results = json.load(fp)
42
42
  self.assertEqual(1, len(results))
@@ -0,0 +1,19 @@
1
+ from typing import List
2
+
3
+ from sflkit.analysis.analysis_type import AnalysisType
4
+ from sflkit.analysis.spectra import Line
5
+ from utils import BaseTest
6
+
7
+
8
+ class TestCoverage(BaseTest):
9
+ def test_coverage(self):
10
+ analyzer = self.run_analysis(
11
+ self.TEST_SUGGESTIONS,
12
+ "line",
13
+ "line",
14
+ relevant=[["2", "1", "3"]],
15
+ irrelevant=[["3", "2", "1"], ["3", "1", "2"]],
16
+ )
17
+ coverage: List[Line] = analyzer.get_coverage(AnalysisType.LINE)
18
+ coverage = {line.line for line in coverage}
19
+ self.assertEqual(coverage, {1, 5, 6, 7, 9, 10, 12, 13, 16, 19, 20})
@@ -307,3 +307,31 @@ class SuggestionsFromConditionsLoopTest(BaseTest):
307
307
  self.assertAlmostEqual(0.5, suggestions[0].suspiciousness, delta=self.DELTA)
308
308
  self.assertEqual(1, len(suggestions[0].lines))
309
309
  self.assertIn(Location("main.py", 7), suggestions[0].lines)
310
+
311
+
312
+ class SuggestionsFromPredicatesFunctionErrorTest(BaseTest):
313
+ @classmethod
314
+ def setUpClass(cls):
315
+ cls.analyzer = cls.run_analysis(
316
+ cls.TEST_ERROR,
317
+ "",
318
+ "function_error",
319
+ relevant=[["-1"]],
320
+ irrelevant=[["1"]],
321
+ )
322
+ cls.original_dir = os.path.join(cls.TEST_RESOURCES, cls.TEST_ERROR)
323
+
324
+ def test_function_error_suggestions(self):
325
+ suggestions = self.analyzer.get_sorted_suggestions(
326
+ base_dir=self.original_dir,
327
+ type_=AnalysisType.FUNCTION_ERROR,
328
+ metric=Spectrum.Tarantula,
329
+ )
330
+ self.assertAlmostEqual(1, suggestions[0].suspiciousness, delta=self.DELTA)
331
+ self.assertEqual(5, len(suggestions[0].lines))
332
+ for i in range(4, 9):
333
+ self.assertIn(Location("main.py", i), suggestions[0].lines)
334
+ self.assertAlmostEqual(0, suggestions[1].suspiciousness, delta=self.DELTA)
335
+ self.assertEqual(5, len(suggestions[1].lines))
336
+ for i in range(11, 16):
337
+ self.assertIn(Location("main.py", i), suggestions[1].lines)
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