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