56
61
from bzrlib.transport.memory import MemoryTransport
59
class TransportTestProviderAdapter(TestScenarioApplier):
60
"""A tool to generate a suite testing all transports for a single test.
62
This is done by copying the test once for each transport and injecting
63
the transport_class and transport_server classes into each copy. Each copy
64
is also given a new id() to make it easy to identify.
68
self.scenarios = self._test_permutations()
70
def get_transport_test_permutations(self, module):
71
"""Get the permutations module wants to have tested."""
72
if getattr(module, 'get_test_permutations', None) is None:
73
raise AssertionError("transport module %s doesn't provide get_test_permutations()"
75
##warning("transport module %s doesn't provide get_test_permutations()"
78
return module.get_test_permutations()
80
def _test_permutations(self):
81
"""Return a list of the klass, server_factory pairs to test."""
83
for module in _get_transport_modules():
85
permutations = self.get_transport_test_permutations(
86
reduce(getattr, (module).split('.')[1:], __import__(module)))
87
for (klass, server_factory) in permutations:
88
scenario = (server_factory.__name__,
89
{"transport_class":klass,
90
"transport_server":server_factory})
91
result.append(scenario)
92
except errors.DependencyNotPresent, e:
93
# Continue even if a dependency prevents us
94
# from running this test
64
def get_transport_test_permutations(module):
65
"""Get the permutations module wants to have tested."""
66
if getattr(module, 'get_test_permutations', None) is None:
68
"transport module %s doesn't provide get_test_permutations()"
71
return module.get_test_permutations()
74
def transport_test_permutations():
75
"""Return a list of the klass, server_factory pairs to test."""
77
for module in _get_transport_modules():
79
permutations = get_transport_test_permutations(
80
reduce(getattr, (module).split('.')[1:], __import__(module)))
81
for (klass, server_factory) in permutations:
82
scenario = (server_factory.__name__,
83
{"transport_class":klass,
84
"transport_server":server_factory})
85
result.append(scenario)
86
except errors.DependencyNotPresent, e:
87
# Continue even if a dependency prevents us
88
# from adding this test
93
def load_tests(standard_tests, module, loader):
94
"""Multiply tests for tranport implementations."""
95
result = loader.suiteClass()
96
scenarios = transport_test_permutations()
97
return multiply_tests(standard_tests, scenarios, result)
100
100
class TransportTests(TestTransportImplementation):
155
155
self.assertEqual(True, t.has('a'))
156
156
self.assertEqual(False, t.has('c'))
157
157
self.assertEqual(True, t.has(urlutils.escape('%')))
158
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
159
[True, True, False, False, True, False, True, False])
158
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd',
159
'e', 'f', 'g', 'h'])),
160
[True, True, False, False,
161
True, False, True, False])
160
162
self.assertEqual(True, t.has_any(['a', 'b', 'c']))
161
self.assertEqual(False, t.has_any(['c', 'd', 'f', urlutils.escape('%%')]))
162
self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
163
[True, True, False, False, True, False, True, False])
163
self.assertEqual(False, t.has_any(['c', 'd', 'f',
164
urlutils.escape('%%')]))
165
self.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd',
166
'e', 'f', 'g', 'h']))),
167
[True, True, False, False,
168
True, False, True, False])
164
169
self.assertEqual(False, t.has_any(['c', 'c', 'c']))
165
170
self.assertEqual(True, t.has_any(['b', 'b', 'b']))
167
172
def test_has_root_works(self):
173
from bzrlib.smart import server
174
if self.transport_server is server.SmartTCPServer_for_testing:
175
raise TestNotApplicable(
176
"SmartTCPServer_for_testing intentionally does not allow "
168
178
current_transport = self.get_transport()
169
179
self.assertTrue(current_transport.has('/'))
170
180
root = current_transport.clone('/')
182
192
self.build_tree(files, transport=t, line_endings='binary')
183
193
self.check_transport_contents('contents of a\n', t, 'a')
184
194
content_f = t.get_multi(files)
185
for content, f in zip(contents, content_f):
195
# Use itertools.izip() instead of use zip() or map(), since they fully
196
# evaluate their inputs, the transport requests should be issued and
197
# handled sequentially (we don't want to force transport to buffer).
198
for content, f in itertools.izip(contents, content_f):
186
199
self.assertEqual(content, f.read())
188
201
content_f = t.get_multi(iter(files))
189
for content, f in zip(contents, content_f):
202
# Use itertools.izip() for the same reason
203
for content, f in itertools.izip(contents, content_f):
190
204
self.assertEqual(content, f.read())
192
206
self.assertRaises(NoSuchFile, t.get, 'c')
231
245
self.assertRaises(NoSuchFile, t.get_bytes, 'c')
234
t = self.get_transport()
239
self.applyDeprecated(zero_eleven, t.put, 'a', 'string\ncontents\n')
240
self.check_transport_contents('string\ncontents\n', t, 'a')
242
self.applyDeprecated(zero_eleven,
243
t.put, 'b', StringIO('file-like\ncontents\n'))
244
self.check_transport_contents('file-like\ncontents\n', t, 'b')
246
self.assertRaises(NoSuchFile,
247
self.applyDeprecated,
249
t.put, 'path/doesnt/exist/c', StringIO('contents'))
247
def test_get_with_open_write_stream_sees_all_content(self):
248
t = self.get_transport()
251
handle = t.open_write_stream('foo')
254
self.assertEqual('b', t.get('foo').read())
258
def test_get_bytes_with_open_write_stream_sees_all_content(self):
259
t = self.get_transport()
262
handle = t.open_write_stream('foo')
265
self.assertEqual('b', t.get_bytes('foo'))
266
self.assertEqual('b', t.get('foo').read())
251
270
def test_put_bytes(self):
252
271
t = self.get_transport()
370
389
t.put_file, 'a', StringIO('some text for a\n'))
373
t.put_file('a', StringIO('some text for a\n'))
392
result = t.put_file('a', StringIO('some text for a\n'))
393
# put_file returns the length of the data written
394
self.assertEqual(16, result)
374
395
self.failUnless(t.has('a'))
375
396
self.check_transport_contents('some text for a\n', t, 'a')
376
397
# Put also replaces contents
377
t.put_file('a', StringIO('new\ncontents for\na\n'))
398
result = t.put_file('a', StringIO('new\ncontents for\na\n'))
399
self.assertEqual(19, result)
378
400
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
379
401
self.assertRaises(NoSuchFile,
380
402
t.put_file, 'path/doesnt/exist/c',
511
527
unicode_file = pyStringIO(u'\u1234')
512
528
self.assertRaises(UnicodeEncodeError, t.put_file, 'foo', unicode_file)
514
def test_put_multi(self):
515
t = self.get_transport()
519
self.assertEqual(2, self.applyDeprecated(zero_eleven,
520
t.put_multi, [('a', StringIO('new\ncontents for\na\n')),
521
('d', StringIO('contents\nfor d\n'))]
523
self.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd'])),
524
[True, False, False, True])
525
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
526
self.check_transport_contents('contents\nfor d\n', t, 'd')
528
self.assertEqual(2, self.applyDeprecated(zero_eleven,
529
t.put_multi, iter([('a', StringIO('diff\ncontents for\na\n')),
530
('d', StringIO('another contents\nfor d\n'))])
532
self.check_transport_contents('diff\ncontents for\na\n', t, 'a')
533
self.check_transport_contents('another contents\nfor d\n', t, 'd')
535
def test_put_permissions(self):
536
t = self.get_transport()
540
if not t._can_roundtrip_unix_modebits():
541
# Can't roundtrip, so no need to run this test
543
self.applyDeprecated(zero_eleven, t.put, 'mode644',
544
StringIO('test text\n'), mode=0644)
545
self.assertTransportMode(t, 'mode644', 0644)
546
self.applyDeprecated(zero_eleven, t.put, 'mode666',
547
StringIO('test text\n'), mode=0666)
548
self.assertTransportMode(t, 'mode666', 0666)
549
self.applyDeprecated(zero_eleven, t.put, 'mode600',
550
StringIO('test text\n'), mode=0600)
551
self.assertTransportMode(t, 'mode600', 0600)
552
# Yes, you can put a file such that it becomes readonly
553
self.applyDeprecated(zero_eleven, t.put, 'mode400',
554
StringIO('test text\n'), mode=0400)
555
self.assertTransportMode(t, 'mode400', 0400)
556
self.applyDeprecated(zero_eleven, t.put_multi,
557
[('mmode644', StringIO('text\n'))], mode=0644)
558
self.assertTransportMode(t, 'mmode644', 0644)
560
# The default permissions should be based on the current umask
561
umask = osutils.get_umask()
562
self.applyDeprecated(zero_eleven, t.put, 'nomode',
563
StringIO('test text\n'), mode=None)
564
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
566
530
def test_mkdir(self):
567
531
t = self.get_transport()
569
533
if t.is_readonly():
570
# cannot mkdir on readonly transports. We're not testing for
534
# cannot mkdir on readonly transports. We're not testing for
571
535
# cache coherency because cache behaviour is not currently
572
536
# defined for the transport interface.
573
537
self.assertRaises(TransportNotPossible, t.mkdir, '.')
633
597
t.mkdir('dnomode', mode=None)
634
598
self.assertTransportMode(t, 'dnomode', 0777 & ~umask)
600
def test_opening_a_file_stream_creates_file(self):
601
t = self.get_transport()
604
handle = t.open_write_stream('foo')
606
self.assertEqual('', t.get_bytes('foo'))
610
def test_opening_a_file_stream_can_set_mode(self):
611
t = self.get_transport()
614
if not t._can_roundtrip_unix_modebits():
615
# Can't roundtrip, so no need to run this test
617
def check_mode(name, mode, expected):
618
handle = t.open_write_stream(name, mode=mode)
620
self.assertTransportMode(t, name, expected)
621
check_mode('mode644', 0644, 0644)
622
check_mode('mode666', 0666, 0666)
623
check_mode('mode600', 0600, 0600)
624
# The default permissions should be based on the current umask
625
check_mode('nomode', None, 0666 & ~osutils.get_umask())
636
627
def test_copy_to(self):
637
628
# FIXME: test: same server to same server (partly done)
638
629
# same protocol two servers
684
675
self.assertTransportMode(temp_transport, f, mode)
686
def test_append(self):
677
def test_create_prefix(self):
687
678
t = self.get_transport()
691
t.put_bytes('a', 'diff\ncontents for\na\n')
692
t.put_bytes('b', 'contents\nfor b\n')
694
self.assertEqual(20, self.applyDeprecated(zero_eleven,
695
t.append, 'a', StringIO('add\nsome\nmore\ncontents\n')))
697
self.check_transport_contents(
698
'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
701
# And we can create new files, too
702
self.assertEqual(0, self.applyDeprecated(zero_eleven,
703
t.append, 'c', StringIO('some text\nfor a missing file\n')))
704
self.check_transport_contents('some text\nfor a missing file\n',
679
sub = t.clone('foo').clone('bar')
682
except TransportNotPossible:
683
self.assertTrue(t.is_readonly())
685
self.assertTrue(t.has('foo/bar'))
706
687
def test_append_file(self):
707
688
t = self.get_transport()
1527
1582
self.assertEqual(d[2], (0, '0'))
1528
1583
self.assertEqual(d[3], (3, '34'))
1585
def test_readv_with_adjust_for_latency(self):
1586
transport = self.get_transport()
1587
# the adjust for latency flag expands the data region returned
1588
# according to a per-transport heuristic, so testing is a little
1589
# tricky as we need more data than the largest combining that our
1590
# transports do. To accomodate this we generate random data and cross
1591
# reference the returned data with the random data. To avoid doing
1592
# multiple large random byte look ups we do several tests on the same
1594
content = osutils.rand_bytes(200*1024)
1595
content_size = len(content)
1596
if transport.is_readonly():
1597
self.build_tree_contents([('a', content)])
1599
transport.put_bytes('a', content)
1600
def check_result_data(result_vector):
1601
for item in result_vector:
1602
data_len = len(item[1])
1603
self.assertEqual(content[item[0]:item[0] + data_len], item[1])
1606
result = list(transport.readv('a', ((0, 30),),
1607
adjust_for_latency=True, upper_limit=content_size))
1608
# we expect 1 result, from 0, to something > 30
1609
self.assertEqual(1, len(result))
1610
self.assertEqual(0, result[0][0])
1611
self.assertTrue(len(result[0][1]) >= 30)
1612
check_result_data(result)
1613
# end of file corner case
1614
result = list(transport.readv('a', ((204700, 100),),
1615
adjust_for_latency=True, upper_limit=content_size))
1616
# we expect 1 result, from 204800- its length, to the end
1617
self.assertEqual(1, len(result))
1618
data_len = len(result[0][1])
1619
self.assertEqual(204800-data_len, result[0][0])
1620
self.assertTrue(data_len >= 100)
1621
check_result_data(result)
1622
# out of order ranges are made in order
1623
result = list(transport.readv('a', ((204700, 100), (0, 50)),
1624
adjust_for_latency=True, upper_limit=content_size))
1625
# we expect 2 results, in order, start and end.
1626
self.assertEqual(2, len(result))
1628
data_len = len(result[0][1])
1629
self.assertEqual(0, result[0][0])
1630
self.assertTrue(data_len >= 30)
1632
data_len = len(result[1][1])
1633
self.assertEqual(204800-data_len, result[1][0])
1634
self.assertTrue(data_len >= 100)
1635
check_result_data(result)
1636
# close ranges get combined (even if out of order)
1637
for request_vector in [((400,50), (800, 234)), ((800, 234), (400,50))]:
1638
result = list(transport.readv('a', request_vector,
1639
adjust_for_latency=True, upper_limit=content_size))
1640
self.assertEqual(1, len(result))
1641
data_len = len(result[0][1])
1642
# minimum length is from 400 to 1034 - 634
1643
self.assertTrue(data_len >= 634)
1644
# must contain the region 400 to 1034
1645
self.assertTrue(result[0][0] <= 400)
1646
self.assertTrue(result[0][0] + data_len >= 1034)
1647
check_result_data(result)
1649
def test_readv_with_adjust_for_latency_with_big_file(self):
1650
transport = self.get_transport()
1651
# test from observed failure case.
1652
if transport.is_readonly():
1653
file('a', 'w').write('a'*1024*1024)
1655
transport.put_bytes('a', 'a'*1024*1024)
1656
broken_vector = [(465219, 800), (225221, 800), (445548, 800),
1657
(225037, 800), (221357, 800), (437077, 800), (947670, 800),
1658
(465373, 800), (947422, 800)]
1659
results = list(transport.readv('a', broken_vector, True, 1024*1024))
1660
found_items = [False]*9
1661
for pos, (start, length) in enumerate(broken_vector):
1662
# check the range is covered by the result
1663
for offset, data in results:
1664
if offset <= start and start + length <= offset + len(data):
1665
found_items[pos] = True
1666
self.assertEqual([True]*9, found_items)
1668
def test_get_with_open_write_stream_sees_all_content(self):
1669
t = self.get_transport()
1672
handle = t.open_write_stream('foo')
1675
self.assertEqual([(0, 'b'), (2, 'd')], list(t.readv('foo', ((0,1), (2,1)))))
1530
1679
def test_get_smart_medium(self):
1531
1680
"""All transports must either give a smart medium, or know they can't.