Skip to content

Commit a90bafd

Browse files
authored
Merge pull request #13083 from ethereum/lsp.py-fixes
lsp.py: Fix various problems with subdirectory edge cases
2 parents baf56af + 6c9754a commit a90bafd

File tree

2 files changed

+66
-31
lines changed

2 files changed

+66
-31
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ defaults:
222222
command: pip install --user deepdiff colorama
223223
- run:
224224
name: Executing solc LSP test suite
225-
command: ./test/lsp.py ./build/solc/solc
225+
command: ./test/lsp.py ./build/solc/solc --non-interactive
226226
- gitter_notify_failure_unless_pr
227227

228228
- steps_build: &steps_build
@@ -1352,7 +1352,7 @@ jobs:
13521352
command: Get-Content ./test/lsp.py
13531353
- run:
13541354
name: Executing solc LSP test suite
1355-
command: python ./test/lsp.py .\build\solc\Release\solc.exe
1355+
command: python ./test/lsp.py .\build\solc\Release\solc.exe --non-interactive
13561356
- store_test_results: *store_test_results
13571357
- store_artifacts: *artifacts_test_results
13581358
- gitter_notify_failure_unless_pr

test/lsp.py

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def getCharFromStdin() -> str:
6969
re.compile(R'^// -> (?P<method>[\w\/]+) {'),
7070
re.compile(R'(?P<tag>"@\w+")'),
7171
re.compile(R'(?P<tag>@\w+)'),
72-
re.compile(R'// (?P<testname>\w+):[ ]?(?P<diagnostics>[\w @]*)'),
72+
re.compile(R'// (?P<testname>\S+):([ ](?P<diagnostics>[\w @]*))?'),
7373
re.compile(R'(?P<tag>@\w+) (?P<code>\d\d\d\d)')
7474
)
7575

@@ -86,7 +86,7 @@ def split_path(path):
8686
"""
8787
Return the test name and the subdir path of the given path.
8888
"""
89-
sub_dir_separator = path.find("/")
89+
sub_dir_separator = path.rfind("/")
9090

9191
if sub_dir_separator == -1:
9292
return (path, None)
@@ -105,7 +105,8 @@ def count_index(lines, start=0):
105105

