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 30 31"""Mocks for testing. 32""" 33 34 35import Queue 36import threading 37 38from mod_pywebsocket import common 39from mod_pywebsocket.stream import StreamHixie75 40 41 42class _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 48 def __init__(self): 49 self._write_data = [] 50 self.remote_addr = 'fake_address' 51 52 def write(self, data): 53 """Override mod_python.apache.mp_conn.write.""" 54 55 self._write_data.append(data) 56 57 def written_data(self): 58 """Get bytes written to this mock.""" 59 60 return ''.join(self._write_data) 61 62 63class MockConn(_MockConnBase): 64 """Mock for mod_python.apache.mp_conn. 65 66 This enables tests to specify what should be read from a (mock) mp_conn as 67 well as to check what is written to it. 68 """ 69 70 def __init__(self, read_data): 71 """Constructs an instance. 72 73 Args: 74 read_data: bytes that should be returned when read* methods are 75 called. 76 """ 77 78 _MockConnBase.__init__(self) 79 self._read_data = read_data 80 self._read_pos = 0 81 82 def readline(self): 83 """Override mod_python.apache.mp_conn.readline.""" 84 85 if self._read_pos >= len(self._read_data): 86 return '' 87 end_index = self._read_data.find('\n', self._read_pos) + 1 88 if not end_index: 89 end_index = len(self._read_data) 90 return self._read_up_to(end_index) 91 92 def read(self, length): 93 """Override mod_python.apache.mp_conn.read.""" 94 95 if self._read_pos >= len(self._read_data): 96 return '' 97 end_index = min(len(self._read_data), self._read_pos + length) 98 return self._read_up_to(end_index) 99 100 def _read_up_to(self, end_index): 101 line = self._read_data[self._read_pos:end_index] 102 self._read_pos = end_index 103 return line 104 105 106class MockBlockingConn(_MockConnBase): 107 """Blocking mock for mod_python.apache.mp_conn. 108 109 This enables tests to specify what should be read from a (mock) mp_conn as 110 well as to check what is written to it. 111 Callers of read* methods will block if there is no bytes available. 112 """ 113 114 def __init__(self): 115 _MockConnBase.__init__(self) 116 self._queue = Queue.Queue() 117 118 def readline(self): 119 """Override mod_python.apache.mp_conn.readline.""" 120 line = '' 121 while True: 122 c = self._queue.get() 123 line += c 124 if c == '\n': 125 return line 126 127 def read(self, length): 128 """Override mod_python.apache.mp_conn.read.""" 129 130 data = '' 131 for unused in range(length): 132 data += self._queue.get() 133 return data 134 135 def put_bytes(self, bytes): 136 """Put bytes to be read from this mock. 137 138 Args: 139 bytes: bytes to be read. 140 """ 141 142 for byte in bytes: 143 self._queue.put(byte) 144 145 146class MockTable(dict): 147 """Mock table. 148 149 This mimics mod_python mp_table. Note that only the methods used by 150 tests are overridden. 151 """ 152 153 def __init__(self, copy_from={}): 154 if isinstance(copy_from, dict): 155 copy_from = 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 169class MockRequest(object): 170 """Mock request. 171 172 This mimics mod_python request. 173 """ 174 175 def __init__(self, uri=None, headers_in={}, connection=None, method='GET', 176 is_https=False): 177 """Construct an instance. 178 179 Arguments: 180 uri: URI of the request. 181 headers_in: Request headers. 182 connection: Connection used for the request. 183 method: request method. 184 is_https: Whether this request is over SSL. 185 186 See the document of mod_python Request for details. 187 """ 188 self.uri = uri 189 self.connection = connection 190 self.method = method 191 self.headers_in = MockTable(headers_in) 192 # self.is_https_ needs to be accessible from tests. To avoid name 193 # conflict with self.is_https(), it is named as such. 194 self.is_https_ = is_https 195 self.ws_stream = StreamHixie75(self, True) 196 self.ws_close_code = None 197 self.ws_close_reason = None 198 self.ws_version = common.VERSION_HYBI00 199 self.ws_deflate = False 200 201 self.drain_received_data_called = False 202 203 def is_https(self): 204 """Return whether this request is over SSL.""" 205 return self.is_https_ 206 207 def _drain_received_data(self): 208 self.drain_received_data_called = True 209 210 211class MockDispatcher(object): 212 """Mock for dispatch.Dispatcher.""" 213 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 225