Skip to content

Commit fbb48de

Browse files
committed
more unit test coverage of framing and websocket
1 parent 0499e4c commit fbb48de

3 files changed

Lines changed: 198 additions & 3 deletions

File tree

test/test_frame.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,93 @@ def test_fragmented_control_frame_is_invalid(self):
185185
f = Frame()
186186
self.assertRaises(ProtocolException, f.parser.send, b'0x9')
187187

188-
def test_frame_sized_below_127(self):
189-
bytes = Frame(opcode=OPCODE_TEXT, body=b'*'*65536, fin=1).build()
188+
def test_fragmented_control_frame_is_too_large(self):
189+
bytes = Frame(opcode=OPCODE_PING, body=b'*'*65536, fin=1).build()
190+
f = Frame()
191+
self.assertRaises(FrameTooLargeException, f.parser.send, bytes)
192+
193+
def test_frame_sized_127(self):
194+
body = b'*'*65536
195+
bytes = Frame(opcode=OPCODE_TEXT, body=body, fin=1).build()
190196

191197
f = Frame()
198+
# determine how the size is stored
192199
f.parser.send(bytes[:3])
193200
self.assertTrue(f.masking_key is None)
201+
# that's a large frame indeed
194202
self.assertEqual(f.payload_length, 127)
195203

204+
# this will compute the actual application data size
205+
# it will also read the first byte of data
206+
# indeed the length is found from byte 3 to 10
207+
f.parser.send(bytes[3:11])
208+
self.assertEqual(f.payload_length, 65536)
209+
210+
# parse the rest of our data
211+
f.parser.send(bytes[11:])
212+
self.assertEqual(f.body, body)
213+
214+
215+
# The same but this time we provide enough
216+
# bytes so that the application's data length
217+
# can be computed from the first generator's send call
218+
f = Frame()
219+
f.parser.send(bytes[:10])
220+
self.assertTrue(f.masking_key is None)
221+
self.assertEqual(f.payload_length, 65536)
222+
223+
# parse the rest of our data
224+
f.parser.send(bytes[10:])
225+
self.assertEqual(f.body, body)
226+
227+
228+
# The same with masking given out gradually
229+
mask = os.urandom(4)
230+
bytes = Frame(opcode=OPCODE_TEXT, body=body, fin=1, masking_key=mask).build()
231+
f = Frame()
232+
f.parser.send(bytes[:10])
233+
self.assertTrue(f.masking_key is None)
234+
self.assertEqual(f.payload_length, 65536)
235+
236+
# parse the mask gradually
237+
f.parser.send(bytes[10:12])
238+
f.parser.send(bytes[12:])
239+
self.assertEqual(f.unmask(f.body), body)
240+
241+
def test_frame_sized_126(self):
242+
body = b'*'*256
243+
bytes = Frame(opcode=OPCODE_TEXT, body=body, fin=1).build()
244+
245+
f = Frame()
246+
# determine how the size is stored
247+
f.parser.send(bytes[:3])
248+
self.assertTrue(f.masking_key is None)
249+
# that's a large frame indeed
250+
self.assertEqual(f.payload_length, 126)
251+
252+
# this will compute the actual application data size
253+
# it will also read the first byte of data
254+
# indeed the length is found from byte 3 to 10
255+
f.parser.send(bytes[3:11])
256+
self.assertEqual(f.payload_length, 256)
257+
258+
# parse the rest of our data
259+
f.parser.send(bytes[11:])
260+
self.assertEqual(f.body, body)
261+
262+
263+
# The same but this time we provide enough
264+
# bytes so that the application's data length
265+
# can be computed from the first generator's send call
266+
f = Frame()
267+
f.parser.send(bytes[:10])
268+
self.assertTrue(f.masking_key is None)
269+
self.assertEqual(f.payload_length, 256)
270+
271+
# parse the rest of our data
272+
f.parser.send(bytes[10:])
273+
self.assertEqual(f.body, body)
274+
196275
if __name__ == '__main__':
197276
suite = unittest.TestSuite()
198277
loader = unittest.TestLoader()

test/test_websocket.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import socket
55
import struct
66

7-
from mock import MagicMock, call
7+
from mock import MagicMock, call, patch
88

99
from ws4py.framing import Frame, \
1010
OPCODE_CONTINUATION, OPCODE_TEXT, \
@@ -36,6 +36,58 @@ def test_get_underlying_connection(self):
3636
ws = WebSocket(sock=m)
3737
self.assertEqual(ws.connection, m)
3838