106106
def tags_only(lines, start=0):
107107
"""
108-
Filter the lines for tag comments and report line number that tags refer to.
108+
Filter the lines for tag comments and report the line number that the tags
109+
_refer_ to (which is not the line they are on!).
109110
"""
110111
n = start
111112
numCommentLines = 0
@@ -272,27 +273,34 @@ def create_cli_parser() -> argparse.ArgumentParser:
272273
parser = argparse.ArgumentParser(description="Solidity LSP Test suite")
273274
parser.set_defaults(fail_fast=False)
274275
parser.add_argument(
275-
"-f, --fail-fast",
276+
"-f", "--fail-fast",
276277
dest="fail_fast",
277278
action="store_true",
278279
help="Terminates the running tests on first failure."
279280
)
281+
parser.set_defaults(non_interactive=False)
282+
parser.add_argument(
283+
"-n", "--non-interactive",
284+
dest="non_interactive",
285+
action="store_true",
286+
help="Prevent interactive queries and just fail instead."
287+
)
280288
parser.set_defaults(trace_io=False)
281289
parser.add_argument(
282-
"-T, --trace-io",
290+
"-T", "--trace-io",
283291
dest="trace_io",
284292
action="store_true",
285293
help="Be more verbose by also printing assertions."
286294
)
287295
parser.set_defaults(print_assertions=False)
288296
parser.add_argument(
289-
"-v, --print-assertions",
297+
"-v", "--print-assertions",
290298
dest="print_assertions",
291299
action="store_true",
292300
help="Be more verbose by also printing assertions."
293301
)
294302
parser.add_argument(
295-
"-t, --test-pattern",
303+
"-t", "--test-pattern",
296304
dest="test_pattern",
297305
type=str,
298306
default="*",
@@ -405,11 +413,13 @@ def parseDiagnostics(self):
405413

406414
testDiagnostics = []
407415

408-
for diagnosticMatch in TEST_REGEXES.diagnostic.finditer(fileDiagMatch.group("diagnostics")):
409-
testDiagnostics.append(self.Diagnostic(
410-
diagnosticMatch.group("tag"),
411-
int(diagnosticMatch.group("code"))
412-
))
416+
diagnostics_string = fileDiagMatch.group("diagnostics")
417+
if diagnostics_string is not None:
418+
for diagnosticMatch in TEST_REGEXES.diagnostic.finditer(diagnostics_string):
419+
testDiagnostics.append(self.Diagnostic(
420+
diagnosticMatch.group("tag"),
421+
int(diagnosticMatch.group("code"))
422+
))
413423

414424
diagnostics["tests"][fileDiagMatch.group("testname")] = testDiagnostics
415425

@@ -537,11 +547,11 @@ def test_diagnostics(self):
537547
self.expected_diagnostics = next(self.parsed_testcases)
538548
assert isinstance(self.expected_diagnostics, TestParser.Diagnostics) is True
539549

540-
tests = self.expected_diagnostics.tests
550+
expected_diagnostics_per_file = self.expected_diagnostics.tests
541551

542552
# Add our own test diagnostics if they didn't exist
543-
if self.test_name not in tests:
544-
tests[self.test_name] = []
553+
if self.test_name not in expected_diagnostics_per_file:
554+
expected_diagnostics_per_file[self.test_name] = []
545555

546556
published_diagnostics = \
547557
self.suite.open_file_and_wait_for_diagnostics(self.solc, self.test_name, self.sub_dir)
@@ -551,25 +561,27 @@ def test_diagnostics(self):
551561
raise Exception(
552562
f"'{self.test_name}.sol' imported file outside of test directory: '{diagnostics['uri']}'"
553563
)
554-
self.open_tests.append(diagnostics["uri"].replace(self.suite.project_root_uri + "/", "")[:-len(".sol")])
564+
self.open_tests.append(self.suite.normalizeUri(diagnostics["uri"]))
555565

556566
self.suite.expect_equal(
557567
len(published_diagnostics),
558-
len(tests),
568+
len(expected_diagnostics_per_file),
559569
description="Amount of reports does not match!")
560570

561-
for diagnostics in published_diagnostics:
562-
testname_and_subdir = diagnostics["uri"].replace(self.suite.project_root_uri + "/", "")[:-len(".sol")]
563-
testname, sub_dir = split_path(testname_and_subdir)
571+
for diagnostics_per_file in published_diagnostics:
572+
testname, sub_dir = split_path(self.suite.normalizeUri(diagnostics_per_file['uri']))
573+
574+
# Clear all processed expectations so we can check at the end
575+
# what's missing
576+
expected_diagnostics = expected_diagnostics_per_file.pop(testname, {})
564577

565-
expected_diagnostics = tests[testname]
566578
self.suite.expect_equal(
567-
len(diagnostics["diagnostics"]),
579+
len(diagnostics_per_file["diagnostics"]),
568580
len(expected_diagnostics),
569581
description="Unexpected amount of diagnostics"
570582
)
571583
markers = self.suite.get_file_tags(testname, sub_dir)
572-
for actual_diagnostic in diagnostics["diagnostics"]:
584+
for actual_diagnostic in diagnostics_per_file["diagnostics"]:
573585
expected_diagnostic = next((diagnostic for diagnostic in
574586
expected_diagnostics if actual_diagnostic['range'] ==
575587
markers[diagnostic.marker]), None)
@@ -586,6 +598,12 @@ def test_diagnostics(self):
586598
marker=markers[expected_diagnostic.marker]
587599
)
588600

