Skip to content

Commit 97150c7

Browse files
committed
Converge unit tests for test_language_server and test_notebook_document
1 parent 15e2447 commit 97150c7

File tree

4 files changed

+174
-164
lines changed

4 files changed

+174
-164
lines changed

test/fixtures.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from io import StringIO
66
from unittest.mock import MagicMock
77

8+
from test.test_utils import ClientServerPair
9+
810
import pytest
911
from pylsp_jsonrpc.dispatchers import MethodDispatcher
1012
from pylsp_jsonrpc.endpoint import Endpoint
@@ -22,12 +24,14 @@
2224
def main():
2325
print sys.stdin.read()
2426
"""
27+
CALL_TIMEOUT_IN_SECONDS = 30
2528

2629

2730
class FakeEditorMethodsMixin:
2831
"""
2932
Represents the methods to be added to a dispatcher class when faking an editor.
3033
"""
34+
3135
def m_window__work_done_progress__create(self, *_args, **_kwargs):
3236
"""
3337
Fake editor method `window/workDoneProgress/create`.
@@ -52,6 +56,7 @@ class FakeEndpoint(Endpoint):
5256
Fake methods in the `dispatcher` should raise `JsonRpcException` for any
5357
error.
5458
"""
59+
5560
def request(self, method, params=None):
5661
request_future = super().request(method, params)
5762
try:
@@ -64,40 +69,32 @@ def request(self, method, params=None):
6469

6570
@pytest.fixture
6671
def pylsp(tmpdir):
67-
""" Return an initialized python LS """
72+
"""Return an initialized python LS"""
6873
ls = FakePythonLSPServer(StringIO, StringIO, endpoint_cls=FakeEndpoint)
6974

7075
ls.m_initialize(
71-
processId=1,
72-
rootUri=uris.from_fs_path(str(tmpdir)),
73-
initializationOptions={}
76+
processId=1, rootUri=uris.from_fs_path(str(tmpdir)), initializationOptions={}
7477
)
7578

7679
return ls
7780

7881

7982
@pytest.fixture
8083
def pylsp_w_workspace_folders(tmpdir):
81-
""" Return an initialized python LS """
84+
"""Return an initialized python LS"""
8285
ls = FakePythonLSPServer(StringIO, StringIO, endpoint_cls=FakeEndpoint)
8386

84-
folder1 = tmpdir.mkdir('folder1')
85-
folder2 = tmpdir.mkdir('folder2')
87+
folder1 = tmpdir.mkdir("folder1")
88+
folder2 = tmpdir.mkdir("folder2")
8689

8790
ls.m_initialize(
8891
processId=1,
8992
rootUri=uris.from_fs_path(str(folder1)),
9093
initializationOptions={},
9194
workspaceFolders=[
92-
{
93-
'uri': uris.from_fs_path(str(folder1)),
94-
'name': 'folder1'
95-
},
96-
{
97-
'uri': uris.from_fs_path(str(folder2)),
98-
'name': 'folder2'
99-
}
100-
]
95+
{"uri": uris.from_fs_path(str(folder1)), "name": "folder1"},
96+
{"uri": uris.from_fs_path(str(folder2)), "name": "folder2"},
97+
],
10198
)
10299

103100
workspace_folders = [folder1, folder2]
@@ -129,7 +126,7 @@ def workspace(tmpdir, endpoint): # pylint: disable=redefined-outer-name
129126
@pytest.fixture
130127
def workspace_other_root_path(tmpdir, endpoint): # pylint: disable=redefined-outer-name
131128
"""Return a workspace with a root_path other than tmpdir."""
132-
ws_path = str(tmpdir.mkdir('test123').mkdir('test456'))
129+
ws_path = str(tmpdir.mkdir("test123").mkdir("test456"))
133130
ws = Workspace(uris.from_fs_path(ws_path), endpoint)
134131
ws._config = Config(ws.root_uri, {}, 0, {})
135132
return ws
@@ -139,7 +136,9 @@ def workspace_other_root_path(tmpdir, endpoint): # pylint: disable=redefined-ou
139136
def config(workspace): # pylint: disable=redefined-outer-name
140137
"""Return a config object."""
141138
cfg = Config(workspace.root_uri, {}, 0, {})
142-
cfg._plugin_settings = {'plugins': {'pylint': {'enabled': False, 'args': [], 'executable': None}}}
139+
cfg._plugin_settings = {
140+
"plugins": {"pylint": {"enabled": False, "args": [], "executable": None}}
141+
}
143142
return cfg
144143

145144

@@ -150,14 +149,15 @@ def doc(workspace): # pylint: disable=redefined-outer-name
150149

151150
@pytest.fixture
152151
def temp_workspace_factory(workspace): # pylint: disable=redefined-outer-name
153-
'''
152+
"""
154153
Returns a function that creates a temporary workspace from the files dict.
155154
The dict is in the format {"file_name": "file_contents"}
156-
'''
155+
"""
156+
157157
def fn(files):
158158
def create_file(name, content):
159159
fn = os.path.join(workspace.root_path, name)
160-
with open(fn, 'w', encoding='utf-8') as f:
160+
with open(fn, "w", encoding="utf-8") as f:
161161
f.write(content)
162162
workspace.put_document(uris.from_fs_path(fn), content)
163163

@@ -166,3 +166,17 @@ def create_file(name, content):
166166
return workspace
167167

168168
return fn
169+
170+
171+
@pytest.fixture
172+
def client_server_pair():
173+
"""A fixture that sets up a client/server pair and shuts down the server"""
174+
client_server_pair_obj = ClientServerPair()
175+
176+
yield (client_server_pair_obj.client, client_server_pair_obj.server)
177+
178+
shutdown_response = client_server_pair_obj.client._endpoint.request(
179+
"shutdown"
180+
).result(timeout=CALL_TIMEOUT_IN_SECONDS)
181+
assert shutdown_response is None
182+
client_server_pair_obj.client._endpoint.notify("exit")

test/test_language_server.py

Lines changed: 55 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,118 +3,92 @@
33

44
import os
55
import time
6-
import multiprocessing
76
import sys
8-
from threading import Thread
7+
8+
from test.test_utils import ClientServerPair
99

1010
from flaky import flaky
1111
from pylsp_jsonrpc.exceptions import JsonRpcMethodNotFound
1212
import pytest
1313

14-
from pylsp.python_lsp import start_io_lang_server, PythonLSPServer
15-
16-
CALL_TIMEOUT = 10
17-
RUNNING_IN_CI = bool(os.environ.get('CI'))
18-
19-
20-
def start_client(client):
21-
client.start()
22-
23-
24-
class _ClientServer:
25-
""" A class to setup a client/server pair """
26-
def __init__(self, check_parent_process=False):
27-
# Client to Server pipe
28-
csr, csw = os.pipe()
29-
# Server to client pipe
30-
scr, scw = os.pipe()
31-
32-
if os.name == 'nt':
33-
ParallelKind = Thread
34-
else:
35-
if sys.version_info[:2] >= (3, 8):
36-
ParallelKind = multiprocessing.get_context("fork").Process
37-
else:
38-
ParallelKind = multiprocessing.Process
39-
40-
self.process = ParallelKind(target=start_io_lang_server, args=(
41-
os.fdopen(csr, 'rb'), os.fdopen(scw, 'wb'), check_parent_process, PythonLSPServer
42-
))
43-
self.process.start()
44-
45-
self.client = PythonLSPServer(os.fdopen(scr, 'rb'), os.fdopen(csw, 'wb'), start_io_lang_server)
46-
self.client_thread = Thread(target=start_client, args=[self.client])
47-
self.client_thread.daemon = True
48-
self.client_thread.start()
49-
50-
51-
@pytest.fixture
52-
def client_server():
53-
""" A fixture that sets up a client/server pair and shuts down the server
54-
This client/server pair does not support checking parent process aliveness
55-
"""
56-
client_server_pair = _ClientServer()
5714

58-
yield client_server_pair.client
15+
RUNNING_IN_CI = bool(os.environ.get("CI"))
5916

60-
shutdown_response = client_server_pair.client._endpoint.request('shutdown').result(timeout=CALL_TIMEOUT)
61-
assert shutdown_response is None
62-
client_server_pair.client._endpoint.notify('exit')
17+
CALL_TIMEOUT_IN_SECONDS = 10
6318

6419

6520
@pytest.fixture
6621
def client_exited_server():
67-
""" A fixture that sets up a client/server pair that support checking parent process aliveness
22+
"""A fixture that sets up a client/server pair that support checking parent process aliveness
6823
and assert the server has already exited
6924
"""
70-
client_server_pair = _ClientServer(True)
25+
client_server_pair_obj = ClientServerPair(True, True)
7126

72-
# yield client_server_pair.client
73-
yield client_server_pair
27+
yield client_server_pair_obj
7428

75-
assert client_server_pair.process.is_alive() is False
29+
assert client_server_pair_obj.server_process.is_alive() is False
7630

7731

7832
@flaky(max_runs=10, min_passes=1)
79-
@pytest.mark.skipif(sys.platform == 'darwin', reason='Too flaky on Mac')
80-
def test_initialize(client_server): # pylint: disable=redefined-outer-name
81-
response = client_server._endpoint.request('initialize', {
82-
'rootPath': os.path.dirname(__file__),
83-
'initializationOptions': {}
84-
}).result(timeout=CALL_TIMEOUT)
85-
assert 'capabilities' in response
33+
@pytest.mark.skipif(sys.platform == "darwin", reason="Too flaky on Mac")
34+
def test_initialize(client_server_pair):
35+
client, _ = client_server_pair
36+
response = client._endpoint.request(
37+
"initialize",
38+
{"rootPath": os.path.dirname(__file__), "initializationOptions": {}},
39+
).result(timeout=CALL_TIMEOUT_IN_SECONDS)
40+
assert "capabilities" in response
8641

8742

8843
@flaky(max_runs=10, min_passes=1)
89-
@pytest.mark.skipif(not sys.platform.startswith('Linux'), reason='Skipped on win and flaky on mac')
90-
def test_exit_with_parent_process_died(client_exited_server): # pylint: disable=redefined-outer-name
44+
@pytest.mark.skipif(
45+
not sys.platform.startswith("Linux"), reason="Skipped on win and flaky on mac"
46+
)
47+
def test_exit_with_parent_process_died(
48+
client_exited_server,
49+
): # pylint: disable=redefined-outer-name
9150
# language server should have already exited before responding
92-
lsp_server, mock_process = client_exited_server.client, client_exited_server.process
51+
lsp_server, mock_process = (
52+
client_exited_server.client,
53+
client_exited_server.server_process,
54+
)
9355
# with pytest.raises(Exception):
94-
lsp_server._endpoint.request('initialize', {
95-
'processId': mock_process.pid,
96-
'rootPath': os.path.dirname(__file__),
97-
'initializationOptions': {}
98-
}).result(timeout=CALL_TIMEOUT)
56+
lsp_server._endpoint.request(
57+
"initialize",
58+
{
59+
"processId": mock_process.pid,
60+
"rootPath": os.path.dirname(__file__),
61+
"initializationOptions": {},
62+
},
63+
).result(timeout=CALL_TIMEOUT_IN_SECONDS)
9964

10065
mock_process.terminate()
101-
time.sleep(CALL_TIMEOUT)
66+
time.sleep(CALL_TIMEOUT_IN_SECONDS)
10267
assert not client_exited_server.client_thread.is_alive()
10368

10469

10570
@flaky(max_runs=10, min_passes=1)
106-
@pytest.mark.skipif(sys.platform.startswith('linux'), reason='Fails on linux')
107-
def test_not_exit_without_check_parent_process_flag(client_server): # pylint: disable=redefined-outer-name
108-
response = client_server._endpoint.request('initialize', {
109-
'processId': 1234,
110-
'rootPath': os.path.dirname(__file__),
111-
'initializationOptions': {}
112-
}).result(timeout=CALL_TIMEOUT)
113-
assert 'capabilities' in response
71+
@pytest.mark.skipif(sys.platform.startswith("linux"), reason="Fails on linux")
72+
def test_not_exit_without_check_parent_process_flag(
73+
client_server_pair,
74+
):
75+
client, _ = client_server_pair
76+
response = client._endpoint.request(
77+
"initialize",
78+
{
79+
"processId": 1234,
80+
"rootPath": os.path.dirname(__file__),
81+
"initializationOptions": {},
82+
},
83+
).result(timeout=CALL_TIMEOUT_IN_SECONDS)
84+
assert "capabilities" in response
11485

11586

11687
@flaky(max_runs=10, min_passes=1)
117-
@pytest.mark.skipif(RUNNING_IN_CI, reason='This test is hanging on CI')
118-
def test_missing_message(client_server): # pylint: disable=redefined-outer-name
88+
@pytest.mark.skipif(RUNNING_IN_CI, reason="This test is hanging on CI")
89+
def test_missing_message(client_server_pair):
90+
client, _ = client_server_pair
11991
with pytest.raises(JsonRpcMethodNotFound):
120-
client_server._endpoint.request('unknown_method').result(timeout=CALL_TIMEOUT)
92+
client._endpoint.request("unknown_method").result(
93+
timeout=CALL_TIMEOUT_IN_SECONDS
94+
)

0 commit comments

Comments
 (0)