39+
def test_close_connection(self):
40+
m = MagicMock()
41+
ws = WebSocket(sock=m)
42+
ws.close_connection()
43+
m.shutdown.assert_called_once_with(socket.SHUT_RDWR)
44+
m.close.assert_called_once_with()
45+
self.assertIsNone(ws.connection)
46+
47+
m = MagicMock()
48+
m.close = MagicMock(side_effect=RuntimeError)
49+
ws = WebSocket(sock=m)
50+
ws.close_connection()
51+
self.assertIsNone(ws.connection)
52+
53+
def test_terminate_with_closing(self):
54+
m = MagicMock()
55+
s = MagicMock()
56+
c = MagicMock()
57+
cc = MagicMock()
58+
59+
ws = WebSocket(sock=m)
60+
with patch.multiple(ws, closed=c, close_connection=cc):
61+
ws.stream = s
62+
ws.stream.closing = CloseControlMessage(code=1000, reason='test closing')
63+
ws.terminate()
64+
self.assertTrue(ws.client_terminated)
65+
self.assertTrue(ws.server_terminated)
66+
self.assertTrue(ws.terminated)
67+
c.assert_called_once_with(1000, 'test closing')
68+
cc.assert_called_once_with()
69+
self.assertIsNone(ws.stream)
70+
self.assertIsNone(ws.environ)
71+
72+
def test_terminate_without_closing(self):
73+
m = MagicMock()
74+
s = MagicMock()
75+
c = MagicMock()
76+
cc = MagicMock()
77+
78+
ws = WebSocket(sock=m)
79+
with patch.multiple(ws, closed=c, close_connection=cc):
80+
ws.stream = s
81+
ws.stream.closing = None
82+
ws.terminate()
83+
self.assertTrue(ws.client_terminated)
84+
self.assertTrue(ws.server_terminated)
85+
self.assertTrue(ws.terminated)
86+
c.assert_called_once_with(1006, "Going away")
87+
cc.assert_called_once_with()
88+
self.assertIsNone(ws.stream)
89+
self.assertIsNone(ws.environ)
90+
3991
def test_cannot_process_more_data_when_stream_is_terminated(self):
4092
m = MagicMock()
4193
ws = WebSocket(sock=m)
@@ -56,6 +108,67 @@ def test_no_bytes_were_read(self):
56108
ws = WebSocket(sock=m)
57109
self.assertFalse(ws.once())
58110

111+
def test_send_bytes_without_masking(self):
112+
tm = TextMessage(b'hello world').single()
113+
114+
m = MagicMock()
115+
ws = WebSocket(sock=m)
116+
ws.send(b'hello world')
117+
m.sendall.assert_called_once_with(tm)
118+
119+
def test_send_bytes_with_masking(self):
120+
tm = TextMessage(b'hello world').single(mask=True)
121+
122+
m = MagicMock()
123+
ws = WebSocket(sock=m)
124+
ws.stream = MagicMock()
125+
ws.stream.always_mask = True
126+
ws.stream.text_message.return_value.single.return_value = tm
127+
128+
ws.send(b'hello world')
129+
m.sendall.assert_called_once_with(tm)
130+
131+
def test_send_message_without_masking(self):
132+
tm = TextMessage(b'hello world')
133+
134+
m = MagicMock()
135+
ws = WebSocket(sock=m)
136+
ws.send(tm)
137+
m.sendall.assert_called_once_with(tm.single())
138+
139+
def test_send_generator_without_masking(self):
140+
tm0 = b'hello'
141+
tm1 = b'world'
142+
143+
def datasource():
144+
yield tm0
145+
yield tm1
146+
147+
gen = datasource()
148+
149+
m = MagicMock()
150+
ws = WebSocket(sock=m)
151+
ws.send(gen)
152+
self.assertEqual(m.sendall.call_count, 2)
153+
self.assertRaises(StopIteration, next, gen)
154+
155+
def test_sending_unknown_datetype(self):
156+
m = MagicMock()
157+
ws = WebSocket(sock=m)
158+
self.assertRaises(ValueError, ws.send, 123)
159+
160+
def test_closing_message_received(self):
161+
s = MagicMock()
162+
m = MagicMock()
163+
c = MagicMock()
164+
165+
ws = WebSocket(sock=m)
166+
with patch.multiple(ws, close=c):
167+
ws.stream = s
168+
ws.stream.closing = CloseControlMessage(code=1000, reason='test closing')
169+
ws.process(b'unused for this test')
170+
c.assert_called_once_with(1000, 'test closing')
171+
59172

60173
if __name__ == '__main__':
61174
suite = unittest.TestSuite()

ws4py/framing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def _parsing(self):
181181
some_bytes = b''
182182

183183
if self.payload_length == 127:
184+
# This will compute the actual application data size
184185
if len(buf) < 8:
185186
nxt_buf_size = 8 - len(buf)
186187
some_bytes = (yield nxt_buf_size)
@@ -191,6 +192,7 @@ def _parsing(self):
191192
some_bytes = some_bytes + b
192193
if len(some_bytes) > 8:
193194
buf = some_bytes[8:]
195+
some_bytes = some_bytes[:8]
194196
else:
195197
some_bytes = buf[:8]
196198
buf = buf[8:]
@@ -210,6 +212,7 @@ def _parsing(self):
210212
some_bytes = some_bytes + b
211213
if len(some_bytes) > 2:
212214
buf = some_bytes[2:]
215+
some_bytes = some_bytes[:2]
213216
else:
214217
some_bytes = buf[:2]
215218
buf = buf[2:]

0 commit comments

Comments
 (0)