601+
if len(expected_diagnostics_per_file) > 0:
602+
raise ExpectationFailed(
603+
f"Expected diagnostics but received none for {expected_diagnostics_per_file}",
604+
ExpectationFailed.Part.Diagnostics
605+
)
606+
589607
except Exception:
590608
self.close_all_open_files()
591609
raise
@@ -759,6 +777,7 @@ def __init__(self):
759777
self.trace_io = args.trace_io
760778
self.test_pattern = args.test_pattern
761779
self.fail_fast = args.fail_fast
780+
self.non_interactive = args.non_interactive
762781

763782
print(f"{SGR_NOTICE}test pattern: {self.test_pattern}{SGR_RESET}")
764783

@@ -773,6 +792,9 @@ def main(self) -> int:
773792
if callable(getattr(SolidityLSPTestSuite, name)) and name.startswith("test_")
774793
])
775794
filtered_tests = fnmatch.filter(all_tests, self.test_pattern)
795+
if filtered_tests.count("generic") == 0:
796+
filtered_tests.append("generic")
797+
776798
for method_name in filtered_tests:
777799
test_fn = getattr(self, 'test_' + method_name)
778800
title: str = test_fn.__name__[5:]
@@ -889,22 +911,25 @@ def wait_for_diagnostics(self, solc: JsonRpcProcess) -> List[dict]:
889911

890912
return sorted(reports, key=lambda x: x['uri'])
891913

914+
def normalizeUri(self, uri):
915+
return uri.replace(self.project_root_uri + "/", "")[:-len(".sol")]
916+
892917
def fetch_and_format_diagnostics(self, solc: JsonRpcProcess, test, sub_dir=None):
893918
expectations = ""
894919

895920
published_diagnostics = self.open_file_and_wait_for_diagnostics(solc, test, sub_dir)
896921

897-
for diagnostics in published_diagnostics:
898-
testname = diagnostics["uri"].replace(f"{self.project_root_uri}/{sub_dir}/", "")[:-len(".sol")]
922+
for file_diagnostics in published_diagnostics:
923+
testname, local_sub_dir = split_path(self.normalizeUri(file_diagnostics["uri"]))
899924

900925
# Skip empty diagnostics within the same file
901-
if len(diagnostics["diagnostics"]) == 0 and testname == test:
926+
if len(file_diagnostics["diagnostics"]) == 0 and testname == test:
902927
continue
903928

904929
expectations += f"// {testname}:"
905930

906-
for diagnostic in diagnostics["diagnostics"]:
907-
tag = self.find_tag_with_range(testname, sub_dir, diagnostic['range'])
931+
for diagnostic in file_diagnostics["diagnostics"]:
932+
tag = self.find_tag_with_range(testname, local_sub_dir, diagnostic['range'])
908933

909934
if tag is None:
910935
raise Exception(f"No tag found for diagnostic range {diagnostic['range']}")
@@ -1121,6 +1146,11 @@ def user_interaction_failed_diagnostics(
11211146
Asks the user how to proceed after an error.
11221147
Returns True if the test/file should be ignored, otherwise False
11231148
"""
1149+
1150+
# Prevent user interaction when in non-interactive mode
1151+
if self.non_interactive:
1152+
return False
1153+
11241154
while True:
11251155
print("(u)pdate/(r)etry/(s)kip file?")
11261156
user_response = getCharFromStdin()
@@ -1324,10 +1354,15 @@ def test_generic(self, solc: JsonRpcProcess) -> None:
13241354

13251355
for sub_dir in map(lambda filepath: filepath.name, sub_dirs):
13261356
tests = map(
1327-
lambda filename: filename[:-len(".sol")],
1357+
lambda filename, sd=sub_dir: sd + "/" + filename[:-len(".sol")],
13281358
os.listdir(f"{self.project_root_dir}/{sub_dir}")
13291359
)
13301360

1361+
tests = map(
1362+
lambda path, sd=sub_dir: path[len(sd)+1:],
1363+
fnmatch.filter(tests, self.test_pattern)
1364+
)
1365+
13311366
print(f"Running tests in subdirectory '{sub_dir}'...")
13321367
for test in tests:
13331368
try_again = True

0 commit comments

Comments
 (0)