Skip to content

Commit

Permalink
lsp.py: Port to support running on Windows & adapt to changes due to …
Browse files Browse the repository at this point in the history
…prior merged PR.

- lsp.py: Fixes invalid-syntax by Python interpreter on Windows CI (older Python version).
- lsp.py: Savely strip CRLF from right side of the string, ignoring accidental multiple occurrences of \r (such as \r\r\n).
- lsp.py: Fixes reading single character from stdin (wrt. Windows platform).
- lsp.py: Adds header line reading to I/O tracing (useful for debugging).
- lsp.py: When running the tests on Windows, don't care test file content's newlines but simply expect LFs (instead of CRLF for example).
- Apply pylint notes.
- Fixing use of @functools.lru_cache for older python versions (CircleCI Windows)
  • Loading branch information
christianparpart committed May 9, 2022
1 parent c2f245b commit e8d0777
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,9 @@ jobs:
- run:
name: Install LSP test dependencies
command: python -m pip install --user deepdiff colorama
- run:
name: Inspect lsp.py
command: Get-Content ./test/lsp.py
- run:
name: Executing solc LSP test suite
command: python ./test/lsp.py .\build\solc\Release\solc.exe
Expand Down
70 changes: 50 additions & 20 deletions test/lsp.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,57 @@
#!/usr/bin/env python3
# pragma pylint: disable=too-many-lines
# test line 1
import argparse
import fnmatch
import functools
import json
import os
import re
import subprocess
import sys
import traceback
import re
import tty
import functools
from collections import namedtuple
from copy import deepcopy
from enum import Enum, auto
from itertools import islice
from pathlib import PurePath
from typing import Any, List, Optional, Tuple, Union
from itertools import islice

from enum import Enum, auto

import colorama # Enables the use of SGR & CUP terminal VT sequences on Windows.
import colorama # Enables the use of SGR & CUP terminal VT sequences on Windows.
from deepdiff import DeepDiff

if os.name == 'nt':
# pragma pylint: disable=import-error
import msvcrt
else:
import tty
# Turn off user input buffering so we get the input immediately,
# not only after a line break
tty.setcbreak(sys.stdin.fileno())


def escape_string(text: str) -> str:
"""
Trivially escapes given input string's \r \n and \\.
"""
return text.translate(str.maketrans({
"\r": r"\r",
"\n": r"\n",
"\\": r"\\"
}))


def getCharFromStdin():
"""
Gets a single character from stdin without line-buffering.
"""
if os.name == 'nt':
# pragma pylint: disable=import-error
return msvcrt.getch().decode("utf-8")
else:
return sys.stdin.buffer.read(1)


"""
Named tuple that holds various regexes used to parse the test specification.
"""
Expand Down Expand Up @@ -101,7 +132,6 @@ class BadHeader(Exception):
def __init__(self, msg: str):
super().__init__("Bad header: " + msg)


class JsonRpcProcess:
exe_path: str
exe_args: List[str]
Expand Down Expand Up @@ -144,10 +174,12 @@ def receive_message(self) -> Union[None, dict]:
# server quit
return None
line = line.decode("utf-8")
if self.trace_io:
print(f"Received header-line: {escape_string(line)}")
if not line.endswith("\r\n"):
raise BadHeader("missing newline")
# remove the "\r\n"
line = line[:-2]
# Safely remove the "\r\n".
line = line.rstrip("\r\n")
if line == '':
break # done with the headers
if line.startswith(CONTENT_LENGTH_HEADER):
Expand Down Expand Up @@ -589,7 +621,7 @@ def user_interaction_failed_method_test(self, testcase, actual, expected):

while True:
print("(u)pdate/(r)etry/(i)gnore?")
user_response = sys.stdin.read(1)
user_response = getCharFromStdin()
if user_response == "i":
return self.TestResult.SuccessOrIgnored

Expand Down Expand Up @@ -787,7 +819,7 @@ def get_test_file_contents(self, test_case_name):
in the test path (test/libsolidity/lsp).
"""
with open(self.get_test_file_path(test_case_name), mode="r", encoding="utf-8", newline='') as f:
return f.read()
return f.read().replace("\r\n", "\n")

def require_params_for_method(self, method_name: str, message: dict) -> Any:
"""
Expand Down Expand Up @@ -1059,7 +1091,7 @@ def user_interaction_failed_diagnostics(
"""
while True:
print("(u)pdate/(r)etry/(s)kip file?")
user_response = sys.stdin.read(1)
user_response = getCharFromStdin()
if user_response == "u":
while True:
try:
Expand All @@ -1068,16 +1100,16 @@ def user_interaction_failed_diagnostics(
# pragma pylint: disable=broad-except
except Exception as e:
print(e)
if ret := self.user_interaction_failed_autoupdate(test):
return ret
if self.user_interaction_failed_autoupdate(test):
return True
elif user_response == 's':
return True
elif user_response == 'r':
return False

def user_interaction_failed_autoupdate(self, test):
print("(e)dit/(r)etry/(s)kip file?")
user_response = sys.stdin.read(1)
user_response = getCharFromStdin()
if user_response == "r":
print("retrying...")
# pragma pylint: disable=no-member
Expand Down Expand Up @@ -1142,7 +1174,7 @@ def test_textDocument_didOpen_with_relative_import(self, solc: JsonRpcProcess) -
marker = self.get_file_tags("lib")["@diagnostics"]
self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker)

@functools.lru_cache # pragma pylint: disable=lru-cache-decorating-method
@functools.lru_cache() # pragma pylint: disable=lru-cache-decorating-method
def get_file_tags(self, test_name: str, verbose=False):
"""
Finds all tags (e.g. @tagname) in the given test and returns them as a
Expand Down Expand Up @@ -1653,10 +1685,8 @@ def test_textDocument_didChange_multi_line(self, solc: JsonRpcProcess) -> None:
# }}}
# }}}


if __name__ == "__main__":
# Turn off user input buffering so we get the input immediately,
# not only after a line break
tty.setcbreak(sys.stdin.fileno())
suite = SolidityLSPTestSuite()
exit_code = suite.main()
sys.exit(exit_code)

0 comments on commit e8d0777

Please sign in to comment.