more tests, and travis yaml integration

This commit is contained in:
yeger 2018-11-15 17:41:21 -05:00
parent 0d7bfb4ea9
commit 11cef6c233
8 changed files with 117 additions and 48 deletions

16
.travis.yml Normal file
View File

@ -0,0 +1,16 @@
language: python
python:
# - "2.6"
- "2.7"
# - "3.3"
# - "3.4"
# - "3.5"
- "3.6"
# - "3.6-dev" # 3.6 development branch
# - "3.7-dev" # 3.7 development branch
# command to install dependencies
#install:
# - pip install -r requirements.txt
# command to run tests
script:
- pytest

View File

@ -1 +1,9 @@
# python-lsp # pylspclient
The library implements a LSP client in Python.
[![Build Status](https://travis-ci.org/yeger00/pylspclient.svg?branch=master)](https://travis-ci.org/yeger00/pylspclient)
# Run the tests
```
tox
```

View File

@ -3,13 +3,10 @@ import subprocess
if __name__ == "__main__": if __name__ == "__main__":
# clangd_path = "/usr/bin/clangd-6.0" clangd_path = "/usr/bin/clangd-6.0"
clangd_path = "/home/osboxes/projects/build/bin/clangd"
p = subprocess.Popen(clangd_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(clangd_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(p.stdin, p.stdout) json_rpc_endpoint = pylspclient.JsonRpcEndpoint(p.stdin, p.stdout)
# Working with socket: # To work with socket: sock_fd = sock.makefile()
# sock_fd = sock.makefile()
# json_rpc_endpoint = JsonRpcEndpoint(sock_fd, stext_document_res = lpc_client.send_notification(text_document_message)ock_fd)
lsp_endpoint = pylspclient.LspEndpoint(json_rpc_endpoint) lsp_endpoint = pylspclient.LspEndpoint(json_rpc_endpoint)
lsp_client = pylspclient.LspClient(lsp_endpoint) lsp_client = pylspclient.LspClient(lsp_endpoint)
@ -125,12 +122,12 @@ if __name__ == "__main__":
25, 25,
26]}},'workspaceEdit': {'documentChanges': True}, 26]}},'workspaceEdit': {'documentChanges': True},
'workspaceFolders': True}} 'workspaceFolders': True}}
workspace_folders = [{'name': 'python-lsp', 'uri': 'file:///home/osboxes/projects/ctest'}] workspace_folders = [{'name': 'python-lsp', 'uri': 'file:///path/to/dir'}]
root_uri = 'file:///home/osboxes/projects/ctest' root_uri = 'file:///path/to/dir'
print(lsp_client.initialize(p.pid, None, root_uri, None, capabilities, "off", workspace_folders)) print(lsp_client.initialize(p.pid, None, root_uri, None, capabilities, "off", workspace_folders))
print(lsp_client.initialized()) print(lsp_client.initialized())
file_path = "/home/osboxes/projects/ctest/test.c" file_path = "/path/to/dir/file.c"
uri = "file://" + file_path uri = "file://" + file_path
text = open(file_path, "r").read() text = open(file_path, "r").read()
languageId = pylspclient.lsp_structs.LANGUAGE_IDENTIFIER.C languageId = pylspclient.lsp_structs.LANGUAGE_IDENTIFIER.C

View File

@ -4,7 +4,7 @@ import re
from pylspclient import lsp_structs from pylspclient import lsp_structs
import threading import threading
JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}\r\n\r\n" JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}"
JSON_RPC_RES_REGEX = "Content-Length: ([0-9]*)\r\n" JSON_RPC_RES_REGEX = "Content-Length: ([0-9]*)\r\n"
# TODO: add content-type # TODO: add content-type
@ -30,11 +30,19 @@ class JsonRpcEndpoint(object):
@staticmethod @staticmethod
def __add_header(json_string): def __add_header(json_string):
'''
Adds a header for the given json string
:param str json_string: The string
:return: the string with the header
'''
return JSON_RPC_REQ_FORMAT.format(json_string_len=len(json_string), json_string=json_string) return JSON_RPC_REQ_FORMAT.format(json_string_len=len(json_string), json_string=json_string)
def send_request(self, message): def send_request(self, message):
''' '''
Sends the given message.
:param dict message: The message to send. :param dict message: The message to send.
''' '''
json_string = json.dumps(message, cls=MyEncoder) json_string = json.dumps(message, cls=MyEncoder)
@ -46,27 +54,27 @@ class JsonRpcEndpoint(object):
def recv_response(self): def recv_response(self):
''' '''
Recives a message.
:return: a message
''' '''
with self.read_lock: with self.read_lock:
line = self.stdout.readline() line = self.stdout.readline()
if line is None: if not line:
return None return None
print(line)
line = line.decode() line = line.decode()
# TODO: handle content type as well. # TODO: handle content type as well.
match = re.match(JSON_RPC_RES_REGEX, line) match = re.match(JSON_RPC_RES_REGEX, line)
if match is None or not match.groups(): if match is None or not match.groups():
# TODO: handle raise RuntimeError("Bad header: " + line)
print("error1: ", line)
return None
size = int(match.groups()[0]) size = int(match.groups()[0])
line = self.stdout.readline() line = self.stdout.readline()
if line is None: if not line:
return None return None
line = line.decode() line = line.decode()
if line != "\r\n": if line != "\r\n":
# TODO: handle raise RuntimeError("Bad header: missing newline")
print("error2")
return None
jsonrpc_res = self.stdout.read(size) jsonrpc_res = self.stdout.read(size)
return json.loads(jsonrpc_res) return json.loads(jsonrpc_res)

