~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Keir Mierle
  • Date: 2006-11-23 18:56:25 UTC
  • mto: (2168.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2171.
  • Revision ID: keir@cs.utoronto.ca-20061123185625-ndto53ylcb8zo1y6
Fix spacing error and add tests for status --short command flag.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
# FIXME: This test should be repeated for each available http client
 
18
# implementation; at the moment we have urllib and pycurl.
 
19
 
 
20
# TODO: Should be renamed to bzrlib.transport.http.tests?
 
21
 
 
22
import select
 
23
import socket
 
24
import threading
 
25
 
 
26
import bzrlib
 
27
from bzrlib import errors
 
28
from bzrlib import osutils
 
29
from bzrlib.tests import (
 
30
    TestCase,
 
31
    TestSkipped,
 
32
    )
 
33
from bzrlib.tests.HttpServer import (
 
34
    HttpServer,
 
35
    HttpServer_PyCurl,
 
36
    HttpServer_urllib,
 
37
    )
 
38
from bzrlib.tests.HTTPTestUtil import (
 
39
    BadProtocolRequestHandler,
 
40
    BadStatusRequestHandler,
 
41
    ForbiddenRequestHandler,
 
42
    InvalidStatusRequestHandler,
 
43
    NoRangeRequestHandler,
 
44
    SingleRangeRequestHandler,
 
45
    TestCaseWithWebserver,
 
46
    WallRequestHandler,
 
47
    )
 
48
from bzrlib.transport import (
 
49
    get_transport,
 
50
    Transport,
 
51
    )
 
52
from bzrlib.transport.http import (
 
53
    extract_auth,
 
54
    HttpTransportBase,
 
55
    )
 
56
from bzrlib.transport.http._urllib import HttpTransport_urllib
 
57
 
 
58
 
 
59
class FakeManager(object):
 
60
 
 
61
    def __init__(self):
 
62
        self.credentials = []
 
63
 
 
64
    def add_password(self, realm, host, username, password):
 
65
        self.credentials.append([realm, host, username, password])
 
66
 
 
67
 
 
68
class RecordingServer(object):
 
69
    """A fake HTTP server.
 
70
    
 
71
    It records the bytes sent to it, and replies with a 200.
 
72
    """
 
73
 
 
74
    def __init__(self, expect_body_tail=None):
 
75
        """Constructor.
 
76
 
 
77
        :type expect_body_tail: str
 
78
        :param expect_body_tail: a reply won't be sent until this string is
 
79
            received.
 
80
        """
 
81
        self._expect_body_tail = expect_body_tail
 
82
        self.host = None
 
83
        self.port = None
 
84
        self.received_bytes = ''
 
85
 
 
86
    def setUp(self):
 
87
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
88
        self._sock.bind(('127.0.0.1', 0))
 
89
        self.host, self.port = self._sock.getsockname()
 
90
        self._ready = threading.Event()
 
91
        self._thread = threading.Thread(target=self._accept_read_and_reply)
 
92
        self._thread.setDaemon(True)
 
93
        self._thread.start()
 
94
        self._ready.wait(5)
 
95
 
 
96
    def _accept_read_and_reply(self):
 
97
        self._sock.listen(1)
 
98
        self._ready.set()
 
99
        self._sock.settimeout(5)
 
100
        try:
 
101
            conn, address = self._sock.accept()
 
102
            # On win32, the accepted connection will be non-blocking to start
 
103
            # with because we're using settimeout.
 
104
            conn.setblocking(True)
 
105
            while not self.received_bytes.endswith(self._expect_body_tail):
 
106
                self.received_bytes += conn.recv(4096)
 
107
            conn.sendall('HTTP/1.1 200 OK\r\n')
 
108
        except socket.timeout:
 
109
            # Make sure the client isn't stuck waiting for us to e.g. accept.
 
110
            self._sock.close()
 
111
 
 
112
    def tearDown(self):
 
113
        try:
 
114
            self._sock.close()
 
115
        except socket.error:
 
116
            # We might have already closed it.  We don't care.
 
117
            pass
 
118
        self.host = None
 
119
        self.port = None
 
120
 
 
121
 
 
122
class TestHttpUrls(TestCase):
 
123
 
 
124
    # FIXME: Some of these tests should be done for both
 
125
    # implementations
 
126
 
 
127
    def test_url_parsing(self):
 
128
        f = FakeManager()
 
129
        url = extract_auth('http://example.com', f)
 
130
        self.assertEquals('http://example.com', url)
 
131
        self.assertEquals(0, len(f.credentials))
 
132
        url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
 
133
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
 
134
        self.assertEquals(1, len(f.credentials))
 
135
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
 
136
                          f.credentials[0])
 
