def setup(self, request): self.family, self.far_address, self.near_address = request.param sock = socket.socket(family=self.family) self.slipsocket = SlipSocket(sock) yield self.slipsocket.close() del self.slipsocket
def setup(self, request, mocker): """Prepare the test.""" self.family, self.far_address, self.near_address = request.param self.sock_mock = mocker.Mock(spec=socket.socket(family=self.family), family=self.family, type=socket.SOCK_STREAM, proto=0) # pylint: disable=no-member self.slipsocket = SlipSocket(self.sock_mock) yield self.sock_mock.close() del self.slipsocket
def test_create_connection(self, mocker): mocker.patch('sliplib.socket.create_connection') sliplib.socket.create_connection.reset_mock() new_sock = socket.socket(self.family) sliplib.socket.create_connection.return_value = new_sock sock = SlipSocket.create_connection(self.far_address) assert isinstance(sock, SlipSocket) assert sock.socket is new_sock sliplib.socket.create_connection.assert_called_once_with( self.far_address, None, None)
def test_create_connection(self, mocker): """Test that create_connection gives a SlipSocket.""" new_sock_mock = mocker.Mock(spec=socket.socket(self.family), family=self.family, type=socket.SOCK_STREAM, proto=0) # pylint: disable=no-member create_connection_mock = mocker.patch( 'sliplib.socket.create_connection', return_value=new_sock_mock) sock = SlipSocket.create_connection(self.far_address) assert isinstance(sock, SlipSocket) assert sock.socket is new_sock_mock create_connection_mock.assert_called_once_with(self.far_address[0:2], None, None)
def test_slipsocket_requires_TCP_socket(self): sock = socket.socket(family=self.family, type=socket.SOCK_DGRAM) with pytest.raises(ValueError): SlipSocket(sock)
def test_slipsocket_instantiation_from_socket(self): sock = socket.socket(family=self.family) slipsocket = SlipSocket(sock=sock) assert slipsocket.socket is sock
class TestSlipSocket: @pytest.fixture( autouse=True, params=[ ( socket.AF_INET, ('93.184.216.34', 54321), # example.com IPv4 address ('127.0.0.1', 12345) # localhost IPv4 address ), ( socket.AF_INET6, ('2606:2800:220:1:248:1893:25c8:1946', 54321, 0, 0), # example.com IPv6 address ('::1', 12345, 0, 0) # localhost IPv6 address ) ]) def setup(self, request): self.family, self.far_address, self.near_address = request.param sock = socket.socket(family=self.family) self.slipsocket = SlipSocket(sock) yield self.slipsocket.close() del self.slipsocket def test_slipsocket_instantiation(self): assert self.slipsocket.family == self.family assert self.slipsocket.type == socket.SOCK_STREAM def test_slipsocket_instantiation_from_socket(self): sock = socket.socket(family=self.family) slipsocket = SlipSocket(sock=sock) assert slipsocket.socket is sock # noinspection PyPep8Naming def test_slipsocket_requires_TCP_socket(self): sock = socket.socket(family=self.family, type=socket.SOCK_DGRAM) with pytest.raises(ValueError): SlipSocket(sock) def test_sending_data(self, mocker): mocker.patch('sliplib.socket.socket.sendall') sliplib.socket.socket.sendall.reset_mock() self.slipsocket.send_msg(b'hallo') sliplib.socket.socket.sendall.assert_called_once_with(END + b'hallo' + END) sliplib.socket.socket.sendall.reset_mock() self.slipsocket.send_msg(b'bye') sliplib.socket.socket.sendall.assert_called_once_with(END + b'bye' + END) def test_receiving_data(self, mocker): def socket_data_generator(): yield END + b'hallo' yield END + END yield b'bye' yield END + END yield b'' mocker.patch('sliplib.socket.socket.recv') sliplib.socket.socket.recv.reset_mock() sliplib.socket.socket.recv.side_effect = socket_data_generator() assert self.slipsocket.recv_msg() == b'hallo' # noinspection PyProtectedMember sz = sliplib.SlipSocket._chunk_size expected_calls = [mocker.call.recv(sz), mocker.call.recv(sz)] assert sliplib.socket.socket.recv.mock_calls == expected_calls assert self.slipsocket.recv_msg() == b'bye' expected_calls += [mocker.call.recv(sz), mocker.call.recv(sz)] assert sliplib.socket.socket.recv.mock_calls == expected_calls assert self.slipsocket.recv_msg() == b'' expected_calls += [mocker.call.recv(sz)] assert sliplib.socket.socket.recv.mock_calls == expected_calls def test_end_of_data_handling(self, mocker): def socket_data_generator(): yield END + b'hallo' yield END + END yield b'bye' yield b'' yield b'' mocker.patch('sliplib.socket.socket.recv') sliplib.socket.socket.recv.reset_mock() sliplib.socket.socket.recv.side_effect = socket_data_generator() assert self.slipsocket.recv_msg() == b'hallo' assert self.slipsocket.recv_msg() == b'bye' assert self.slipsocket.recv_msg() == b'' def test_exception_on_protocol_error(self, mocker): def socket_data_generator(): yield END + b'hallo' + END + ESC + b'error' + END + b'bye' + END yield b'some additional bytes, not relevant' mocker.patch('sliplib.socket.socket.recv') sliplib.socket.socket.recv.reset_mock() sliplib.socket.socket.recv.side_effect = socket_data_generator() assert self.slipsocket.recv_msg() == b'hallo' with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) assert self.slipsocket.recv_msg() == b'bye' def test_exception_on_protocol_error_in_first_packet(self, mocker): def socket_data_generator(): yield END + ESC + b'error' + END + b'hallo' + END + b'bye' + END yield b'some additional bytes, not relevant' mocker.patch('sliplib.socket.socket.recv') sliplib.socket.socket.recv.reset_mock() sliplib.socket.socket.recv.side_effect = socket_data_generator() with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) assert self.slipsocket.recv_msg() == b'hallo' assert self.slipsocket.recv_msg() == b'bye' def test_exceptions_on_consecutive_invalid_packets(self, mocker): def socket_data_generator(): yield END + b'hallo' + END + ESC + b'error' + END + b'another' + ESC + END + b'bye' + END yield b'some additional bytes, not relevant' mocker.patch('sliplib.socket.socket.recv') sliplib.socket.socket.recv.reset_mock() sliplib.socket.socket.recv.side_effect = socket_data_generator() assert self.slipsocket.recv_msg() == b'hallo' with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) with pytest.raises(sliplib.ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (b'another' + ESC, ) assert self.slipsocket.recv_msg() == b'bye' def test_accept_method(self, mocker): mocker.patch('sliplib.socket.socket.accept', unsafe=True) sliplib.socket.socket.accept.reset_mock() new_socket = sliplib.socket.socket(self.family, socket.SOCK_STREAM) sliplib.socket.socket.accept.return_value = (new_socket, self.far_address) new_slip_socket, address = self.slipsocket.accept() sliplib.socket.socket.accept.assert_called_once() assert isinstance(new_slip_socket, SlipSocket) assert new_slip_socket.socket is new_socket assert address == self.far_address @pytest.mark.parametrize('method', not_delegated_methods) def test_exception_for_not_supported_operations(self, method): with pytest.raises(AttributeError): getattr(self.slipsocket, method) @pytest.mark.parametrize('method', delegated_methods) def test_delegated_methods(self, method, mocker): mocker.patch.object(sliplib.socket.socket, method) mocked_method = getattr(sliplib.socket.socket, method) mocked_method.reset_mock() mocked_method() # Don't care about the arguments mocked_method.assert_called_once_with() @pytest.mark.parametrize('attr', ['family', 'type', 'proto']) def test_read_only_attribute(self, attr): assert getattr(self.slipsocket, attr) == getattr(self.slipsocket.socket, attr) with pytest.raises(AttributeError): setattr(self.slipsocket, attr, "some value") def test_create_connection(self, mocker): mocker.patch('sliplib.socket.create_connection') sliplib.socket.create_connection.reset_mock() new_sock = socket.socket(self.family) sliplib.socket.create_connection.return_value = new_sock sock = SlipSocket.create_connection(self.far_address) assert isinstance(sock, SlipSocket) assert sock.socket is new_sock sliplib.socket.create_connection.assert_called_once_with( self.far_address, None, None)
def __init__(self, address): self.sock = SlipSocket.create_connection(address)
def test_slipsocket_requires_tcp_socket(self): """Test that non-TCP sockets are rejected.""" self.sock_mock.configure_mock(type=socket.SOCK_DGRAM) # pylint: disable=no-member with pytest.raises(ValueError): SlipSocket(self.sock_mock)
class TestSlipSocket: """Tests for SlipSocket""" @pytest.fixture( autouse=True, params=[ ( socket.AF_INET, # pylint: disable=no-member ('93.184.216.34', 54321), # example.com IPv4 address ('127.0.0.1', 12345) # localhost IPv4 address ), ( socket.AF_INET6, # pylint: disable=no-member ('2606:2800:220:1:248:1893:25c8:1946', 54321, 0, 0), # example.com IPv6 address ('::1', 12345, 0, 0) # localhost IPv6 address ) ]) def setup(self, request, mocker): """Prepare the test.""" self.family, self.far_address, self.near_address = request.param self.sock_mock = mocker.Mock(spec=socket.socket(family=self.family), family=self.family, type=socket.SOCK_STREAM, proto=0) # pylint: disable=no-member self.slipsocket = SlipSocket(self.sock_mock) yield self.sock_mock.close() del self.slipsocket def test_slipsocket_instantiation(self): """Test that the slipsocket has been created properly.""" assert self.slipsocket.family == self.family assert self.slipsocket.type == socket.SOCK_STREAM # pylint: disable=no-member assert self.slipsocket.proto == 0 assert self.slipsocket.socket is self.sock_mock def test_slipsocket_requires_tcp_socket(self): """Test that non-TCP sockets are rejected.""" self.sock_mock.configure_mock(type=socket.SOCK_DGRAM) # pylint: disable=no-member with pytest.raises(ValueError): SlipSocket(self.sock_mock) def test_sending_data(self, mocker): """Test that the sendall method on the socket is called when sending a message.""" self.slipsocket.send_msg(b'hallo') self.slipsocket.send_msg(b'bye') self.sock_mock.sendall.assert_has_calls([ mocker.call(END + b'hallo' + END), mocker.call(END + b'bye' + END) ]) def test_receiving_data(self, mocker): """Test that the recv method on the socket is called when receiving a message. Also test that fragmented packets are buffered appropriately.""" def socket_data_generator(): yield END + b'hallo' yield END + END yield b'bye' yield END + END yield b'' self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) assert self.slipsocket.recv_msg() == b'hallo' # noinspection PyProtectedMember chunk_size = sliplib.SlipSocket._chunk_size # pylint: disable=protected-access expected_calls = [mocker.call.recv(chunk_size)] * 2 self.sock_mock.recv.assert_has_calls(expected_calls) assert self.slipsocket.recv_msg() == b'bye' expected_calls = [mocker.call.recv(chunk_size)] * 4 self.sock_mock.recv.assert_has_calls(expected_calls) assert self.slipsocket.recv_msg() == b'' expected_calls = [mocker.call.recv(chunk_size)] * 5 self.sock_mock.recv.assert_has_calls(expected_calls) def test_end_of_data_handling(self, mocker): """Test that receipt of an empty byte string indicates the end of the connection.""" def socket_data_generator(): yield END + b'hallo' yield END + END yield b'bye' yield b'' yield b'' self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) assert self.slipsocket.recv_msg() == b'hallo' assert self.slipsocket.recv_msg() == b'bye' assert self.slipsocket.recv_msg() == b'' chunk_size = sliplib.SlipSocket._chunk_size # pylint: disable=protected-access expected_calls = [mocker.call.recv(chunk_size)] * 4 self.sock_mock.recv.assert_has_calls(expected_calls) def test_exception_on_protocol_error_in_first_packet(self, mocker): """Test that an invalid packet causes a ProtocolError. Packets after the invalid packet are handled correctly.""" def socket_data_generator(): yield END + ESC + b'error' + END + b'hallo' + END + b'bye' + END self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) assert self.slipsocket.recv_msg() == b'hallo' assert self.slipsocket.recv_msg() == b'bye' def test_exception_on_protocol_error_in_subsequent_packet(self, mocker): """Test that an invalid packet causes a ProtocolError Packets before the invalid packet are decoded correctly.""" def socket_data_generator(): yield END + b'hallo' + END + ESC + b'error' + END self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) assert self.slipsocket.recv_msg() == b'hallo' with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) def test_exceptions_on_consecutive_invalid_packets(self, mocker): """Test that multiple invalid packets result in a ProtocolError for each invalid packet.""" def socket_data_generator(): yield END + b'hallo' + END + ESC + b'error' + END + b'another' + ESC + END + b'bye' + END self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) assert self.slipsocket.recv_msg() == b'hallo' with pytest.raises(ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (ESC + b'error', ) with pytest.raises(sliplib.ProtocolError) as exc: self.slipsocket.recv_msg() assert exc.value.args == (b'another' + ESC, ) assert self.slipsocket.recv_msg() == b'bye' def test_accept_method(self, mocker): """Test that the accept method is delegated to the socket, and that the result is a SlipSocket.""" new_socket = mocker.Mock(spec=socket.socket(family=self.family), family=self.family, type=socket.SOCK_STREAM, proto=0) # pylint: disable=no-member self.sock_mock.accept = mocker.Mock(return_value=(new_socket, self.far_address)) new_slip_socket, address = self.slipsocket.accept() self.sock_mock.accept.assert_called_once_with() assert isinstance(new_slip_socket, SlipSocket) assert new_slip_socket.socket is new_socket assert address == self.far_address def test_bind_method(self, mocker): """Test that the bind method is delegated to the socket.""" self.sock_mock.bind = mocker.Mock() self.slipsocket.bind(self.near_address) self.sock_mock.bind.assert_called_once_with(self.near_address) def test_close_method(self, mocker): """Test that the close method is delegated to the socket.""" self.sock_mock.close = mocker.Mock() self.slipsocket.close() self.sock_mock.close.assert_called_once_with() def test_connect_method(self, mocker): """Test that the connect method is delegated to the socket.""" self.sock_mock.connect = mocker.Mock() self.slipsocket.connect(self.far_address) self.sock_mock.connect.assert_called_once_with(self.far_address) def test_connect_ex_method(self, mocker): """Test that the connect_ex method is delegated to the socket.""" self.sock_mock.connect_ex = mocker.Mock() self.slipsocket.connect_ex(self.far_address) self.sock_mock.connect_ex.assert_called_once_with(self.far_address) def test_getpeername_method(self, mocker): """Test that the getpeername method is delegated to the socket.""" self.sock_mock.getpeername = mocker.Mock(return_value=self.far_address) peername = self.slipsocket.getpeername() self.sock_mock.getpeername.assert_called_once_with() assert peername == self.far_address def test_getsockname_method(self, mocker): """Test that the getsockname method is delegated to the socket.""" self.sock_mock.getsockname = mocker.Mock( return_value=self.near_address) sockname = self.slipsocket.getsockname() self.sock_mock.getsockname.assert_called_once_with() assert sockname == self.near_address def test_listen_method(self, mocker): """Test that the listen method (with or without arguments) is delegated to the socket.""" self.sock_mock.listen = mocker.Mock() self.slipsocket.listen() self.slipsocket.listen(5) assert self.sock_mock.listen.mock_calls == [ mocker.call(), mocker.call(5) ] def test_shutdown_method(self, mocker): """Test that the shutdown method is delegated to the socket.""" self.sock_mock.shutdown = mocker.Mock() self.slipsocket.shutdown(0) self.sock_mock.shutdown.assert_called_once_with(0) @pytest.mark.parametrize('method', NOT_DELEGATED_METHODS) def test_exception_for_not_supported_operations(self, method): """Test that non-delegated methods on the SlipSocket raise an AttributeError.""" with pytest.raises(AttributeError): getattr(self.slipsocket, method) # Testing delegated methods. # This will be removed due to deprecation of delegating methods to the wrapped socket. @pytest.mark.parametrize('method', DELEGATED_METHODS) def test_delegated_methods(self, method, mocker): """Test that other delegated methods are delegated to the socket, but also issue a deprecation warning.""" mock_method = mocker.Mock() setattr(self.sock_mock, method, mock_method) with warnings.catch_warnings(record=True) as warning: socket_method = getattr(self.slipsocket, method) assert len(warning) == 1 assert issubclass(warning[0].category, DeprecationWarning) assert "will be removed in version 1.0" in str(warning[0].message) socket_method() mock_method.assert_called_once_with() @pytest.mark.parametrize('attr', ['family', 'type', 'proto']) def test_read_only_attribute(self, attr): """Test that read-only attributes can be read but not written.""" assert getattr(self.slipsocket, attr) == getattr(self.slipsocket.socket, attr) with pytest.raises(AttributeError): setattr(self.slipsocket, attr, "some value") def test_create_connection(self, mocker): """Test that create_connection gives a SlipSocket.""" new_sock_mock = mocker.Mock(spec=socket.socket(self.family), family=self.family, type=socket.SOCK_STREAM, proto=0) # pylint: disable=no-member create_connection_mock = mocker.patch( 'sliplib.socket.create_connection', return_value=new_sock_mock) sock = SlipSocket.create_connection(self.far_address) assert isinstance(sock, SlipSocket) assert sock.socket is new_sock_mock create_connection_mock.assert_called_once_with(self.far_address[0:2], None, None) def test_slip_socket_iteration(self, mocker): """Test that a SlipSocket can be iterated over.""" def socket_data_generator(): yield END + b'hallo' yield END + END yield b'bye' yield END + END yield b'' self.sock_mock.recv = mocker.Mock(side_effect=socket_data_generator()) expected = (b'hallo', b'bye') for exp, act in zip(expected, self.slipsocket): assert exp == act