From 4b281c0cd05f158e8d3faf750f171e07547b0e42 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Thu, 10 Aug 2023 20:41:22 +0100 Subject: [PATCH] pytest-lsp: Passthrough requested fixtures to user fixture func --- docs/pytest-lsp/guide/fixtures.rst | 11 ++++ lib/pytest-lsp/changes/71.enhancement.rst | 1 + lib/pytest-lsp/pytest_lsp/plugin.py | 37 +++++++++++-- .../examples/fixture-passthrough/server.py | 18 +++++++ .../examples/fixture-passthrough/t_server.py | 52 +++++++++++++++++++ lib/pytest-lsp/tests/test_examples.py | 1 + 6 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 lib/pytest-lsp/changes/71.enhancement.rst create mode 100644 lib/pytest-lsp/tests/examples/fixture-passthrough/server.py create mode 100644 lib/pytest-lsp/tests/examples/fixture-passthrough/t_server.py diff --git a/docs/pytest-lsp/guide/fixtures.rst b/docs/pytest-lsp/guide/fixtures.rst index 81546d6..3e5cfd0 100644 --- a/docs/pytest-lsp/guide/fixtures.rst +++ b/docs/pytest-lsp/guide/fixtures.rst @@ -13,3 +13,14 @@ This can be used to run the same set of tests while pretending to be a different :language: python :start-at: @pytest_lsp.fixture :end-at: await lsp_client.shutdown_session() + + +Requesting Other Fixtures +------------------------- + +As you would expect, it's possible to request other fixtures to help set up your client. + +.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/fixture-passthrough/t_server.py + :language: python + :start-at: @pytest.fixture + :end-at: await lsp_client.shutdown_session() diff --git a/lib/pytest-lsp/changes/71.enhancement.rst b/lib/pytest-lsp/changes/71.enhancement.rst new file mode 100644 index 0000000..d1d97a9 --- /dev/null +++ b/lib/pytest-lsp/changes/71.enhancement.rst @@ -0,0 +1 @@ +Fixtures created with the `@pytest_lsp.fixture` decorator can now request additional pytest fixtures diff --git a/lib/pytest-lsp/pytest_lsp/plugin.py b/lib/pytest-lsp/pytest_lsp/plugin.py index c529b7b..7e1682a 100644 --- a/lib/pytest-lsp/pytest_lsp/plugin.py +++ b/lib/pytest-lsp/pytest_lsp/plugin.py @@ -121,17 +121,46 @@ async def anext(it): return await it.__anext__() -def get_fixture_arguments(fn: Callable, client: LanguageClient, request) -> dict: - """Return the arguments to pass to the user's fixture function""" +def get_fixture_arguments( + fn: Callable, + client: LanguageClient, + request: pytest.FixtureRequest, +) -> dict: + """Return the arguments to pass to the user's fixture function. + + Parameters + ---------- + fn + The user's fixture function + + client + The language client instance to inject + + request + pytest's request fixture + + Returns + ------- + dict + The set of arguments to pass to the user's fixture function + """ kwargs = {} + required_parameters = set(inspect.signature(fn).parameters.keys()) - parameters = inspect.signature(fn).parameters - if "request" in parameters: + # Inject the 'request' fixture if requested + if "request" in required_parameters: kwargs["request"] = request + required_parameters.remove("request") + # Inject the language client for name, cls in typing.get_type_hints(fn).items(): if issubclass(cls, LanguageClient): kwargs[name] = client + required_parameters.remove(name) + + # Assume all remaining parameters are pytest fixtures + for name in required_parameters: + kwargs[name] = request.getfixturevalue(name) return kwargs diff --git a/lib/pytest-lsp/tests/examples/fixture-passthrough/server.py b/lib/pytest-lsp/tests/examples/fixture-passthrough/server.py new file mode 100644 index 0000000..2a54853 --- /dev/null +++ b/lib/pytest-lsp/tests/examples/fixture-passthrough/server.py @@ -0,0 +1,18 @@ +from lsprotocol.types import TEXT_DOCUMENT_COMPLETION +from lsprotocol.types import CompletionItem +from lsprotocol.types import CompletionParams +from pygls.server import LanguageServer + +server = LanguageServer("hello-world", "v1") + + +@server.feature(TEXT_DOCUMENT_COMPLETION) +def completion(ls: LanguageServer, params: CompletionParams): + return [ + CompletionItem(label="hello"), + CompletionItem(label="world"), + ] + + +if __name__ == "__main__": + server.start_io() diff --git a/lib/pytest-lsp/tests/examples/fixture-passthrough/t_server.py b/lib/pytest-lsp/tests/examples/fixture-passthrough/t_server.py new file mode 100644 index 0000000..890454c --- /dev/null +++ b/lib/pytest-lsp/tests/examples/fixture-passthrough/t_server.py @@ -0,0 +1,52 @@ +import sys + +import pytest +from lsprotocol.types import CompletionList +from lsprotocol.types import CompletionParams +from lsprotocol.types import InitializeParams +from lsprotocol.types import Position +from lsprotocol.types import TextDocumentIdentifier + +import pytest_lsp +from pytest_lsp import ClientServerConfig +from pytest_lsp import LanguageClient +from pytest_lsp import client_capabilities + + +@pytest.fixture(scope="module") +def client_name(): + return "neovim" + + +@pytest_lsp.fixture( + config=ClientServerConfig(server_command=[sys.executable, "server.py"]), +) +async def client(client_name: str, lsp_client: LanguageClient): + # Setup + params = InitializeParams(capabilities=client_capabilities(client_name)) + await lsp_client.initialize_session(params) + + yield + + # Teardown + await lsp_client.shutdown_session() + + +async def test_completions(client: LanguageClient): + """Ensure that the server implements completions correctly.""" + + results = await client.text_document_completion_async( + params=CompletionParams( + position=Position(line=1, character=0), + text_document=TextDocumentIdentifier(uri="file:///path/to/file.txt"), + ) + ) + assert results is not None + + if isinstance(results, CompletionList): + items = results.items + else: + items = results + + labels = [item.label for item in items] + assert labels == ["hello", "world"] diff --git a/lib/pytest-lsp/tests/test_examples.py b/lib/pytest-lsp/tests/test_examples.py index ae6fcb7..e900f12 100644 --- a/lib/pytest-lsp/tests/test_examples.py +++ b/lib/pytest-lsp/tests/test_examples.py @@ -27,6 +27,7 @@ def setup_test(pytester: pytest.Pytester, example_name: str): [ ("diagnostics", dict(passed=1)), ("getting-started", dict(passed=1)), + ("fixture-passthrough", dict(passed=1)), ("parameterised-clients", dict(passed=2)), ("window-log-message", dict(passed=1)), ("window-show-document", dict(passed=1)),