Skip to content

Commit

Permalink
More coverage (jupyter-server#1067)
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 authored Nov 12, 2022
1 parent ca3c5b2 commit 81535d4
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 29 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ jobs:
pip check
- name: Run the tests
run: |
pytest -vv --integration_tests=true tests
hatch run cov:integration
integration_check: # This job does nothing and is only used for the branch protection
if: always()
Expand Down
21 changes: 4 additions & 17 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install texlive-plain-generic inkscape texlive-xetex
sudo apt-get install xvfb x11-utils libxkbcommon-x11-0
pip install pandoc
sudo apt-get install xvfb x11-utils libxkbcommon-x11-0 pandoc
- name: Run the tests
if: ${{ !startsWith(matrix.python-version, 'pypy') && !startsWith(matrix.os, 'windows') }}
run: hatch run cov:test -W default || hatch run cov:test -W default --lf
run: hatch run cov:test -W default || hatch run test:test -W default --lf
- name: Run the tests on pypy and windows
if: ${{ startsWith(matrix.python-version, 'pypy') || startsWith(matrix.os, 'windows') }}
run: hatch run test:test -W default || hatch run test:test -W default --lf
Expand Down Expand Up @@ -71,20 +70,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: pre-commit/[email protected]
with:
extra_args: --all-files --hook-stage=manual
- name: Help message if pre-commit fail
if: ${{ failure() }}
run: |
echo "You can install pre-commit hooks to automatically run formatting"
echo "on each commit with:"
echo " pre-commit install"
echo "or you can run by hand on staged files with"
echo " pre-commit run"
echo "or after-the-fact on already committed files with"
echo " pre-commit run --all-files --hook-stage=manual"
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- uses: jupyterlab/maintainer-tools/.github/actions/pre-commit@v1

test_docs:
name: Test Docs
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ coverage:
project:
default:
target: auto
threshold: 10
threshold: 1
patch:
default:
target: 0%
7 changes: 5 additions & 2 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,6 @@ def shutdown_server(server_info, timeout=5, log=None):

url = server_info["url"]
pid = server_info["pid"]

try:
shutdown_url = urljoin(url, "api/shutdown")
if log:
Expand Down Expand Up @@ -2891,7 +2890,11 @@ def list_running_servers(runtime_dir=None, log=None):
for file_name in os.listdir(runtime_dir):
if re.match("jpserver-(.+).json", file_name):
with open(os.path.join(runtime_dir, file_name), encoding="utf-8") as f:
info = json.load(f)
# Handle race condition where file is being written.
try:
info = json.load(f)
except json.JSONDecodeError:
continue

# Simple check whether that process is really still running
# Also remove leftover files from IPython 2.x without a pid field
Expand Down
17 changes: 16 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ dependencies = ["coverage", "pytest-cov"]
[tool.hatch.envs.cov.env-vars]
ARGS = "-vv --cov jupyter_server --cov-branch --cov-report term-missing:skip-covered"
[tool.hatch.envs.cov.scripts]
test = "python -m pytest $ARGS --cov-fail-under 70 {args}"
test = "python -m pytest $ARGS --cov-fail-under 75 {args}"
integration = "python -m pytest $ARGS --integration_tests=true {args}"

[tool.hatch.version]
path = "jupyter_server/_version.py"
Expand Down Expand Up @@ -145,6 +146,20 @@ filterwarnings = [
"module:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"class .*\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]

[tool.jupyter-releaser.hooks]
before-build-python = ["npm install", "npm run build"]

Expand Down
65 changes: 64 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import os
import socket
import subprocess
import sys
import warnings
from pathlib import Path
from unittest.mock import patch

import pytest
from traitlets.tests.utils import check_help_all_output

from jupyter_server.utils import is_namespace_package, url_escape, url_unescape
from jupyter_server.utils import (
check_pid,
check_version,
is_namespace_package,
path2url,
run_sync_in_loop,
samefile_simple,
to_api_path,
unix_socket_in_use,
url2path,
url_escape,
url_unescape,
)


def test_help_output():
Expand Down Expand Up @@ -59,3 +76,49 @@ def test_is_namespace_package_no_spec():

assert is_namespace_package("dummy") is None
mocked_spec.assert_called_once_with("dummy")


@pytest.mark.skipif(os.name == "nt", reason="Paths are annoying on Windows")
def test_path_utils(tmp_path):
path = str(tmp_path)
assert os.path.basename(path2url(path)) == os.path.basename(path)

url = path2url(path)
assert path.endswith(url2path(url))

assert samefile_simple(path, path)

assert to_api_path(path, os.path.dirname(path)) == os.path.basename(path)


def test_check_version():
assert check_version("1.0.2", "1.0.1")
assert not check_version("1.0.0", "1.0.1")
assert check_version(1.0, "1.0.1")


def test_check_pid():
proc = subprocess.Popen([sys.executable])
proc.kill()
proc.wait()
check_pid(proc.pid)


async def test_run_sync_in_loop():
async def foo():
pass

with warnings.catch_warnings():
warnings.simplefilter("ignore")
await run_sync_in_loop(foo())


@pytest.mark.skipif(os.name != "posix", reason="Requires unix sockets")
def test_unix_socket_in_use(tmp_path):
root_tmp_dir = Path("/tmp").resolve()
server_address = os.path.join(root_tmp_dir, os.path.basename(tmp_path))
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(server_address)
sock.listen(0)
assert unix_socket_in_use(server_address)
sock.close()
37 changes: 31 additions & 6 deletions tests/unix_sockets/test_serverapp_integration.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import os
import stat
import subprocess
import sys
import time

import pytest

from jupyter_server.serverapp import list_running_servers, shutdown_server
from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path

# Skip this module if on Windows. Unix sockets are not available on Windows.
pytestmark = pytest.mark.skipif(
sys.platform.startswith("win"), reason="Unix sockets are not available on Windows."
)

import os
import subprocess
import time

from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path


def _cleanup_process(proc):
proc.wait()
Expand Down Expand Up @@ -171,3 +171,28 @@ def test_launch_socket_collision(jp_unix_socket_file):
_ensure_stopped()

_cleanup_process(p1)


@pytest.mark.integration_test
def test_shutdown_server(jp_environ):
# Start a server in another process
# Stop that server
import subprocess

from jupyter_client.connect import LocalPortCache

port = LocalPortCache().find_available_port("localhost")
p = subprocess.Popen(["jupyter-server", f"--port={port}"])
servers = []
while 1:
servers = list(list_running_servers())
if len(servers):
break
time.sleep(0.1)
while 1:
try:
shutdown_server(servers[0])
break
except ConnectionRefusedError:
time.sleep(0.1)
p.wait()

0 comments on commit 81535d4

Please sign in to comment.