# Copyright 2011, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Mocks for testing. """ from __future__ import absolute_import import six.moves.queue import threading import struct import six from mod_pywebsocket import common from mod_pywebsocket import util from mod_pywebsocket.stream import Stream from mod_pywebsocket.stream import StreamOptions from six.moves import range class _MockConnBase(object): """Base class of mocks for mod_python.apache.mp_conn. This enables tests to check what is written to a (mock) mp_conn. """ def __init__(self): self._write_data = [] self.remote_addr = b'fake_address' def write(self, data): """Override mod_python.apache.mp_conn.write. data should be bytes when touching this method manually. """ self._write_data.append(data) def written_data(self): """Get bytes written to this mock.""" return b''.join(self._write_data) class MockConn(_MockConnBase): """Mock for mod_python.apache.mp_conn. This enables tests to specify what should be read from a (mock) mp_conn as well as to check what is written to it. """ def __init__(self, read_data): """Constructs an instance. Args: read_data: bytes that should be returned when read* methods are called. """ _MockConnBase.__init__(self) self._read_data = read_data self._read_pos = 0 def readline(self): """Override mod_python.apache.mp_conn.readline.""" if self._read_pos >= len(self._read_data): return b'' end_index = self._read_data.find(b'\n', self._read_pos) + 1 if not end_index: end_index = len(self._read_data) return self._read_up_to(end_index) def read(self, length): """Override mod_python.apache.mp_conn.read.""" if self._read_pos >= len(self._read_data): return b'' end_index = min(len(self._read_data), self._read_pos + length) return self._read_up_to(end_index) def _read_up_to(self, end_index): line = self._read_data[self._read_pos:end_index] self._read_pos = end_index return line class MockBlockingConn(_MockConnBase): """Blocking mock for mod_python.apache.mp_conn. This enables tests to specify what should be read from a (mock) mp_conn as well as to check what is written to it. Callers of read* methods will block if there is no bytes available. """ def __init__(self): _MockConnBase.__init__(self) self._queue = six.moves.queue.Queue() def readline(self): """Override mod_python.apache.mp_conn.readline.""" line = bytearray() while True: c = self._queue.get() line.append(c) if c == ord(b'\n'): return bytes(line) def read(self, length): """Override mod_python.apache.mp_conn.read.""" data = bytearray() for unused in range(length): data.append(self._queue.get()) return bytes(data) def put_bytes(self, bytes): """Put bytes to be read from this mock. Args: bytes: bytes to be read. """ for byte in six.iterbytes(bytes): self._queue.put(byte) class MockTable(dict): """Mock table. This mimics mod_python mp_table. Note that only the methods used by tests are overridden. """ def __init__(self, copy_from={}): if isinstance(copy_from, dict): copy_from = list(copy_from.items()) for key, value in copy_from: self.__setitem__(key, value) def __getitem__(self, key): return super(MockTable, self).__getitem__(key.lower()) def __setitem__(self, key, value): super(MockTable, self).__setitem__(key.lower(), value) def get(self, key, def_value=None): return super(MockTable, self).get(key.lower(), def_value) class MockRequest(object): """Mock request. This mimics mod_python request. """ def __init__(self, uri=None, headers_in={}, connection=None, method='GET', protocol='HTTP/1.1', is_https=False): """Construct an instance. Arguments: uri: URI of the request. headers_in: Request headers. connection: Connection used for the request. method: request method. is_https: Whether this request is over SSL. See the document of mod_python Request for details. """ self.uri = uri self.unparsed_uri = uri self.connection = connection self.method = method self.protocol = protocol self.headers_in = MockTable(headers_in) # self.is_https_ needs to be accessible from tests. To avoid name # conflict with self.is_https(), it is named as such. self.is_https_ = is_https self.ws_stream = Stream(self, StreamOptions()) self.ws_close_code = None self.ws_close_reason = None self.ws_version = common.VERSION_HYBI_LATEST self.ws_deflate = False def is_https(self): """Return whether this request is over SSL.""" return self.is_https_ class MockDispatcher(object): """Mock for dispatch.Dispatcher.""" def __init__(self): self.do_extra_handshake_called = False def do_extra_handshake(self, conn_context): self.do_extra_handshake_called = True def transfer_data(self, conn_context): pass # vi:sts=4 sw=4 et