mock.py (7040B)
1 # Copyright 2011, Google Inc. 2 # All rights reserved. 3 # 4 # Redistribution and use in source and binary forms, with or without 5 # modification, are permitted provided that the following conditions are 6 # met: 7 # 8 # * Redistributions of source code must retain the above copyright 9 # notice, this list of conditions and the following disclaimer. 10 # * Redistributions in binary form must reproduce the above 11 # copyright notice, this list of conditions and the following disclaimer 12 # in the documentation and/or other materials provided with the 13 # distribution. 14 # * Neither the name of Google Inc. nor the names of its 15 # contributors may be used to endorse or promote products derived from 16 # this software without specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 """Mocks for testing. 30 """ 31 32 from __future__ import absolute_import 33 34 import six 35 import six.moves.queue 36 from six.moves import range 37 38 from pywebsocket3 import common, util 39 from pywebsocket3.stream import Stream, StreamOptions 40 41 42 class _MockConnBase(object): 43 """Base class of mocks for mod_python.apache.mp_conn. 44 45 This enables tests to check what is written to a (mock) mp_conn. 46 """ 47 def __init__(self): 48 self._write_data = [] 49 self.remote_addr = b'fake_address' 50 51 def write(self, data): 52 """Override mod_python.apache.mp_conn.write. 53 54 data should be bytes when touching this method manually. 55 """ 56 57 self._write_data.append(data) 58 59 def written_data(self): 60 """Get bytes written to this mock.""" 61 62 return b''.join(self._write_data) 63 64 65 class MockConn(_MockConnBase): 66 """Mock for mod_python.apache.mp_conn. 67 68 This enables tests to specify what should be read from a (mock) mp_conn as 69 well as to check what is written to it. 70 """ 71 def __init__(self, read_data): 72 """Constructs an instance. 73 74 Args: 75 read_data: bytes that should be returned when read* methods are 76 called. 77 """ 78 79 _MockConnBase.__init__(self) 80 self._read_data = read_data 81 self._read_pos = 0 82 83 def readline(self): 84 """Override mod_python.apache.mp_conn.readline.""" 85 86 if self._read_pos >= len(self._read_data): 87 return b'' 88 end_index = self._read_data.find(b'\n', self._read_pos) + 1 89 if not end_index: 90 end_index = len(self._read_data) 91 return self._read_up_to(end_index) 92 93 def read(self, length): 94 """Override mod_python.apache.mp_conn.read.""" 95 96 if self._read_pos >= len(self._read_data): 97 return b'' 98 end_index = min(len(self._read_data), self._read_pos + length) 99 return self._read_up_to(end_index) 100 101 def _read_up_to(self, end_index): 102 line = self._read_data[self._read_pos:end_index] 103 self._read_pos = end_index 104 return line 105 106 107 class MockBlockingConn(_MockConnBase): 108 """Blocking mock for mod_python.apache.mp_conn. 109 110 This enables tests to specify what should be read from a (mock) mp_conn as 111 well as to check what is written to it. 112 Callers of read* methods will block if there is no bytes available. 113 """ 114 def __init__(self): 115 _MockConnBase.__init__(self) 116 self._queue = six.moves.queue.Queue() 117 118 def readline(self): 119 """Override mod_python.apache.mp_conn.readline.""" 120 line = bytearray() 121 while True: 122 c = self._queue.get() 123 line.append(c) 124 if c == ord(b'\n'): 125 return bytes(line) 126 127 def read(self, length): 128 """Override mod_python.apache.mp_conn.read.""" 129 130 data = bytearray() 131 for unused in range(length): 132 data.append(self._queue.get()) 133 134 return bytes(data) 135 136 def put_bytes(self, bytes): 137 """Put bytes to be read from this mock. 138 139 Args: 140 bytes: bytes to be read. 141 """ 142 143 for byte in six.iterbytes(bytes): 144 self._queue.put(byte) 145 146 147 class MockTable(dict): 148 """Mock table. 149 150 This mimics mod_python mp_table. Note that only the methods used by 151 tests are overridden. 152 """ 153 def __init__(self, copy_from={}): 154 if isinstance(copy_from, dict): 155 copy_from = list(copy_from.items()) 156 for key, value in copy_from: 157 self.__setitem__(key, value) 158 159 def __getitem__(self, key): 160 return super(MockTable, self).__getitem__(key.lower()) 161 162 def __setitem__(self, key, value): 163 super(MockTable, self).__setitem__(key.lower(), value) 164 165 def get(self, key, def_value=None): 166 return super(MockTable, self).get(key.lower(), def_value) 167 168 169 class MockRequest(object): 170 """Mock request. 171 172 This mimics mod_python request. 173 """ 174 def __init__(self, 175 uri=None, 176 headers_in={}, 177 connection=None, 178 method='GET', 179 protocol='HTTP/1.1', 180 is_https=False): 181 """Construct an instance. 182 183 Arguments: 184 uri: URI of the request. 185 headers_in: Request headers. 186 connection: Connection used for the request. 187 method: request method. 188 is_https: Whether this request is over SSL. 189 190 See the document of mod_python Request for details. 191 """ 192 self.uri = uri 193 self.unparsed_uri = uri 194 self.connection = connection 195 self.method = method 196 self.protocol = protocol 197 self.headers_in = MockTable(headers_in) 198 # self.is_https_ needs to be accessible from tests. To avoid name 199 # conflict with self.is_https(), it is named as such. 200 self.is_https_ = is_https 201 self.ws_stream = Stream(self, StreamOptions()) 202 self.ws_close_code = None 203 self.ws_close_reason = None 204 self.ws_version = common.VERSION_HYBI_LATEST 205 self.ws_deflate = False 206 207 def is_https(self): 208 """Return whether this request is over SSL.""" 209 return self.is_https_ 210 211 212 class MockDispatcher(object): 213 """Mock for dispatch.Dispatcher.""" 214 def __init__(self): 215 self.do_extra_handshake_called = False 216 217 def do_extra_handshake(self, conn_context): 218 self.do_extra_handshake_called = True 219 220 def transfer_data(self, conn_context): 221 pass 222 223 224 # vi:sts=4 sw=4 et