View File

@ -2,6 +2,11 @@ from pylspclient import lsp_structs
class LspClient(object): class LspClient(object):
def __init__(self, lsp_endpoint): def __init__(self, lsp_endpoint):
"""
Constructs a new LspClient instance.
:param lsp_endpoint: TODO
"""
self.lsp_endpoint = lsp_endpoint self.lsp_endpoint = lsp_endpoint

View File

@ -11,7 +11,7 @@ class PyTest(TestCommand):
def initialize_options(self): def initialize_options(self):
TestCommand.initialize_options(self) TestCommand.initialize_options(self)
self.pytest_args = "" self.pytest_args = []
def run_tests(self): def run_tests(self):
# import here, cause outside the eggs aren't loaded # import here, cause outside the eggs aren't loaded

View File

@ -1,5 +1,7 @@
import os
#from pytest_mock import mocker
import pytest
import pylspclient import pylspclient
from pytest_mock import mocker
class StdinMock(object): class StdinMock(object):
@ -10,36 +12,72 @@ class StdinMock(object):
pass pass
def test_sanity(mocker): class StdoutMock(object):
stdin_mock = StdinMock(); def readline(self):
mocker.patch.object(stdin_mock, 'write') pass
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(stdin_mock, None)
def read(self):
pass
JSON_RPC_RESULT_LIST = [
'Content-Length: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"),
'Content-Length: 40\r\n\r\n{"key_num": 1, "key_str": "some_string"}'.encode("utf-8")
]
def test_send_sanity():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(pipeout, None)
json_rpc_endpoint.send_request({"key_num":1, "key_str":"some_string"}) json_rpc_endpoint.send_request({"key_num":1, "key_str":"some_string"})
stdin_mock.write.assert_called() result = pipein.read(len(JSON_RPC_RESULT_LIST[0]))
assert(result in JSON_RPC_RESULT_LIST)
assert(stdin_mock.write.call_args not in [
'''Content-Length: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}\r\n\r\n'''.encode("utf-8"),
'''Content-Length: 40\r\n\r\n{"key_num": 1, "key_str": "some_string"}\r\n\r\n'''.encode("utf-8")
])
def test_class(mocker): def test_send_class():
class RpcClass(object): class RpcClass(object):
def __init__(self, value_num, value_str): def __init__(self, value_num, value_str):
self.key_num = value_num self.key_num = value_num
self.key_str = value_str self.key_str = value_str
stdin_mock = StdinMock(); pipein, pipeout = os.pipe()
mocker.patch.object(stdin_mock, 'write') pipein = os.fdopen(pipein, "rb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(stdin_mock, None) pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(pipeout, None)
json_rpc_endpoint.send_request(RpcClass(1, "some_string")) json_rpc_endpoint.send_request(RpcClass(1, "some_string"))
stdin_mock.write.assert_called() result = pipein.read(len(JSON_RPC_RESULT_LIST[0]))
assert(result in JSON_RPC_RESULT_LIST)
def test_recv_sanity():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(None, pipein)
pipeout.write('Content-Length: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"))
pipeout.flush()
result = json_rpc_endpoint.recv_response()
assert({"key_num":1, "key_str":"some_string"} == result)
def test_recv_wrong_header():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(None, pipein)
pipeout.write('Contentength: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"))
pipeout.flush()
with pytest.raises(RuntimeError):
result = json_rpc_endpoint.recv_response()
def test_recv_close_pipe():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(None, pipein)
pipeout.close()
result = json_rpc_endpoint.recv_response()
assert(result is None)
assert(stdin_mock.write.call_args not in [
'''Content-Length: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}\r\n\r\n'''.encode("utf-8"),
'''Content-Length: 40\r\n\r\n{"key_num": 1, "key_str": "some_string"}\r\n\r\n'''.encode("utf-8")
])
# content of test_sample.py
def func(x):
return x + 1

View File

@ -2,8 +2,5 @@
envlist = py27,py36 envlist = py27,py36
[testenv] [testenv]
deps =
pytest
pytest_mock
commands = commands =
pytest python setup.py test