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:
Jarryd Tilbrook 2018-02-13 20:58:39 +08:00 committed by Nick Steel
parent 9e31e76a05
commit b4c98ec4a5
17 changed files with 299 additions and 107 deletions

View File

@ -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)
=================== ===================

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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):

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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):