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.
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17}/PKG-INFO +1 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/pyproject.toml +1 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/analysis_type.py +2 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/analyzer.py +27 -4
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/factory.py +25 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/mapping.py +2 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/predicate.py +51 -12
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/spectra.py +0 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/cli.py +45 -6
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/config.py +11 -5
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/dir_instrumentation.py +1 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/meta.py +1 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/factory.py +1 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/mapping.py +6 -2
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/event_file.py +7 -1
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/model.py +3 -2
- {sflkit-0.2.16 → sflkit-0.2.17/src/sflkit.egg-info}/PKG-INFO +1 -1
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/SOURCES.txt +1 -5
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_cli.py +2 -2
- sflkit-0.2.17/tests/test_coverage.py +19 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_suggestions.py +28 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/LICENSE +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/README.md +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/setup.cfg +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/similarity.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/analysis/suggestion.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/color.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/extract.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/finder.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/language.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/extract.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/finder.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/python/visitor.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/language/visitor.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/logger.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/model/scope.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/runners/__init__.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/src/sflkit/runners/run.py +0 -0
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/dependency_links.txt +0 -0
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/entry_points.txt +0 -0
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/requires.txt +0 -0
- {sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/top_level.txt +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_analysis_objects.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_color.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_config.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_events.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_execution.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_instrumentation.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_language.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_predicates.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_runner.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_scope.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_spectra.py +0 -0
- {sflkit-0.2.16 → sflkit-0.2.17}/tests/test_visitors.py +0 -0
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
106
|
+
self.events = self.file_instrumentation.events
|
|
107
107
|
LOGGER.info(f"I found {len(self.events)} events in {src}.")
|
|
@@ -521,7 +521,7 @@ class FunctionErrorEventFactory(FunctionEventFactory):
|
|
|
521
521
|
function_error_event = FunctionErrorEvent(
|
|
522
522
|
self.file,
|
|
523
523
|
node.lineno,
|
|
524
|
-
self.
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,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
|
|
37
|
+
main("instrument", "-c", self.config_path)
|
|
38
38
|
self.execute_subject([], 0)
|
|
39
|
-
main("-c", self.config_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
|
|
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
|
{sflkit-0.2.16/src/SFLKit.egg-info → sflkit-0.2.17/src/sflkit.egg-info}/dependency_links.txt
RENAMED
|
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
|