forked from leusonmario/SMAT
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsemantic_conflicts_output_generator.py
More file actions
102 lines (84 loc) · 4.67 KB
/
Copy pathsemantic_conflicts_output_generator.py
File metadata and controls
102 lines (84 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from typing import Dict, List, TypedDict, Union
from nimrod.output_generation.output_generator import OutputGenerator, OutputGeneratorContext
from nimrod.test_suites_execution.main import TestSuitesExecution
from os import path
from bs4 import BeautifulSoup
import logging
class SemanticConflictsOutput(TypedDict):
project_name: str
scenario_commits: Dict[str, str]
criteria: str
test_case_name: str
test_case_results: Dict[str, str]
test_suite_path: str
scenario_targets: Dict[str, Union[List[Dict[str, str]], List[str]]]
exercised_targets: Dict[str, List[str]]
class SemanticConflictsOutputGenerator(OutputGenerator[List[SemanticConflictsOutput]]):
def __init__(self, test_suites_execution: TestSuitesExecution) -> None:
super().__init__("semantic_conflicts")
self._test_suites_execution = test_suites_execution
def _generate_report_data(self, context: OutputGeneratorContext) -> List[SemanticConflictsOutput]:
report_data: List[SemanticConflictsOutput] = list()
for semantic_conflict in context.semantic_conflicts:
# We need to detect which targets from the input were exercised in this conflict.
exercised_targets: Dict[str, List[str]] = dict()
try:
coverage_report_root = self._test_suites_execution.execute_test_suite_with_coverage(
test_suite=semantic_conflict.detected_in.test_suite,
target_jar=context.scenario.scenario_jars.merge,
test_cases=[semantic_conflict.detected_in.name]
)
exercised_targets = self._extract_exercised_targets_from_coverage_report(
coverage_report_root=coverage_report_root,
targets=context.scenario.targets
)
except Exception as e:
# If we cannot execute the test suite with coverage, we log the error and continue.
logging.error("Error executing test suite with coverage for semantic conflict: %s", e)
finally:
report_data.append({
"project_name": context.scenario.project_name,
"scenario_commits": context.scenario.scenario_commits.__dict__,
"criteria": semantic_conflict._satisfying_criteria.__class__.__name__,
"test_case_name": semantic_conflict.detected_in.name,
"test_case_results": {
"base": semantic_conflict.detected_in.base,
"left": semantic_conflict.detected_in.left,
"right": semantic_conflict.detected_in.right,
"merge": semantic_conflict.detected_in.merge
},
"test_suite_path": semantic_conflict.detected_in.test_suite.path,
"scenario_targets": context.scenario.targets,
"exercised_targets": exercised_targets
})
return report_data
def _extract_exercised_targets_from_coverage_report(self, coverage_report_root: str, targets: Dict[str, Union[List[Dict[str, str]], List[str]]]):
exercised_targets: Dict[str, List[str]] = dict()
for class_name in targets.keys():
for method_item in targets[class_name]:
if isinstance(method_item, dict):
method_name = method_item.get("method", "")
else:
method_name = method_item
if self._was_target_exercised(coverage_report_root, class_name, method_name):
exercised_targets[class_name] = exercised_targets.get(
class_name, []) + [method_name]
return exercised_targets
def _was_target_exercised(self, coverage_report_root: str, fqcn: str, method_signature: str) -> bool:
[package_name, class_name] = fqcn.rsplit('.', 1)
class_report_path = path.join(
coverage_report_root, package_name, f"{class_name}.html")
method_name = method_signature[:method_signature.index("(") + 1]
report_file = open(class_report_path)
decoded_report = BeautifulSoup(report_file, 'html.parser')
method_report_rows = decoded_report.select("#coveragetable > tbody > tr")
# We itereate in each method row
for method_row in method_report_rows:
if method_row.get_text().find(method_name) != -1:
tag = method_row.select_one('td:nth-last-child(2)')
if tag is None:
continue
# If the second last column is 0, it means the method was not executed
if tag.get_text() == "0":
return True
return False