import asyncio import json import os from threading import Thread from unittest.mock import Mock import pytest from pygls import IS_PYODIDE from pygls.server import LanguageServer try: import websockets WEBSOCKETS_AVAILABLE = True except ImportError: WEBSOCKETS_AVAILABLE = False @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_tcp_connection_lost(): loop = asyncio.new_event_loop() server = LanguageServer("pygls-test", "v1", loop=loop) server.lsp.connection_made = Mock() server.lsp.connection_lost = Mock() # Run the server over TCP in a separate thread server_thread = Thread( target=server.start_tcp, args=( "127.0.0.1", 0, ), ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while server._server is None: await asyncio.sleep(0.5) # Simulate client's connection port = server._server.sockets[0].getsockname()[1] reader, writer = await asyncio.open_connection("127.0.0.1", port) await asyncio.sleep(1) assert server.lsp.connection_made.called # Socket is closed (client's process is terminated) writer.close() await asyncio.sleep(1) assert server.lsp.connection_lost.called @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_io_connection_lost(): # Client to Server pipe. csr, csw = os.pipe() # Server to client pipe. scr, scw = os.pipe() server = LanguageServer("pygls-test", "v1", loop=asyncio.new_event_loop()) server.lsp.connection_made = Mock() server_thread = Thread( target=server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")) ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while not server.lsp.connection_made.called: await asyncio.sleep(0.5) # Pipe is closed (client's process is terminated) os.close(csw) server_thread.join() @pytest.mark.asyncio @pytest.mark.skipif( IS_PYODIDE or not WEBSOCKETS_AVAILABLE, reason="threads are not available in pyodide", ) async def test_ws_server(): """Smoke test to ensure we can send/receive messages over websockets""" loop = asyncio.new_event_loop() server = LanguageServer("pygls-test", "v1", loop=loop) # Run the server over Websockets in a separate thread server_thread = Thread( target=server.start_ws, args=( "127.0.0.1", 0, ), ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while server._server is None: await asyncio.sleep(0.5) port = server._server.sockets[0].getsockname()[1] # Simulate client's connection async with websockets.connect(f"ws://127.0.0.1:{port}") as connection: # Send an 'initialize' request msg = dict( jsonrpc="2.0", id=1, method="initialize", params=dict(capabilities=dict()) ) await connection.send(json.dumps(msg)) response = await connection.recv() assert "result" in response # Shut the server down msg = dict( jsonrpc="2.0", id=2, method="shutdown", params=dict(capabilities=dict()) ) await connection.send(json.dumps(msg)) response = await connection.recv() assert "result" in response # Finally, tell it to exit msg = dict(jsonrpc="2.0", id=2, method="exit", params=None) await connection.send(json.dumps(msg)) server_thread.join()