124 lines
4.1 KiB
Python
124 lines
4.1 KiB
Python
|
"""Tests for kernel connection utilities"""
|
||
|
|
||
|
# Copyright (c) IPython Development Team.
|
||
|
# Distributed under the terms of the Modified BSD License.
|
||
|
|
||
|
import errno
|
||
|
import json
|
||
|
import os
|
||
|
from unittest.mock import patch
|
||
|
|
||
|
import pytest
|
||
|
import zmq
|
||
|
|
||
|
from traitlets.config import Config
|
||
|
from ipython_genutils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
|
||
|
from ipython_genutils.py3compat import str_to_bytes
|
||
|
from ipykernel import connect
|
||
|
from ipykernel.kernelapp import IPKernelApp
|
||
|
|
||
|
|
||
|
sample_info = dict(ip='1.2.3.4', transport='ipc',
|
||
|
shell_port=1, hb_port=2, iopub_port=3, stdin_port=4, control_port=5,
|
||
|
key=b'abc123', signature_scheme='hmac-md5',
|
||
|
)
|
||
|
|
||
|
|
||
|
class DummyKernelApp(IPKernelApp):
|
||
|
def _default_shell_port(self):
|
||
|
return 0
|
||
|
|
||
|
def initialize(self, argv=[]):
|
||
|
self.init_profile_dir()
|
||
|
self.init_connection_file()
|
||
|
|
||
|
|
||
|
def test_get_connection_file():
|
||
|
cfg = Config()
|
||
|
with TemporaryWorkingDirectory() as d:
|
||
|
cfg.ProfileDir.location = d
|
||
|
cf = 'kernel.json'
|
||
|
app = DummyKernelApp(config=cfg, connection_file=cf)
|
||
|
app.initialize()
|
||
|
|
||
|
profile_cf = os.path.join(app.connection_dir, cf)
|
||
|
assert profile_cf == app.abs_connection_file
|
||
|
with open(profile_cf, 'w') as f:
|
||
|
f.write("{}")
|
||
|
assert os.path.exists(profile_cf)
|
||
|
assert connect.get_connection_file(app) == profile_cf
|
||
|
|
||
|
app.connection_file = cf
|
||
|
assert connect.get_connection_file(app) == profile_cf
|
||
|
|
||
|
|
||
|
def test_get_connection_info():
|
||
|
with TemporaryDirectory() as d:
|
||
|
cf = os.path.join(d, 'kernel.json')
|
||
|
connect.write_connection_file(cf, **sample_info)
|
||
|
json_info = connect.get_connection_info(cf)
|
||
|
info = connect.get_connection_info(cf, unpack=True)
|
||
|
assert isinstance(json_info, str)
|
||
|
|
||
|
sub_info = {k:v for k,v in info.items() if k in sample_info}
|
||
|
assert sub_info == sample_info
|
||
|
|
||
|
info2 = json.loads(json_info)
|
||
|
info2['key'] = str_to_bytes(info2['key'])
|
||
|
sub_info2 = {k:v for k,v in info.items() if k in sample_info}
|
||
|
assert sub_info2 == sample_info
|
||
|
|
||
|
|
||
|
def test_port_bind_failure_raises(request):
|
||
|
cfg = Config()
|
||
|
with TemporaryWorkingDirectory() as d:
|
||
|
cfg.ProfileDir.location = d
|
||
|
cf = 'kernel.json'
|
||
|
app = DummyKernelApp(config=cfg, connection_file=cf)
|
||
|
request.addfinalizer(app.close)
|
||
|
app.initialize()
|
||
|
with patch.object(app, '_try_bind_socket') as mock_try_bind:
|
||
|
mock_try_bind.side_effect = zmq.ZMQError(-100, "fails for unknown error types")
|
||
|
with pytest.raises(zmq.ZMQError):
|
||
|
app.init_sockets()
|
||
|
assert mock_try_bind.call_count == 1
|
||
|
|
||
|
|
||
|
def test_port_bind_failure_recovery(request):
|
||
|
try:
|
||
|
errno.WSAEADDRINUSE
|
||
|
except AttributeError:
|
||
|
# Fake windows address in-use code
|
||
|
p = patch.object(errno, 'WSAEADDRINUSE', 12345, create=True)
|
||
|
p.start()
|
||
|
request.addfinalizer(p.stop)
|
||
|
|
||
|
cfg = Config()
|
||
|
with TemporaryWorkingDirectory() as d:
|
||
|
cfg.ProfileDir.location = d
|
||
|
cf = 'kernel.json'
|
||
|
app = DummyKernelApp(config=cfg, connection_file=cf)
|
||
|
request.addfinalizer(app.close)
|
||
|
app.initialize()
|
||
|
with patch.object(app, '_try_bind_socket') as mock_try_bind:
|
||
|
mock_try_bind.side_effect = [
|
||
|
zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind unix"),
|
||
|
zmq.ZMQError(errno.WSAEADDRINUSE, "fails for non-bind windows")
|
||
|
] + [0] * 100
|
||
|
# Shouldn't raise anything as retries will kick in
|
||
|
app.init_sockets()
|
||
|
|
||
|
def test_port_bind_failure_gives_up_retries(request):
|
||
|
cfg = Config()
|
||
|
with TemporaryWorkingDirectory() as d:
|
||
|
cfg.ProfileDir.location = d
|
||
|
cf = 'kernel.json'
|
||
|
app = DummyKernelApp(config=cfg, connection_file=cf)
|
||
|
request.addfinalizer(app.close)
|
||
|
app.initialize()
|
||
|
with patch.object(app, '_try_bind_socket') as mock_try_bind:
|
||
|
mock_try_bind.side_effect = zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind")
|
||
|
with pytest.raises(zmq.ZMQError):
|
||
|
app.init_sockets()
|
||
|
assert mock_try_bind.call_count == 100
|