Skip to content

Commit 1e5bed2

Browse files
committed
Test
1 parent cc6d398 commit 1e5bed2

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

test/test_python_lsp.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import asyncio
2+
import json
3+
import os
4+
import socket
5+
import subprocess
6+
import sys
7+
import threading
8+
import time
9+
10+
import pytest
11+
import websockets
12+
13+
NUM_REQUESTS = 10
14+
TEST_PORT = 5102
15+
HOST = "127.0.0.1"
16+
MAX_STARTUP_SECONDS = 5.0
17+
CHECK_INTERVAL = 0.1
18+
19+
20+
@pytest.fixture(scope="module", autouse=True)
21+
def ws_server_subprocess():
22+
cmd = [
23+
sys.executable,
24+
"-m",
25+
"pylsp.__main__",
26+
"--ws",
27+
"--host",
28+
HOST,
29+
"--port",
30+
str(TEST_PORT),
31+
"-vv",
32+
]
33+
34+
proc = subprocess.Popen(
35+
cmd,
36+
stdout=subprocess.PIPE,
37+
stderr=subprocess.PIPE,
38+
env=os.environ.copy(),
39+
)
40+
41+
deadline = time.time() + MAX_STARTUP_SECONDS
42+
while True:
43+
try:
44+
with socket.create_connection(
45+
("127.0.0.1", TEST_PORT), timeout=CHECK_INTERVAL
46+
):
47+
break
48+
except (ConnectionRefusedError, OSError):
49+
if time.time() > deadline:
50+
proc.kill()
51+
out, err = proc.communicate(timeout=1)
52+
raise RuntimeError(
53+
f"Server didn’t start listening on port {TEST_PORT} in time.\n"
54+
f"STDOUT:\n{out.decode()}\nSTDERR:\n{err.decode()}"
55+
)
56+
time.sleep(CHECK_INTERVAL)
57+
58+
yield # run the tests
59+
60+
proc.terminate()
61+
try:
62+
proc.wait(timeout=2)
63+
except subprocess.TimeoutExpired:
64+
proc.kill()
65+
66+
67+
def test_concurrent_ws_requests():
68+
received = []
69+
lock = threading.Lock()
70+
71+
def thread_target(i: int):
72+
async def do_initialize(idx):
73+
uri = f"ws://{HOST}:{TEST_PORT}"
74+
async with websockets.connect(uri) as ws:
75+
# send initialize
76+
req = {
77+
"jsonrpc": "2.0",
78+
"id": idx,
79+
"method": "initialize",
80+
"params": {},
81+
}
82+
83+
try:
84+
await asyncio.wait_for(
85+
ws.send(json.dumps(req, ensure_ascii=False)), timeout=5
86+
)
87+
raw = await asyncio.wait_for(ws.recv(), timeout=5)
88+
except asyncio.TimeoutError:
89+
return None
90+
obj = json.loads(raw)
91+
return obj.get("id")
92+
93+
returned_id = asyncio.run(do_initialize(i))
94+
with lock:
95+
received.append(returned_id)
96+
97+
# launch threads
98+
threads = []
99+
for i in range(1, NUM_REQUESTS + 1):
100+
t = threading.Thread(target=thread_target, args=(i,))
101+
t.start()
102+
threads.append(t)
103+
104+
# wait for them all
105+
for t in threads:
106+
t.join(timeout=20)
107+
assert not t.is_alive(), f"Worker thread {t} hung!"
108+
109+
# validate
110+
assert set(received) == set(range(1, NUM_REQUESTS + 1)), f"got IDs {received}"

0 commit comments

Comments
 (0)