Skip to content

Commit 3c1bdd8

Browse files
committed
Merge branch 'topic/testsuite/fix_flag_checking' into 'master'
Fix and enhance the flag checking in the testsuite Closes #479 See merge request eng/libadalang/langkit-query-language!488
2 parents b2da4e5 + eed94df commit 3c1bdd8

File tree

31 files changed

+381
-398
lines changed

31 files changed

+381
-398
lines changed

testsuite/drivers/base_driver.py

Lines changed: 210 additions & 233 deletions
Large diffs are not rendered by default.

testsuite/drivers/benchmarks_driver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class BenchmarksDriver(BaseDriver):
1212
"""
1313

1414
perf_supported = True
15+
flag_checking_supported = False
1516

1617
@property
1718
def baseline(self) -> tuple[str, str, bool]:

testsuite/drivers/checker_driver.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22

3-
from drivers.base_driver import BaseDriver, Flags
3+
from drivers.base_driver import BaseDriver, TaggedLines
44

55

66
class CheckerDriver(BaseDriver):
@@ -28,6 +28,10 @@ class CheckerDriver(BaseDriver):
2828
perf_supported = True
2929
flag_checking_supported = True
3030

31+
_flag_line_pattern = re.compile(
32+
rf"^({BaseDriver.ada_file_pattern}):(\d+):\d+: rule violation: .*$"
33+
)
34+
3135
def run(self) -> None:
3236
args = []
3337

@@ -71,7 +75,7 @@ def run(self) -> None:
7175
assert auto_fix_mode in ["DISPLAY", "NEW_FILE", "PATCH_FILE"]
7276
self.check_run(
7377
self.lkql_fix_exe + args + ['--auto-fix-mode', auto_fix_mode],
74-
parse_flags=False,
78+
check_flags=False,
7579
catch_error=False,
7680
)
7781

@@ -92,21 +96,18 @@ def run(self) -> None:
9296
self.output += f"=== {pf} content:\n"
9397
self.output += f.read()
9498

95-
def parse_flagged_lines(self, output: str) -> Flags:
96-
# Compile the pattern to match a checker output
97-
pattern = re.compile(
98-
r"^([a-zA-Z][a-zA-Z0-9_\-]*\.(adb|ads)):(\d+):\d+: rule violation: .*$"
99-
)
100-
99+
def parse_flagged_lines(self, output: str) -> dict[str, TaggedLines]:
101100
# Prepare the result
102-
res = Flags()
101+
res: dict[str, TaggedLines] = {}
103102

104103
# For each line of the output search the groups in the line
105104
for line in output.splitlines():
106-
search_result = pattern.search(line)
105+
search_result = self._flag_line_pattern.search(line)
107106
if search_result is not None:
108107
(file, _, line_num) = search_result.groups()
109-
res.add_flag(file, int(line_num))
108+
if not res.get(file):
109+
res[file] = TaggedLines()
110+
res[file].tag_line(int(line_num))
110111

111112
# Return the result
112113
return res

testsuite/drivers/gnatcheck_driver.py

Lines changed: 90 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,80 +3,7 @@
33
import re
44
import xml.etree.ElementTree as ET
55

6-
from drivers.base_driver import BaseDriver, Flags
7-
8-
9-
# --- GNATcheck output parsing functions
10-
11-
_flag_line_pattern = re.compile(
12-
r"^([a-zA-Z][a-zA-Z0-9_\.\-]*\.(adb|ads|ada|ada_spec)):(\d+):\d+: .*$"
13-
)
14-
15-
def _parse_full(output: str) -> Flags:
16-
"""
17-
Parse the full formatted gnatcheck output.
18-
"""
19-
# Prepare the result
20-
res = Flags()
21-
22-
# Parse the gnatcheck full output
23-
is_parsing = False
24-
for line in output.splitlines():
25-
if not is_parsing:
26-
is_parsing = "2. Exempted Coding Standard Violations" in line
27-
else:
28-
search_result = _flag_line_pattern.search(line)
29-
if search_result is not None:
30-
(file, _, line_num) = search_result.groups()
31-
res.add_flag(file, int(line_num))
32-
is_parsing = "5. Language violations" not in line
33-
34-
# Return the result
35-
return res
36-
37-
def _parse_short_and_brief(output: str) -> Flags:
38-
"""
39-
Parse the short formatted gnatcheck output.
40-
"""
41-
# Prepare the result
42-
res = Flags()
43-
44-
# Parse the output
45-
for line in output.splitlines():
46-
search_result = _flag_line_pattern.search(line)
47-
if search_result is not None:
48-
(file, _, line_num) = search_result.groups()
49-
res.add_flag(file, int(line_num))
50-
51-
# Return the result
52-
return res
53-
54-
def _parse_xml(output: str) -> Flags:
55-
"""
56-
Parse the xml formatted gnatcheck output.
57-
"""
58-
# Prepare the result
59-
res = Flags()
60-
61-
# Parse the xml result
62-
xml_tree = ET.fromstring(output)
63-
violations = xml_tree.find("violations")
64-
65-
# If the "violations" tag exists in the output, parse it as a full XML output
66-
if violations is not None:
67-
for violation in violations:
68-
file, line_num = violation.attrib["file"], int(violation.attrib["line"])
69-
res.add_flag(file, line_num)
70-
71-
# Else the ouput is a brief XML one
72-
else:
73-
for elem in xml_tree.findall("*"):
74-
if elem.tag in ("violation", "exemption-problem", "exempted-violation"):
75-
file, line_num = elem.attrib["file"], int(elem.attrib["line"])
76-
res.add_flag(file, line_num)
77-
78-
# Return the result
79-
return res
6+
from drivers.base_driver import BaseDriver, TaggedLines
807

818

829
class GnatcheckDriver(BaseDriver):
@@ -191,12 +118,86 @@ class GnatcheckDriver(BaseDriver):
191118
"gnatkp": "gnatkp"
192119
}
193120
output_formats = set(['brief', 'full', 'short', 'xml'])
194-
parsers = {
195-
'full': _parse_full,
196-
'short': _parse_short_and_brief,
197-
'brief': _parse_short_and_brief,
198-
'xml': _parse_xml
199-
}
121+
122+
flag_line_pattern = re.compile(
123+
rf"^({BaseDriver.ada_file_pattern}):(\d+):\d+: (rule violation|warning|error): .*$"
124+
)
125+
126+
@classmethod
127+
def _parse_full(cls, output: str) -> dict[str, TaggedLines]:
128+
"""
129+
Parse the full formatted GNATcheck output.
130+
"""
131+
# Remove useless parts of the full output
132+
try:
133+
output = output[output.index("2. Exempted Coding Standard Violations"):]
134+
output = output[:output.index("5. Language violations")]
135+
136+
# Then use the "short" parser since we remove the useless part of the
137+
# "full" format.
138+
return cls._parse_short_and_brief(output)
139+
except ValueError:
140+
# When catching this error, it means that the `index` method failed
141+
# to find required textual bounds.
142+
return {}
143+
144+
@classmethod
145+
def _parse_short_and_brief(cls, output: str) -> dict[str, TaggedLines]:
146+
"""
147+
Parse the short formatted GNATcheck output.
148+
"""
149+
res: dict[str, TaggedLines] = {}
150+
151+
for line in output.splitlines():
152+
search_result = cls.flag_line_pattern.search(line)
153+
if search_result is not None:
154+
(file, _, line_num, _) = search_result.groups()
155+
if not res.get(file):
156+
res[file] = TaggedLines()
157+
res[file].tag_line(int(line_num))
158+
159+
return res
160+
161+
@classmethod
162+
def _parse_xml(cls, output: str) -> dict[str, TaggedLines]:
163+
"""
164+
Parse the XML formatted GNATcheck output.
165+
"""
166+
res: dict[str, TaggedLines] = {}
167+
168+
def tag_line(file: str, line: int):
169+
if not res.get(file):
170+
res[file] = TaggedLines()
171+
res[file].tag_line(line)
172+
173+
# Parse the xml result
174+
xml_tree = ET.fromstring(output)
175+
violations = xml_tree.find("violations")
176+
177+
# If the "violations" tag exists in the output, parse it as a full XML output
178+
if violations is not None:
179+
for violation in violations:
180+
file, line_num = violation.attrib["file"], int(violation.attrib["line"])
181+
tag_line(file, line_num)
182+
183+
# Else the output is a brief XML one
184+
else:
185+
for elem in xml_tree.findall("*"):
186+
if elem.tag in ("violation", "exemption-problem", "exempted-violation"):
187+
file, line_num = elem.attrib["file"], int(elem.attrib["line"])
188+
tag_line(file, line_num)
189+
190+
# Return the result
191+
return res
192+
193+
@classmethod
194+
def parsers(cls):
195+
return {
196+
'full': cls._parse_full,
197+
'short': cls._parse_short_and_brief,
198+
'brief': cls._parse_short_and_brief,
199+
'xml': cls._parse_xml
200+
}
200201

201202
@property
202203
def default_process_timeout(self):
@@ -262,7 +263,11 @@ def cat(
262263
exec(global_python, globs, locs)
263264

264265
# Prepare the execution flagged lines as an empty object
265-
flagged_lines = Flags()
266+
files_flagged_lines: dict[str, TaggedLines] = {}
267+
268+
def add_to_files_flagged_lines(to_add: dict[str, TaggedLines]):
269+
for file, tagged_lines in to_add.items():
270+
files_flagged_lines[file] = files_flagged_lines.get(file, TaggedLines()).combine(tagged_lines)
266271

267272
def capture_exec_python(code: str) -> None:
268273
"""
@@ -466,7 +471,7 @@ def run_one_test(test_data: dict[str, any]) -> None:
466471

