##// END OF EJS Templates
tests: correct version check in clientreactor test...
Augie Fackler -
r41154:685cf59a default
parent child Browse files
Show More
@@ -1,610 +1,610 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import sys
3 import sys
4 import unittest
4 import unittest
5 import zlib
5 import zlib
6
6
7 from mercurial import (
7 from mercurial import (
8 error,
8 error,
9 ui as uimod,
9 ui as uimod,
10 wireprotoframing as framing,
10 wireprotoframing as framing,
11 )
11 )
12 from mercurial.utils import (
12 from mercurial.utils import (
13 cborutil,
13 cborutil,
14 )
14 )
15
15
16 try:
16 try:
17 from mercurial import zstd
17 from mercurial import zstd
18 zstd.__version__
18 zstd.__version__
19 except ImportError:
19 except ImportError:
20 zstd = None
20 zstd = None
21
21
22 ffs = framing.makeframefromhumanstring
22 ffs = framing.makeframefromhumanstring
23
23
24 globalui = uimod.ui()
24 globalui = uimod.ui()
25
25
26 def sendframe(reactor, frame):
26 def sendframe(reactor, frame):
27 """Send a frame bytearray to a reactor."""
27 """Send a frame bytearray to a reactor."""
28 header = framing.parseheader(frame)
28 header = framing.parseheader(frame)
29 payload = frame[framing.FRAME_HEADER_SIZE:]
29 payload = frame[framing.FRAME_HEADER_SIZE:]
30 assert len(payload) == header.length
30 assert len(payload) == header.length
31
31
32 return reactor.onframerecv(framing.frame(header.requestid,
32 return reactor.onframerecv(framing.frame(header.requestid,
33 header.streamid,
33 header.streamid,
34 header.streamflags,
34 header.streamflags,
35 header.typeid,
35 header.typeid,
36 header.flags,
36 header.flags,
37 payload))
37 payload))
38
38
39 class SingleSendTests(unittest.TestCase):
39 class SingleSendTests(unittest.TestCase):
40 """A reactor that can only send once rejects subsequent sends."""
40 """A reactor that can only send once rejects subsequent sends."""
41
41
42 if not getattr(unittest.TestCase, 'assertRaisesRegex', False):
42 if not getattr(unittest.TestCase, 'assertRaisesRegex', False):
43 # Python 3.7 deprecates the regex*p* version, but 2.7 lacks
43 # Python 3.7 deprecates the regex*p* version, but 2.7 lacks
44 # the regex version.
44 # the regex version.
45 assertRaisesRegex = (# camelcase-required
45 assertRaisesRegex = (# camelcase-required
46 unittest.TestCase.assertRaisesRegexp)
46 unittest.TestCase.assertRaisesRegexp)
47
47
48 def testbasic(self):
48 def testbasic(self):
49 reactor = framing.clientreactor(globalui,
49 reactor = framing.clientreactor(globalui,
50 hasmultiplesend=False,
50 hasmultiplesend=False,
51 buffersends=True)
51 buffersends=True)
52
52
53 request, action, meta = reactor.callcommand(b'foo', {})
53 request, action, meta = reactor.callcommand(b'foo', {})
54 self.assertEqual(request.state, b'pending')
54 self.assertEqual(request.state, b'pending')
55 self.assertEqual(action, b'noop')
55 self.assertEqual(action, b'noop')
56
56
57 action, meta = reactor.flushcommands()
57 action, meta = reactor.flushcommands()
58 self.assertEqual(action, b'sendframes')
58 self.assertEqual(action, b'sendframes')
59
59
60 for frame in meta[b'framegen']:
60 for frame in meta[b'framegen']:
61 self.assertEqual(request.state, b'sending')
61 self.assertEqual(request.state, b'sending')
62
62
63 self.assertEqual(request.state, b'sent')
63 self.assertEqual(request.state, b'sent')
64
64
65 with self.assertRaisesRegex(error.ProgrammingError,
65 with self.assertRaisesRegex(error.ProgrammingError,
66 'cannot issue new commands'):
66 'cannot issue new commands'):
67 reactor.callcommand(b'foo', {})
67 reactor.callcommand(b'foo', {})
68
68
69 with self.assertRaisesRegex(error.ProgrammingError,
69 with self.assertRaisesRegex(error.ProgrammingError,
70 'cannot issue new commands'):
70 'cannot issue new commands'):
71 reactor.callcommand(b'foo', {})
71 reactor.callcommand(b'foo', {})
72
72
73 class NoBufferTests(unittest.TestCase):
73 class NoBufferTests(unittest.TestCase):
74 """A reactor without send buffering sends requests immediately."""
74 """A reactor without send buffering sends requests immediately."""
75 def testbasic(self):
75 def testbasic(self):
76 reactor = framing.clientreactor(globalui,
76 reactor = framing.clientreactor(globalui,
77 hasmultiplesend=True,
77 hasmultiplesend=True,
78 buffersends=False)
78 buffersends=False)
79
79
80 request, action, meta = reactor.callcommand(b'command1', {})
80 request, action, meta = reactor.callcommand(b'command1', {})
81 self.assertEqual(request.requestid, 1)
81 self.assertEqual(request.requestid, 1)
82 self.assertEqual(action, b'sendframes')
82 self.assertEqual(action, b'sendframes')
83
83
84 self.assertEqual(request.state, b'pending')
84 self.assertEqual(request.state, b'pending')
85
85
86 for frame in meta[b'framegen']:
86 for frame in meta[b'framegen']:
87 self.assertEqual(request.state, b'sending')
87 self.assertEqual(request.state, b'sending')
88
88
89 self.assertEqual(request.state, b'sent')
89 self.assertEqual(request.state, b'sent')
90
90
91 action, meta = reactor.flushcommands()
91 action, meta = reactor.flushcommands()
92 self.assertEqual(action, b'noop')
92 self.assertEqual(action, b'noop')
93
93
94 # And we can send another command.
94 # And we can send another command.
95 request, action, meta = reactor.callcommand(b'command2', {})
95 request, action, meta = reactor.callcommand(b'command2', {})
96 self.assertEqual(request.requestid, 3)
96 self.assertEqual(request.requestid, 3)
97 self.assertEqual(action, b'sendframes')
97 self.assertEqual(action, b'sendframes')
98
98
99 for frame in meta[b'framegen']:
99 for frame in meta[b'framegen']:
100 self.assertEqual(request.state, b'sending')
100 self.assertEqual(request.state, b'sending')
101
101
102 self.assertEqual(request.state, b'sent')
102 self.assertEqual(request.state, b'sent')
103
103
104 class BadFrameRecvTests(unittest.TestCase):
104 class BadFrameRecvTests(unittest.TestCase):
105 if not getattr(unittest.TestCase, 'assertRaisesRegex', False):
105 if not getattr(unittest.TestCase, 'assertRaisesRegex', False):
106 # Python 3.7 deprecates the regex*p* version, but 2.7 lacks
106 # Python 3.7 deprecates the regex*p* version, but 2.7 lacks
107 # the regex version.
107 # the regex version.
108 assertRaisesRegex = (# camelcase-required
108 assertRaisesRegex = (# camelcase-required
109 unittest.TestCase.assertRaisesRegexp)
109 unittest.TestCase.assertRaisesRegexp)
110
110
111 def testoddstream(self):
111 def testoddstream(self):
112 reactor = framing.clientreactor(globalui)
112 reactor = framing.clientreactor(globalui)
113
113
114 action, meta = sendframe(reactor, ffs(b'1 1 0 1 0 foo'))
114 action, meta = sendframe(reactor, ffs(b'1 1 0 1 0 foo'))
115 self.assertEqual(action, b'error')
115 self.assertEqual(action, b'error')
116 self.assertEqual(meta[b'message'],
116 self.assertEqual(meta[b'message'],
117 b'received frame with odd numbered stream ID: 1')
117 b'received frame with odd numbered stream ID: 1')
118
118
119 def testunknownstream(self):
119 def testunknownstream(self):
120 reactor = framing.clientreactor(globalui)
120 reactor = framing.clientreactor(globalui)
121
121
122 action, meta = sendframe(reactor, ffs(b'1 0 0 1 0 foo'))
122 action, meta = sendframe(reactor, ffs(b'1 0 0 1 0 foo'))
123 self.assertEqual(action, b'error')
123 self.assertEqual(action, b'error')
124 self.assertEqual(meta[b'message'],
124 self.assertEqual(meta[b'message'],
125 b'received frame on unknown stream without beginning '
125 b'received frame on unknown stream without beginning '
126 b'of stream flag set')
126 b'of stream flag set')
127
127
128 def testunhandledframetype(self):
128 def testunhandledframetype(self):
129 reactor = framing.clientreactor(globalui, buffersends=False)
129 reactor = framing.clientreactor(globalui, buffersends=False)
130
130
131 request, action, meta = reactor.callcommand(b'foo', {})
131 request, action, meta = reactor.callcommand(b'foo', {})
132 for frame in meta[b'framegen']:
132 for frame in meta[b'framegen']:
133 pass
133 pass
134
134
135 with self.assertRaisesRegex(error.ProgrammingError,
135 with self.assertRaisesRegex(error.ProgrammingError,
136 'unhandled frame type'):
136 'unhandled frame type'):
137 sendframe(reactor, ffs(b'1 0 stream-begin text-output 0 foo'))
137 sendframe(reactor, ffs(b'1 0 stream-begin text-output 0 foo'))
138
138
139 class StreamTests(unittest.TestCase):
139 class StreamTests(unittest.TestCase):
140 def testmultipleresponseframes(self):
140 def testmultipleresponseframes(self):
141 reactor = framing.clientreactor(globalui, buffersends=False)
141 reactor = framing.clientreactor(globalui, buffersends=False)
142
142
143 request, action, meta = reactor.callcommand(b'foo', {})
143 request, action, meta = reactor.callcommand(b'foo', {})
144
144
145 self.assertEqual(action, b'sendframes')
145 self.assertEqual(action, b'sendframes')
146 for f in meta[b'framegen']:
146 for f in meta[b'framegen']:
147 pass
147 pass
148
148
149 action, meta = sendframe(
149 action, meta = sendframe(
150 reactor,
150 reactor,
151 ffs(b'%d 0 stream-begin command-response 0 foo' %
151 ffs(b'%d 0 stream-begin command-response 0 foo' %
152 request.requestid))
152 request.requestid))
153 self.assertEqual(action, b'responsedata')
153 self.assertEqual(action, b'responsedata')
154
154
155 action, meta = sendframe(
155 action, meta = sendframe(
156 reactor,
156 reactor,
157 ffs(b'%d 0 0 command-response eos bar' % request.requestid))
157 ffs(b'%d 0 0 command-response eos bar' % request.requestid))
158 self.assertEqual(action, b'responsedata')
158 self.assertEqual(action, b'responsedata')
159
159
160 class RedirectTests(unittest.TestCase):
160 class RedirectTests(unittest.TestCase):
161 def testredirect(self):
161 def testredirect(self):
162 reactor = framing.clientreactor(globalui, buffersends=False)
162 reactor = framing.clientreactor(globalui, buffersends=False)
163
163
164 redirect = {
164 redirect = {
165 b'targets': [b'a', b'b'],
165 b'targets': [b'a', b'b'],
166 b'hashes': [b'sha256'],
166 b'hashes': [b'sha256'],
167 }
167 }
168
168
169 request, action, meta = reactor.callcommand(
169 request, action, meta = reactor.callcommand(
170 b'foo', {}, redirect=redirect)
170 b'foo', {}, redirect=redirect)
171
171
172 self.assertEqual(action, b'sendframes')
172 self.assertEqual(action, b'sendframes')
173
173
174 frames = list(meta[b'framegen'])
174 frames = list(meta[b'framegen'])
175 self.assertEqual(len(frames), 1)
175 self.assertEqual(len(frames), 1)
176
176
177 self.assertEqual(frames[0],
177 self.assertEqual(frames[0],
178 ffs(b'1 1 stream-begin command-request new '
178 ffs(b'1 1 stream-begin command-request new '
179 b"cbor:{b'name': b'foo', "
179 b"cbor:{b'name': b'foo', "
180 b"b'redirect': {b'targets': [b'a', b'b'], "
180 b"b'redirect': {b'targets': [b'a', b'b'], "
181 b"b'hashes': [b'sha256']}}"))
181 b"b'hashes': [b'sha256']}}"))
182
182
183 class StreamSettingsTests(unittest.TestCase):
183 class StreamSettingsTests(unittest.TestCase):
184 def testnoflags(self):
184 def testnoflags(self):
185 reactor = framing.clientreactor(globalui, buffersends=False)
185 reactor = framing.clientreactor(globalui, buffersends=False)
186
186
187 request, action, meta = reactor.callcommand(b'foo', {})
187 request, action, meta = reactor.callcommand(b'foo', {})
188 for f in meta[b'framegen']:
188 for f in meta[b'framegen']:
189 pass
189 pass
190
190
191 action, meta = sendframe(reactor,
191 action, meta = sendframe(reactor,
192 ffs(b'1 2 stream-begin stream-settings 0 '))
192 ffs(b'1 2 stream-begin stream-settings 0 '))
193
193
194 self.assertEqual(action, b'error')
194 self.assertEqual(action, b'error')
195 self.assertEqual(meta, {
195 self.assertEqual(meta, {
196 b'message': b'stream encoding settings frame must have '
196 b'message': b'stream encoding settings frame must have '
197 b'continuation or end of stream flag set',
197 b'continuation or end of stream flag set',
198 })
198 })
199
199
200 def testconflictflags(self):
200 def testconflictflags(self):
201 reactor = framing.clientreactor(globalui, buffersends=False)
201 reactor = framing.clientreactor(globalui, buffersends=False)
202
202
203 request, action, meta = reactor.callcommand(b'foo', {})
203 request, action, meta = reactor.callcommand(b'foo', {})
204 for f in meta[b'framegen']:
204 for f in meta[b'framegen']:
205 pass
205 pass
206
206
207 action, meta = sendframe(reactor,
207 action, meta = sendframe(reactor,
208 ffs(b'1 2 stream-begin stream-settings continuation|eos '))
208 ffs(b'1 2 stream-begin stream-settings continuation|eos '))
209
209
210 self.assertEqual(action, b'error')
210 self.assertEqual(action, b'error')
211 self.assertEqual(meta, {
211 self.assertEqual(meta, {
212 b'message': b'stream encoding settings frame cannot have both '
212 b'message': b'stream encoding settings frame cannot have both '
213 b'continuation and end of stream flags set',
213 b'continuation and end of stream flags set',
214 })
214 })
215
215
216 def testemptypayload(self):
216 def testemptypayload(self):
217 reactor = framing.clientreactor(globalui, buffersends=False)
217 reactor = framing.clientreactor(globalui, buffersends=False)
218
218
219 request, action, meta = reactor.callcommand(b'foo', {})
219 request, action, meta = reactor.callcommand(b'foo', {})
220 for f in meta[b'framegen']:
220 for f in meta[b'framegen']:
221 pass
221 pass
222
222
223 action, meta = sendframe(reactor,
223 action, meta = sendframe(reactor,
224 ffs(b'1 2 stream-begin stream-settings eos '))
224 ffs(b'1 2 stream-begin stream-settings eos '))
225
225
226 self.assertEqual(action, b'error')
226 self.assertEqual(action, b'error')
227 self.assertEqual(meta, {
227 self.assertEqual(meta, {
228 b'message': b'stream encoding settings frame did not contain '
228 b'message': b'stream encoding settings frame did not contain '
229 b'CBOR data'
229 b'CBOR data'
230 })
230 })
231
231
232 def testbadcbor(self):
232 def testbadcbor(self):
233 reactor = framing.clientreactor(globalui, buffersends=False)
233 reactor = framing.clientreactor(globalui, buffersends=False)
234
234
235 request, action, meta = reactor.callcommand(b'foo', {})
235 request, action, meta = reactor.callcommand(b'foo', {})
236 for f in meta[b'framegen']:
236 for f in meta[b'framegen']:
237 pass
237 pass
238
238
239 action, meta = sendframe(reactor,
239 action, meta = sendframe(reactor,
240 ffs(b'1 2 stream-begin stream-settings eos badvalue'))
240 ffs(b'1 2 stream-begin stream-settings eos badvalue'))
241
241
242 self.assertEqual(action, b'error')
242 self.assertEqual(action, b'error')
243
243
244 def testsingleobject(self):
244 def testsingleobject(self):
245 reactor = framing.clientreactor(globalui, buffersends=False)
245 reactor = framing.clientreactor(globalui, buffersends=False)
246
246
247 request, action, meta = reactor.callcommand(b'foo', {})
247 request, action, meta = reactor.callcommand(b'foo', {})
248 for f in meta[b'framegen']:
248 for f in meta[b'framegen']:
249 pass
249 pass
250
250
251 action, meta = sendframe(reactor,
251 action, meta = sendframe(reactor,
252 ffs(b'1 2 stream-begin stream-settings eos cbor:b"identity"'))
252 ffs(b'1 2 stream-begin stream-settings eos cbor:b"identity"'))
253
253
254 self.assertEqual(action, b'noop')
254 self.assertEqual(action, b'noop')
255 self.assertEqual(meta, {})
255 self.assertEqual(meta, {})
256
256
257 def testmultipleobjects(self):
257 def testmultipleobjects(self):
258 reactor = framing.clientreactor(globalui, buffersends=False)
258 reactor = framing.clientreactor(globalui, buffersends=False)
259
259
260 request, action, meta = reactor.callcommand(b'foo', {})
260 request, action, meta = reactor.callcommand(b'foo', {})
261 for f in meta[b'framegen']:
261 for f in meta[b'framegen']:
262 pass
262 pass
263
263
264 data = b''.join([
264 data = b''.join([
265 b''.join(cborutil.streamencode(b'identity')),
265 b''.join(cborutil.streamencode(b'identity')),
266 b''.join(cborutil.streamencode({b'foo', b'bar'})),
266 b''.join(cborutil.streamencode({b'foo', b'bar'})),
267 ])
267 ])
268
268
269 action, meta = sendframe(reactor,
269 action, meta = sendframe(reactor,
270 ffs(b'1 2 stream-begin stream-settings eos %s' % data))
270 ffs(b'1 2 stream-begin stream-settings eos %s' % data))
271
271
272 self.assertEqual(action, b'error')
272 self.assertEqual(action, b'error')
273 self.assertEqual(meta, {
273 self.assertEqual(meta, {
274 b'message': b'error setting stream decoder: identity decoder '
274 b'message': b'error setting stream decoder: identity decoder '
275 b'received unexpected additional values',
275 b'received unexpected additional values',
276 })
276 })
277
277
278 def testmultipleframes(self):
278 def testmultipleframes(self):
279 reactor = framing.clientreactor(globalui, buffersends=False)
279 reactor = framing.clientreactor(globalui, buffersends=False)
280
280
281 request, action, meta = reactor.callcommand(b'foo', {})
281 request, action, meta = reactor.callcommand(b'foo', {})
282 for f in meta[b'framegen']:
282 for f in meta[b'framegen']:
283 pass
283 pass
284
284
285 data = b''.join(cborutil.streamencode(b'identity'))
285 data = b''.join(cborutil.streamencode(b'identity'))
286
286
287 action, meta = sendframe(reactor,
287 action, meta = sendframe(reactor,
288 ffs(b'1 2 stream-begin stream-settings continuation %s' %
288 ffs(b'1 2 stream-begin stream-settings continuation %s' %
289 data[0:3]))
289 data[0:3]))
290
290
291 self.assertEqual(action, b'noop')
291 self.assertEqual(action, b'noop')
292 self.assertEqual(meta, {})
292 self.assertEqual(meta, {})
293
293
294 action, meta = sendframe(reactor,
294 action, meta = sendframe(reactor,
295 ffs(b'1 2 0 stream-settings eos %s' % data[3:]))
295 ffs(b'1 2 0 stream-settings eos %s' % data[3:]))
296
296
297 self.assertEqual(action, b'noop')
297 self.assertEqual(action, b'noop')
298 self.assertEqual(meta, {})
298 self.assertEqual(meta, {})
299
299
300 def testinvalidencoder(self):
300 def testinvalidencoder(self):
301 reactor = framing.clientreactor(globalui, buffersends=False)
301 reactor = framing.clientreactor(globalui, buffersends=False)
302
302
303 request, action, meta = reactor.callcommand(b'foo', {})
303 request, action, meta = reactor.callcommand(b'foo', {})
304 for f in meta[b'framegen']:
304 for f in meta[b'framegen']:
305 pass
305 pass
306
306
307 action, meta = sendframe(reactor,
307 action, meta = sendframe(reactor,
308 ffs(b'1 2 stream-begin stream-settings eos cbor:b"badvalue"'))
308 ffs(b'1 2 stream-begin stream-settings eos cbor:b"badvalue"'))
309
309
310 self.assertEqual(action, b'error')
310 self.assertEqual(action, b'error')
311 self.assertEqual(meta, {
311 self.assertEqual(meta, {
312 b'message': b'error setting stream decoder: unknown stream '
312 b'message': b'error setting stream decoder: unknown stream '
313 b'decoder: badvalue',
313 b'decoder: badvalue',
314 })
314 })
315
315
316 def testzlibencoding(self):
316 def testzlibencoding(self):
317 reactor = framing.clientreactor(globalui, buffersends=False)
317 reactor = framing.clientreactor(globalui, buffersends=False)
318
318
319 request, action, meta = reactor.callcommand(b'foo', {})
319 request, action, meta = reactor.callcommand(b'foo', {})
320 for f in meta[b'framegen']:
320 for f in meta[b'framegen']:
321 pass
321 pass
322
322
323 action, meta = sendframe(reactor,
323 action, meta = sendframe(reactor,
324 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
324 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
325 request.requestid))
325 request.requestid))
326
326
327 self.assertEqual(action, b'noop')
327 self.assertEqual(action, b'noop')
328 self.assertEqual(meta, {})
328 self.assertEqual(meta, {})
329
329
330 result = {
330 result = {
331 b'status': b'ok',
331 b'status': b'ok',
332 }
332 }
333 encoded = b''.join(cborutil.streamencode(result))
333 encoded = b''.join(cborutil.streamencode(result))
334
334
335 compressed = zlib.compress(encoded)
335 compressed = zlib.compress(encoded)
336 self.assertEqual(zlib.decompress(compressed), encoded)
336 self.assertEqual(zlib.decompress(compressed), encoded)
337
337
338 action, meta = sendframe(reactor,
338 action, meta = sendframe(reactor,
339 ffs(b'%d 2 encoded command-response eos %s' %
339 ffs(b'%d 2 encoded command-response eos %s' %
340 (request.requestid, compressed)))
340 (request.requestid, compressed)))
341
341
342 self.assertEqual(action, b'responsedata')
342 self.assertEqual(action, b'responsedata')
343 self.assertEqual(meta[b'data'], encoded)
343 self.assertEqual(meta[b'data'], encoded)
344
344
345 def testzlibencodingsinglebyteframes(self):
345 def testzlibencodingsinglebyteframes(self):
346 reactor = framing.clientreactor(globalui, buffersends=False)
346 reactor = framing.clientreactor(globalui, buffersends=False)
347
347
348 request, action, meta = reactor.callcommand(b'foo', {})
348 request, action, meta = reactor.callcommand(b'foo', {})
349 for f in meta[b'framegen']:
349 for f in meta[b'framegen']:
350 pass
350 pass
351
351
352 action, meta = sendframe(reactor,
352 action, meta = sendframe(reactor,
353 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
353 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
354 request.requestid))
354 request.requestid))
355
355
356 self.assertEqual(action, b'noop')
356 self.assertEqual(action, b'noop')
357 self.assertEqual(meta, {})
357 self.assertEqual(meta, {})
358
358
359 result = {
359 result = {
360 b'status': b'ok',
360 b'status': b'ok',
361 }
361 }
362 encoded = b''.join(cborutil.streamencode(result))
362 encoded = b''.join(cborutil.streamencode(result))
363
363
364 compressed = zlib.compress(encoded)
364 compressed = zlib.compress(encoded)
365 self.assertEqual(zlib.decompress(compressed), encoded)
365 self.assertEqual(zlib.decompress(compressed), encoded)
366
366
367 chunks = []
367 chunks = []
368
368
369 for i in range(len(compressed)):
369 for i in range(len(compressed)):
370 char = compressed[i:i + 1]
370 char = compressed[i:i + 1]
371 if char == b'\\':
371 if char == b'\\':
372 char = b'\\\\'
372 char = b'\\\\'
373 action, meta = sendframe(reactor,
373 action, meta = sendframe(reactor,
374 ffs(b'%d 2 encoded command-response continuation %s' %
374 ffs(b'%d 2 encoded command-response continuation %s' %
375 (request.requestid, char)))
375 (request.requestid, char)))
376
376
377 self.assertEqual(action, b'responsedata')
377 self.assertEqual(action, b'responsedata')
378 chunks.append(meta[b'data'])
378 chunks.append(meta[b'data'])
379 self.assertTrue(meta[b'expectmore'])
379 self.assertTrue(meta[b'expectmore'])
380 self.assertFalse(meta[b'eos'])
380 self.assertFalse(meta[b'eos'])
381
381
382 # zlib will have the full data decoded at this point, even though
382 # zlib will have the full data decoded at this point, even though
383 # we haven't flushed.
383 # we haven't flushed.
384 self.assertEqual(b''.join(chunks), encoded)
384 self.assertEqual(b''.join(chunks), encoded)
385
385
386 # End the stream for good measure.
386 # End the stream for good measure.
387 action, meta = sendframe(reactor,
387 action, meta = sendframe(reactor,
388 ffs(b'%d 2 stream-end command-response eos ' % request.requestid))
388 ffs(b'%d 2 stream-end command-response eos ' % request.requestid))
389
389
390 self.assertEqual(action, b'responsedata')
390 self.assertEqual(action, b'responsedata')
391 self.assertEqual(meta[b'data'], b'')
391 self.assertEqual(meta[b'data'], b'')
392 self.assertFalse(meta[b'expectmore'])
392 self.assertFalse(meta[b'expectmore'])
393 self.assertTrue(meta[b'eos'])
393 self.assertTrue(meta[b'eos'])
394
394
395 def testzlibmultipleresponses(self):
395 def testzlibmultipleresponses(self):
396 # We feed in zlib compressed data on the same stream but belonging to
396 # We feed in zlib compressed data on the same stream but belonging to
397 # 2 different requests. This tests our flushing behavior.
397 # 2 different requests. This tests our flushing behavior.
398 reactor = framing.clientreactor(globalui, buffersends=False,
398 reactor = framing.clientreactor(globalui, buffersends=False,
399 hasmultiplesend=True)
399 hasmultiplesend=True)
400
400
401 request1, action, meta = reactor.callcommand(b'foo', {})
401 request1, action, meta = reactor.callcommand(b'foo', {})
402 for f in meta[b'framegen']:
402 for f in meta[b'framegen']:
403 pass
403 pass
404
404
405 request2, action, meta = reactor.callcommand(b'foo', {})
405 request2, action, meta = reactor.callcommand(b'foo', {})
406 for f in meta[b'framegen']:
406 for f in meta[b'framegen']:
407 pass
407 pass
408
408
409 outstream = framing.outputstream(2)
409 outstream = framing.outputstream(2)
410 outstream.setencoder(globalui, b'zlib')
410 outstream.setencoder(globalui, b'zlib')
411
411
412 response1 = b''.join(cborutil.streamencode({
412 response1 = b''.join(cborutil.streamencode({
413 b'status': b'ok',
413 b'status': b'ok',
414 b'extra': b'response1' * 10,
414 b'extra': b'response1' * 10,
415 }))
415 }))
416
416
417 response2 = b''.join(cborutil.streamencode({
417 response2 = b''.join(cborutil.streamencode({
418 b'status': b'error',
418 b'status': b'error',
419 b'extra': b'response2' * 10,
419 b'extra': b'response2' * 10,
420 }))
420 }))
421
421
422 action, meta = sendframe(reactor,
422 action, meta = sendframe(reactor,
423 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
423 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zlib"' %
424 request1.requestid))
424 request1.requestid))
425
425
426 self.assertEqual(action, b'noop')
426 self.assertEqual(action, b'noop')
427 self.assertEqual(meta, {})
427 self.assertEqual(meta, {})
428
428
429 # Feeding partial data in won't get anything useful out.
429 # Feeding partial data in won't get anything useful out.
430 action, meta = sendframe(reactor,
430 action, meta = sendframe(reactor,
431 ffs(b'%d 2 encoded command-response continuation %s' % (
431 ffs(b'%d 2 encoded command-response continuation %s' % (
432 request1.requestid, outstream.encode(response1))))
432 request1.requestid, outstream.encode(response1))))
433 self.assertEqual(action, b'responsedata')
433 self.assertEqual(action, b'responsedata')
434 self.assertEqual(meta[b'data'], b'')
434 self.assertEqual(meta[b'data'], b'')
435
435
436 # But flushing data at both ends will get our original data.
436 # But flushing data at both ends will get our original data.
437 action, meta = sendframe(reactor,
437 action, meta = sendframe(reactor,
438 ffs(b'%d 2 encoded command-response eos %s' % (
438 ffs(b'%d 2 encoded command-response eos %s' % (
439 request1.requestid, outstream.flush())))
439 request1.requestid, outstream.flush())))
440 self.assertEqual(action, b'responsedata')
440 self.assertEqual(action, b'responsedata')
441 self.assertEqual(meta[b'data'], response1)
441 self.assertEqual(meta[b'data'], response1)
442
442
443 # We should be able to reuse the compressor/decompressor for the
443 # We should be able to reuse the compressor/decompressor for the
444 # 2nd response.
444 # 2nd response.
445 action, meta = sendframe(reactor,
445 action, meta = sendframe(reactor,
446 ffs(b'%d 2 encoded command-response continuation %s' % (
446 ffs(b'%d 2 encoded command-response continuation %s' % (
447 request2.requestid, outstream.encode(response2))))
447 request2.requestid, outstream.encode(response2))))
448 self.assertEqual(action, b'responsedata')
448 self.assertEqual(action, b'responsedata')
449 self.assertEqual(meta[b'data'], b'')
449 self.assertEqual(meta[b'data'], b'')
450
450
451 action, meta = sendframe(reactor,
451 action, meta = sendframe(reactor,
452 ffs(b'%d 2 encoded command-response eos %s' % (
452 ffs(b'%d 2 encoded command-response eos %s' % (
453 request2.requestid, outstream.flush())))
453 request2.requestid, outstream.flush())))
454 self.assertEqual(action, b'responsedata')
454 self.assertEqual(action, b'responsedata')
455 self.assertEqual(meta[b'data'], response2)
455 self.assertEqual(meta[b'data'], response2)
456
456
457 @unittest.skipUnless(zstd, 'zstd not available')
457 @unittest.skipUnless(zstd, 'zstd not available')
458 def testzstd8mbencoding(self):
458 def testzstd8mbencoding(self):
459 reactor = framing.clientreactor(globalui, buffersends=False)
459 reactor = framing.clientreactor(globalui, buffersends=False)
460
460
461 request, action, meta = reactor.callcommand(b'foo', {})
461 request, action, meta = reactor.callcommand(b'foo', {})
462 for f in meta[b'framegen']:
462 for f in meta[b'framegen']:
463 pass
463 pass
464
464
465 action, meta = sendframe(reactor,
465 action, meta = sendframe(reactor,
466 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
466 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
467 request.requestid))
467 request.requestid))
468
468
469 self.assertEqual(action, b'noop')
469 self.assertEqual(action, b'noop')
470 self.assertEqual(meta, {})
470 self.assertEqual(meta, {})
471
471
472 result = {
472 result = {
473 b'status': b'ok',
473 b'status': b'ok',
474 }
474 }
475 encoded = b''.join(cborutil.streamencode(result))
475 encoded = b''.join(cborutil.streamencode(result))
476
476
477 encoder = framing.zstd8mbencoder(globalui)
477 encoder = framing.zstd8mbencoder(globalui)
478 compressed = encoder.encode(encoded) + encoder.finish()
478 compressed = encoder.encode(encoded) + encoder.finish()
479 self.assertEqual(zstd.ZstdDecompressor().decompress(
479 self.assertEqual(zstd.ZstdDecompressor().decompress(
480 compressed, max_output_size=len(encoded)), encoded)
480 compressed, max_output_size=len(encoded)), encoded)
481
481
482 action, meta = sendframe(reactor,
482 action, meta = sendframe(reactor,
483 ffs(b'%d 2 encoded command-response eos %s' %
483 ffs(b'%d 2 encoded command-response eos %s' %
484 (request.requestid, compressed)))
484 (request.requestid, compressed)))
485
485
486 self.assertEqual(action, b'responsedata')
486 self.assertEqual(action, b'responsedata')
487 self.assertEqual(meta[b'data'], encoded)
487 self.assertEqual(meta[b'data'], encoded)
488
488
489 @unittest.skipUnless(zstd, 'zstd not available')
489 @unittest.skipUnless(zstd, 'zstd not available')
490 def testzstd8mbencodingsinglebyteframes(self):
490 def testzstd8mbencodingsinglebyteframes(self):
491 reactor = framing.clientreactor(globalui, buffersends=False)
491 reactor = framing.clientreactor(globalui, buffersends=False)
492
492
493 request, action, meta = reactor.callcommand(b'foo', {})
493 request, action, meta = reactor.callcommand(b'foo', {})
494 for f in meta[b'framegen']:
494 for f in meta[b'framegen']:
495 pass
495 pass
496
496
497 action, meta = sendframe(reactor,
497 action, meta = sendframe(reactor,
498 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
498 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
499 request.requestid))
499 request.requestid))
500
500
501 self.assertEqual(action, b'noop')
501 self.assertEqual(action, b'noop')
502 self.assertEqual(meta, {})
502 self.assertEqual(meta, {})
503
503
504 result = {
504 result = {
505 b'status': b'ok',
505 b'status': b'ok',
506 }
506 }
507 encoded = b''.join(cborutil.streamencode(result))
507 encoded = b''.join(cborutil.streamencode(result))
508
508
509 compressed = zstd.ZstdCompressor().compress(encoded)
509 compressed = zstd.ZstdCompressor().compress(encoded)
510 self.assertEqual(zstd.ZstdDecompressor().decompress(compressed),
510 self.assertEqual(zstd.ZstdDecompressor().decompress(compressed),
511 encoded)
511 encoded)
512
512
513 chunks = []
513 chunks = []
514
514
515 for i in range(len(compressed)):
515 for i in range(len(compressed)):
516 char = compressed[i:i + 1]
516 char = compressed[i:i + 1]
517 if char == b'\\':
517 if char == b'\\':
518 char = b'\\\\'
518 char = b'\\\\'
519 action, meta = sendframe(reactor,
519 action, meta = sendframe(reactor,
520 ffs(b'%d 2 encoded command-response continuation %s' %
520 ffs(b'%d 2 encoded command-response continuation %s' %
521 (request.requestid, char)))
521 (request.requestid, char)))
522
522
523 self.assertEqual(action, b'responsedata')
523 self.assertEqual(action, b'responsedata')
524 chunks.append(meta[b'data'])
524 chunks.append(meta[b'data'])
525 self.assertTrue(meta[b'expectmore'])
525 self.assertTrue(meta[b'expectmore'])
526 self.assertFalse(meta[b'eos'])
526 self.assertFalse(meta[b'eos'])
527
527
528 # zstd decompressor will flush at frame boundaries.
528 # zstd decompressor will flush at frame boundaries.
529 self.assertEqual(b''.join(chunks), encoded)
529 self.assertEqual(b''.join(chunks), encoded)
530
530
531 # End the stream for good measure.
531 # End the stream for good measure.
532 action, meta = sendframe(reactor,
532 action, meta = sendframe(reactor,
533 ffs(b'%d 2 stream-end command-response eos ' % request.requestid))
533 ffs(b'%d 2 stream-end command-response eos ' % request.requestid))
534
534
535 self.assertEqual(action, b'responsedata')
535 self.assertEqual(action, b'responsedata')
536 self.assertEqual(meta[b'data'], b'')
536 self.assertEqual(meta[b'data'], b'')
537 self.assertFalse(meta[b'expectmore'])
537 self.assertFalse(meta[b'expectmore'])
538 self.assertTrue(meta[b'eos'])
538 self.assertTrue(meta[b'eos'])
539
539
540 @unittest.skipUnless(zstd, 'zstd not available')
540 @unittest.skipUnless(zstd, 'zstd not available')
541 def testzstd8mbmultipleresponses(self):
541 def testzstd8mbmultipleresponses(self):
542 # We feed in zstd compressed data on the same stream but belonging to
542 # We feed in zstd compressed data on the same stream but belonging to
543 # 2 different requests. This tests our flushing behavior.
543 # 2 different requests. This tests our flushing behavior.
544 reactor = framing.clientreactor(globalui, buffersends=False,
544 reactor = framing.clientreactor(globalui, buffersends=False,
545 hasmultiplesend=True)
545 hasmultiplesend=True)
546
546
547 request1, action, meta = reactor.callcommand(b'foo', {})
547 request1, action, meta = reactor.callcommand(b'foo', {})
548 for f in meta[b'framegen']:
548 for f in meta[b'framegen']:
549 pass
549 pass
550
550
551 request2, action, meta = reactor.callcommand(b'foo', {})
551 request2, action, meta = reactor.callcommand(b'foo', {})
552 for f in meta[b'framegen']:
552 for f in meta[b'framegen']:
553 pass
553 pass
554
554
555 outstream = framing.outputstream(2)
555 outstream = framing.outputstream(2)
556 outstream.setencoder(globalui, b'zstd-8mb')
556 outstream.setencoder(globalui, b'zstd-8mb')
557
557
558 response1 = b''.join(cborutil.streamencode({
558 response1 = b''.join(cborutil.streamencode({
559 b'status': b'ok',
559 b'status': b'ok',
560 b'extra': b'response1' * 10,
560 b'extra': b'response1' * 10,
561 }))
561 }))
562
562
563 response2 = b''.join(cborutil.streamencode({
563 response2 = b''.join(cborutil.streamencode({
564 b'status': b'error',
564 b'status': b'error',
565 b'extra': b'response2' * 10,
565 b'extra': b'response2' * 10,
566 }))
566 }))
567
567
568 action, meta = sendframe(reactor,
568 action, meta = sendframe(reactor,
569 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
569 ffs(b'%d 2 stream-begin stream-settings eos cbor:b"zstd-8mb"' %
570 request1.requestid))
570 request1.requestid))
571
571
572 self.assertEqual(action, b'noop')
572 self.assertEqual(action, b'noop')
573 self.assertEqual(meta, {})
573 self.assertEqual(meta, {})
574
574
575 # Feeding partial data in won't get anything useful out.
575 # Feeding partial data in won't get anything useful out.
576 action, meta = sendframe(reactor,
576 action, meta = sendframe(reactor,
577 ffs(b'%d 2 encoded command-response continuation %s' % (
577 ffs(b'%d 2 encoded command-response continuation %s' % (
578 request1.requestid, outstream.encode(response1))))
578 request1.requestid, outstream.encode(response1))))
579 self.assertEqual(action, b'responsedata')
579 self.assertEqual(action, b'responsedata')
580 self.assertEqual(meta[b'data'], b'')
580 self.assertEqual(meta[b'data'], b'')
581
581
582 # But flushing data at both ends will get our original data.
582 # But flushing data at both ends will get our original data.
583 action, meta = sendframe(reactor,
583 action, meta = sendframe(reactor,
584 ffs(b'%d 2 encoded command-response eos %s' % (
584 ffs(b'%d 2 encoded command-response eos %s' % (
585 request1.requestid, outstream.flush())))
585 request1.requestid, outstream.flush())))
586 self.assertEqual(action, b'responsedata')
586 self.assertEqual(action, b'responsedata')
587 self.assertEqual(meta[b'data'], response1)
587 self.assertEqual(meta[b'data'], response1)
588
588
589 # We should be able to reuse the compressor/decompressor for the
589 # We should be able to reuse the compressor/decompressor for the
590 # 2nd response.
590 # 2nd response.
591 action, meta = sendframe(reactor,
591 action, meta = sendframe(reactor,
592 ffs(b'%d 2 encoded command-response continuation %s' % (
592 ffs(b'%d 2 encoded command-response continuation %s' % (
593 request2.requestid, outstream.encode(response2))))
593 request2.requestid, outstream.encode(response2))))
594 self.assertEqual(action, b'responsedata')
594 self.assertEqual(action, b'responsedata')
595 self.assertEqual(meta[b'data'], b'')
595 self.assertEqual(meta[b'data'], b'')
596
596
597 action, meta = sendframe(reactor,
597 action, meta = sendframe(reactor,
598 ffs(b'%d 2 encoded command-response eos %s' % (
598 ffs(b'%d 2 encoded command-response eos %s' % (
599 request2.requestid, outstream.flush())))
599 request2.requestid, outstream.flush())))
600 self.assertEqual(action, b'responsedata')
600 self.assertEqual(action, b'responsedata')
601 self.assertEqual(meta[b'data'], response2)
601 self.assertEqual(meta[b'data'], response2)
602
602
603 if __name__ == '__main__':
603 if __name__ == '__main__':
604 if (3, 6, 0) <= sys.version_info <= (3, 6, 3):
604 if (3, 6, 0) <= sys.version_info < (3, 6, 4):
605 # Python 3.6.0 through 3.6.3 inclusive shipped with
605 # Python 3.6.0 through 3.6.3 inclusive shipped with
606 # https://bugs.python.org/issue31825 and we can't run these
606 # https://bugs.python.org/issue31825 and we can't run these
607 # tests on those specific versions of Python. Sigh.
607 # tests on those specific versions of Python. Sigh.
608 sys.exit(80)
608 sys.exit(80)
609 import silenttestrunner
609 import silenttestrunner
610 silenttestrunner.main(__name__)
610 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now