137
 
 
138
    def test_abs_url(self):
 
139
        """Construction of absolute http URLs"""
 
140
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
141
        eq = self.assertEqualDiff
 
142
        eq(t.abspath('.'),
 
143
           'http://bazaar-vcs.org/bzr/bzr.dev')
 
144
        eq(t.abspath('foo/bar'),
 
145
           'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
 
146
        eq(t.abspath('.bzr'),
 
147
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
148
        eq(t.abspath('.bzr/1//2/./3'),
 
149
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
150
 
 
151
    def test_invalid_http_urls(self):
 
152
        """Trap invalid construction of urls"""
 
153
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
154
        self.assertRaises(ValueError,
 
155
            t.abspath,
 
156
            '.bzr/')
 
157
        t = HttpTransport_urllib('http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
158
        self.assertRaises(errors.InvalidURL, t.has, 'foo/bar')
 
159
 
 
160
    def test_http_root_urls(self):
 
161
        """Construction of URLs from server root"""
 
162
        t = HttpTransport_urllib('http://bzr.ozlabs.org/')
 
163
        eq = self.assertEqualDiff
 
164
        eq(t.abspath('.bzr/tree-version'),
 
165
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
166
 
 
167
    def test_http_impl_urls(self):
 
168
        """There are servers which ask for particular clients to connect"""
 
169
        server = HttpServer_PyCurl()
 
170
        try:
 
171
            server.setUp()
 
172
            url = server.get_url()
 
173
            self.assertTrue(url.startswith('http+pycurl://'))
 
174
        finally:
 
175
            server.tearDown()
 
176
 
 
177
 
 
178
class TestHttpConnections(object):
 
179
    """Test the http connections.
 
180
 
 
181
    This MUST be used by daughter classes that also inherit from
 
182
    TestCaseWithWebserver.
 
183
 
 
184
    We can't inherit directly from TestCaseWithWebserver or the
 
185
    test framework will try to create an instance which cannot
 
186
    run, its implementation being incomplete.
 
187
    """
 
188
 
 
189
    def setUp(self):
 
190
        TestCaseWithWebserver.setUp(self)
 
191
        self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
 
192
                        transport=self.get_transport())
 
193
 
 
194
    def test_http_has(self):
 
195
        server = self.get_readonly_server()
 
196
        t = self._transport(server.get_url())
 
197
        self.assertEqual(t.has('foo/bar'), True)
 
198
        self.assertEqual(len(server.logs), 1)
 
199
        self.assertContainsRe(server.logs[0],
 
200
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
 
201
 
 
202
    def test_http_has_not_found(self):
 
203
        server = self.get_readonly_server()
 
204
        t = self._transport(server.get_url())
 
205
        self.assertEqual(t.has('not-found'), False)
 
206
        self.assertContainsRe(server.logs[1],
 
207
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
 
208
 
 
209
    def test_http_get(self):
 
210
        server = self.get_readonly_server()
 
211
        t = self._transport(server.get_url())
 
212
        fp = t.get('foo/bar')
 
213
        self.assertEqualDiff(
 
214
            fp.read(),
 
215
            'contents of foo/bar\n')
 
216
        self.assertEqual(len(server.logs), 1)
 
217
        self.assertTrue(server.logs[0].find(
 
218
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
 
219
            % bzrlib.__version__) > -1)
 
220
 
 
221
    def test_get_smart_medium(self):
 
222
        # For HTTP, get_smart_medium should return the transport object.
 
223
        server = self.get_readonly_server()
 
224
        http_transport = self._transport(server.get_url())
 
225
        medium = http_transport.get_smart_medium()
 
226
        self.assertIs(medium, http_transport)
 
227
 
 
228
    def test_has_on_bogus_host(self):
 
229
        # Get a free address and don't 'accept' on it, so that we
 
230
        # can be sure there is no http handler there, but set a
 
231
        # reasonable timeout to not slow down tests too much.
 
232
        default_timeout = socket.getdefaulttimeout()
 
233
        try:
 
234
            socket.setdefaulttimeout(2)
 
235
            s = socket.socket()
 
236
            s.bind(('localhost', 0))
 
237
            t = self._transport('http://%s:%s/' % s.getsockname())
 
238
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
 
239
        finally:
 
240
            socket.setdefaulttimeout(default_timeout)
 
241
 
 
242
 
 
243
class TestWithTransport_pycurl(object):
 
244
    """Test case to inherit from if pycurl is present"""
 
245
 
 
246
    def _get_pycurl_maybe(self):
 
247
        try:
 
248
            from bzrlib.transport.http._pycurl import PyCurlTransport
 
249
            return PyCurlTransport
 
250
        except errors.DependencyNotPresent:
 
251
            raise TestSkipped('pycurl not present')
 
252
 
 
253
    _transport = property(_get_pycurl_maybe)
 
254
 
 
255
 
 
256
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
 
257
    """Test http connections with urllib"""
 
258
 
 
259
    _transport = HttpTransport_urllib
 
260
 
 
261
 
 
262
 
 
263
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
 
264
                                 TestHttpConnections,
 
265
                                 TestCaseWithWebserver):
 
266
    """Test http connections with pycurl"""
 
267
 
 
268
 
 
269
class TestHttpTransportRegistration(TestCase):
 
270
    """Test registrations of various http implementations"""
 
271
 
 
272
    def test_http_registered(self):
 
273
        # urlllib should always be present
 
274
        t = get_transport('http+urllib://bzr.google.com/')
 
275
        self.assertIsInstance(t, Transport)
 
276
        self.assertIsInstance(t, HttpTransport_urllib)
 
277
 
 
278
 
 
279
class TestOffsets(TestCase):
 
280
    """Test offsets_to_ranges method"""
 
281
 
 
282
    def test_offsets_to_ranges_simple(self):
 
283
        to_range = HttpTransportBase.offsets_to_ranges
 
284
        ranges = to_range([(10, 1)])
 
285
        self.assertEqual([[10, 10]], ranges)
 
286
 
 
287
        ranges = to_range([(0, 1), (1, 1)])
 
288
        self.assertEqual([[0, 1]], ranges)
 
289
 
 
290
        ranges = to_range([(1, 1), (0, 1)])
 
291
        self.assertEqual([[0, 1]], ranges)
 
292
 
 
293
    def test_offset_to_ranges_overlapped(self):
 
294
        to_range = HttpTransportBase.offsets_to_ranges
 
295
 
 
296
        ranges = to_range([(10, 1), (20, 2), (22, 5)])
 
297
        self.assertEqual([[10, 10], [20, 26]], ranges)
 
298
 
 
299
        ranges = to_range([(10, 1), (11, 2), (22, 5)])
 
300
        self.assertEqual([[10, 12], [22, 26]], ranges)
 
301
 
 
302
 
 
303
class TestPost(TestCase):
 
304
 
 
305
    def _test_post_body_is_received(self, scheme):
 
306
        server = RecordingServer(expect_body_tail='end-of-body')
 
307
        server.setUp()
 
308
        self.addCleanup(server.tearDown)
 
309
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
 
310
        try:
 
311
            http_transport = get_transport(url)
 
312
        except errors.UnsupportedProtocol:
 
313
            raise TestSkipped('%s not available' % scheme)
 
314
        code, response = http_transport._post('abc def end-of-body')
 
315
        self.assertTrue(
 
316
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
 
317
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
 
318
        # The transport should not be assuming that the server can accept
 
319
        # chunked encoding the first time it connects, because HTTP/1.1, so we
 
320
        # check for the literal string.
 
321
        self.assertTrue(
 
322
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
 
323
 
 
324
    def test_post_body_is_received_urllib(self):
 
325
        self._test_post_body_is_received('http+urllib')
 
326
 
 
327
    def test_post_body_is_received_pycurl(self):
 
328
        self._test_post_body_is_received('http+pycurl')
 
329
 
 
330
 
 
331
class TestRangeHeader(TestCase):
 
332
    """Test range_header method"""
 
333
 
 
334
    def check_header(self, value, ranges=[], tail=0):
 
335
        range_header = HttpTransportBase.range_header
 
336
        self.assertEqual(value, range_header(ranges, tail))
 
337
 
 
338
    def test_range_header_single(self):
 
339
        self.check_header('0-9', ranges=[[0,9]])
 
340
        self.check_header('100-109', ranges=[[100,109]])
 
341
 
 
342
    def test_range_header_tail(self):
 
343
        self.check_header('-10', tail=10)
 
344
        self.check_header('-50', tail=50)
 
345
 
 
346
    def test_range_header_multi(self):
 
347
        self.check_header('0-9,100-200,300-5000',
 
348
                          ranges=[(0,9), (100, 200), (300,5000)])
 
349
 
 
350
    def test_range_header_mixed(self):
 
351
        self.check_header('0-9,300-5000,-50',
 
352
                          ranges=[(0,9), (300,5000)],
 
353
                          tail=50)
 
354
 
 
355
 
 
356
class TestWallServer(object):
 
357
    """Tests exceptions during the connection phase"""
 
358
 
 
359
    def create_transport_readonly_server(self):
 
360
        return HttpServer(WallRequestHandler)
 
361
 
 
362
    def test_http_has(self):
 
363
        server = self.get_readonly_server()
 
364
        t = self._transport(server.get_url())
 
365
        # Unfortunately httplib (see HTTPResponse._read_status
 
366
        # for details) make no distinction between a closed
 
367
        # socket and badly formatted status line, so we can't
 
368
        # just test for ConnectionError, we have to test
 
369
        # InvalidHttpResponse too.
 
370
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
371
                          t.has, 'foo/bar')
 
372
 
 
373
    def test_http_get(self):
 
374
        server = self.get_readonly_server()
 
375
        t = self._transport(server.get_url())
 
376
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
377
                          t.get, 'foo/bar')
 
378
 
 
379
 
 
380
class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
 
381
    """Tests "wall" server for urllib implementation"""
 
382
 
 
383
    _transport = HttpTransport_urllib
 
384
 
 
385
 
 
386
class TestWallServer_pycurl(TestWithTransport_pycurl,
 
387
                            TestWallServer,
 
388
                            TestCaseWithWebserver):
 
389
    """Tests "wall" server for pycurl implementation"""
 
390
 
 
391
 
 
392
class TestBadStatusServer(object):
 
393
    """Tests bad status from server."""
 
394
 
 
395
    def create_transport_readonly_server(self):
 
396
        return HttpServer(BadStatusRequestHandler)
 
397
 
 
398
    def test_http_has(self):
 
399
        server = self.get_readonly_server()
 
400
        t = self._transport(server.get_url())
 
401
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
402
 
 
403
    def test_http_get(self):
 
404
        server = self.get_readonly_server()
 
405
        t = self._transport(server.get_url())
 
406
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
407
 
 
408
 
 
409
class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
 
410
    """Tests bad status server for urllib implementation"""
 
411
 
 
412
    _transport = HttpTransport_urllib
 
413
 
 
414
 
 
415
class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
 
416
                                 TestBadStatusServer,
 
417
                                 TestCaseWithWebserver):
 
418
    """Tests bad status server for pycurl implementation"""
 
419
 
 
420
 
 
421
class TestInvalidStatusServer(TestBadStatusServer):
 
422
    """Tests invalid status from server.
 
423
 
 
424
    Both implementations raises the same error as for a bad status.
 
425
    """
 
426
 
 
427
    def create_transport_readonly_server(self):
 
428
        return HttpServer(InvalidStatusRequestHandler)
 
429
 
 
430
 
 
431
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
 
432
                                     TestCaseWithWebserver):
 
433
    """Tests invalid status server for urllib implementation"""
 
434
 
 
435
    _transport = HttpTransport_urllib
 
436
 
 
437
 
 
438
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
 
439
                                     TestInvalidStatusServer,
 
440
                                     TestCaseWithWebserver):
 
441
    """Tests invalid status server for pycurl implementation"""
 
442
 
 
443
 
 
444
class TestBadProtocolServer(object):
 
445
    """Tests bad protocol from server."""
 
446
 
 
447
    def create_transport_readonly_server(self):
 
448
        return HttpServer(BadProtocolRequestHandler)
 
449
 
 
450
    def test_http_has(self):
 
451
        server = self.get_readonly_server()
 
452
        t = self._transport(server.get_url())
 
453
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
454
 
 
455
    def test_http_get(self):
 
456
        server = self.get_readonly_server()
 
457
        t = self._transport(server.get_url())
 
458
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
459
 
 
460
 
 
461
class TestBadProtocolServer_urllib(TestBadProtocolServer,
 
462
                                   TestCaseWithWebserver):
 
463
    """Tests bad protocol server for urllib implementation"""
 
464
 
 
465
    _transport = HttpTransport_urllib
 
466
 
 
467
# curl don't check the protocol version
 
468
#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
 
469
#                                   TestBadProtocolServer,
 
470
#                                   TestCaseWithWebserver):
 
471
#    """Tests bad protocol server for pycurl implementation"""
 
472
 
 
473
 
 
474
class TestForbiddenServer(object):
 
475
    """Tests forbidden server"""
 
476
 
 
477
    def create_transport_readonly_server(self):
 
478
        return HttpServer(ForbiddenRequestHandler)
 
479
 
 
480
    def test_http_has(self):
 
481
        server = self.get_readonly_server()
 
482
        t = self._transport(server.get_url())
 
483
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
 
484
 
 
485
    def test_http_get(self):
 
486
        server = self.get_readonly_server()
 
487
        t = self._transport(server.get_url())
 
488
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
 
489
 
 
490
 
 
491
class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
 
492
    """Tests forbidden server for urllib implementation"""
 
493
 
 
494
    _transport = HttpTransport_urllib
 
495
 
 
496
 
 
497
class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
 
498
                                 TestForbiddenServer,
 
499
                                 TestCaseWithWebserver):
 
500
    """Tests forbidden server for pycurl implementation"""
 
501
 
 
502
 
 
503
class TestRecordingServer(TestCase):
 
504
 
 
505
    def test_create(self):
 
506
        server = RecordingServer(expect_body_tail=None)
 
507
        self.assertEqual('', server.received_bytes)
 
508
        self.assertEqual(None, server.host)
 
509
        self.assertEqual(None, server.port)
 
510
 
 
511
    def test_setUp_and_tearDown(self):
 
512
        server = RecordingServer(expect_body_tail=None)
 
513
        server.setUp()
 
514
        try:
 
515
            self.assertNotEqual(None, server.host)
 
516
            self.assertNotEqual(None, server.port)
 
517
        finally:
 
518
            server.tearDown()
 
519
        self.assertEqual(None, server.host)
 
520
        self.assertEqual(None, server.port)
 
521
 
 
522
    def test_send_receive_bytes(self):
 
523
        server = RecordingServer(expect_body_tail='c')
 
524
        server.setUp()
 
525
        self.addCleanup(server.tearDown)
 
526
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
527
        sock.connect((server.host, server.port))
 
528
        sock.sendall('abc')
 
529
        self.assertEqual('HTTP/1.1 200 OK\r\n',
 
530
                         osutils.recv_all(sock, 4096))
 
531
        self.assertEqual('abc', server.received_bytes)
 
532
 
 
533
 
 
534
class TestRangeRequestServer(object):
 
535
    """Test the http connections.
 
536
 
 
537
    This MUST be used by daughter classes that also inherit from
 
538
    TestCaseWithWebserver.
 
539
 
 
540
    We can't inherit directly from TestCaseWithWebserver or the
 
541
    test framework will try to create an instance which cannot
 
542
    run, its implementation being incomplete.
 
543
    """
 
544
 
 
545
    def setUp(self):
 
546
        TestCaseWithWebserver.setUp(self)
 
547
        self.build_tree_contents([('a', '0123456789')],)
 
548
 
 
549
    """Tests readv requests against server"""
 
550
 
 
551
    def test_readv(self):
 
552
        server = self.get_readonly_server()
 
553
        t = self._transport(server.get_url())
 
554
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
 
555
        self.assertEqual(l[0], (0, '0'))
 
556
        self.assertEqual(l[1], (1, '1'))
 
557
        self.assertEqual(l[2], (3, '34'))
 
558
        self.assertEqual(l[3], (9, '9'))
 
559
 
 
560
    def test_readv_out_of_order(self):
 
561
        server = self.get_readonly_server()
 
562
        t = self._transport(server.get_url())
 
563
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
 
564
        self.assertEqual(l[0], (1, '1'))
 
565
        self.assertEqual(l[1], (9, '9'))
 
566
        self.assertEqual(l[2], (0, '0'))
 
567
        self.assertEqual(l[3], (3, '34'))
 
568
 
 
569
    def test_readv_short_read(self):
 
570
        server = self.get_readonly_server()
 
571
        t = self._transport(server.get_url())
 
572
 
 
573
        # This is intentionally reading off the end of the file
 
574
        # since we are sure that it cannot get there
 
575
        self.assertListRaises((errors.ShortReadvError, AssertionError),
 
576
                              t.readv, 'a', [(1,1), (8,10)])
 
577
 
 
578
        # This is trying to seek past the end of the file, it should
 
579
        # also raise a special error
 
580
        self.assertListRaises(errors.ShortReadvError,
 
581
                              t.readv, 'a', [(12,2)])
 
582
 
 
583
 
 
584
class TestSingleRangeRequestServer(TestRangeRequestServer):
 
585
    """Test readv against a server which accept only single range requests"""
 
586
 
 
587
    def create_transport_readonly_server(self):
 
588
        return HttpServer(SingleRangeRequestHandler)
 
589
 
 
590
 
 
591
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
 
592
                                          TestCaseWithWebserver):
 
593
    """Tests single range requests accepting server for urllib implementation"""
 
594
 
 
595
    _transport = HttpTransport_urllib
 
596
 
 
597
 
 
598
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
599
                                          TestSingleRangeRequestServer,
 
600
                                          TestCaseWithWebserver):
 
601
    """Tests single range requests accepting server for pycurl implementation"""
 
602
 
 
603
 
 
604
class TestNoRangeRequestServer(TestRangeRequestServer):
 
605
    """Test readv against a server which do not accept range requests"""
 
606
 
 
607
    def create_transport_readonly_server(self):
 
608
        return HttpServer(NoRangeRequestHandler)
 
609
 
 
610
 
 
611
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
 
612
                                      TestCaseWithWebserver):
 
613
    """Tests range requests refusing server for urllib implementation"""
 
614
 
 
615
    _transport = HttpTransport_urllib
 
616
 
 
617
 
 
618
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
619
                               TestNoRangeRequestServer,
 
620
                               TestCaseWithWebserver):
 
621
    """Tests range requests refusing server for pycurl implementation"""
 
622
 
 
623