467472
# Get the lines flagged by the test running and add it to all flagged lines
468473
if self.flag_checking and parse_output_for_flags:
469-
flagged_lines.add_other_flags(
474+
add_to_files_flagged_lines(
470475
self.parse_flagged_lines(
471476
(
472477
exec_output + report_file_content
@@ -521,8 +526,8 @@ def run_one_test(test_data: dict[str, any]) -> None:
521526

522527
# Check the execution flagged lines
523528
if self.flag_checking:
524-
self.check_flags(flagged_lines)
529+
self.check_flags(files_flagged_lines)
525530

526-
def parse_flagged_lines(self, output: str, format: str) -> Flags:
531+
def parse_flagged_lines(self, output: str, format: str) -> dict[str, TaggedLines]:
527532
assert format in self.output_formats
528-
return self.parsers[format](output)
533+
return self.parsers()[format](output)

testsuite/drivers/interpreter_driver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class InterpreterDriver(BaseDriver):
1919
"""
2020

2121
perf_supported = True
22+
flag_checking_supported = False
2223

2324
def run(self) -> None:
2425
# Build the process's arguments list

testsuite/drivers/java_driver.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class JavaDriver(BaseDriver):
1414
in the test directory.
1515
"""
1616

17+
perf_supported = False
18+
flag_checking_supported = False
19+
1720
def run(self) -> None:
1821
# Get and check the test Java main file
1922
main_java_file = self.working_dir("Main.java")

testsuite/drivers/parser_driver.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class ParserDriver(BaseDriver):
1313
Test arguments:
1414
- rule: name of the grammar rule to pass to `parse`
1515
"""
16+
17+
perf_supported = False
18+
flag_checking_supported = False
19+
1620
def run(self) -> None:
1721
rule = self.test_env['rule']
1822

testsuite/drivers/refactor_driver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class RefactorDriver(BaseDriver):
1515
"""
1616

1717
perf_supported = True
18+
flag_checking_supported = False
1819

1920
def run(self) -> None:
2021
self.check_run([
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
driver: checker
22
rule_name: KP_19142
33
project: prj.gpr
4+
control:
5+
- ["XFAIL", "True", "KP detector is invalid (eng/libadalang/langkit-query-language#506)"]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
separate (Test)
2-
procedure Prim3 (Self : T6; Other : Integer) is
2+
procedure Prim3 (Self : T6; Other : Integer) is -- FLAG
33
begin
44
null;
55
end Prim3;

0 commit comments

Comments
 (0)