108
117
# this just ensures that the constructor stays parameter-free which
109
118
# is important for reuse : some subclasses will dynamically connect,
110
119
# others are always on, etc.
111
medium = smart.SmartClientStreamMedium()
120
client_medium = medium.SmartClientStreamMedium()
113
122
def test_construct_smart_client_medium(self):
114
123
# the base client medium takes no parameters
115
medium = smart.SmartClientMedium()
124
client_medium = medium.SmartClientMedium()
117
126
def test_construct_smart_simple_pipes_client_medium(self):
118
127
# the SimplePipes client medium takes two pipes:
119
128
# readable pipe, writeable pipe.
120
129
# Constructing one should just save these and do nothing.
121
130
# We test this by passing in None.
122
medium = smart.SmartSimplePipesClientMedium(None, None)
131
client_medium = medium.SmartSimplePipesClientMedium(None, None)
124
133
def test_simple_pipes_client_request_type(self):
125
134
# SimplePipesClient should use SmartClientStreamMediumRequest's.
126
medium = smart.SmartSimplePipesClientMedium(None, None)
127
request = medium.get_request()
128
self.assertIsInstance(request, smart.SmartClientStreamMediumRequest)
135
client_medium = medium.SmartSimplePipesClientMedium(None, None)
136
request = client_medium.get_request()
137
self.assertIsInstance(request, medium.SmartClientStreamMediumRequest)
130
139
def test_simple_pipes_client_get_concurrent_requests(self):
131
140
# the simple_pipes client does not support pipelined requests:
135
144
# classes - as the sibling classes share this logic, they do not have
136
145
# explicit tests for this.
137
146
output = StringIO()
138
medium = smart.SmartSimplePipesClientMedium(None, output)
139
request = medium.get_request()
147
client_medium = medium.SmartSimplePipesClientMedium(None, output)
148
request = client_medium.get_request()
140
149
request.finished_writing()
141
150
request.finished_reading()
142
request2 = medium.get_request()
151
request2 = client_medium.get_request()
143
152
request2.finished_writing()
144
153
request2.finished_reading()
146
155
def test_simple_pipes_client__accept_bytes_writes_to_writable(self):
147
156
# accept_bytes writes to the writeable pipe.
148
157
output = StringIO()
149
medium = smart.SmartSimplePipesClientMedium(None, output)
150
medium._accept_bytes('abc')
158
client_medium = medium.SmartSimplePipesClientMedium(None, output)
159
client_medium._accept_bytes('abc')
151
160
self.assertEqual('abc', output.getvalue())
153
162
def test_simple_pipes_client_disconnect_does_nothing(self):
154
163
# calling disconnect does nothing.
155
164
input = StringIO()
156
165
output = StringIO()
157
medium = smart.SmartSimplePipesClientMedium(input, output)
166
client_medium = medium.SmartSimplePipesClientMedium(input, output)
158
167
# send some bytes to ensure disconnecting after activity still does not
160
medium._accept_bytes('abc')
169
client_medium._accept_bytes('abc')
170
client_medium.disconnect()
162
171
self.assertFalse(input.closed)
163
172
self.assertFalse(output.closed)
167
176
# accept_bytes writes to.
168
177
input = StringIO()
169
178
output = StringIO()
170
medium = smart.SmartSimplePipesClientMedium(input, output)
171
medium._accept_bytes('abc')
173
medium._accept_bytes('abc')
179
client_medium = medium.SmartSimplePipesClientMedium(input, output)
180
client_medium._accept_bytes('abc')
181
client_medium.disconnect()
182
client_medium._accept_bytes('abc')
174
183
self.assertFalse(input.closed)
175
184
self.assertFalse(output.closed)
176
185
self.assertEqual('abcabc', output.getvalue())
178
187
def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
179
188
# Doing a disconnect on a new (and thus unconnected) SimplePipes medium
181
medium = smart.SmartSimplePipesClientMedium(None, None)
190
client_medium = medium.SmartSimplePipesClientMedium(None, None)
191
client_medium.disconnect()
184
193
def test_simple_pipes_client_can_always_read(self):
185
194
# SmartSimplePipesClientMedium is never disconnected, so read_bytes
186
195
# always tries to read from the underlying pipe.
187
196
input = StringIO('abcdef')
188
medium = smart.SmartSimplePipesClientMedium(input, None)
189
self.assertEqual('abc', medium.read_bytes(3))
191
self.assertEqual('def', medium.read_bytes(3))
197
client_medium = medium.SmartSimplePipesClientMedium(input, None)
198
self.assertEqual('abc', client_medium.read_bytes(3))
199
client_medium.disconnect()
200
self.assertEqual('def', client_medium.read_bytes(3))
193
202
def test_simple_pipes_client_supports__flush(self):
194
203
# invoking _flush on a SimplePipesClient should flush the output
201
210
def logging_flush(): flush_calls.append('flush')
202
211
output.flush = logging_flush
203
medium = smart.SmartSimplePipesClientMedium(input, output)
212
client_medium = medium.SmartSimplePipesClientMedium(input, output)
204
213
# this call is here to ensure we only flush once, not on every
205
214
# _accept_bytes call.
206
medium._accept_bytes('abc')
215
client_medium._accept_bytes('abc')
216
client_medium._flush()
217
client_medium.disconnect()
209
218
self.assertEqual(['flush'], flush_calls)
211
220
def test_construct_smart_ssh_client_medium(self):
229
238
output = StringIO()
230
239
vendor = StringIOSSHVendor(StringIO(), output)
231
medium = smart.SmartSSHClientMedium('a hostname', 'a port', 'a username',
232
'a password', vendor)
233
medium._accept_bytes('abc')
240
client_medium = medium.SmartSSHClientMedium(
241
'a hostname', 'a port', 'a username', 'a password', vendor)
242
client_medium._accept_bytes('abc')
234
243
self.assertEqual('abc', output.getvalue())
235
244
self.assertEqual([('connect_ssh', 'a username', 'a password',
236
245
'a hostname', 'a port',
247
256
osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
248
257
self.addCleanup(cleanup_environ)
249
258
os.environ['BZR_REMOTE_PATH'] = 'fugly'
250
medium = smart.SmartSSHClientMedium('a hostname', 'a port', 'a username',
259
client_medium = medium.SmartSSHClientMedium('a hostname', 'a port', 'a username',
251
260
'a password', vendor)
252
medium._accept_bytes('abc')
261
client_medium._accept_bytes('abc')
253
262
self.assertEqual('abc', output.getvalue())
254
263
self.assertEqual([('connect_ssh', 'a username', 'a password',
255
264
'a hostname', 'a port',
282
291
input = StringIO()
283
292
output = StringIO()
284
293
vendor = StringIOSSHVendor(input, output)
285
medium = smart.SmartSSHClientMedium('a hostname', vendor=vendor)
286
medium._accept_bytes('abc')
294
client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
295
client_medium._accept_bytes('abc')
296
client_medium.disconnect()
288
297
# the disconnect has closed output, so we need a new output for the
289
298
# new connection to write to.
290
299
input2 = StringIO()
291
300
output2 = StringIO()
292
301
vendor.read_from = input2
293
302
vendor.write_to = output2
294
medium._accept_bytes('abc')
303
client_medium._accept_bytes('abc')
304
client_medium.disconnect()
296
305
self.assertTrue(input.closed)
297
306
self.assertTrue(output.closed)
298
307
self.assertTrue(input2.closed)
310
319
def test_ssh_client_ignores_disconnect_when_not_connected(self):
311
320
# Doing a disconnect on a new (and thus unconnected) SSH medium
312
321
# does not fail. It's ok to disconnect an unconnected medium.
313
medium = smart.SmartSSHClientMedium(None)
322
client_medium = medium.SmartSSHClientMedium(None)
323
client_medium.disconnect()
316
325
def test_ssh_client_raises_on_read_when_not_connected(self):
317
326
# Doing a read on a new (and thus unconnected) SSH medium raises
318
327
# MediumNotConnected.
319
medium = smart.SmartSSHClientMedium(None)
320
self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 0)
321
self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 1)
328
client_medium = medium.SmartSSHClientMedium(None)
329
self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
330
self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
323
332
def test_ssh_client_supports__flush(self):
324
333
# invoking _flush on a SSHClientMedium should flush the output
331
340
def logging_flush(): flush_calls.append('flush')
332
341
output.flush = logging_flush
333
342
vendor = StringIOSSHVendor(input, output)
334
medium = smart.SmartSSHClientMedium('a hostname', vendor=vendor)
343
client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
335
344
# this call is here to ensure we only flush once, not on every
336
345
# _accept_bytes call.
337
medium._accept_bytes('abc')
346
client_medium._accept_bytes('abc')
347
client_medium._flush()
348
client_medium.disconnect()
340
349
self.assertEqual(['flush'], flush_calls)
342
351
def test_construct_smart_tcp_client_medium(self):
378
387
def test_tcp_client_ignores_disconnect_when_not_connected(self):
379
388
# Doing a disconnect on a new (and thus unconnected) TCP medium
380
389
# does not fail. It's ok to disconnect an unconnected medium.
381
medium = smart.SmartTCPClientMedium(None, None)
390
client_medium = medium.SmartTCPClientMedium(None, None)
391
client_medium.disconnect()
384
393
def test_tcp_client_raises_on_read_when_not_connected(self):
385
394
# Doing a read on a new (and thus unconnected) TCP medium raises
386
395
# MediumNotConnected.
387
medium = smart.SmartTCPClientMedium(None, None)
388
self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 0)
389
self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 1)
396
client_medium = medium.SmartTCPClientMedium(None, None)
397
self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
398
self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
391
400
def test_tcp_client_supports__flush(self):
392
401
# invoking _flush on a TCPClientMedium should do something useful.
421
430
# WritingCompleted to prevent bad assumptions on stream environments
422
431
# breaking the needs of message-based environments.
423
432
output = StringIO()
424
medium = smart.SmartSimplePipesClientMedium(None, output)
425
request = smart.SmartClientStreamMediumRequest(medium)
433
client_medium = medium.SmartSimplePipesClientMedium(None, output)
434
request = medium.SmartClientStreamMediumRequest(client_medium)
426
435
request.finished_writing()
427
436
self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
444
453
# constructing a SmartClientStreamMediumRequest on a StreamMedium sets
445
454
# the current request to the new SmartClientStreamMediumRequest
446
455
output = StringIO()
447
medium = smart.SmartSimplePipesClientMedium(None, output)
448
request = smart.SmartClientStreamMediumRequest(medium)
449
self.assertIs(medium._current_request, request)
456
client_medium = medium.SmartSimplePipesClientMedium(None, output)
457
request = medium.SmartClientStreamMediumRequest(client_medium)
458
self.assertIs(client_medium._current_request, request)
451
460
def test_construct_while_another_request_active_throws(self):
452
461
# constructing a SmartClientStreamMediumRequest on a StreamMedium with
453
462
# a non-None _current_request raises TooManyConcurrentRequests.
454
463
output = StringIO()
455
medium = smart.SmartSimplePipesClientMedium(None, output)
456
medium._current_request = "a"
464
client_medium = medium.SmartSimplePipesClientMedium(None, output)
465
client_medium._current_request = "a"
457
466
self.assertRaises(errors.TooManyConcurrentRequests,
458
smart.SmartClientStreamMediumRequest, medium)
467
medium.SmartClientStreamMediumRequest, client_medium)
460
469
def test_finished_read_clears_current_request(self):
461
470
# calling finished_reading clears the current request from the requests
463
472
output = StringIO()
464
medium = smart.SmartSimplePipesClientMedium(None, output)
465
request = smart.SmartClientStreamMediumRequest(medium)
473
client_medium = medium.SmartSimplePipesClientMedium(None, output)
474
request = medium.SmartClientStreamMediumRequest(client_medium)
466
475
request.finished_writing()
467
476
request.finished_reading()
468
self.assertEqual(None, medium._current_request)
477
self.assertEqual(None, client_medium._current_request)
470
479
def test_finished_read_before_finished_write_errors(self):
471
480
# calling finished_reading before calling finished_writing triggers a
472
481
# WritingNotComplete error.
473
medium = smart.SmartSimplePipesClientMedium(None, None)
474
request = smart.SmartClientStreamMediumRequest(medium)
482
client_medium = medium.SmartSimplePipesClientMedium(None, None)
483
request = medium.SmartClientStreamMediumRequest(client_medium)
475
484
self.assertRaises(errors.WritingNotComplete, request.finished_reading)
477
486
def test_read_bytes(self):
496
505
# WritingNotComplete error because the Smart protocol is designed to be
497
506
# compatible with strict message based protocols like HTTP where the
498
507
# request cannot be submitted until the writing has completed.
499
medium = smart.SmartSimplePipesClientMedium(None, None)
500
request = smart.SmartClientStreamMediumRequest(medium)
508
client_medium = medium.SmartSimplePipesClientMedium(None, None)
509
request = medium.SmartClientStreamMediumRequest(client_medium)
501
510
self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
503
512
def test_read_bytes_after_finished_reading_errors(self):
505
514
# ReadingCompleted to prevent bad assumptions on stream environments
506
515
# breaking the needs of message-based environments.
507
516
output = StringIO()
508
medium = smart.SmartSimplePipesClientMedium(None, output)
509
request = smart.SmartClientStreamMediumRequest(medium)
517
client_medium = medium.SmartSimplePipesClientMedium(None, output)
518
request = medium.SmartClientStreamMediumRequest(client_medium)
510
519
request.finished_writing()
511
520
request.finished_reading()
512
521
self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
515
class RemoteTransportTests(tests.TestCaseWithTransport):
518
super(RemoteTransportTests, self).setUp()
519
# We're allowed to set the transport class here, so that we don't use
520
# the default or a parameterized class, but rather use the
521
# TestCaseWithTransport infrastructure to set up a smart server and
523
self.transport_server = smart.SmartTCPServer_for_testing
524
class RemoteTransportTests(TestCaseWithSmartMedium):
525
526
def test_plausible_url(self):
526
527
self.assert_(self.get_url().startswith('bzr://'))
528
529
def test_probe_transport(self):
529
530
t = self.get_transport()
530
self.assertIsInstance(t, smart.SmartTransport)
531
self.assertIsInstance(t, remote.RemoteTransport)
532
533
def test_get_medium_from_transport(self):
533
534
"""Remote transport has a medium always, which it can return."""
534
535
t = self.get_transport()
535
medium = t.get_smart_medium()
536
self.assertIsInstance(medium, smart.SmartClientMedium)
536
client_medium = t.get_smart_medium()
537
self.assertIsInstance(client_medium, medium.SmartClientMedium)
539
540
class ErrorRaisingProtocol(object):
588
593
to_server = StringIO('hello\n')
589
594
from_server = StringIO()
590
595
transport = local.LocalTransport(urlutils.local_path_to_url('/'))
591
server = smart.SmartServerPipeStreamMedium(
596
server = medium.SmartServerPipeStreamMedium(
592
597
to_server, from_server, transport)
593
protocol = smart.SmartServerRequestProtocolOne(transport,
598
smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
594
599
from_server.write)
595
server._serve_one_request(protocol)
600
server._serve_one_request(smart_protocol)
596
601
self.assertEqual('ok\0011\n',
597
602
from_server.getvalue())
601
606
transport.put_bytes('testfile', 'contents\nof\nfile\n')
602
607
to_server = StringIO('get\001./testfile\n')
603
608
from_server = StringIO()
604
server = smart.SmartServerPipeStreamMedium(
609
server = medium.SmartServerPipeStreamMedium(
605
610
to_server, from_server, transport)
606
protocol = smart.SmartServerRequestProtocolOne(transport,
611
smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
607
612
from_server.write)
608
server._serve_one_request(protocol)
613
server._serve_one_request(smart_protocol)
609
614
self.assertEqual('ok\n'
611
616
'contents\nof\nfile\n'
619
624
transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
620
625
to_server = StringIO('get\001' + utf8_filename + '\n')
621
626
from_server = StringIO()
622
server = smart.SmartServerPipeStreamMedium(
627
server = medium.SmartServerPipeStreamMedium(
623
628
to_server, from_server, transport)
624
protocol = smart.SmartServerRequestProtocolOne(transport,
629
smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
625
630
from_server.write)
626
server._serve_one_request(protocol)
631
server._serve_one_request(smart_protocol)
627
632
self.assertEqual('ok\n'
629
634
'contents\nof\nfile\n'
634
639
sample_request_bytes = 'command\n9\nbulk datadone\n'
635
640
to_server = StringIO(sample_request_bytes)
636
641
from_server = StringIO()
637
server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
642
server = medium.SmartServerPipeStreamMedium(
643
to_server, from_server, None)
638
644
sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
639
645
server._serve_one_request(sample_protocol)
640
646
self.assertEqual('', from_server.getvalue())
657
663
def test_pipe_like_stream_shutdown_detection(self):
658
664
to_server = StringIO('')
659
665
from_server = StringIO()
660
server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
666
server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
661
667
server._serve_one_request(SampleRequest('x'))
662
668
self.assertTrue(server.finished)
664
670
def test_socket_stream_shutdown_detection(self):
665
671
server_sock, client_sock = self.portable_socket_pair()
666
672
client_sock.close()
667
server = smart.SmartServerSocketStreamMedium(
673
server = medium.SmartServerSocketStreamMedium(
668
674
server_sock, None)
669
675
server._serve_one_request(SampleRequest('x'))
670
676
self.assertTrue(server.finished)
676
682
sample_request_bytes = 'command\n'
677
683
to_server = StringIO(sample_request_bytes * 2)
678
684
from_server = StringIO()
679
server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
685
server = medium.SmartServerPipeStreamMedium(
686
to_server, from_server, None)
680
687
first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
681
688
server._serve_one_request(first_protocol)
682
689
self.assertEqual(0, first_protocol.next_read_size())
731
739
self.assertTrue(server.finished)
733
741
def test_socket_stream_error_handling(self):
734
# Use plain python StringIO so we can monkey-patch the close method to
735
# not discard the contents.
736
from StringIO import StringIO
737
742
server_sock, client_sock = self.portable_socket_pair()
738
server = smart.SmartServerSocketStreamMedium(
743
server = medium.SmartServerSocketStreamMedium(
739
744
server_sock, None)
740
745
fake_protocol = ErrorRaisingProtocol(Exception('boom'))
741
746
server._serve_one_request(fake_protocol)
745
750
self.assertTrue(server.finished)
747
752
def test_pipe_like_stream_keyboard_interrupt_handling(self):
748
# Use plain python StringIO so we can monkey-patch the close method to
749
# not discard the contents.
750
753
to_server = StringIO('')
751
754
from_server = StringIO()
752
server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
755
server = medium.SmartServerPipeStreamMedium(
756
to_server, from_server, None)
753
757
fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
754
758
self.assertRaises(
755
759
KeyboardInterrupt, server._serve_one_request, fake_protocol)
771
775
def test_get_error_unexpected(self):
772
776
"""Error reported by server with no specific representation"""
777
self._captureVar('BZR_NO_SMART_VFS', None)
773
778
class FlakyTransport(object):
774
780
def get_bytes(self, path):
775
781
raise Exception("some random exception from inside server")
776
server = smart.SmartTCPServer(backing_transport=FlakyTransport())
777
server.start_background_thread()
782
smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
783
smart_server.start_background_thread()
779
transport = smart.SmartTCPTransport(server.get_url())
785
transport = remote.RemoteTCPTransport(smart_server.get_url())
781
787
transport.get('something')
782
788
except errors.TransportError, e:
783
789
self.assertContainsRe(str(e), 'some random exception')
785
791
self.fail("get did not raise expected error")
792
transport.disconnect()
787
server.stop_background_thread()
794
smart_server.stop_background_thread()
790
797
class SmartTCPTests(tests.TestCase):
806
813
self.real_backing_transport = self.backing_transport
807
814
self.backing_transport = get_transport("readonly+" + self.backing_transport.abspath('.'))
808
self.server = smart.SmartTCPServer(self.backing_transport)
815
self.server = server.SmartTCPServer(self.backing_transport)
809
816
self.server.start_background_thread()
810
self.transport = smart.SmartTCPTransport(self.server.get_url())
817
self.transport = remote.RemoteTCPTransport(self.server.get_url())
818
self.addCleanup(self.tearDownServer)
820
def tearDownServer(self):
813
821
if getattr(self, 'transport', None):
814
822
self.transport.disconnect()
815
824
if getattr(self, 'server', None):
816
825
self.server.stop_background_thread()
817
super(SmartTCPTests, self).tearDown()
829
class TestServerSocketUsage(SmartTCPTests):
831
def test_server_setup_teardown(self):
832
"""It should be safe to teardown the server with no requests."""
835
transport = remote.RemoteTCPTransport(self.server.get_url())
836
self.tearDownServer()
837
self.assertRaises(errors.ConnectionError, transport.has, '.')
839
def test_server_closes_listening_sock_on_shutdown_after_request(self):
840
"""The server should close its listening socket when it's stopped."""
843
self.transport.has('.')
844
self.tearDownServer()
845
# if the listening socket has closed, we should get a BADFD error
846
# when connecting, rather than a hang.
847
transport = remote.RemoteTCPTransport(server.get_url())
848
self.assertRaises(errors.ConnectionError, transport.has, '.')
820
851
class WritableEndToEndTests(SmartTCPTests):
821
852
"""Client to server tests that require a writable transport."""
831
862
def test_smart_transport_has(self):
832
863
"""Checking for file existence over smart."""
864
self._captureVar('BZR_NO_SMART_VFS', None)
833
865
self.backing_transport.put_bytes("foo", "contents of foo\n")
834
866
self.assertTrue(self.transport.has("foo"))
835
867
self.assertFalse(self.transport.has("non-foo"))
837
869
def test_smart_transport_get(self):
838
870
"""Read back a file over smart."""
871
self._captureVar('BZR_NO_SMART_VFS', None)
839
872
self.backing_transport.put_bytes("foo", "contents\nof\nfoo\n")
840
873
fp = self.transport.get("foo")
841
874
self.assertEqual('contents\nof\nfoo\n', fp.read())
899
934
def test_mkdir_error_readonly(self):
900
935
"""TransportNotPossible should be preserved from the backing transport."""
936
self._captureVar('BZR_NO_SMART_VFS', None)
901
937
self.setUpServer(readonly=True)
902
938
self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
942
class TestServerHooks(SmartTCPTests):
944
def capture_server_call(self, backing_url, public_url):
945
"""Record a server_started|stopped hook firing."""
946
self.hook_calls.append((backing_url, public_url))
948
def test_server_started_hook(self):
949
"""The server_started hook fires when the server is started."""
951
server.SmartTCPServer.hooks.install_hook('server_started',
952
self.capture_server_call)
954
# at this point, the server will be starting a thread up.
955
# there is no indicator at the moment, so bodge it by doing a request.
956
self.transport.has('.')
957
self.assertEqual([(self.backing_transport.base, self.transport.base)],
960
def test_server_stopped_hook_simple(self):
961
"""The server_stopped hook fires when the server is stopped."""
963
server.SmartTCPServer.hooks.install_hook('server_stopped',
964
self.capture_server_call)
966
result = [(self.backing_transport.base, self.transport.base)]
967
# check the stopping message isn't emitted up front.
968
self.assertEqual([], self.hook_calls)
969
# nor after a single message
970
self.transport.has('.')
971
self.assertEqual([], self.hook_calls)
972
# clean up the server
973
self.tearDownServer()
974
# now it should have fired.
975
self.assertEqual(result, self.hook_calls)
977
# TODO: test that when the server suffers an exception that it calls the
978
# server-stopped hook.
981
class SmartServerCommandTests(tests.TestCaseWithTransport):
982
"""Tests that call directly into the command objects, bypassing the network
983
and the request dispatching.
906
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
907
"""Test that call directly into the handler logic, bypassing the network."""
909
def test_construct_request_handler(self):
910
"""Constructing a request handler should be easy and set defaults."""
911
handler = smart.SmartServerRequestHandler(None)
912
self.assertFalse(handler.finished_reading)
914
986
def test_hello(self):
915
handler = smart.SmartServerRequestHandler(None)
916
handler.dispatch_command('hello', ())
917
self.assertEqual(('ok', '1'), handler.response.args)
918
self.assertEqual(None, handler.response.body)
987
cmd = request.HelloRequest(None)
988
response = cmd.execute()
989
self.assertEqual(('ok', '1'), response.args)
990
self.assertEqual(None, response.body)
920
992
def test_get_bundle(self):
921
993
from bzrlib.bundle import serializer
925
997
rev_id = wt.commit('add hello')
927
handler = smart.SmartServerRequestHandler(self.get_transport())
928
handler.dispatch_command('get_bundle', ('.', rev_id))
929
bundle = serializer.read_bundle(StringIO(handler.response.body))
930
self.assertEqual((), handler.response.args)
999
cmd = request.GetBundleRequest(self.get_transport())
1000
response = cmd.execute('.', rev_id)
1001
bundle = serializer.read_bundle(StringIO(response.body))
1002
self.assertEqual((), response.args)
1005
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1006
"""Test that call directly into the handler logic, bypassing the network."""
1009
super(SmartServerRequestHandlerTests, self).setUp()
1010
self._captureVar('BZR_NO_SMART_VFS', None)
1012
def build_handler(self, transport):
1013
"""Returns a handler for the commands in protocol version one."""
1014
return request.SmartServerRequestHandler(transport,
1015
request.request_handlers)
1017
def test_construct_request_handler(self):
1018
"""Constructing a request handler should be easy and set defaults."""
1019
handler = request.SmartServerRequestHandler(None, None)
1020
self.assertFalse(handler.finished_reading)
1022
def test_hello(self):
1023
handler = self.build_handler(None)
1024
handler.dispatch_command('hello', ())
1025
self.assertEqual(('ok', '1'), handler.response.args)
1026
self.assertEqual(None, handler.response.body)
1028
def test_disable_vfs_handler_classes_via_environment(self):
1029
# VFS handler classes will raise an error from "execute" if
1030
# BZR_NO_SMART_VFS is set.
1031
handler = vfs.HasRequest(None)
1032
# set environment variable after construction to make sure it's
1034
# Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp
1035
# has called _captureVar, so it will be restored to the right state
1037
os.environ['BZR_NO_SMART_VFS'] = ''
1038
self.assertRaises(errors.DisabledMethod, handler.execute)
932
1040
def test_readonly_exception_becomes_transport_not_possible(self):
933
1041
"""The response for a read-only error is ('ReadOnlyError')."""
934
handler = smart.SmartServerRequestHandler(self.get_readonly_transport())
1042
handler = self.build_handler(self.get_readonly_transport())
935
1043
# send a mkdir for foo, with no explicit mode - should fail.
936
1044
handler.dispatch_command('mkdir', ('foo', ''))
937
1045
# and the failure should be an explicit ReadOnlyError
944
1052
def test_hello_has_finished_body_on_dispatch(self):
945
1053
"""The 'hello' command should set finished_reading."""
946
handler = smart.SmartServerRequestHandler(None)
1054
handler = self.build_handler(None)
947
1055
handler.dispatch_command('hello', ())
948
1056
self.assertTrue(handler.finished_reading)
949
1057
self.assertNotEqual(None, handler.response)
951
1059
def test_put_bytes_non_atomic(self):
952
1060
"""'put_...' should set finished_reading after reading the bytes."""
953
handler = smart.SmartServerRequestHandler(self.get_transport())
1061
handler = self.build_handler(self.get_transport())
954
1062
handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
955
1063
self.assertFalse(handler.finished_reading)
956
1064
handler.accept_body('1234')
990
1098
self.assertEqual(None, handler.response.body)
993
class SmartTransportRegistration(tests.TestCase):
1101
class RemoteTransportRegistration(tests.TestCase):
995
1103
def test_registration(self):
996
1104
t = get_transport('bzr+ssh://example.com/path')
997
self.assertIsInstance(t, smart.SmartSSHTransport)
1105
self.assertIsInstance(t, remote.RemoteSSHTransport)
998
1106
self.assertEqual('example.com', t._host)
1001
class TestSmartTransport(tests.TestCase):
1109
class TestRemoteTransport(tests.TestCase):
1003
1111
def test_use_connection_factory(self):
1004
# We want to be able to pass a client as a parameter to SmartTransport.
1112
# We want to be able to pass a client as a parameter to RemoteTransport.
1005
1113
input = StringIO("ok\n3\nbardone\n")
1006
1114
output = StringIO()
1007
medium = smart.SmartSimplePipesClientMedium(input, output)
1008
transport = smart.SmartTransport('bzr://localhost/', medium=medium)
1115
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1116
transport = remote.RemoteTransport(
1117
'bzr://localhost/', medium=client_medium)
1010
1119
# We want to make sure the client is used when the first remote
1011
1120
# method is called. No data should have been sent, or read.
1024
1133
def test__translate_error_readonly(self):
1025
1134
"""Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
1026
medium = smart.SmartClientMedium()
1027
transport = smart.SmartTransport('bzr://localhost/', medium=medium)
1135
client_medium = medium.SmartClientMedium()
1136
transport = remote.RemoteTransport(
1137
'bzr://localhost/', medium=client_medium)
1028
1138
self.assertRaises(errors.TransportNotPossible,
1029
1139
transport._translate_error, ("ReadOnlyError", ))
1032
class InstrumentedServerProtocol(smart.SmartServerStreamMedium):
1142
class InstrumentedServerProtocol(medium.SmartServerStreamMedium):
1033
1143
"""A smart server which is backed by memory and saves its write requests."""
1035
1145
def __init__(self, write_output_list):
1036
smart.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1146
medium.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1037
1147
self._write_output_list = write_output_list
1053
1163
def setUp(self):
1054
1164
super(TestSmartProtocol, self).setUp()
1165
# XXX: self.server_to_client doesn't seem to be used. If so,
1166
# InstrumentedServerProtocol is redundant too.
1055
1167
self.server_to_client = []
1056
1168
self.to_server = StringIO()
1057
1169
self.to_client = StringIO()
1058
self.client_medium = smart.SmartSimplePipesClientMedium(self.to_client,
1170
self.client_medium = medium.SmartSimplePipesClientMedium(self.to_client,
1059
1171
self.to_server)
1060
self.client_protocol = smart.SmartClientRequestProtocolOne(
1172
self.client_protocol = protocol.SmartClientRequestProtocolOne(
1061
1173
self.client_medium)
1062
1174
self.smart_server = InstrumentedServerProtocol(self.server_to_client)
1063
self.smart_server_request = smart.SmartServerRequestHandler(None)
1175
self.smart_server_request = request.SmartServerRequestHandler(
1176
None, request.request_handlers)
1065
1178
def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
1066
client, smart_server_request):
1067
1180
"""Check that smart (de)serialises offsets as expected.
1069
1182
We check both serialisation and deserialisation at the same time
1073
1186
:param expected_offsets: a readv offset list.
1074
1187
:param expected_seralised: an expected serial form of the offsets.
1075
:param smart_server_request: a SmartServerRequestHandler instance.
1077
# XXX: 'smart_server_request' should be a SmartServerRequestProtocol in
1079
offsets = smart_server_request._deserialise_offsets(expected_serialised)
1189
# XXX: '_deserialise_offsets' should be a method of the
1190
# SmartServerRequestProtocol in future.
1191
readv_cmd = vfs.ReadvRequest(None)
1192
offsets = readv_cmd._deserialise_offsets(expected_serialised)
1080
1193
self.assertEqual(expected_offsets, offsets)
1081
1194
serialised = client._serialise_offsets(offsets)
1082
1195
self.assertEqual(expected_serialised, serialised)
1084
1197
def build_protocol_waiting_for_body(self):
1085
1198
out_stream = StringIO()
1086
protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1087
protocol.has_dispatched = True
1088
protocol.request = smart.SmartServerRequestHandler(None)
1089
def handle_end_of_bytes():
1090
self.end_received = True
1091
self.assertEqual('abcdefg', protocol.request._body_bytes)
1092
protocol.request.response = smart.SmartServerResponse(('ok', ))
1093
protocol.request._end_of_body_handler = handle_end_of_bytes
1199
smart_protocol = protocol.SmartServerRequestProtocolOne(None,
1201
smart_protocol.has_dispatched = True
1202
smart_protocol.request = self.smart_server_request
1203
class FakeCommand(object):
1204
def do_body(cmd, body_bytes):
1205
self.end_received = True
1206
self.assertEqual('abcdefg', body_bytes)
1207
return request.SmartServerResponse(('ok', ))
1208
smart_protocol.request._command = FakeCommand()
1094
1209
# Call accept_bytes to make sure that internal state like _body_decoder
1095
1210
# is initialised. This test should probably be given a clearer
1096
1211
# interface to work with that will not cause this inconsistency.
1097
1212
# -- Andrew Bennetts, 2006-09-28
1098
protocol.accept_bytes('')
1213
smart_protocol.accept_bytes('')
1214
return smart_protocol
1101
1216
def test_construct_version_one_server_protocol(self):
1102
protocol = smart.SmartServerRequestProtocolOne(None, None)
1103
self.assertEqual('', protocol.excess_buffer)
1104
self.assertEqual('', protocol.in_buffer)
1105
self.assertFalse(protocol.has_dispatched)
1106
self.assertEqual(1, protocol.next_read_size())
1217
smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1218
self.assertEqual('', smart_protocol.excess_buffer)
1219
self.assertEqual('', smart_protocol.in_buffer)
1220
self.assertFalse(smart_protocol.has_dispatched)
1221
self.assertEqual(1, smart_protocol.next_read_size())
1108
1223
def test_construct_version_one_client_protocol(self):
1109
1224
# we can construct a client protocol from a client medium request
1110
1225
output = StringIO()
1111
medium = smart.SmartSimplePipesClientMedium(None, output)
1112
request = medium.get_request()
1113
client_protocol = smart.SmartClientRequestProtocolOne(request)
1226
client_medium = medium.SmartSimplePipesClientMedium(None, output)
1227
request = client_medium.get_request()
1228
client_protocol = protocol.SmartClientRequestProtocolOne(request)
1115
1230
def test_server_offset_serialisation(self):
1116
1231
"""The Smart protocol serialises offsets as a comma and \n string.
1119
1234
one with the order of reads not increasing (an out of order read), and
1120
1235
one that should coalesce.
1122
self.assertOffsetSerialisation([], '',
1123
self.client_protocol, self.smart_server_request)
1124
self.assertOffsetSerialisation([(1,2)], '1,2',
1125
self.client_protocol, self.smart_server_request)
1237
self.assertOffsetSerialisation([], '', self.client_protocol)
1238
self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1126
1239
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1127
self.client_protocol, self.smart_server_request)
1240
self.client_protocol)
1128
1241
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1129
'1,2\n3,4\n100,200', self.client_protocol, self.smart_server_request)
1242
'1,2\n3,4\n100,200', self.client_protocol)
1131
1244
def test_accept_bytes_of_bad_request_to_protocol(self):
1132
1245
out_stream = StringIO()
1133
protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1134
protocol.accept_bytes('abc')
1135
self.assertEqual('abc', protocol.in_buffer)
1136
protocol.accept_bytes('\n')
1137
self.assertEqual("error\x01Generic bzr smart protocol error: bad request"
1138
" 'abc'\n", out_stream.getvalue())
1139
self.assertTrue(protocol.has_dispatched)
1140
self.assertEqual(0, protocol.next_read_size())
1246
smart_protocol = protocol.SmartServerRequestProtocolOne(
1247
None, out_stream.write)
1248
smart_protocol.accept_bytes('abc')
1249
self.assertEqual('abc', smart_protocol.in_buffer)
1250
smart_protocol.accept_bytes('\n')
1252
"error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1253
out_stream.getvalue())
1254
self.assertTrue(smart_protocol.has_dispatched)
1255
self.assertEqual(0, smart_protocol.next_read_size())
1142
1257
def test_accept_body_bytes_to_protocol(self):
1143
1258
protocol = self.build_protocol_waiting_for_body()
1150
1265
self.assertTrue(self.end_received)
1152
1267
def test_accept_request_and_body_all_at_once(self):
1268
self._captureVar('BZR_NO_SMART_VFS', None)
1153
1269
mem_transport = memory.MemoryTransport()
1154
1270
mem_transport.put_bytes('foo', 'abcdefghij')
1155
1271
out_stream = StringIO()
1156
protocol = smart.SmartServerRequestProtocolOne(mem_transport,
1272
smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
1157
1273
out_stream.write)
1158
protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1159
self.assertEqual(0, protocol.next_read_size())
1274
smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1275
self.assertEqual(0, smart_protocol.next_read_size())
1160
1276
self.assertEqual('readv\n3\ndefdone\n', out_stream.getvalue())
1161
self.assertEqual('', protocol.excess_buffer)
1162
self.assertEqual('', protocol.in_buffer)
1277
self.assertEqual('', smart_protocol.excess_buffer)
1278
self.assertEqual('', smart_protocol.in_buffer)
1164
1280
def test_accept_excess_bytes_are_preserved(self):
1165
1281
out_stream = StringIO()
1166
protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1167
protocol.accept_bytes('hello\nhello\n')
1282
smart_protocol = protocol.SmartServerRequestProtocolOne(
1283
None, out_stream.write)
1284
smart_protocol.accept_bytes('hello\nhello\n')
1168
1285
self.assertEqual("ok\x011\n", out_stream.getvalue())
1169
self.assertEqual("hello\n", protocol.excess_buffer)
1170
self.assertEqual("", protocol.in_buffer)
1286
self.assertEqual("hello\n", smart_protocol.excess_buffer)
1287
self.assertEqual("", smart_protocol.in_buffer)
1172
1289
def test_accept_excess_bytes_after_body(self):
1173
1290
protocol = self.build_protocol_waiting_for_body()
1182
1299
def test_accept_excess_bytes_after_dispatch(self):
1183
1300
out_stream = StringIO()
1184
protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1185
protocol.accept_bytes('hello\n')
1301
smart_protocol = protocol.SmartServerRequestProtocolOne(
1302
None, out_stream.write)
1303
smart_protocol.accept_bytes('hello\n')
1186
1304
self.assertEqual("ok\x011\n", out_stream.getvalue())
1187
protocol.accept_bytes('hel')
1188
self.assertEqual("hel", protocol.excess_buffer)
1189
protocol.accept_bytes('lo\n')
1190
self.assertEqual("hello\n", protocol.excess_buffer)
1191
self.assertEqual("", protocol.in_buffer)
1305
smart_protocol.accept_bytes('hel')
1306
self.assertEqual("hel", smart_protocol.excess_buffer)
1307
smart_protocol.accept_bytes('lo\n')
1308
self.assertEqual("hello\n", smart_protocol.excess_buffer)
1309
self.assertEqual("", smart_protocol.in_buffer)
1193
1311
def test__send_response_sets_finished_reading(self):
1194
protocol = smart.SmartServerRequestProtocolOne(None, lambda x: None)
1195
self.assertEqual(1, protocol.next_read_size())
1196
protocol._send_response(('x',))
1197
self.assertEqual(0, protocol.next_read_size())
1312
smart_protocol = protocol.SmartServerRequestProtocolOne(
1313
None, lambda x: None)
1314
self.assertEqual(1, smart_protocol.next_read_size())
1315
smart_protocol._send_response(('x',))
1316
self.assertEqual(0, smart_protocol.next_read_size())
1199
1318
def test_query_version(self):
1200
1319
"""query_version on a SmartClientProtocolOne should return a number.
1208
1327
# the error if the response is a non-understood version.
1209
1328
input = StringIO('ok\x011\n')
1210
1329
output = StringIO()
1211
medium = smart.SmartSimplePipesClientMedium(input, output)
1212
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1213
self.assertEqual(1, protocol.query_version())
1330
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1331
request = client_medium.get_request()
1332
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1333
self.assertEqual(1, smart_protocol.query_version())
1215
1335
def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1221
1341
# expected bytes
1222
1342
for input_tuple in input_tuples:
1223
1343
server_output = StringIO()
1224
server_protocol = smart.SmartServerRequestProtocolOne(
1344
server_protocol = protocol.SmartServerRequestProtocolOne(
1225
1345
None, server_output.write)
1226
1346
server_protocol._send_response(input_tuple)
1227
1347
self.assertEqual(expected_bytes, server_output.getvalue())
1228
# check the decoding of the client protocol from expected_bytes:
1348
# check the decoding of the client smart_protocol from expected_bytes:
1229
1349
input = StringIO(expected_bytes)
1230
1350
output = StringIO()
1231
medium = smart.SmartSimplePipesClientMedium(input, output)
1232
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1233
protocol.call('foo')
1234
self.assertEqual(expected_tuple, protocol.read_response_tuple())
1351
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1352
request = client_medium.get_request()
1353
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1354
smart_protocol.call('foo')
1355
self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1236
1357
def test_client_call_empty_response(self):
1237
1358
# protocol.call() can get back an empty tuple as a response. This occurs
1246
1367
[('a', 'b', '34')])
1248
1369
def test_client_call_with_body_bytes_uploads(self):
1249
# protocol.call_with_upload should length-prefix the bytes onto the
1370
# protocol.call_with_body_bytes should length-prefix the bytes onto the
1251
1372
expected_bytes = "foo\n7\nabcdefgdone\n"
1252
1373
input = StringIO("\n")
1253
1374
output = StringIO()
1254
medium = smart.SmartSimplePipesClientMedium(input, output)
1255
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1256
protocol.call_with_body_bytes(('foo', ), "abcdefg")
1375
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1376
request = client_medium.get_request()
1377
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1378
smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1257
1379
self.assertEqual(expected_bytes, output.getvalue())
1259
1381
def test_client_call_with_body_readv_array(self):
1262
1384
expected_bytes = "foo\n7\n1,2\n5,6done\n"
1263
1385
input = StringIO("\n")
1264
1386
output = StringIO()
1265
medium = smart.SmartSimplePipesClientMedium(input, output)
1266
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1267
protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1387
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1388
request = client_medium.get_request()
1389
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1390
smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1268
1391
self.assertEqual(expected_bytes, output.getvalue())
1270
1393
def test_client_read_body_bytes_all(self):
1274
1397
server_bytes = "ok\n7\n1234567done\n"
1275
1398
input = StringIO(server_bytes)
1276
1399
output = StringIO()
1277
medium = smart.SmartSimplePipesClientMedium(input, output)
1278
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1279
protocol.call('foo')
1280
protocol.read_response_tuple(True)
1281
self.assertEqual(expected_bytes, protocol.read_body_bytes())
1400
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1401
request = client_medium.get_request()
1402
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1403
smart_protocol.call('foo')
1404
smart_protocol.read_response_tuple(True)
1405
self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1283
1407
def test_client_read_body_bytes_incremental(self):
1284
1408
# test reading a few bytes at a time from the body
1290
1414
server_bytes = "ok\n7\n1234567done\n"
1291
1415
input = StringIO(server_bytes)
1292
1416
output = StringIO()
1293
medium = smart.SmartSimplePipesClientMedium(input, output)
1294
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1295
protocol.call('foo')
1296
protocol.read_response_tuple(True)
1297
self.assertEqual(expected_bytes[0:2], protocol.read_body_bytes(2))
1298
self.assertEqual(expected_bytes[2:4], protocol.read_body_bytes(2))
1299
self.assertEqual(expected_bytes[4:6], protocol.read_body_bytes(2))
1300
self.assertEqual(expected_bytes[6], protocol.read_body_bytes())
1417
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1418
request = client_medium.get_request()
1419
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1420
smart_protocol.call('foo')
1421
smart_protocol.read_response_tuple(True)
1422
self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1423
self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1424
self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1425
self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1302
1427
def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1303
1428
# cancelling the expected body needs to finish the request, but not
1306
1431
server_bytes = "ok\n7\n1234567done\n"
1307
1432
input = StringIO(server_bytes)
1308
1433
output = StringIO()
1309
medium = smart.SmartSimplePipesClientMedium(input, output)
1310
protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1311
protocol.call('foo')
1312
protocol.read_response_tuple(True)
1313
protocol.cancel_read_body()
1434
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1435
request = client_medium.get_request()
1436
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1437
smart_protocol.call('foo')
1438
smart_protocol.read_response_tuple(True)
1439
smart_protocol.cancel_read_body()
1314
1440
self.assertEqual(3, input.tell())
1315
self.assertRaises(errors.ReadingCompleted, protocol.read_body_bytes)
1442
errors.ReadingCompleted, smart_protocol.read_body_bytes)
1445
class TestSmartClientUnicode(tests.TestCase):
1446
"""_SmartClient tests for unicode arguments.
1448
Unicode arguments to call_with_body_bytes are not correct (remote method
1449
names, arguments, and bodies must all be expressed as byte strings), but
1450
_SmartClient should gracefully reject them, rather than getting into a
1451
broken state that prevents future correct calls from working. That is, it
1452
should be possible to issue more requests on the medium afterwards, rather
1453
than allowing one bad call to call_with_body_bytes to cause later calls to
1454
mysteriously fail with TooManyConcurrentRequests.
1457
def assertCallDoesNotBreakMedium(self, method, args, body):
1458
"""Call a medium with the given method, args and body, then assert that
1459
the medium is left in a sane state, i.e. is capable of allowing further
1462
input = StringIO("\n")
1464
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1465
smart_client = client._SmartClient(client_medium)
1466
self.assertRaises(TypeError,
1467
smart_client.call_with_body_bytes, method, args, body)
1468
self.assertEqual("", output.getvalue())
1469
self.assertEqual(None, client_medium._current_request)
1471
def test_call_with_body_bytes_unicode_method(self):
1472
self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
1474
def test_call_with_body_bytes_unicode_args(self):
1475
self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
1476
self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
1478
def test_call_with_body_bytes_unicode_body(self):
1479
self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
1318
1482
class LengthPrefixedBodyDecoder(tests.TestCase):
1321
1485
# something similar to the ProtocolBase method.
1323
1487
def test_construct(self):
1324
decoder = smart.LengthPrefixedBodyDecoder()
1488
decoder = protocol.LengthPrefixedBodyDecoder()
1325
1489
self.assertFalse(decoder.finished_reading)
1326
1490
self.assertEqual(6, decoder.next_read_size())
1327
1491
self.assertEqual('', decoder.read_pending_data())
1328
1492
self.assertEqual('', decoder.unused_data)
1330
1494
def test_accept_bytes(self):
1331
decoder = smart.LengthPrefixedBodyDecoder()
1495
decoder = protocol.LengthPrefixedBodyDecoder()
1332
1496
decoder.accept_bytes('')
1333
1497
self.assertFalse(decoder.finished_reading)
1334
1498
self.assertEqual(6, decoder.next_read_size())
1394
1558
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1561
super(HTTPTunnellingSmokeTest, self).setUp()
1562
# We use the VFS layer as part of HTTP tunnelling tests.
1563
self._captureVar('BZR_NO_SMART_VFS', None)
1396
1565
def _test_bulk_data(self, url_protocol):
1397
1566
# We should be able to send and receive bulk data in a single message.
1398
1567
# The 'readv' command in the smart protocol both sends and receives bulk
1399
1568
# data, so we use that.
1400
1569
self.build_tree(['data-file'])
1401
http_server = HTTPServerWithSmarts()
1402
http_server._url_protocol = url_protocol
1404
self.addCleanup(http_server.tearDown)
1406
http_transport = get_transport(http_server.get_url())
1570
self.transport_readonly_server = HTTPServerWithSmarts
1572
http_transport = self.get_readonly_transport()
1408
1573
medium = http_transport.get_smart_medium()
1409
1574
#remote_transport = RemoteTransport('fake_url', medium)
1410
remote_transport = smart.SmartTransport('/', medium=medium)
1575
remote_transport = remote.RemoteTransport('/', medium=medium)
1411
1576
self.assertEqual(
1412
1577
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1454
1619
self._test_http_send_smart_request('http+urllib')
1456
1621
def test_http_server_with_smarts(self):
1457
http_server = HTTPServerWithSmarts()
1459
self.addCleanup(http_server.tearDown)
1622
self.transport_readonly_server = HTTPServerWithSmarts
1461
1624
post_body = 'hello\n'
1462
1625
expected_reply_body = 'ok\x011\n'
1464
smart_server_url = http_server.get_url() + '.bzr/smart'
1627
smart_server_url = self.get_readonly_url('.bzr/smart')
1465
1628
reply = urllib2.urlopen(smart_server_url, post_body).read()
1467
1630
self.assertEqual(expected_reply_body, reply)
1469
1632
def test_smart_http_server_post_request_handler(self):
1470
http_server = HTTPServerWithSmarts()
1472
self.addCleanup(http_server.tearDown)
1473
httpd = http_server._get_httpd()
1633
self.transport_readonly_server = HTTPServerWithSmarts
1634
httpd = self.get_readonly_server()._get_httpd()
1475
1636
socket = SampleSocket(
1476
1637
'POST /.bzr/smart HTTP/1.0\r\n'
1478
1639
'Content-Length: 6\r\n'
1481
request_handler = SmartRequestHandler(
1482
socket, ('localhost', 80), httpd)
1642
# Beware: the ('localhost', 80) below is the
1643
# client_address parameter, but we don't have one because
1644
# we have defined a socket which is not bound to an
1645
# address. The test framework never uses this client
1646
# address, so far...
1647
request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
1483
1648
response = socket.writefile.getvalue()
1484
1649
self.assertStartsWith(response, 'HTTP/1.0 200 ')
1485
1650
# This includes the end of the HTTP headers, and all the body.