conftest.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import json
  2. import os
  3. import pathlib
  4. import shutil
  5. from pathlib import Path
  6. from typing import Text
  7. from jupyter_server.serverapp import ServerApp
  8. from pytest import fixture
  9. from tornado.httpserver import HTTPRequest
  10. from tornado.httputil import HTTPServerRequest
  11. from tornado.queues import Queue
  12. from tornado.web import Application
  13. # local imports
  14. from jupyter_lsp import LanguageServerManager
  15. from jupyter_lsp.constants import APP_CONFIG_D_SECTIONS
  16. from jupyter_lsp.handlers import LanguageServersHandler, LanguageServerWebSocketHandler
  17. # these should always be available in a test environment
  18. KNOWN_SERVERS = [
  19. "bash-language-server",
  20. "dockerfile-language-server-nodejs",
  21. "typescript-language-server",
  22. "pylsp",
  23. "unified-language-server",
  24. "sql-language-server",
  25. "vscode-css-languageserver-bin",
  26. "vscode-html-languageserver-bin",
  27. "vscode-json-languageserver-bin",
  28. "yaml-language-server",
  29. ]
  30. CMD_BASED_SERVERS = {
  31. "Rscript": ["r-languageserver"],
  32. "texlab": ["texlab"],
  33. "jedi-language-server": ["jedi-language-server"],
  34. "julia": ["julia-language-server"],
  35. }
  36. KNOWN_SERVERS += sum(
  37. [langs for cmd, langs in CMD_BASED_SERVERS.items() if shutil.which(cmd)], []
  38. )
  39. KNOWN_UNKNOWN_SERVERS = ["foo-language-server"]
  40. def extra_node_roots():
  41. root = Path(os.environ.get("JLSP_TEST_ROOT") or Path.cwd())
  42. return dict(extra_node_roots=[str(root)] if root else [])
  43. @fixture
  44. def manager() -> LanguageServerManager:
  45. return LanguageServerManager(**extra_node_roots())
  46. @fixture
  47. def echo_spec():
  48. return {"argv": ["echo", "no server here"], "languages": ["klingon"], "version": 2}
  49. @fixture
  50. def echo_conf_json(echo_spec) -> str:
  51. return json.dumps(
  52. {"LanguageServerManager": {"language_servers": {"_echo_": echo_spec}}},
  53. indent=2,
  54. sort_keys=True,
  55. )
  56. @fixture(params=sorted(APP_CONFIG_D_SECTIONS))
  57. def app_config_d(request, tmp_path, monkeypatch) -> pathlib.Path:
  58. conf_d = tmp_path / f"jupyter{request.param}config.d"
  59. conf_d.mkdir()
  60. monkeypatch.setenv("JUPYTER_CONFIG_PATH", f"{tmp_path}")
  61. return conf_d
  62. @fixture(params=sorted(KNOWN_SERVERS))
  63. def known_server(request):
  64. return request.param
  65. @fixture(params=sorted(KNOWN_UNKNOWN_SERVERS))
  66. def known_unknown_server(request):
  67. return request.param
  68. @fixture
  69. def handlers(manager):
  70. ws_handler = MockWebsocketHandler()
  71. ws_handler.initialize(manager)
  72. handler = MockHandler()
  73. handler.initialize(manager)
  74. return handler, ws_handler
  75. @fixture
  76. def jsonrpc_init_msg():
  77. return json.dumps(
  78. {
  79. "id": 0,
  80. "jsonrpc": "2.0",
  81. "method": "initialize",
  82. "params": {
  83. "capabilities": {
  84. # see: https://github.com/julia-vscode/LanguageServer.jl/issues/1008
  85. # LanguageServer.jl assumes that it is not missing
  86. "workspace": {"didChangeConfiguration": {}},
  87. # LanguageServer.jl assumes that it is not missing
  88. "textDocument": {},
  89. },
  90. "initializationOptions": None,
  91. "processId": None,
  92. "rootUri": pathlib.Path(__file__).parent.as_uri(),
  93. "workspaceFolders": None,
  94. },
  95. }
  96. )
  97. @fixture
  98. def app():
  99. return MockServerApp()
  100. # mocks
  101. class MockWebsocketHandler(LanguageServerWebSocketHandler):
  102. _messages_wrote = None # type: Queue
  103. _ping_sent = None # type: bool
  104. def __init__(self):
  105. self.request = HTTPServerRequest()
  106. self.application = Application()
  107. def initialize(self, manager):
  108. super().initialize(manager)
  109. self._messages_wrote = Queue()
  110. self._ping_sent = False
  111. def write_message(self, message: Text) -> None: # type: ignore
  112. self.log.warning("write_message %s", message)
  113. self._messages_wrote.put_nowait(message)
  114. def send_ping(self):
  115. self._ping_sent = True
  116. class MockHandler(LanguageServersHandler):
  117. _payload = None
  118. _jupyter_current_user = "foo" # type:ignore[assignment]
  119. def __init__(self):
  120. self.request = HTTPRequest("GET")
  121. self.application = Application()
  122. def finish(self, payload):
  123. self._payload = payload
  124. class MockServerApp(ServerApp):
  125. pass