Skip to content

Commit

Permalink
test: add the option to run the end-to-end tests over TCP
Browse files Browse the repository at this point in the history
This extends pygls' test suite with a `--lsp-transport` command line
argument.

- `--lsp-transport stdio` (the default), runs the end-to-end tests
  over stdin/stdout.
- `--lsp-transport tcp`, runs the end-to-end tests over a TCP
  connection

The `poe test` task used in CI has been updated to run both style of
end-to-end tests.
  • Loading branch information
alcarney committed Oct 7, 2024
1 parent 64a87d2 commit e00f179
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 7 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ poetry_lock_check = "poetry check"
[tool.poe.tasks.test]
sequence = [
{ cmd = "pytest --cov" },
{ cmd = "pytest tests/e2e --lsp-transport tcp" },
]
ignore_fail = "return_non_zero"

Expand Down
49 changes: 42 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
# See the License for the specific language governing permissions and #
# limitations under the License. #
############################################################################
from __future__ import annotations

import asyncio
import pathlib
import sys
Expand Down Expand Up @@ -126,6 +128,15 @@ def pytest_addoption(parser):
help="Choose the runtime in which to run servers under test.",
)

group.addoption(
"--lsp-transport",
dest="lsp_transport",
action="store",
default="stdio",
choices=("stdio", "tcp"),
help="Choose the transport to use with servers under test.",
)


@pytest.fixture(scope="session")
def runtime(request):
Expand All @@ -134,6 +145,13 @@ def runtime(request):
return request.config.getoption("lsp_runtime")


@pytest.fixture(scope="session")
def transport(request):
"""This fixture is the source of truth for the transport we should run the
end-to-end tests with."""
return request.config.getoption("lsp_transport")


@pytest.fixture(scope="session")
def path_for():
"""Returns the path corresponding to a file in the example workspace"""
Expand Down Expand Up @@ -171,14 +189,31 @@ def server_dir():
return path.resolve()


def get_client_for_cpython_server(uri_fixture):
def get_client_for_cpython_server(transport, uri_fixture):
"""Return a client configured to communicate with a server running under cpython."""

async def fn(
server_name: str, capabilities: Optional[types.ClientCapabilities] = None
):
client = LanguageClient("pygls-test-suite", "v1")
await client.start_io(sys.executable, str(SERVER_DIR / server_name))

server_cmd = [sys.executable, str(SERVER_DIR / server_name)]
server: asyncio.subprocess.Process | None = None

if transport == "stdio":
await client.start_io(*server_cmd)

elif transport == "tcp":
# TODO: Make host/port configurable?
host, port = 'localhost', 8888
server_cmd.extend(["--tcp", "--host", host, "--port", f"{port}"])

server = await asyncio.create_subprocess_exec(*server_cmd)
await asyncio.sleep(1)
await client.start_tcp(host, port)

else:
raise NotImplementedError(f"Unsupported transport: {transport!r}")

response = await client.initialize_async(
types.InitializeParams(
Expand All @@ -193,19 +228,19 @@ async def fn(
client.exit(None)

await client.stop()
if server is not None and server.returncode is None:
server.terminate()

return fn


@pytest.fixture(scope="session")
def get_client_for(runtime, uri_for):
def get_client_for(runtime, transport, uri_for):
"""Return a client configured to communicate with the specified server.
Takes into account the current runtime.
It's the consuming fixture's responsibility to stop the client.
Takes into account the current runtime and transport.
"""
if runtime not in {"cpython"}:
raise NotImplementedError(f"get_client_for: {runtime=}")

return get_client_for_cpython_server(uri_for)
return get_client_for_cpython_server(transport, uri_for)

0 comments on commit e00f179

Please sign in to comment.