Skip to content

Commit 81535d4

Browse files
authored
More coverage (#1067)
1 parent ca3c5b2 commit 81535d4

File tree

7 files changed

+123
-29
lines changed

7 files changed

+123
-29
lines changed

.github/workflows/integration-tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ jobs:
2727
pip check
2828
- name: Run the tests
2929
run: |
30-
pytest -vv --integration_tests=true tests
30+
hatch run cov:integration
31+
3132
3233
integration_check: # This job does nothing and is only used for the branch protection
3334
if: always()

.github/workflows/python-tests.yml

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@ jobs:
3636
run: |
3737
sudo apt-get update
3838
sudo apt-get install texlive-plain-generic inkscape texlive-xetex
39-
sudo apt-get install xvfb x11-utils libxkbcommon-x11-0
40-
pip install pandoc
39+
sudo apt-get install xvfb x11-utils libxkbcommon-x11-0 pandoc
4140
- name: Run the tests
4241
if: ${{ !startsWith(matrix.python-version, 'pypy') && !startsWith(matrix.os, 'windows') }}
43-
run: hatch run cov:test -W default || hatch run cov:test -W default --lf
42+
run: hatch run cov:test -W default || hatch run test:test -W default --lf
4443
- name: Run the tests on pypy and windows
4544
if: ${{ startsWith(matrix.python-version, 'pypy') || startsWith(matrix.os, 'windows') }}
4645
run: hatch run test:test -W default || hatch run test:test -W default --lf
@@ -71,20 +70,8 @@ jobs:
7170
runs-on: ubuntu-latest
7271
steps:
7372
- uses: actions/checkout@v3
74-
- uses: actions/setup-python@v4
75-
- uses: pre-commit/[email protected]
76-
with:
77-
extra_args: --all-files --hook-stage=manual
78-
- name: Help message if pre-commit fail
79-
if: ${{ failure() }}
80-
run: |
81-
echo "You can install pre-commit hooks to automatically run formatting"
82-
echo "on each commit with:"
83-
echo " pre-commit install"
84-
echo "or you can run by hand on staged files with"
85-
echo " pre-commit run"
86-
echo "or after-the-fact on already committed files with"
87-
echo " pre-commit run --all-files --hook-stage=manual"
73+
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
74+
- uses: jupyterlab/maintainer-tools/.github/actions/pre-commit@v1
8875

8976
test_docs:
9077
name: Test Docs

codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ coverage:
33
project:
44
default:
55
target: auto
6-
threshold: 10
6+
threshold: 1
77
patch:
88
default:
99
target: 0%

jupyter_server/serverapp.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,6 @@ def shutdown_server(server_info, timeout=5, log=None):
510510

511511
url = server_info["url"]
512512
pid = server_info["pid"]
513-
514513
try:
515514
shutdown_url = urljoin(url, "api/shutdown")
516515
if log:
@@ -2891,7 +2890,11 @@ def list_running_servers(runtime_dir=None, log=None):
28912890
for file_name in os.listdir(runtime_dir):
28922891
if re.match("jpserver-(.+).json", file_name):
28932892
with open(os.path.join(runtime_dir, file_name), encoding="utf-8") as f:
2894-
info = json.load(f)
2893+
# Handle race condition where file is being written.
2894+
try:
2895+
info = json.load(f)
2896+
except json.JSONDecodeError:
2897+
continue
28952898

28962899
# Simple check whether that process is really still running
28972900
# Also remove leftover files from IPython 2.x without a pid field

pyproject.toml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ dependencies = ["coverage", "pytest-cov"]
104104
[tool.hatch.envs.cov.env-vars]
105105
ARGS = "-vv --cov jupyter_server --cov-branch --cov-report term-missing:skip-covered"
106106
[tool.hatch.envs.cov.scripts]
107-
test = "python -m pytest $ARGS --cov-fail-under 70 {args}"
107+
test = "python -m pytest $ARGS --cov-fail-under 75 {args}"
108+
integration = "python -m pytest $ARGS --integration_tests=true {args}"
108109

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

149+
[tool.coverage.report]
150+
exclude_lines = [
151+
"pragma: no cover",
152+
"def __repr__",
153+
"if self.debug:",
154+
"if settings.DEBUG",
155+
"raise AssertionError",
156+
"raise NotImplementedError",
157+
"if 0:",
158+
"if __name__ == .__main__.:",
159+
"class .*\bProtocol\\):",
160+
"@(abc\\.)?abstractmethod",
161+
]
162+
148163
[tool.jupyter-releaser.hooks]
149164
before-build-python = ["npm install", "npm run build"]
150165

tests/test_utils.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
1+
import os
2+
import socket
3+
import subprocess
4+
import sys
5+
import warnings
16
from pathlib import Path
27
from unittest.mock import patch
38

49
import pytest
510
from traitlets.tests.utils import check_help_all_output
611

7-
from jupyter_server.utils import is_namespace_package, url_escape, url_unescape
12+
from jupyter_server.utils import (
13+
check_pid,
14+
check_version,
15+
is_namespace_package,
16+
path2url,
17+
run_sync_in_loop,
18+
samefile_simple,
19+
to_api_path,
20+
unix_socket_in_use,
21+
url2path,
22+
url_escape,
23+
url_unescape,
24+
)
825

926

1027
def test_help_output():
@@ -59,3 +76,49 @@ def test_is_namespace_package_no_spec():
5976

6077
assert is_namespace_package("dummy") is None
6178
mocked_spec.assert_called_once_with("dummy")
79+
80+
81+
@pytest.mark.skipif(os.name == "nt", reason="Paths are annoying on Windows")
82+
def test_path_utils(tmp_path):
83+
path = str(tmp_path)
84+
assert os.path.basename(path2url(path)) == os.path.basename(path)
85+
86+
url = path2url(path)
87+
assert path.endswith(url2path(url))
88+
89+
assert samefile_simple(path, path)
90+
91+
assert to_api_path(path, os.path.dirname(path)) == os.path.basename(path)
92+
93+
94+
def test_check_version():
95+
assert check_version("1.0.2", "1.0.1")
96+
assert not check_version("1.0.0", "1.0.1")
97+
assert check_version(1.0, "1.0.1")
98+
99+
100+
def test_check_pid():
101+
proc = subprocess.Popen([sys.executable])
102+
proc.kill()
103+
proc.wait()
104+
check_pid(proc.pid)
105+
106+
107+
async def test_run_sync_in_loop():
108+
async def foo():
109+
pass
110+
111+
with warnings.catch_warnings():
112+
warnings.simplefilter("ignore")
113+
await run_sync_in_loop(foo())
114+
115+
116+
@pytest.mark.skipif(os.name != "posix", reason="Requires unix sockets")
117+
def test_unix_socket_in_use(tmp_path):
118+
root_tmp_dir = Path("/tmp").resolve()
119+
server_address = os.path.join(root_tmp_dir, os.path.basename(tmp_path))
120+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
121+
sock.bind(server_address)
122+
sock.listen(0)
123+
assert unix_socket_in_use(server_address)
124+
sock.close()

tests/unix_sockets/test_serverapp_integration.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1+
import os
12
import stat
3+
import subprocess
24
import sys
5+
import time
36

47
import pytest
58

9+
from jupyter_server.serverapp import list_running_servers, shutdown_server
10+
from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path
11+
612
# Skip this module if on Windows. Unix sockets are not available on Windows.
713
pytestmark = pytest.mark.skipif(
814
sys.platform.startswith("win"), reason="Unix sockets are not available on Windows."
915
)
1016

11-
import os
12-
import subprocess
13-
import time
14-
15-
from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path
16-
1717

1818
def _cleanup_process(proc):
1919
proc.wait()
@@ -171,3 +171,28 @@ def test_launch_socket_collision(jp_unix_socket_file):
171171
_ensure_stopped()
172172

173173
_cleanup_process(p1)
174+
175+
176+
@pytest.mark.integration_test
177+
def test_shutdown_server(jp_environ):
178+
# Start a server in another process
179+
# Stop that server
180+
import subprocess
181+
182+
from jupyter_client.connect import LocalPortCache
183+
184+
port = LocalPortCache().find_available_port("localhost")
185+
p = subprocess.Popen(["jupyter-server", f"--port={port}"])
186+
servers = []
187+
while 1:
188+
servers = list(list_running_servers())
189+
if len(servers):
190+
break
191+
time.sleep(0.1)
192+
while 1:
193+
try:
194+
shutdown_server(servers[0])
195+
break
196+
except ConnectionRefusedError:
197+
time.sleep(0.1)
198+
p.wait()

0 commit comments

Comments
 (0)