Fix/1531 add unix domain socket (#1629)
mpd: add functionality for unix domain socket (Fixes #1531) The Hostname config type now supports a Unix socket path prefixed with `unix:`
This commit is contained in:
parent
9e31e76a05
commit
b4c98ec4a5
@ -13,6 +13,9 @@ Bug fix release.
|
|||||||
- MPD: Added ``idle`` to the list of available commands.
|
- MPD: Added ``idle`` to the list of available commands.
|
||||||
(Fixes: :issue:`1593`, PR: :issue:`1597`)
|
(Fixes: :issue:`1593`, PR: :issue:`1597`)
|
||||||
|
|
||||||
|
- MPD: Added Unix domain sockets for binding MPD to.
|
||||||
|
(Fixes: :issue:`1531`, PR: :issue:`1629`)
|
||||||
|
|
||||||
|
|
||||||
v2.1.0 (2017-01-02)
|
v2.1.0 (2017-01-02)
|
||||||
===================
|
===================
|
||||||
|
|||||||
@ -63,7 +63,8 @@ See :ref:`config` for general help on configuring Mopidy.
|
|||||||
|
|
||||||
.. confval:: mpd/hostname
|
.. confval:: mpd/hostname
|
||||||
|
|
||||||
Which address the MPD server should bind to.
|
Which address the MPD server should bind to. This can be a network address
|
||||||
|
or the path to a Unix socket.
|
||||||
|
|
||||||
``127.0.0.1``
|
``127.0.0.1``
|
||||||
Listens only on the IPv4 loopback interface
|
Listens only on the IPv4 loopback interface
|
||||||
@ -73,6 +74,9 @@ See :ref:`config` for general help on configuring Mopidy.
|
|||||||
Listens on all IPv4 interfaces
|
Listens on all IPv4 interfaces
|
||||||
``::``
|
``::``
|
||||||
Listens on all interfaces, both IPv4 and IPv6
|
Listens on all interfaces, both IPv4 and IPv6
|
||||||
|
``unix:/path/to/unix/socket.sock``
|
||||||
|
Listen on the Unix socket at the specified path. Must be prefixed with
|
||||||
|
``unix:``
|
||||||
|
|
||||||
.. confval:: mpd/port
|
.. confval:: mpd/port
|
||||||
|
|
||||||
|
|||||||
@ -258,6 +258,9 @@ class Hostname(ConfigValue):
|
|||||||
validators.validate_required(value, self._required)
|
validators.validate_required(value, self._required)
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
return None
|
return None
|
||||||
|
socket_path = path.get_unix_socket_path(value)
|
||||||
|
if socket_path is not None:
|
||||||
|
return 'unix:' + Path(not self._required).deserialize(socket_path)
|
||||||
try:
|
try:
|
||||||
socket.getaddrinfo(value, None)
|
socket.getaddrinfo(value, None)
|
||||||
except socket.error:
|
except socket.error:
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import os
|
|||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
import mopidy
|
import mopidy
|
||||||
|
|
||||||
from mopidy import audio, backend, mixer
|
from mopidy import audio, backend, mixer
|
||||||
from mopidy.audio import PlaybackState
|
from mopidy.audio import PlaybackState
|
||||||
from mopidy.core.history import HistoryController
|
from mopidy.core.history import HistoryController
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
import errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
@ -9,13 +10,20 @@ import threading
|
|||||||
|
|
||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
from mopidy.internal import encoding
|
from mopidy.internal import encoding, path, validation
|
||||||
from mopidy.internal.gi import GObject
|
from mopidy.internal.gi import GObject
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_unix_socket(sock):
|
||||||
|
"""Check if the provided socket is a Unix domain socket"""
|
||||||
|
if hasattr(socket, 'AF_UNIX'):
|
||||||
|
return sock.family == socket.AF_UNIX
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ShouldRetrySocketCall(Exception):
|
class ShouldRetrySocketCall(Exception):
|
||||||
|
|
||||||
"""Indicate that attempted socket call should be retried"""
|
"""Indicate that attempted socket call should be retried"""
|
||||||
@ -40,7 +48,7 @@ def try_ipv6_socket():
|
|||||||
has_ipv6 = try_ipv6_socket()
|
has_ipv6 = try_ipv6_socket()
|
||||||
|
|
||||||
|
|
||||||
def create_socket():
|
def create_tcp_socket():
|
||||||
"""Create a TCP socket with or without IPv6 depending on system support"""
|
"""Create a TCP socket with or without IPv6 depending on system support"""
|
||||||
if has_ipv6:
|
if has_ipv6:
|
||||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||||
@ -57,6 +65,19 @@ def create_socket():
|
|||||||
return sock
|
return sock
|
||||||
|
|
||||||
|
|
||||||
|
def create_unix_socket():
|
||||||
|
"""Create a Unix domain socket"""
|
||||||
|
return socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
|
||||||
|
def format_socket_name(sock):
|
||||||
|
"""Format the connection string for the given socket"""
|
||||||
|
if is_unix_socket(sock):
|
||||||
|
return '%s' % sock.getsockname()
|
||||||
|
else:
|
||||||
|
return '[%s]:%s' % sock.getsockname()[:2]
|
||||||
|
|
||||||
|
|
||||||
def format_hostname(hostname):
|
def format_hostname(hostname):
|
||||||
"""Format hostname for display."""
|
"""Format hostname for display."""
|
||||||
if (has_ipv6 and re.match(r'\d+.\d+.\d+.\d+', hostname) is not None):
|
if (has_ipv6 and re.match(r'\d+.\d+.\d+.\d+', hostname) is not None):
|
||||||
@ -76,17 +97,42 @@ class Server(object):
|
|||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.server_socket = self.create_server_socket(host, port)
|
self.server_socket = self.create_server_socket(host, port)
|
||||||
|
|
||||||
self.register_server_socket(self.server_socket.fileno())
|
self.watcher = self.register_server_socket(self.server_socket.fileno())
|
||||||
|
|
||||||
def create_server_socket(self, host, port):
|
def create_server_socket(self, host, port):
|
||||||
sock = create_socket()
|
socket_path = path.get_unix_socket_path(host)
|
||||||
|
if socket_path is not None: # host is a path so use unix socket
|
||||||
|
sock = create_unix_socket()
|
||||||
|
sock.bind(socket_path)
|
||||||
|
else:
|
||||||
|
# ensure the port is supplied
|
||||||
|
validation.check_integer(port)
|
||||||
|
sock = create_tcp_socket()
|
||||||
|
sock.bind((host, port))
|
||||||
|
|
||||||
sock.setblocking(False)
|
sock.setblocking(False)
|
||||||
sock.bind((host, port))
|
|
||||||
sock.listen(1)
|
sock.listen(1)
|
||||||
return sock
|
return sock
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
GObject.source_remove(self.watcher)
|
||||||
|
if is_unix_socket(self.server_socket):
|
||||||
|
unix_socket_path = self.server_socket.getsockname()
|
||||||
|
else:
|
||||||
|
unix_socket_path = None
|
||||||
|
|
||||||
|
self.server_socket.shutdown(socket.SHUT_RDWR)
|
||||||
|
self.server_socket.close()
|
||||||
|
|
||||||
|
# clean up the socket file
|
||||||
|
if unix_socket_path is not None:
|
||||||
|
os.unlink(unix_socket_path)
|
||||||
|
|
||||||
def register_server_socket(self, fileno):
|
def register_server_socket(self, fileno):
|
||||||
GObject.io_add_watch(fileno, GObject.IO_IN, self.handle_connection)
|
return GObject.io_add_watch(
|
||||||
|
fileno,
|
||||||
|
GObject.IO_IN,
|
||||||
|
self.handle_connection)
|
||||||
|
|
||||||
def handle_connection(self, fd, flags):
|
def handle_connection(self, fd, flags):
|
||||||
try:
|
try:
|
||||||
@ -102,7 +148,10 @@ class Server(object):
|
|||||||
|
|
||||||
def accept_connection(self):
|
def accept_connection(self):
|
||||||
try:
|
try:
|
||||||
return self.server_socket.accept()
|
sock, addr = self.server_socket.accept()
|
||||||
|
if is_unix_socket(sock):
|
||||||
|
addr = (sock.getsockname(), None)
|
||||||
|
return sock, addr
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.errno in (errno.EAGAIN, errno.EINTR):
|
if e.errno in (errno.EAGAIN, errno.EINTR):
|
||||||
raise ShouldRetrySocketCall
|
raise ShouldRetrySocketCall
|
||||||
@ -117,7 +166,9 @@ class Server(object):
|
|||||||
|
|
||||||
def reject_connection(self, sock, addr):
|
def reject_connection(self, sock, addr):
|
||||||
# FIXME provide more context in logging?
|
# FIXME provide more context in logging?
|
||||||
logger.warning('Rejected connection from [%s]:%s', addr[0], addr[1])
|
logger.warning(
|
||||||
|
'Rejected connection from %s',
|
||||||
|
format_socket_name(sock))
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except socket.error:
|
except socket.error:
|
||||||
@ -142,7 +193,7 @@ class Connection(object):
|
|||||||
|
|
||||||
self.host, self.port = addr[:2] # IPv6 has larger addr
|
self.host, self.port = addr[:2] # IPv6 has larger addr
|
||||||
|
|
||||||
self.sock = sock
|
self._sock = sock
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.protocol_kwargs = protocol_kwargs
|
self.protocol_kwargs = protocol_kwargs
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
@ -180,7 +231,7 @@ class Connection(object):
|
|||||||
self.disable_send()
|
self.disable_send()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sock.close()
|
self._sock.close()
|
||||||
except socket.error:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -195,7 +246,7 @@ class Connection(object):
|
|||||||
def send(self, data):
|
def send(self, data):
|
||||||
"""Send data to client, return any unsent data."""
|
"""Send data to client, return any unsent data."""
|
||||||
try:
|
try:
|
||||||
sent = self.sock.send(data)
|
sent = self._sock.send(data)
|
||||||
return data[sent:]
|
return data[sent:]
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.errno in (errno.EWOULDBLOCK, errno.EINTR):
|
if e.errno in (errno.EWOULDBLOCK, errno.EINTR):
|
||||||
@ -226,7 +277,7 @@ class Connection(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.recv_id = GObject.io_add_watch(
|
self.recv_id = GObject.io_add_watch(
|
||||||
self.sock.fileno(),
|
self._sock.fileno(),
|
||||||
GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP,
|
GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP,
|
||||||
self.recv_callback)
|
self.recv_callback)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
@ -244,7 +295,7 @@ class Connection(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.send_id = GObject.io_add_watch(
|
self.send_id = GObject.io_add_watch(
|
||||||
self.sock.fileno(),
|
self._sock.fileno(),
|
||||||
GObject.IO_OUT | GObject.IO_ERR | GObject.IO_HUP,
|
GObject.IO_OUT | GObject.IO_ERR | GObject.IO_HUP,
|
||||||
self.send_callback)
|
self.send_callback)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
@ -263,7 +314,7 @@ class Connection(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = self.sock.recv(4096)
|
data = self._sock.recv(4096)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.errno not in (errno.EWOULDBLOCK, errno.EINTR):
|
if e.errno not in (errno.EWOULDBLOCK, errno.EINTR):
|
||||||
self.stop('Unexpected client error: %s' % e)
|
self.stop('Unexpected client error: %s' % e)
|
||||||
@ -304,6 +355,9 @@ class Connection(object):
|
|||||||
self.stop('Client inactive for %ds; closing connection' % self.timeout)
|
self.stop('Client inactive for %ds; closing connection' % self.timeout)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return format_socket_name(self._sock)
|
||||||
|
|
||||||
|
|
||||||
class LineProtocol(pykka.ThreadingActor):
|
class LineProtocol(pykka.ThreadingActor):
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import stat
|
import stat
|
||||||
import string
|
import string
|
||||||
import threading
|
import threading
|
||||||
@ -47,6 +48,13 @@ def get_or_create_file(file_path, mkdir=True, content=None):
|
|||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_unix_socket_path(socket_path):
|
||||||
|
match = re.search('^unix:(.*)', socket_path)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
|
||||||
def path_to_uri(path):
|
def path_to_uri(path):
|
||||||
"""
|
"""
|
||||||
Convert OS specific path to file:// URI.
|
Convert OS specific path to file:// URI.
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import mopidy
|
import mopidy
|
||||||
|
|
||||||
from mopidy import compat, local, models
|
from mopidy import compat, local, models
|
||||||
from mopidy.internal import storage as internal_storage
|
from mopidy.internal import storage as internal_storage
|
||||||
from mopidy.internal import timer
|
from mopidy.internal import timer
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class Extension(ext.Extension):
|
|||||||
def get_config_schema(self):
|
def get_config_schema(self):
|
||||||
schema = super(Extension, self).get_config_schema()
|
schema = super(Extension, self).get_config_schema()
|
||||||
schema['hostname'] = config.Hostname()
|
schema['hostname'] = config.Hostname()
|
||||||
schema['port'] = config.Port()
|
schema['port'] = config.Port(optional=True)
|
||||||
schema['password'] = config.Secret(optional=True)
|
schema['password'] = config.Secret(optional=True)
|
||||||
schema['max_connections'] = config.Integer(minimum=1)
|
schema['max_connections'] = config.Integer(minimum=1)
|
||||||
schema['connection_timeout'] = config.Integer(minimum=1)
|
schema['connection_timeout'] = config.Integer(minimum=1)
|
||||||
|
|||||||
@ -41,11 +41,11 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
|
|||||||
self.zeroconf_name = config['mpd']['zeroconf']
|
self.zeroconf_name = config['mpd']['zeroconf']
|
||||||
self.zeroconf_service = None
|
self.zeroconf_service = None
|
||||||
|
|
||||||
self._setup_server(config, core)
|
self.server = self._setup_server(config, core)
|
||||||
|
|
||||||
def _setup_server(self, config, core):
|
def _setup_server(self, config, core):
|
||||||
try:
|
try:
|
||||||
network.Server(
|
server = network.Server(
|
||||||
self.hostname, self.port,
|
self.hostname, self.port,
|
||||||
protocol=session.MpdSession,
|
protocol=session.MpdSession,
|
||||||
protocol_kwargs={
|
protocol_kwargs={
|
||||||
@ -60,10 +60,15 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
|
|||||||
'MPD server startup failed: %s' %
|
'MPD server startup failed: %s' %
|
||||||
encoding.locale_decode(error))
|
encoding.locale_decode(error))
|
||||||
|
|
||||||
logger.info('MPD server running at [%s]:%s', self.hostname, self.port)
|
logger.info(
|
||||||
|
'MPD server running at %s',
|
||||||
|
network.format_socket_name(server.server_socket))
|
||||||
|
|
||||||
|
return server
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
if self.zeroconf_name:
|
if (self.zeroconf_name and not
|
||||||
|
network.is_unix_socket(self.server.server_socket)):
|
||||||
self.zeroconf_service = zeroconf.Zeroconf(
|
self.zeroconf_service = zeroconf.Zeroconf(
|
||||||
name=self.zeroconf_name,
|
name=self.zeroconf_name,
|
||||||
stype='_mpd._tcp',
|
stype='_mpd._tcp',
|
||||||
@ -75,6 +80,7 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
|
|||||||
self.zeroconf_service.unpublish()
|
self.zeroconf_service.unpublish()
|
||||||
|
|
||||||
process.stop_actors_by_class(session.MpdSession)
|
process.stop_actors_by_class(session.MpdSession)
|
||||||
|
self.server.stop()
|
||||||
|
|
||||||
def on_event(self, event, **kwargs):
|
def on_event(self, event, **kwargs):
|
||||||
if event not in _CORE_EVENTS_TO_IDLE_SUBSYSTEMS:
|
if event not in _CORE_EVENTS_TO_IDLE_SUBSYSTEMS:
|
||||||
|
|||||||
@ -25,18 +25,19 @@ class MpdSession(network.LineProtocol):
|
|||||||
session=self, config=config, core=core, uri_map=uri_map)
|
session=self, config=config, core=core, uri_map=uri_map)
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
logger.info('New MPD connection from [%s]:%s', self.host, self.port)
|
logger.info('New MPD connection from %s', self.connection)
|
||||||
self.send_lines(['OK MPD %s' % protocol.VERSION])
|
self.send_lines(['OK MPD %s' % protocol.VERSION])
|
||||||
|
|
||||||
def on_line_received(self, line):
|
def on_line_received(self, line):
|
||||||
logger.debug('Request from [%s]:%s: %s', self.host, self.port, line)
|
logger.debug('Request from [%s]: %s', self.connection, line)
|
||||||
|
|
||||||
response = self.dispatcher.handle_request(line)
|
response = self.dispatcher.handle_request(line)
|
||||||
if not response:
|
if not response:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Response to [%s]:%s: %s', self.host, self.port,
|
'Response to [%s]: %s',
|
||||||
|
self.connection,
|
||||||
formatting.indent(self.terminator.join(response)))
|
formatting.indent(self.terminator.join(response)))
|
||||||
|
|
||||||
self.send_lines(response)
|
self.send_lines(response)
|
||||||
|
|||||||
@ -344,6 +344,12 @@ class HostnameTest(unittest.TestCase):
|
|||||||
self.assertIsNone(value.deserialize(' '))
|
self.assertIsNone(value.deserialize(' '))
|
||||||
self.assertEqual(0, getaddrinfo_mock.call_count)
|
self.assertEqual(0, getaddrinfo_mock.call_count)
|
||||||
|
|
||||||
|
@mock.patch('mopidy.internal.path.expand_path')
|
||||||
|
def test_deserialize_with_unix_socket(self, expand_path_mock):
|
||||||
|
value = types.Hostname()
|
||||||
|
self.assertIsNotNone(value.deserialize('unix:/tmp/mopidy.socket'))
|
||||||
|
expand_path_mock.assert_called_once_with('/tmp/mopidy.socket')
|
||||||
|
|
||||||
|
|
||||||
class PortTest(unittest.TestCase):
|
class PortTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import mock
|
|||||||
import pykka
|
import pykka
|
||||||
|
|
||||||
import mopidy
|
import mopidy
|
||||||
|
|
||||||
from mopidy.core import Core
|
from mopidy.core import Core
|
||||||
from mopidy.internal import models, storage, versioning
|
from mopidy.internal import models, storage, versioning
|
||||||
from mopidy.models import Track
|
from mopidy.models import Track
|
||||||
|
|
||||||
from tests import dummy_mixer
|
from tests import dummy_mixer
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import pykka
|
|||||||
|
|
||||||
from mopidy import core, mixer
|
from mopidy import core, mixer
|
||||||
from mopidy.internal.models import MixerState
|
from mopidy.internal.models import MixerState
|
||||||
|
|
||||||
from tests import dummy_mixer
|
from tests import dummy_mixer
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -51,7 +51,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
network.Connection.__init__(
|
network.Connection.__init__(
|
||||||
self.mock, protocol, protocol_kwargs, sock, addr, sentinel.timeout)
|
self.mock, protocol, protocol_kwargs, sock, addr, sentinel.timeout)
|
||||||
self.assertEqual(sock, self.mock.sock)
|
self.assertEqual(sock, self.mock._sock)
|
||||||
self.assertEqual(protocol, self.mock.protocol)
|
self.assertEqual(protocol, self.mock.protocol)
|
||||||
self.assertEqual(protocol_kwargs, self.mock.protocol_kwargs)
|
self.assertEqual(protocol_kwargs, self.mock.protocol_kwargs)
|
||||||
self.assertEqual(sentinel.timeout, self.mock.timeout)
|
self.assertEqual(sentinel.timeout, self.mock.timeout)
|
||||||
@ -73,7 +73,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_disables_recv_send_and_timeout(self):
|
def test_stop_disables_recv_send_and_timeout(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.mock.disable_timeout.assert_called_once_with()
|
self.mock.disable_timeout.assert_called_once_with()
|
||||||
@ -83,24 +83,24 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_closes_socket(self):
|
def test_stop_closes_socket(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.mock.sock.close.assert_called_once_with()
|
self.mock._sock.close.assert_called_once_with()
|
||||||
|
|
||||||
def test_stop_closes_socket_error(self):
|
def test_stop_closes_socket_error(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.close.side_effect = socket.error
|
self.mock._sock.close.side_effect = socket.error
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.mock.sock.close.assert_called_once_with()
|
self.mock._sock.close.assert_called_once_with()
|
||||||
|
|
||||||
def test_stop_stops_actor(self):
|
def test_stop_stops_actor(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.mock.actor_ref.stop.assert_called_once_with(block=False)
|
self.mock.actor_ref.stop.assert_called_once_with(block=False)
|
||||||
@ -109,7 +109,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.actor_ref.stop.side_effect = pykka.ActorDeadError()
|
self.mock.actor_ref.stop.side_effect = pykka.ActorDeadError()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.mock.actor_ref.stop.assert_called_once_with(block=False)
|
self.mock.actor_ref.stop.assert_called_once_with(block=False)
|
||||||
@ -117,7 +117,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_sets_stopping_to_true(self):
|
def test_stop_sets_stopping_to_true(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.assertEqual(True, self.mock.stopping)
|
self.assertEqual(True, self.mock.stopping)
|
||||||
@ -125,17 +125,17 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_does_not_proceed_when_already_stopping(self):
|
def test_stop_does_not_proceed_when_already_stopping(self):
|
||||||
self.mock.stopping = True
|
self.mock.stopping = True
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
self.assertEqual(0, self.mock.actor_ref.stop.call_count)
|
self.assertEqual(0, self.mock.actor_ref.stop.call_count)
|
||||||
self.assertEqual(0, self.mock.sock.close.call_count)
|
self.assertEqual(0, self.mock._sock.close.call_count)
|
||||||
|
|
||||||
@patch.object(network.logger, 'log', new=Mock())
|
@patch.object(network.logger, 'log', new=Mock())
|
||||||
def test_stop_logs_reason(self):
|
def test_stop_logs_reason(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
network.logger.log.assert_called_once_with(
|
network.logger.log.assert_called_once_with(
|
||||||
@ -145,7 +145,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_logs_reason_with_level(self):
|
def test_stop_logs_reason_with_level(self):
|
||||||
self.mock.stopping = False
|
self.mock.stopping = False
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(
|
network.Connection.stop(
|
||||||
self.mock, sentinel.reason, level=sentinel.level)
|
self.mock, sentinel.reason, level=sentinel.level)
|
||||||
@ -156,7 +156,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
def test_stop_logs_that_it_is_calling_itself(self):
|
def test_stop_logs_that_it_is_calling_itself(self):
|
||||||
self.mock.stopping = True
|
self.mock.stopping = True
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.stop(self.mock, sentinel.reason)
|
network.Connection.stop(self.mock, sentinel.reason)
|
||||||
network.logger.log(any_int, any_unicode)
|
network.logger.log(any_int, any_unicode)
|
||||||
@ -164,8 +164,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
@patch.object(GObject, 'io_add_watch', new=Mock())
|
@patch.object(GObject, 'io_add_watch', new=Mock())
|
||||||
def test_enable_recv_registers_with_gobject(self):
|
def test_enable_recv_registers_with_gobject(self):
|
||||||
self.mock.recv_id = None
|
self.mock.recv_id = None
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.fileno.return_value = sentinel.fileno
|
self.mock._sock.fileno.return_value = sentinel.fileno
|
||||||
GObject.io_add_watch.return_value = sentinel.tag
|
GObject.io_add_watch.return_value = sentinel.tag
|
||||||
|
|
||||||
network.Connection.enable_recv(self.mock)
|
network.Connection.enable_recv(self.mock)
|
||||||
@ -177,7 +177,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
@patch.object(GObject, 'io_add_watch', new=Mock())
|
@patch.object(GObject, 'io_add_watch', new=Mock())
|
||||||
def test_enable_recv_already_registered(self):
|
def test_enable_recv_already_registered(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.recv_id = sentinel.tag
|
self.mock.recv_id = sentinel.tag
|
||||||
|
|
||||||
network.Connection.enable_recv(self.mock)
|
network.Connection.enable_recv(self.mock)
|
||||||
@ -185,7 +185,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_enable_recv_does_not_change_tag(self):
|
def test_enable_recv_does_not_change_tag(self):
|
||||||
self.mock.recv_id = sentinel.tag
|
self.mock.recv_id = sentinel.tag
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.enable_recv(self.mock)
|
network.Connection.enable_recv(self.mock)
|
||||||
self.assertEqual(sentinel.tag, self.mock.recv_id)
|
self.assertEqual(sentinel.tag, self.mock.recv_id)
|
||||||
@ -208,8 +208,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_enable_recv_on_closed_socket(self):
|
def test_enable_recv_on_closed_socket(self):
|
||||||
self.mock.recv_id = None
|
self.mock.recv_id = None
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.fileno.side_effect = socket.error(errno.EBADF, '')
|
self.mock._sock.fileno.side_effect = socket.error(errno.EBADF, '')
|
||||||
|
|
||||||
network.Connection.enable_recv(self.mock)
|
network.Connection.enable_recv(self.mock)
|
||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
@ -218,8 +218,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
@patch.object(GObject, 'io_add_watch', new=Mock())
|
@patch.object(GObject, 'io_add_watch', new=Mock())
|
||||||
def test_enable_send_registers_with_gobject(self):
|
def test_enable_send_registers_with_gobject(self):
|
||||||
self.mock.send_id = None
|
self.mock.send_id = None
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.fileno.return_value = sentinel.fileno
|
self.mock._sock.fileno.return_value = sentinel.fileno
|
||||||
GObject.io_add_watch.return_value = sentinel.tag
|
GObject.io_add_watch.return_value = sentinel.tag
|
||||||
|
|
||||||
network.Connection.enable_send(self.mock)
|
network.Connection.enable_send(self.mock)
|
||||||
@ -231,7 +231,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
@patch.object(GObject, 'io_add_watch', new=Mock())
|
@patch.object(GObject, 'io_add_watch', new=Mock())
|
||||||
def test_enable_send_already_registered(self):
|
def test_enable_send_already_registered(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.send_id = sentinel.tag
|
self.mock.send_id = sentinel.tag
|
||||||
|
|
||||||
network.Connection.enable_send(self.mock)
|
network.Connection.enable_send(self.mock)
|
||||||
@ -239,7 +239,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_enable_send_does_not_change_tag(self):
|
def test_enable_send_does_not_change_tag(self):
|
||||||
self.mock.send_id = sentinel.tag
|
self.mock.send_id = sentinel.tag
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
network.Connection.enable_send(self.mock)
|
network.Connection.enable_send(self.mock)
|
||||||
self.assertEqual(sentinel.tag, self.mock.send_id)
|
self.assertEqual(sentinel.tag, self.mock.send_id)
|
||||||
@ -262,8 +262,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_enable_send_on_closed_socket(self):
|
def test_enable_send_on_closed_socket(self):
|
||||||
self.mock.send_id = None
|
self.mock.send_id = None
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.fileno.side_effect = socket.error(errno.EBADF, '')
|
self.mock._sock.fileno.side_effect = socket.error(errno.EBADF, '')
|
||||||
|
|
||||||
network.Connection.enable_send(self.mock)
|
network.Connection.enable_send(self.mock)
|
||||||
self.assertEqual(None, self.mock.send_id)
|
self.assertEqual(None, self.mock.send_id)
|
||||||
@ -367,7 +367,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.assertEqual('', self.mock.send_buffer)
|
self.assertEqual('', self.mock.send_buffer)
|
||||||
|
|
||||||
def test_recv_callback_respects_io_err(self):
|
def test_recv_callback_respects_io_err(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
@ -375,7 +375,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_recv_callback_respects_io_hup(self):
|
def test_recv_callback_respects_io_hup(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
@ -383,7 +383,7 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_recv_callback_respects_io_hup_and_io_err(self):
|
def test_recv_callback_respects_io_hup_and_io_err(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
@ -392,8 +392,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_recv_callback_sends_data_to_actor(self):
|
def test_recv_callback_sends_data_to_actor(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.recv.return_value = 'data'
|
self.mock._sock.recv.return_value = 'data'
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
@ -402,8 +402,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
{'received': 'data'})
|
{'received': 'data'})
|
||||||
|
|
||||||
def test_recv_callback_handles_dead_actors(self):
|
def test_recv_callback_handles_dead_actors(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.recv.return_value = 'data'
|
self.mock._sock.recv.return_value = 'data'
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.actor_ref.tell.side_effect = pykka.ActorDeadError()
|
self.mock.actor_ref.tell.side_effect = pykka.ActorDeadError()
|
||||||
|
|
||||||
@ -412,38 +412,38 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_recv_callback_gets_no_data(self):
|
def test_recv_callback_gets_no_data(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.recv.return_value = ''
|
self.mock._sock.recv.return_value = ''
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
self.mock, sentinel.fd, GObject.IO_IN))
|
self.mock, sentinel.fd, GObject.IO_IN))
|
||||||
self.assertEqual(self.mock.mock_calls, [
|
self.assertEqual(self.mock.mock_calls, [
|
||||||
call.sock.recv(any_int),
|
call._sock.recv(any_int),
|
||||||
call.disable_recv(),
|
call.disable_recv(),
|
||||||
call.actor_ref.tell({'close': True}),
|
call.actor_ref.tell({'close': True}),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_recv_callback_recoverable_error(self):
|
def test_recv_callback_recoverable_error(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
for error in (errno.EWOULDBLOCK, errno.EINTR):
|
for error in (errno.EWOULDBLOCK, errno.EINTR):
|
||||||
self.mock.sock.recv.side_effect = socket.error(error, '')
|
self.mock._sock.recv.side_effect = socket.error(error, '')
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
self.mock, sentinel.fd, GObject.IO_IN))
|
self.mock, sentinel.fd, GObject.IO_IN))
|
||||||
self.assertEqual(0, self.mock.stop.call_count)
|
self.assertEqual(0, self.mock.stop.call_count)
|
||||||
|
|
||||||
def test_recv_callback_unrecoverable_error(self):
|
def test_recv_callback_unrecoverable_error(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.recv.side_effect = socket.error
|
self.mock._sock.recv.side_effect = socket.error
|
||||||
|
|
||||||
self.assertTrue(network.Connection.recv_callback(
|
self.assertTrue(network.Connection.recv_callback(
|
||||||
self.mock, sentinel.fd, GObject.IO_IN))
|
self.mock, sentinel.fd, GObject.IO_IN))
|
||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_send_callback_respects_io_err(self):
|
def test_send_callback_respects_io_err(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 1
|
self.mock._sock.send.return_value = 1
|
||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.send_buffer = ''
|
self.mock.send_buffer = ''
|
||||||
@ -453,8 +453,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_send_callback_respects_io_hup(self):
|
def test_send_callback_respects_io_hup(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 1
|
self.mock._sock.send.return_value = 1
|
||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.send_buffer = ''
|
self.mock.send_buffer = ''
|
||||||
@ -464,8 +464,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|
||||||
def test_send_callback_respects_io_hup_and_io_err(self):
|
def test_send_callback_respects_io_hup_and_io_err(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 1
|
self.mock._sock.send.return_value = 1
|
||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
self.mock.actor_ref = Mock()
|
self.mock.actor_ref = Mock()
|
||||||
self.mock.send_buffer = ''
|
self.mock.send_buffer = ''
|
||||||
@ -479,8 +479,8 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
self.mock.send_lock.acquire.return_value = True
|
self.mock.send_lock.acquire.return_value = True
|
||||||
self.mock.send_buffer = ''
|
self.mock.send_buffer = ''
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 0
|
self.mock._sock.send.return_value = 0
|
||||||
|
|
||||||
self.assertTrue(network.Connection.send_callback(
|
self.assertTrue(network.Connection.send_callback(
|
||||||
self.mock, sentinel.fd, GObject.IO_IN))
|
self.mock, sentinel.fd, GObject.IO_IN))
|
||||||
@ -491,13 +491,13 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
self.mock.send_lock.acquire.return_value = False
|
self.mock.send_lock.acquire.return_value = False
|
||||||
self.mock.send_buffer = ''
|
self.mock.send_buffer = ''
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 0
|
self.mock._sock.send.return_value = 0
|
||||||
|
|
||||||
self.assertTrue(network.Connection.send_callback(
|
self.assertTrue(network.Connection.send_callback(
|
||||||
self.mock, sentinel.fd, GObject.IO_IN))
|
self.mock, sentinel.fd, GObject.IO_IN))
|
||||||
self.mock.send_lock.acquire.assert_called_once_with(False)
|
self.mock.send_lock.acquire.assert_called_once_with(False)
|
||||||
self.assertEqual(0, self.mock.sock.send.call_count)
|
self.assertEqual(0, self.mock._sock.send.call_count)
|
||||||
|
|
||||||
def test_send_callback_sends_all_data(self):
|
def test_send_callback_sends_all_data(self):
|
||||||
self.mock.send_lock = Mock()
|
self.mock.send_lock = Mock()
|
||||||
@ -523,31 +523,31 @@ class ConnectionTest(unittest.TestCase):
|
|||||||
self.assertEqual('ta', self.mock.send_buffer)
|
self.assertEqual('ta', self.mock.send_buffer)
|
||||||
|
|
||||||
def test_send_recoverable_error(self):
|
def test_send_recoverable_error(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
for error in (errno.EWOULDBLOCK, errno.EINTR):
|
for error in (errno.EWOULDBLOCK, errno.EINTR):
|
||||||
self.mock.sock.send.side_effect = socket.error(error, '')
|
self.mock._sock.send.side_effect = socket.error(error, '')
|
||||||
|
|
||||||
network.Connection.send(self.mock, 'data')
|
network.Connection.send(self.mock, 'data')
|
||||||
self.assertEqual(0, self.mock.stop.call_count)
|
self.assertEqual(0, self.mock.stop.call_count)
|
||||||
|
|
||||||
def test_send_calls_socket_send(self):
|
def test_send_calls_socket_send(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 4
|
self.mock._sock.send.return_value = 4
|
||||||
|
|
||||||
self.assertEqual('', network.Connection.send(self.mock, 'data'))
|
self.assertEqual('', network.Connection.send(self.mock, 'data'))
|
||||||
self.mock.sock.send.assert_called_once_with('data')
|
self.mock._sock.send.assert_called_once_with('data')
|
||||||
|
|
||||||
def test_send_calls_socket_send_partial_send(self):
|
def test_send_calls_socket_send_partial_send(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.return_value = 2
|
self.mock._sock.send.return_value = 2
|
||||||
|
|
||||||
self.assertEqual('ta', network.Connection.send(self.mock, 'data'))
|
self.assertEqual('ta', network.Connection.send(self.mock, 'data'))
|
||||||
self.mock.sock.send.assert_called_once_with('data')
|
self.mock._sock.send.assert_called_once_with('data')
|
||||||
|
|
||||||
def test_send_unrecoverable_error(self):
|
def test_send_unrecoverable_error(self):
|
||||||
self.mock.sock = Mock(spec=socket.SocketType)
|
self.mock._sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.sock.send.side_effect = socket.error
|
self.mock._sock.send.side_effect = socket.error
|
||||||
|
|
||||||
self.assertEqual('', network.Connection.send(self.mock, 'data'))
|
self.assertEqual('', network.Connection.send(self.mock, 'data'))
|
||||||
self.mock.stop.assert_called_once_with(any_unicode)
|
self.mock.stop.assert_called_once_with(any_unicode)
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mock import Mock, patch, sentinel
|
from mock import Mock, patch, sentinel
|
||||||
|
|
||||||
|
from mopidy import exceptions
|
||||||
from mopidy.internal import network
|
from mopidy.internal import network
|
||||||
from mopidy.internal.gi import GObject
|
from mopidy.internal.gi import GObject
|
||||||
|
|
||||||
@ -22,6 +24,7 @@ class ServerTest(unittest.TestCase):
|
|||||||
self.mock, sentinel.host, sentinel.port, sentinel.protocol)
|
self.mock, sentinel.host, sentinel.port, sentinel.protocol)
|
||||||
self.mock.create_server_socket.assert_called_once_with(
|
self.mock.create_server_socket.assert_called_once_with(
|
||||||
sentinel.host, sentinel.port)
|
sentinel.host, sentinel.port)
|
||||||
|
self.mock.stop()
|
||||||
|
|
||||||
def test_init_calls_register_server(self):
|
def test_init_calls_register_server(self):
|
||||||
sock = Mock(spec=socket.SocketType)
|
sock = Mock(spec=socket.SocketType)
|
||||||
@ -55,40 +58,99 @@ class ServerTest(unittest.TestCase):
|
|||||||
self.assertEqual(sentinel.timeout, self.mock.timeout)
|
self.assertEqual(sentinel.timeout, self.mock.timeout)
|
||||||
self.assertEqual(sock, self.mock.server_socket)
|
self.assertEqual(sock, self.mock.server_socket)
|
||||||
|
|
||||||
@patch.object(network, 'create_socket', spec=socket.SocketType)
|
def test_create_server_socket_no_port(self):
|
||||||
def test_create_server_socket_sets_up_listener(self, create_socket):
|
with self.assertRaises(exceptions.ValidationError):
|
||||||
sock = create_socket.return_value
|
network.Server.create_server_socket(
|
||||||
|
self.mock, str(sentinel.host), None)
|
||||||
|
|
||||||
|
def test_create_server_socket_invalid_port(self):
|
||||||
|
with self.assertRaises(exceptions.ValidationError):
|
||||||
|
network.Server.create_server_socket(
|
||||||
|
self.mock, str(sentinel.host), str(sentinel.port))
|
||||||
|
|
||||||
|
@patch.object(network, 'create_tcp_socket', spec=socket.SocketType)
|
||||||
|
def test_create_server_socket_sets_up_listener(self, create_tcp_socket):
|
||||||
|
sock = create_tcp_socket.return_value
|
||||||
|
|
||||||
network.Server.create_server_socket(
|
network.Server.create_server_socket(
|
||||||
self.mock, sentinel.host, sentinel.port)
|
self.mock, str(sentinel.host), 1234)
|
||||||
sock.setblocking.assert_called_once_with(False)
|
sock.setblocking.assert_called_once_with(False)
|
||||||
sock.bind.assert_called_once_with((sentinel.host, sentinel.port))
|
sock.bind.assert_called_once_with((str(sentinel.host), 1234))
|
||||||
sock.listen.assert_called_once_with(any_int)
|
sock.listen.assert_called_once_with(any_int)
|
||||||
|
create_tcp_socket.assert_called_once()
|
||||||
|
|
||||||
@patch.object(network, 'create_socket', new=Mock())
|
@patch.object(network, 'create_unix_socket', spec=socket.SocketType)
|
||||||
|
def test_create_server_socket_sets_up_listener_unix(
|
||||||
|
self,
|
||||||
|
create_unix_socket):
|
||||||
|
sock = create_unix_socket.return_value
|
||||||
|
|
||||||
|
network.Server.create_server_socket(
|
||||||
|
self.mock, 'unix:' + str(sentinel.host), sentinel.port)
|
||||||
|
sock.setblocking.assert_called_once_with(False)
|
||||||
|
sock.bind.assert_called_once_with(str(sentinel.host))
|
||||||
|
sock.listen.assert_called_once_with(any_int)
|
||||||
|
create_unix_socket.assert_called_once()
|
||||||
|
|
||||||
|
@patch.object(network, 'create_tcp_socket', new=Mock())
|
||||||
def test_create_server_socket_fails(self):
|
def test_create_server_socket_fails(self):
|
||||||
network.create_socket.side_effect = socket.error
|
network.create_tcp_socket.side_effect = socket.error
|
||||||
with self.assertRaises(socket.error):
|
with self.assertRaises(socket.error):
|
||||||
network.Server.create_server_socket(
|
network.Server.create_server_socket(
|
||||||
self.mock, sentinel.host, sentinel.port)
|
self.mock, str(sentinel.host), 1234)
|
||||||
|
|
||||||
@patch.object(network, 'create_socket', new=Mock())
|
@patch.object(network, 'create_unix_socket', new=Mock())
|
||||||
|
def test_create_server_socket_fails_unix(self):
|
||||||
|
network.create_unix_socket.side_effect = socket.error
|
||||||
|
with self.assertRaises(socket.error):
|
||||||
|
network.Server.create_server_socket(
|
||||||
|
self.mock, 'unix:' + str(sentinel.host), sentinel.port)
|
||||||
|
|
||||||
|
@patch.object(network, 'create_tcp_socket', new=Mock())
|
||||||
def test_create_server_bind_fails(self):
|
def test_create_server_bind_fails(self):
|
||||||
sock = network.create_socket.return_value
|
sock = network.create_tcp_socket.return_value
|
||||||
sock.bind.side_effect = socket.error
|
sock.bind.side_effect = socket.error
|
||||||
|
|
||||||
with self.assertRaises(socket.error):
|
with self.assertRaises(socket.error):
|
||||||
network.Server.create_server_socket(
|
network.Server.create_server_socket(
|
||||||
self.mock, sentinel.host, sentinel.port)
|
self.mock, str(sentinel.host), 1234)
|
||||||
|
|
||||||
@patch.object(network, 'create_socket', new=Mock())
|
@patch.object(network, 'create_unix_socket', new=Mock())
|
||||||
|
def test_create_server_bind_fails_unix(self):
|
||||||
|
sock = network.create_unix_socket.return_value
|
||||||
|
sock.bind.side_effect = socket.error
|
||||||
|
|
||||||
|
with self.assertRaises(socket.error):
|
||||||
|
network.Server.create_server_socket(
|
||||||
|
self.mock, 'unix:' + str(sentinel.host), sentinel.port)
|
||||||
|
|
||||||
|
@patch.object(network, 'create_tcp_socket', new=Mock())
|
||||||
def test_create_server_listen_fails(self):
|
def test_create_server_listen_fails(self):
|
||||||
sock = network.create_socket.return_value
|
sock = network.create_tcp_socket.return_value
|
||||||
sock.listen.side_effect = socket.error
|
sock.listen.side_effect = socket.error
|
||||||
|
|
||||||
with self.assertRaises(socket.error):
|
with self.assertRaises(socket.error):
|
||||||
network.Server.create_server_socket(
|
network.Server.create_server_socket(
|
||||||
self.mock, sentinel.host, sentinel.port)
|
self.mock, str(sentinel.host), 1234)
|
||||||
|
|
||||||
|
@patch.object(network, 'create_unix_socket', new=Mock())
|
||||||
|
def test_create_server_listen_fails_unix(self):
|
||||||
|
sock = network.create_unix_socket.return_value
|
||||||
|
sock.listen.side_effect = socket.error
|
||||||
|
|
||||||
|
with self.assertRaises(socket.error):
|
||||||
|
network.Server.create_server_socket(
|
||||||
|
self.mock, 'unix:' + str(sentinel.host), sentinel.port)
|
||||||
|
|
||||||
|
@patch.object(os, 'unlink', new=Mock())
|
||||||
|
@patch.object(GObject, 'source_remove', new=Mock())
|
||||||
|
def test_stop_server_cleans_unix_socket(self):
|
||||||
|
self.mock.watcher = Mock()
|
||||||
|
sock = Mock()
|
||||||
|
sock.family = socket.AF_UNIX
|
||||||
|
self.mock.server_socket = sock
|
||||||
|
network.Server.stop(self.mock)
|
||||||
|
os.unlink.assert_called_once_with(sock.getsockname())
|
||||||
|
|
||||||
@patch.object(GObject, 'io_add_watch', new=Mock())
|
@patch.object(GObject, 'io_add_watch', new=Mock())
|
||||||
def test_register_server_socket_sets_up_io_watch(self):
|
def test_register_server_socket_sets_up_io_watch(self):
|
||||||
@ -124,13 +186,26 @@ class ServerTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_accept_connection(self):
|
def test_accept_connection(self):
|
||||||
sock = Mock(spec=socket.SocketType)
|
sock = Mock(spec=socket.SocketType)
|
||||||
sock.accept.return_value = (sentinel.sock, sentinel.addr)
|
connected_sock = Mock(spec=socket.SocketType)
|
||||||
|
sock.accept.return_value = (connected_sock, sentinel.addr)
|
||||||
self.mock.server_socket = sock
|
self.mock.server_socket = sock
|
||||||
|
|
||||||
sock, addr = network.Server.accept_connection(self.mock)
|
sock, addr = network.Server.accept_connection(self.mock)
|
||||||
self.assertEqual(sentinel.sock, sock)
|
self.assertEqual(connected_sock, sock)
|
||||||
self.assertEqual(sentinel.addr, addr)
|
self.assertEqual(sentinel.addr, addr)
|
||||||
|
|
||||||
|
def test_accept_connection_unix(self):
|
||||||
|
sock = Mock(spec=socket.SocketType)
|
||||||
|
connected_sock = Mock(spec=socket.SocketType)
|
||||||
|
connected_sock.family = socket.AF_UNIX
|
||||||
|
connected_sock.getsockname.return_value = sentinel.sockname
|
||||||
|
sock.accept.return_value = (connected_sock, sentinel.addr)
|
||||||
|
self.mock.server_socket = sock
|
||||||
|
|
||||||
|
sock, addr = network.Server.accept_connection(self.mock)
|
||||||
|
self.assertEqual(connected_sock, sock)
|
||||||
|
self.assertEqual((sentinel.sockname, None), addr)
|
||||||
|
|
||||||
def test_accept_connection_recoverable_error(self):
|
def test_accept_connection_recoverable_error(self):
|
||||||
sock = Mock(spec=socket.SocketType)
|
sock = Mock(spec=socket.SocketType)
|
||||||
self.mock.server_socket = sock
|
self.mock.server_socket = sock
|
||||||
@ -182,6 +257,7 @@ class ServerTest(unittest.TestCase):
|
|||||||
sentinel.protocol, {}, sentinel.sock, sentinel.addr,
|
sentinel.protocol, {}, sentinel.sock, sentinel.addr,
|
||||||
sentinel.timeout)
|
sentinel.timeout)
|
||||||
|
|
||||||
|
@patch.object(network, 'format_socket_name', new=Mock())
|
||||||
def test_reject_connection(self):
|
def test_reject_connection(self):
|
||||||
sock = Mock(spec=socket.SocketType)
|
sock = Mock(spec=socket.SocketType)
|
||||||
|
|
||||||
@ -189,6 +265,7 @@ class ServerTest(unittest.TestCase):
|
|||||||
self.mock, sock, (sentinel.host, sentinel.port))
|
self.mock, sock, (sentinel.host, sentinel.port))
|
||||||
sock.close.assert_called_once_with()
|
sock.close.assert_called_once_with()
|
||||||
|
|
||||||
|
@patch.object(network, 'format_socket_name', new=Mock())
|
||||||
def test_reject_connection_error(self):
|
def test_reject_connection_error(self):
|
||||||
sock = Mock(spec=socket.SocketType)
|
sock = Mock(spec=socket.SocketType)
|
||||||
sock.close.side_effect = socket.error
|
sock.close.side_effect = socket.error
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
import socket
|
import socket
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mock import Mock, patch
|
from mock import Mock, patch, sentinel
|
||||||
|
|
||||||
from mopidy.internal import network
|
from mopidy.internal import network
|
||||||
|
|
||||||
@ -22,6 +22,25 @@ class FormatHostnameTest(unittest.TestCase):
|
|||||||
self.assertEqual(network.format_hostname('0.0.0.0'), '0.0.0.0')
|
self.assertEqual(network.format_hostname('0.0.0.0'), '0.0.0.0')
|
||||||
|
|
||||||
|
|
||||||
|
class FormatSocketConnectionTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_format_socket_name(self):
|
||||||
|
sock = Mock(spec=socket.SocketType)
|
||||||
|
sock.family = socket.AF_INET
|
||||||
|
sock.getsockname.return_value = (sentinel.ip, sentinel.port)
|
||||||
|
self.assertEqual(
|
||||||
|
network.format_socket_name(sock),
|
||||||
|
'[%s]:%s' % (sentinel.ip, sentinel.port))
|
||||||
|
|
||||||
|
def test_format_socket_name_unix(self):
|
||||||
|
sock = Mock(spec=socket.SocketType)
|
||||||
|
sock.family = socket.AF_UNIX
|
||||||
|
sock.getsockname.return_value = sentinel.sockname
|
||||||
|
self.assertEqual(
|
||||||
|
network.format_socket_name(sock),
|
||||||
|
str(sentinel.sockname))
|
||||||
|
|
||||||
|
|
||||||
class TryIPv6SocketTest(unittest.TestCase):
|
class TryIPv6SocketTest(unittest.TestCase):
|
||||||
|
|
||||||
@patch('socket.has_ipv6', False)
|
@patch('socket.has_ipv6', False)
|
||||||
@ -46,14 +65,14 @@ class CreateSocketTest(unittest.TestCase):
|
|||||||
@patch('mopidy.internal.network.has_ipv6', False)
|
@patch('mopidy.internal.network.has_ipv6', False)
|
||||||
@patch('socket.socket')
|
@patch('socket.socket')
|
||||||
def test_ipv4_socket(self, socket_mock):
|
def test_ipv4_socket(self, socket_mock):
|
||||||
network.create_socket()
|
network.create_tcp_socket()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
socket_mock.call_args[0], (socket.AF_INET, socket.SOCK_STREAM))
|
socket_mock.call_args[0], (socket.AF_INET, socket.SOCK_STREAM))
|
||||||
|
|
||||||
@patch('mopidy.internal.network.has_ipv6', True)
|
@patch('mopidy.internal.network.has_ipv6', True)
|
||||||
@patch('socket.socket')
|
@patch('socket.socket')
|
||||||
def test_ipv6_socket(self, socket_mock):
|
def test_ipv6_socket(self, socket_mock):
|
||||||
network.create_socket()
|
network.create_tcp_socket()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
socket_mock.call_args[0], (socket.AF_INET6, socket.SOCK_STREAM))
|
socket_mock.call_args[0], (socket.AF_INET6, socket.SOCK_STREAM))
|
||||||
|
|
||||||
|
|||||||
@ -137,6 +137,18 @@ class GetOrCreateFileTest(unittest.TestCase):
|
|||||||
self.assertEqual(fh.read(), b'foobar\xc3\xa6\xc3\xb8\xc3\xa5')
|
self.assertEqual(fh.read(), b'foobar\xc3\xa6\xc3\xb8\xc3\xa5')
|
||||||
|
|
||||||
|
|
||||||
|
class GetUnixSocketPathTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_correctly_matched_socket_path(self):
|
||||||
|
self.assertEqual(
|
||||||
|
path.get_unix_socket_path('unix:/tmp/mopidy.socket'),
|
||||||
|
'/tmp/mopidy.socket'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_correctly_no_match_socket_path(self):
|
||||||
|
self.assertIsNone(path.get_unix_socket_path('127.0.0.1'))
|
||||||
|
|
||||||
|
|
||||||
class PathToFileURITest(unittest.TestCase):
|
class PathToFileURITest(unittest.TestCase):
|
||||||
|
|
||||||
def test_simple_path(self):
|
def test_simple_path(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user