169
183
def test_get(self):
170
184
t = self.get_transport()
173
content = b'contents of a\n'
174
self.build_tree(['a'], transport=t, line_endings='binary')
175
self.check_transport_contents(b'contents of a\n', t, 'a')
177
self.assertEqual(content, f.read())
186
files = ['a', 'b', 'e', 'g']
187
contents = ['contents of a\n',
192
self.build_tree(files, transport=t, line_endings='binary')
193
self.check_transport_contents('contents of a\n', t, 'a')
194
content_f = t.get_multi(files)
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):
199
self.assertEqual(content, f.read())
201
content_f = t.get_multi(iter(files))
202
# Use itertools.izip() for the same reason
203
for content, f in itertools.izip(contents, content_f):
204
self.assertEqual(content, f.read())
179
206
def test_get_unknown_file(self):
180
207
t = self.get_transport()
181
208
files = ['a', 'b']
182
contents = [b'contents of a\n',
209
contents = ['contents of a\n',
185
212
self.build_tree(files, transport=t, line_endings='binary')
186
213
self.assertRaises(NoSuchFile, t.get, 'c')
188
def iterate_and_close(func, *args):
189
for f in func(*args):
190
# We call f.read() here because things like paramiko actually
191
# spawn a thread to prefetch the content, which we want to
192
# consume before we close the handle.
214
self.assertListRaises(NoSuchFile, t.get_multi, ['a', 'b', 'c'])
215
self.assertListRaises(NoSuchFile, t.get_multi, iter(['a', 'b', 'c']))
196
217
def test_get_directory_read_gives_ReadError(self):
197
218
"""consistent errors for read() on a file returned by get()."""
217
238
t = self.get_transport()
219
240
files = ['a', 'b', 'e', 'g']
220
contents = [b'contents of a\n',
241
contents = ['contents of a\n',
225
246
self.build_tree(files, transport=t, line_endings='binary')
226
self.check_transport_contents(b'contents of a\n', t, 'a')
247
self.check_transport_contents('contents of a\n', t, 'a')
228
249
for content, fname in zip(contents, files):
229
250
self.assertEqual(content, t.get_bytes(fname))
231
252
def test_get_bytes_unknown_file(self):
232
253
t = self.get_transport()
233
255
self.assertRaises(NoSuchFile, t.get_bytes, 'c')
235
257
def test_get_with_open_write_stream_sees_all_content(self):
236
258
t = self.get_transport()
237
259
if t.is_readonly():
239
with t.open_write_stream('foo') as handle:
241
self.assertEqual(b'b', t.get_bytes('foo'))
261
handle = t.open_write_stream('foo')
264
self.assertEqual('b', t.get('foo').read())
243
268
def test_get_bytes_with_open_write_stream_sees_all_content(self):
244
269
t = self.get_transport()
245
270
if t.is_readonly():
247
with t.open_write_stream('foo') as handle:
249
self.assertEqual(b'b', t.get_bytes('foo'))
250
with t.get('foo') as f:
251
self.assertEqual(b'b', f.read())
272
handle = t.open_write_stream('foo')
275
self.assertEqual('b', t.get_bytes('foo'))
276
self.assertEqual('b', t.get('foo').read())
253
280
def test_put_bytes(self):
254
281
t = self.get_transport()
256
283
if t.is_readonly():
257
284
self.assertRaises(TransportNotPossible,
258
t.put_bytes, 'a', b'some text for a\n')
285
t.put_bytes, 'a', 'some text for a\n')
261
t.put_bytes('a', b'some text for a\n')
262
self.assertTrue(t.has('a'))
263
self.check_transport_contents(b'some text for a\n', t, 'a')
288
t.put_bytes('a', 'some text for a\n')
289
self.failUnless(t.has('a'))
290
self.check_transport_contents('some text for a\n', t, 'a')
265
292
# The contents should be overwritten
266
t.put_bytes('a', b'new text for a\n')
267
self.check_transport_contents(b'new text for a\n', t, 'a')
293
t.put_bytes('a', 'new text for a\n')
294
self.check_transport_contents('new text for a\n', t, 'a')
269
296
self.assertRaises(NoSuchFile,
270
t.put_bytes, 'path/doesnt/exist/c', b'contents')
297
t.put_bytes, 'path/doesnt/exist/c', 'contents')
272
299
def test_put_bytes_non_atomic(self):
273
300
t = self.get_transport()
275
302
if t.is_readonly():
276
303
self.assertRaises(TransportNotPossible,
277
t.put_bytes_non_atomic, 'a', b'some text for a\n')
304
t.put_bytes_non_atomic, 'a', 'some text for a\n')
280
self.assertFalse(t.has('a'))
281
t.put_bytes_non_atomic('a', b'some text for a\n')
282
self.assertTrue(t.has('a'))
283
self.check_transport_contents(b'some text for a\n', t, 'a')
307
self.failIf(t.has('a'))
308
t.put_bytes_non_atomic('a', 'some text for a\n')
309
self.failUnless(t.has('a'))
310
self.check_transport_contents('some text for a\n', t, 'a')
284
311
# Put also replaces contents
285
t.put_bytes_non_atomic('a', b'new\ncontents for\na\n')
286
self.check_transport_contents(b'new\ncontents for\na\n', t, 'a')
312
t.put_bytes_non_atomic('a', 'new\ncontents for\na\n')
313
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
288
315
# Make sure we can create another file
289
t.put_bytes_non_atomic('d', b'contents for\nd\n')
316
t.put_bytes_non_atomic('d', 'contents for\nd\n')
290
317
# And overwrite 'a' with empty contents
291
t.put_bytes_non_atomic('a', b'')
292
self.check_transport_contents(b'contents for\nd\n', t, 'd')
293
self.check_transport_contents(b'', t, 'a')
318
t.put_bytes_non_atomic('a', '')
319
self.check_transport_contents('contents for\nd\n', t, 'd')
320
self.check_transport_contents('', t, 'a')
295
322
self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'no/such/path',
297
324
# Now test the create_parent flag
298
325
self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'dir/a',
300
self.assertFalse(t.has('dir/a'))
301
t.put_bytes_non_atomic('dir/a', b'contents for dir/a\n',
327
self.failIf(t.has('dir/a'))
328
t.put_bytes_non_atomic('dir/a', 'contents for dir/a\n',
302
329
create_parent_dir=True)
303
self.check_transport_contents(b'contents for dir/a\n', t, 'dir/a')
330
self.check_transport_contents('contents for dir/a\n', t, 'dir/a')
305
332
# But we still get NoSuchFile if we can't make the parent dir
306
333
self.assertRaises(NoSuchFile, t.put_bytes_non_atomic, 'not/there/a',
308
create_parent_dir=True)
335
create_parent_dir=True)
310
337
def test_put_bytes_permissions(self):
311
338
t = self.get_transport()
315
342
if not t._can_roundtrip_unix_modebits():
316
343
# Can't roundtrip, so no need to run this test
318
t.put_bytes('mode644', b'test text\n', mode=0o644)
319
self.assertTransportMode(t, 'mode644', 0o644)
320
t.put_bytes('mode666', b'test text\n', mode=0o666)
321
self.assertTransportMode(t, 'mode666', 0o666)
322
t.put_bytes('mode600', b'test text\n', mode=0o600)
323
self.assertTransportMode(t, 'mode600', 0o600)
345
t.put_bytes('mode644', 'test text\n', mode=0644)
346
self.assertTransportMode(t, 'mode644', 0644)
347
t.put_bytes('mode666', 'test text\n', mode=0666)
348
self.assertTransportMode(t, 'mode666', 0666)
349
t.put_bytes('mode600', 'test text\n', mode=0600)
350
self.assertTransportMode(t, 'mode600', 0600)
324
351
# Yes, you can put_bytes a file such that it becomes readonly
325
t.put_bytes('mode400', b'test text\n', mode=0o400)
326
self.assertTransportMode(t, 'mode400', 0o400)
352
t.put_bytes('mode400', 'test text\n', mode=0400)
353
self.assertTransportMode(t, 'mode400', 0400)
328
355
# The default permissions should be based on the current umask
329
356
umask = osutils.get_umask()
330
t.put_bytes('nomode', b'test text\n', mode=None)
331
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
357
t.put_bytes('nomode', 'test text\n', mode=None)
358
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
333
360
def test_put_bytes_non_atomic_permissions(self):
334
361
t = self.get_transport()
338
365
if not t._can_roundtrip_unix_modebits():
339
366
# Can't roundtrip, so no need to run this test
341
t.put_bytes_non_atomic('mode644', b'test text\n', mode=0o644)
342
self.assertTransportMode(t, 'mode644', 0o644)
343
t.put_bytes_non_atomic('mode666', b'test text\n', mode=0o666)
344
self.assertTransportMode(t, 'mode666', 0o666)
345
t.put_bytes_non_atomic('mode600', b'test text\n', mode=0o600)
346
self.assertTransportMode(t, 'mode600', 0o600)
347
t.put_bytes_non_atomic('mode400', b'test text\n', mode=0o400)
348
self.assertTransportMode(t, 'mode400', 0o400)
368
t.put_bytes_non_atomic('mode644', 'test text\n', mode=0644)
369
self.assertTransportMode(t, 'mode644', 0644)
370
t.put_bytes_non_atomic('mode666', 'test text\n', mode=0666)
371
self.assertTransportMode(t, 'mode666', 0666)
372
t.put_bytes_non_atomic('mode600', 'test text\n', mode=0600)
373
self.assertTransportMode(t, 'mode600', 0600)
374
t.put_bytes_non_atomic('mode400', 'test text\n', mode=0400)
375
self.assertTransportMode(t, 'mode400', 0400)
350
377
# The default permissions should be based on the current umask
351
378
umask = osutils.get_umask()
352
t.put_bytes_non_atomic('nomode', b'test text\n', mode=None)
353
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
379
t.put_bytes_non_atomic('nomode', 'test text\n', mode=None)
380
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
355
382
# We should also be able to set the mode for a parent directory
356
383
# when it is created
357
t.put_bytes_non_atomic('dir700/mode664', b'test text\n', mode=0o664,
358
dir_mode=0o700, create_parent_dir=True)
359
self.assertTransportMode(t, 'dir700', 0o700)
360
t.put_bytes_non_atomic('dir770/mode664', b'test text\n', mode=0o664,
361
dir_mode=0o770, create_parent_dir=True)
362
self.assertTransportMode(t, 'dir770', 0o770)
363
t.put_bytes_non_atomic('dir777/mode664', b'test text\n', mode=0o664,
364
dir_mode=0o777, create_parent_dir=True)
365
self.assertTransportMode(t, 'dir777', 0o777)
384
t.put_bytes_non_atomic('dir700/mode664', 'test text\n', mode=0664,
385
dir_mode=0700, create_parent_dir=True)
386
self.assertTransportMode(t, 'dir700', 0700)
387
t.put_bytes_non_atomic('dir770/mode664', 'test text\n', mode=0664,
388
dir_mode=0770, create_parent_dir=True)
389
self.assertTransportMode(t, 'dir770', 0770)
390
t.put_bytes_non_atomic('dir777/mode664', 'test text\n', mode=0664,
391
dir_mode=0777, create_parent_dir=True)
392
self.assertTransportMode(t, 'dir777', 0777)
367
394
def test_put_file(self):
368
395
t = self.get_transport()
370
397
if t.is_readonly():
371
398
self.assertRaises(TransportNotPossible,
372
t.put_file, 'a', BytesIO(b'some text for a\n'))
399
t.put_file, 'a', StringIO('some text for a\n'))
375
result = t.put_file('a', BytesIO(b'some text for a\n'))
402
result = t.put_file('a', StringIO('some text for a\n'))
376
403
# put_file returns the length of the data written
377
404
self.assertEqual(16, result)
378
self.assertTrue(t.has('a'))
379
self.check_transport_contents(b'some text for a\n', t, 'a')
405
self.failUnless(t.has('a'))
406
self.check_transport_contents('some text for a\n', t, 'a')
380
407
# Put also replaces contents
381
result = t.put_file('a', BytesIO(b'new\ncontents for\na\n'))
408
result = t.put_file('a', StringIO('new\ncontents for\na\n'))
382
409
self.assertEqual(19, result)
383
self.check_transport_contents(b'new\ncontents for\na\n', t, 'a')
410
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
384
411
self.assertRaises(NoSuchFile,
385
412
t.put_file, 'path/doesnt/exist/c',
386
BytesIO(b'contents'))
413
StringIO('contents'))
388
415
def test_put_file_non_atomic(self):
389
416
t = self.get_transport()
391
418
if t.is_readonly():
392
419
self.assertRaises(TransportNotPossible,
393
t.put_file_non_atomic, 'a', BytesIO(b'some text for a\n'))
420
t.put_file_non_atomic, 'a', StringIO('some text for a\n'))
396
self.assertFalse(t.has('a'))
397
t.put_file_non_atomic('a', BytesIO(b'some text for a\n'))
398
self.assertTrue(t.has('a'))
399
self.check_transport_contents(b'some text for a\n', t, 'a')
423
self.failIf(t.has('a'))
424
t.put_file_non_atomic('a', StringIO('some text for a\n'))
425
self.failUnless(t.has('a'))
426
self.check_transport_contents('some text for a\n', t, 'a')
400
427
# Put also replaces contents
401
t.put_file_non_atomic('a', BytesIO(b'new\ncontents for\na\n'))
402
self.check_transport_contents(b'new\ncontents for\na\n', t, 'a')
428
t.put_file_non_atomic('a', StringIO('new\ncontents for\na\n'))
429
self.check_transport_contents('new\ncontents for\na\n', t, 'a')
404
431
# Make sure we can create another file
405
t.put_file_non_atomic('d', BytesIO(b'contents for\nd\n'))
432
t.put_file_non_atomic('d', StringIO('contents for\nd\n'))
406
433
# And overwrite 'a' with empty contents
407
t.put_file_non_atomic('a', BytesIO(b''))
408
self.check_transport_contents(b'contents for\nd\n', t, 'd')
409
self.check_transport_contents(b'', t, 'a')
434
t.put_file_non_atomic('a', StringIO(''))
435
self.check_transport_contents('contents for\nd\n', t, 'd')
436
self.check_transport_contents('', t, 'a')
411
438
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'no/such/path',
412
BytesIO(b'contents\n'))
439
StringIO('contents\n'))
413
440
# Now test the create_parent flag
414
441
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'dir/a',
415
BytesIO(b'contents\n'))
416
self.assertFalse(t.has('dir/a'))
417
t.put_file_non_atomic('dir/a', BytesIO(b'contents for dir/a\n'),
442
StringIO('contents\n'))
443
self.failIf(t.has('dir/a'))
444
t.put_file_non_atomic('dir/a', StringIO('contents for dir/a\n'),
418
445
create_parent_dir=True)
419
self.check_transport_contents(b'contents for dir/a\n', t, 'dir/a')
446
self.check_transport_contents('contents for dir/a\n', t, 'dir/a')
421
448
# But we still get NoSuchFile if we can't make the parent dir
422
449
self.assertRaises(NoSuchFile, t.put_file_non_atomic, 'not/there/a',
423
BytesIO(b'contents\n'),
424
create_parent_dir=True)
450
StringIO('contents\n'),
451
create_parent_dir=True)
426
453
def test_put_file_permissions(self):
432
459
if not t._can_roundtrip_unix_modebits():
433
460
# Can't roundtrip, so no need to run this test
435
t.put_file('mode644', BytesIO(b'test text\n'), mode=0o644)
436
self.assertTransportMode(t, 'mode644', 0o644)
437
t.put_file('mode666', BytesIO(b'test text\n'), mode=0o666)
438
self.assertTransportMode(t, 'mode666', 0o666)
439
t.put_file('mode600', BytesIO(b'test text\n'), mode=0o600)
440
self.assertTransportMode(t, 'mode600', 0o600)
462
t.put_file('mode644', StringIO('test text\n'), mode=0644)
463
self.assertTransportMode(t, 'mode644', 0644)
464
t.put_file('mode666', StringIO('test text\n'), mode=0666)
465
self.assertTransportMode(t, 'mode666', 0666)
466
t.put_file('mode600', StringIO('test text\n'), mode=0600)
467
self.assertTransportMode(t, 'mode600', 0600)
441
468
# Yes, you can put a file such that it becomes readonly
442
t.put_file('mode400', BytesIO(b'test text\n'), mode=0o400)
443
self.assertTransportMode(t, 'mode400', 0o400)
469
t.put_file('mode400', StringIO('test text\n'), mode=0400)
470
self.assertTransportMode(t, 'mode400', 0400)
444
471
# The default permissions should be based on the current umask
445
472
umask = osutils.get_umask()
446
t.put_file('nomode', BytesIO(b'test text\n'), mode=None)
447
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
473
t.put_file('nomode', StringIO('test text\n'), mode=None)
474
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
449
476
def test_put_file_non_atomic_permissions(self):
450
477
t = self.get_transport()
454
481
if not t._can_roundtrip_unix_modebits():
455
482
# Can't roundtrip, so no need to run this test
457
t.put_file_non_atomic('mode644', BytesIO(b'test text\n'), mode=0o644)
458
self.assertTransportMode(t, 'mode644', 0o644)
459
t.put_file_non_atomic('mode666', BytesIO(b'test text\n'), mode=0o666)
460
self.assertTransportMode(t, 'mode666', 0o666)
461
t.put_file_non_atomic('mode600', BytesIO(b'test text\n'), mode=0o600)
462
self.assertTransportMode(t, 'mode600', 0o600)
484
t.put_file_non_atomic('mode644', StringIO('test text\n'), mode=0644)
485
self.assertTransportMode(t, 'mode644', 0644)
486
t.put_file_non_atomic('mode666', StringIO('test text\n'), mode=0666)
487
self.assertTransportMode(t, 'mode666', 0666)
488
t.put_file_non_atomic('mode600', StringIO('test text\n'), mode=0600)
489
self.assertTransportMode(t, 'mode600', 0600)
463
490
# Yes, you can put_file_non_atomic a file such that it becomes readonly
464
t.put_file_non_atomic('mode400', BytesIO(b'test text\n'), mode=0o400)
465
self.assertTransportMode(t, 'mode400', 0o400)
491
t.put_file_non_atomic('mode400', StringIO('test text\n'), mode=0400)
492
self.assertTransportMode(t, 'mode400', 0400)
467
494
# The default permissions should be based on the current umask
468
495
umask = osutils.get_umask()
469
t.put_file_non_atomic('nomode', BytesIO(b'test text\n'), mode=None)
470
self.assertTransportMode(t, 'nomode', 0o666 & ~umask)
496
t.put_file_non_atomic('nomode', StringIO('test text\n'), mode=None)
497
self.assertTransportMode(t, 'nomode', 0666 & ~umask)
472
499
# We should also be able to set the mode for a parent directory
473
500
# when it is created
475
t.put_file_non_atomic('dir700/mode664', sio, mode=0o664,
476
dir_mode=0o700, create_parent_dir=True)
477
self.assertTransportMode(t, 'dir700', 0o700)
478
t.put_file_non_atomic('dir770/mode664', sio, mode=0o664,
479
dir_mode=0o770, create_parent_dir=True)
480
self.assertTransportMode(t, 'dir770', 0o770)
481
t.put_file_non_atomic('dir777/mode664', sio, mode=0o664,
482
dir_mode=0o777, create_parent_dir=True)
483
self.assertTransportMode(t, 'dir777', 0o777)
502
t.put_file_non_atomic('dir700/mode664', sio, mode=0664,
503
dir_mode=0700, create_parent_dir=True)
504
self.assertTransportMode(t, 'dir700', 0700)
505
t.put_file_non_atomic('dir770/mode664', sio, mode=0664,
506
dir_mode=0770, create_parent_dir=True)
507
self.assertTransportMode(t, 'dir770', 0770)
508
t.put_file_non_atomic('dir777/mode664', sio, mode=0664,
509
dir_mode=0777, create_parent_dir=True)
510
self.assertTransportMode(t, 'dir777', 0777)
485
512
def test_put_bytes_unicode(self):
513
# Expect put_bytes to raise AssertionError or UnicodeEncodeError if
514
# given unicode "bytes". UnicodeEncodeError doesn't really make sense
515
# (we don't want to encode unicode here at all, callers should be
516
# strictly passing bytes to put_bytes), but we allow it for backwards
517
# compatibility. At some point we should use a specific exception.
518
# See https://bugs.launchpad.net/bzr/+bug/106898.
486
519
t = self.get_transport()
487
520
if t.is_readonly():
489
522
unicode_string = u'\u1234'
490
self.assertRaises(TypeError, t.put_bytes, 'foo', unicode_string)
524
(AssertionError, UnicodeEncodeError),
525
t.put_bytes, 'foo', unicode_string)
527
def test_put_file_unicode(self):
528
# Like put_bytes, except with a StringIO.StringIO of a unicode string.
529
# This situation can happen (and has) if code is careless about the type
530
# of "string" they initialise/write to a StringIO with. We cannot use
531
# cStringIO, because it never returns unicode from read.
532
# Like put_bytes, UnicodeEncodeError isn't quite the right exception to
533
# raise, but we raise it for hysterical raisins.
534
t = self.get_transport()
537
unicode_file = pyStringIO(u'\u1234')
538
self.assertRaises(UnicodeEncodeError, t.put_file, 'foo', unicode_file)
492
540
def test_mkdir(self):
493
541
t = self.get_transport()
538
591
# no sense testing on this transport
540
593
# Test mkdir with a mode
541
t.mkdir('dmode755', mode=0o755)
542
self.assertTransportMode(t, 'dmode755', 0o755)
543
t.mkdir('dmode555', mode=0o555)
544
self.assertTransportMode(t, 'dmode555', 0o555)
545
t.mkdir('dmode777', mode=0o777)
546
self.assertTransportMode(t, 'dmode777', 0o777)
547
t.mkdir('dmode700', mode=0o700)
548
self.assertTransportMode(t, 'dmode700', 0o700)
549
t.mkdir('mdmode755', mode=0o755)
550
self.assertTransportMode(t, 'mdmode755', 0o755)
594
t.mkdir('dmode755', mode=0755)
595
self.assertTransportMode(t, 'dmode755', 0755)
596
t.mkdir('dmode555', mode=0555)
597
self.assertTransportMode(t, 'dmode555', 0555)
598
t.mkdir('dmode777', mode=0777)
599
self.assertTransportMode(t, 'dmode777', 0777)
600
t.mkdir('dmode700', mode=0700)
601
self.assertTransportMode(t, 'dmode700', 0700)
602
t.mkdir_multi(['mdmode755'], mode=0755)
603
self.assertTransportMode(t, 'mdmode755', 0755)
552
605
# Default mode should be based on umask
553
606
umask = osutils.get_umask()
554
607
t.mkdir('dnomode', mode=None)
555
self.assertTransportMode(t, 'dnomode', 0o777 & ~umask)
608
self.assertTransportMode(t, 'dnomode', 0777 & ~umask)
557
610
def test_opening_a_file_stream_creates_file(self):
558
611
t = self.get_transport()
561
614
handle = t.open_write_stream('foo')
563
self.assertEqual(b'', t.get_bytes('foo'))
616
self.assertEqual('', t.get_bytes('foo'))
567
620
def test_opening_a_file_stream_can_set_mode(self):
568
621
t = self.get_transport()
569
622
if t.is_readonly():
570
self.assertRaises((TransportNotPossible, NotImplementedError),
571
t.open_write_stream, 'foo')
573
624
if not t._can_roundtrip_unix_modebits():
574
625
# Can't roundtrip, so no need to run this test
577
627
def check_mode(name, mode, expected):
578
628
handle = t.open_write_stream(name, mode=mode)
580
630
self.assertTransportMode(t, name, expected)
581
check_mode('mode644', 0o644, 0o644)
582
check_mode('mode666', 0o666, 0o666)
583
check_mode('mode600', 0o600, 0o600)
631
check_mode('mode644', 0644, 0644)
632
check_mode('mode666', 0666, 0666)
633
check_mode('mode600', 0600, 0600)
584
634
# The default permissions should be based on the current umask
585
check_mode('nomode', None, 0o666 & ~osutils.get_umask())
635
check_mode('nomode', None, 0666 & ~osutils.get_umask())
587
637
def test_copy_to(self):
588
638
# FIXME: test: same server to same server (partly done)
651
700
if t.is_readonly():
652
701
self.assertRaises(TransportNotPossible,
653
t.append_file, 'a', 'add\nsome\nmore\ncontents\n')
702
t.append_file, 'a', 'add\nsome\nmore\ncontents\n')
655
t.put_bytes('a', b'diff\ncontents for\na\n')
656
t.put_bytes('b', b'contents\nfor b\n')
704
t.put_bytes('a', 'diff\ncontents for\na\n')
705
t.put_bytes('b', 'contents\nfor b\n')
658
707
self.assertEqual(20,
659
t.append_file('a', BytesIO(b'add\nsome\nmore\ncontents\n')))
708
t.append_file('a', StringIO('add\nsome\nmore\ncontents\n')))
661
710
self.check_transport_contents(
662
b'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
711
'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
665
714
# a file with no parent should fail..
666
715
self.assertRaises(NoSuchFile,
667
t.append_file, 'missing/path', BytesIO(b'content'))
716
t.append_file, 'missing/path', StringIO('content'))
669
718
# And we can create new files, too
670
719
self.assertEqual(0,
671
t.append_file('c', BytesIO(b'some text\nfor a missing file\n')))
672
self.check_transport_contents(b'some text\nfor a missing file\n',
720
t.append_file('c', StringIO('some text\nfor a missing file\n')))
721
self.check_transport_contents('some text\nfor a missing file\n',
675
724
def test_append_bytes(self):
678
727
if t.is_readonly():
679
728
self.assertRaises(TransportNotPossible,
680
t.append_bytes, 'a', b'add\nsome\nmore\ncontents\n')
729
t.append_bytes, 'a', 'add\nsome\nmore\ncontents\n')
683
self.assertEqual(0, t.append_bytes('a', b'diff\ncontents for\na\n'))
684
self.assertEqual(0, t.append_bytes('b', b'contents\nfor b\n'))
732
self.assertEqual(0, t.append_bytes('a', 'diff\ncontents for\na\n'))
733
self.assertEqual(0, t.append_bytes('b', 'contents\nfor b\n'))
686
735
self.assertEqual(20,
687
t.append_bytes('a', b'add\nsome\nmore\ncontents\n'))
736
t.append_bytes('a', 'add\nsome\nmore\ncontents\n'))
689
738
self.check_transport_contents(
690
b'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
739
'diff\ncontents for\na\nadd\nsome\nmore\ncontents\n',
693
742
# a file with no parent should fail..
694
743
self.assertRaises(NoSuchFile,
695
t.append_bytes, 'missing/path', b'content')
744
t.append_bytes, 'missing/path', 'content')
746
def test_append_multi(self):
747
t = self.get_transport()
751
t.put_bytes('a', 'diff\ncontents for\na\n'
752
'add\nsome\nmore\ncontents\n')
753
t.put_bytes('b', 'contents\nfor b\n')
755
self.assertEqual((43, 15),
756
t.append_multi([('a', StringIO('and\nthen\nsome\nmore\n')),
757
('b', StringIO('some\nmore\nfor\nb\n'))]))
759
self.check_transport_contents(
760
'diff\ncontents for\na\n'
761
'add\nsome\nmore\ncontents\n'
762
'and\nthen\nsome\nmore\n',
764
self.check_transport_contents(
766
'some\nmore\nfor\nb\n',
769
self.assertEqual((62, 31),
770
t.append_multi(iter([('a', StringIO('a little bit more\n')),
771
('b', StringIO('from an iterator\n'))])))
772
self.check_transport_contents(
773
'diff\ncontents for\na\n'
774
'add\nsome\nmore\ncontents\n'
775
'and\nthen\nsome\nmore\n'
776
'a little bit more\n',
778
self.check_transport_contents(
780
'some\nmore\nfor\nb\n'
781
'from an iterator\n',
784
self.assertEqual((80, 0),
785
t.append_multi([('a', StringIO('some text in a\n')),
786
('d', StringIO('missing file r\n'))]))
788
self.check_transport_contents(
789
'diff\ncontents for\na\n'
790
'add\nsome\nmore\ncontents\n'
791
'and\nthen\nsome\nmore\n'
792
'a little bit more\n'
795
self.check_transport_contents('missing file r\n', t, 'd')
697
797
def test_append_file_mode(self):
698
798
"""Check that append accepts a mode parameter"""
722
822
self.assertRaises(TransportNotPossible, t.delete, 'missing')
725
t.put_bytes('a', b'a little bit of text\n')
726
self.assertTrue(t.has('a'))
825
t.put_bytes('a', 'a little bit of text\n')
826
self.failUnless(t.has('a'))
728
self.assertFalse(t.has('a'))
828
self.failIf(t.has('a'))
730
830
self.assertRaises(NoSuchFile, t.delete, 'a')
732
t.put_bytes('a', b'a text\n')
733
t.put_bytes('b', b'b text\n')
734
t.put_bytes('c', b'c text\n')
832
t.put_bytes('a', 'a text\n')
833
t.put_bytes('b', 'b text\n')
834
t.put_bytes('c', 'c text\n')
735
835
self.assertEqual([True, True, True],
736
[t.has(n) for n in ['a', 'b', 'c']])
836
list(t.has_multi(['a', 'b', 'c'])))
837
t.delete_multi(['a', 'c'])
739
838
self.assertEqual([False, True, False],
740
[t.has(n) for n in ['a', 'b', 'c']])
741
self.assertFalse(t.has('a'))
742
self.assertTrue(t.has('b'))
743
self.assertFalse(t.has('c'))
745
for name in ['a', 'c', 'd']:
746
self.assertRaises(NoSuchFile, t.delete, name)
839
list(t.has_multi(['a', 'b', 'c'])))
840
self.failIf(t.has('a'))
841
self.failUnless(t.has('b'))
842
self.failIf(t.has('c'))
844
self.assertRaises(NoSuchFile,
845
t.delete_multi, ['a', 'b', 'c'])
847
self.assertRaises(NoSuchFile,
848
t.delete_multi, iter(['a', 'b', 'c']))
850
t.put_bytes('a', 'another a text\n')
851
t.put_bytes('c', 'another c text\n')
852
t.delete_multi(iter(['a', 'b', 'c']))
748
854
# We should have deleted everything
749
855
# SftpServer creates control files in the
889
991
# creates control files in the working directory
890
992
# perhaps all of this could be done in a subdirectory
892
t.put_bytes('a', b'a first file\n')
893
self.assertEqual([True, False], [t.has(n) for n in ['a', 'b']])
994
t.put_bytes('a', 'a first file\n')
995
self.assertEquals([True, False], list(t.has_multi(['a', 'b'])))
896
self.assertTrue(t.has('b'))
897
self.assertFalse(t.has('a'))
998
self.failUnless(t.has('b'))
999
self.failIf(t.has('a'))
899
self.check_transport_contents(b'a first file\n', t, 'b')
900
self.assertEqual([False, True], [t.has(n) for n in ['a', 'b']])
1001
self.check_transport_contents('a first file\n', t, 'b')
1002
self.assertEquals([False, True], list(t.has_multi(['a', 'b'])))
902
1004
# Overwrite a file
903
t.put_bytes('c', b'c this file\n')
1005
t.put_bytes('c', 'c this file\n')
904
1006
t.move('c', 'b')
905
self.assertFalse(t.has('c'))
906
self.check_transport_contents(b'c this file\n', t, 'b')
1007
self.failIf(t.has('c'))
1008
self.check_transport_contents('c this file\n', t, 'b')
908
1010
# TODO: Try to write a test for atomicity
909
1011
# TODO: Test moving into a non-existent subdirectory
1012
# TODO: Test Transport.move_multi
911
1014
def test_copy(self):
912
1015
t = self.get_transport()
1200
1294
self.build_tree(['a', 'b/', 'b/c'], transport=t1)
1202
self.assertTrue(t1.has('a'))
1203
self.assertTrue(t1.has('b/c'))
1204
self.assertFalse(t1.has('c'))
1296
self.failUnless(t1.has('a'))
1297
self.failUnless(t1.has('b/c'))
1298
self.failIf(t1.has('c'))
1206
1300
t2 = t1.clone('b')
1207
1301
self.assertEqual(t1.base + 'b/', t2.base)
1209
self.assertTrue(t2.has('c'))
1210
self.assertFalse(t2.has('a'))
1303
self.failUnless(t2.has('c'))
1304
self.failIf(t2.has('a'))
1212
1306
t3 = t2.clone('..')
1213
self.assertTrue(t3.has('a'))
1214
self.assertFalse(t3.has('c'))
1307
self.failUnless(t3.has('a'))
1308
self.failIf(t3.has('c'))
1216
self.assertFalse(t1.has('b/d'))
1217
self.assertFalse(t2.has('d'))
1218
self.assertFalse(t3.has('b/d'))
1310
self.failIf(t1.has('b/d'))
1311
self.failIf(t2.has('d'))
1312
self.failIf(t3.has('b/d'))
1220
1314
if t1.is_readonly():
1221
self.build_tree_contents([('b/d', b'newfile\n')])
1315
self.build_tree_contents([('b/d', 'newfile\n')])
1223
t2.put_bytes('d', b'newfile\n')
1317
t2.put_bytes('d', 'newfile\n')
1225
self.assertTrue(t1.has('b/d'))
1226
self.assertTrue(t2.has('d'))
1227
self.assertTrue(t3.has('b/d'))
1319
self.failUnless(t1.has('b/d'))
1320
self.failUnless(t2.has('d'))
1321
self.failUnless(t3.has('b/d'))
1229
1323
def test_clone_to_root(self):
1230
1324
orig_transport = self.get_transport()
1442
1535
# '\xe5' and '\xe4' actually map to the same file
1443
1536
# adding a suffix kicks in the 'preserving but insensitive'
1444
1537
# route, and maintains the right files
1445
files = [u'\xe5.1', # a w/ circle iso-8859-1
1446
u'\xe4.2', # a w/ dots iso-8859-1
1447
u'\u017d', # Z with umlat iso-8859-2
1448
u'\u062c', # Arabic j
1449
u'\u0410', # Russian A
1450
u'\u65e5', # Kanji person
1538
files = [u'\xe5.1', # a w/ circle iso-8859-1
1539
u'\xe4.2', # a w/ dots iso-8859-1
1540
u'\u017d', # Z with umlat iso-8859-2
1541
u'\u062c', # Arabic j
1542
u'\u0410', # Russian A
1543
u'\u65e5', # Kanji person
1453
1546
no_unicode_support = getattr(self._server, 'no_unicode_support', False)
1454
1547
if no_unicode_support:
1455
self.knownFailure("test server cannot handle unicode paths")
1548
raise tests.KnownFailure("test server cannot handle unicode paths")
1458
1551
self.build_tree(files, transport=t, line_endings='binary')
1459
1552
except UnicodeError:
1461
"cannot handle unicode paths in current encoding")
1553
raise TestSkipped("cannot handle unicode paths in current encoding")
1463
1555
# A plain unicode string is not a valid url
1464
1556
for fname in files:
1465
self.assertRaises(urlutils.InvalidURL, t.get, fname)
1557
self.assertRaises(InvalidURL, t.get, fname)
1467
1559
for fname in files:
1468
1560
fname_utf8 = fname.encode('utf-8')
1469
contents = b'contents of %s\n' % (fname_utf8,)
1561
contents = 'contents of %s\n' % (fname_utf8,)
1470
1562
self.check_transport_contents(contents, t, urlutils.escape(fname))
1472
1564
def test_connect_twice_is_same_content(self):
1525
1616
def test_readv(self):
1526
1617
transport = self.get_transport()
1527
1618
if transport.is_readonly():
1528
with open('a', 'w') as f:
1529
f.write('0123456789')
1619
file('a', 'w').write('0123456789')
1531
transport.put_bytes('a', b'0123456789')
1621
transport.put_bytes('a', '0123456789')
1533
1623
d = list(transport.readv('a', ((0, 1),)))
1534
self.assertEqual(d[0], (0, b'0'))
1624
self.assertEqual(d[0], (0, '0'))
1536
1626
d = list(transport.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
1537
self.assertEqual(d[0], (0, b'0'))
1538
self.assertEqual(d[1], (1, b'1'))
1539
self.assertEqual(d[2], (3, b'34'))
1540
self.assertEqual(d[3], (9, b'9'))
1627
self.assertEqual(d[0], (0, '0'))
1628
self.assertEqual(d[1], (1, '1'))
1629
self.assertEqual(d[2], (3, '34'))
1630
self.assertEqual(d[3], (9, '9'))
1542
1632
def test_readv_out_of_order(self):
1543
1633
transport = self.get_transport()
1544
1634
if transport.is_readonly():
1545
with open('a', 'w') as f:
1546
f.write('0123456789')
1635
file('a', 'w').write('0123456789')
1548
transport.put_bytes('a', b'01234567890')
1637
transport.put_bytes('a', '01234567890')
1550
1639
d = list(transport.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
1551
self.assertEqual(d[0], (1, b'1'))
1552
self.assertEqual(d[1], (9, b'9'))
1553
self.assertEqual(d[2], (0, b'0'))
1554
self.assertEqual(d[3], (3, b'34'))
1640
self.assertEqual(d[0], (1, '1'))
1641
self.assertEqual(d[1], (9, '9'))
1642
self.assertEqual(d[2], (0, '0'))
1643
self.assertEqual(d[3], (3, '34'))
1556
1645
def test_readv_with_adjust_for_latency(self):
1557
1646
transport = self.get_transport()
1622
1710
transport = self.get_transport()
1623
1711
# test from observed failure case.
1624
1712
if transport.is_readonly():
1625
with open('a', 'w') as f:
1626
f.write('a' * 1024 * 1024)
1713
file('a', 'w').write('a'*1024*1024)
1628
transport.put_bytes('a', b'a' * 1024 * 1024)
1715
transport.put_bytes('a', 'a'*1024*1024)
1629
1716
broken_vector = [(465219, 800), (225221, 800), (445548, 800),
1630
(225037, 800), (221357, 800), (437077, 800), (947670, 800),
1631
(465373, 800), (947422, 800)]
1632
results = list(transport.readv('a', broken_vector, True, 1024 * 1024))
1633
found_items = [False] * 9
1717
(225037, 800), (221357, 800), (437077, 800), (947670, 800),
1718
(465373, 800), (947422, 800)]
1719
results = list(transport.readv('a', broken_vector, True, 1024*1024))
1720
found_items = [False]*9
1634
1721
for pos, (start, length) in enumerate(broken_vector):
1635
1722
# check the range is covered by the result
1636
1723
for offset, data in results:
1637
1724
if offset <= start and start + length <= offset + len(data):
1638
1725
found_items[pos] = True
1639
self.assertEqual([True] * 9, found_items)
1726
self.assertEqual([True]*9, found_items)
1641
1728
def test_get_with_open_write_stream_sees_all_content(self):
1642
1729
t = self.get_transport()
1643
1730
if t.is_readonly():
1645
with t.open_write_stream('foo') as handle:
1646
handle.write(b'bcd')
1647
self.assertEqual([(0, b'b'), (2, b'd')], list(
1648
t.readv('foo', ((0, 1), (2, 1)))))
1732
handle = t.open_write_stream('foo')
1735
self.assertEqual([(0, 'b'), (2, 'd')], list(t.readv('foo', ((0,1), (2,1)))))
1650
1739
def test_get_smart_medium(self):
1651
1740
"""All transports must either give a smart medium, or know they can't.
1653
1742
transport = self.get_transport()
1655
1744
client_medium = transport.get_smart_medium()
1745
self.assertIsInstance(client_medium, medium.SmartClientMedium)
1656
1746
except errors.NoSmartMedium:
1657
1747
# as long as we got it we're fine
1660
from ..bzr.smart import medium
1661
self.assertIsInstance(client_medium, medium.SmartClientMedium)
1663
1750
def test_readv_short_read(self):
1664
1751
transport = self.get_transport()
1665
1752
if transport.is_readonly():
1666
with open('a', 'w') as f:
1667
f.write('0123456789')
1753
file('a', 'w').write('0123456789')
1669
transport.put_bytes('a', b'01234567890')
1755
transport.put_bytes('a', '01234567890')
1671
1757
# This is intentionally reading off the end of the file
1672
1758
# since we are sure that it cannot get there
1673
1759
self.assertListRaises((errors.ShortReadvError, errors.InvalidRange,
1674
1760
# Can be raised by paramiko
1675
1761
AssertionError),
1676
transport.readv, 'a', [(1, 1), (8, 10)])
1762
transport.readv, 'a', [(1,1), (8,10)])
1678
1764
# This is trying to seek past the end of the file, it should
1679
1765
# also raise a special error
1680
1766
self.assertListRaises((errors.ShortReadvError, errors.InvalidRange),
1681
transport.readv, 'a', [(12, 2)])
1683
def test_no_segment_parameters(self):
1684
"""Segment parameters should be stripped and stored in
1685
transport.segment_parameters."""
1686
transport = self.get_transport("foo")
1687
self.assertEqual({}, transport.get_segment_parameters())
1689
def test_segment_parameters(self):
1690
"""Segment parameters should be stripped and stored in
1691
transport.get_segment_parameters()."""
1692
base_url = self._server.get_url()
1693
parameters = {"key1": "val1", "key2": "val2"}
1694
url = urlutils.join_segment_parameters(base_url, parameters)
1695
transport = _mod_transport.get_transport_from_url(url)
1696
self.assertEqual(parameters, transport.get_segment_parameters())
1698
def test_set_segment_parameters(self):
1699
"""Segment parameters can be set and show up in base."""
1700
transport = self.get_transport("foo")
1701
orig_base = transport.base
1702
transport.set_segment_parameter("arm", "board")
1703
self.assertEqual("%s,arm=board" % orig_base, transport.base)
1704
self.assertEqual({"arm": "board"}, transport.get_segment_parameters())
1705
transport.set_segment_parameter("arm", None)
1706
transport.set_segment_parameter("nonexistant", None)
1707
self.assertEqual({}, transport.get_segment_parameters())
1708
self.assertEqual(orig_base, transport.base)
1710
def test_stat_symlink(self):
1711
# if a transport points directly to a symlink (and supports symlinks
1712
# at all) you can tell this. helps with bug 32669.
1713
t = self.get_transport()
1715
t.symlink('target', 'link')
1716
except TransportNotPossible:
1717
raise TestSkipped("symlinks not supported")
1718
t2 = t.clone('link')
1720
self.assertTrue(stat.S_ISLNK(st.st_mode))
1722
def test_abspath_url_unquote_unreserved(self):
1723
"""URLs from abspath should have unreserved characters unquoted
1725
Need consistent quoting notably for tildes, see lp:842223 for more.
1727
t = self.get_transport()
1728
needlessly_escaped_dir = "%2D%2E%30%39%41%5A%5F%61%7A%7E/"
1729
self.assertEqual(t.base + "-.09AZ_az~",
1730
t.abspath(needlessly_escaped_dir))
1732
def test_clone_url_unquote_unreserved(self):
1733
"""Base URL of a cloned branch needs unreserved characters unquoted
1735
Cloned transports should be prefix comparable for things like the
1736
isolation checking of tests, see lp:842223 for more.
1738
t1 = self.get_transport()
1739
needlessly_escaped_dir = "%2D%2E%30%39%41%5A%5F%61%7A%7E/"
1740
self.build_tree([needlessly_escaped_dir], transport=t1)
1741
t2 = t1.clone(needlessly_escaped_dir)
1742
self.assertEqual(t1.base + "-.09AZ_az~/", t2.base)
1744
def test_hook_post_connection_one(self):
1745
"""Fire post_connect hook after a ConnectedTransport is first used"""
1747
Transport.hooks.install_named_hook("post_connect", log.append, None)
1748
t = self.get_transport()
1749
self.assertEqual([], log)
1750
t.has("non-existant")
1751
if isinstance(t, RemoteTransport):
1752
self.assertEqual([t.get_smart_medium()], log)
1753
elif isinstance(t, ConnectedTransport):
1754
self.assertEqual([t], log)
1756
self.assertEqual([], log)
1758
def test_hook_post_connection_multi(self):
1759
"""Fire post_connect hook once per unshared underlying connection"""
1761
Transport.hooks.install_named_hook("post_connect", log.append, None)
1762
t1 = self.get_transport()
1764
t3 = self.get_transport()
1765
self.assertEqual([], log)
1769
if isinstance(t1, RemoteTransport):
1770
self.assertEqual([t.get_smart_medium() for t in [t1, t3]], log)
1771
elif isinstance(t1, ConnectedTransport):
1772
self.assertEqual([t1, t3], log)
1774
self.assertEqual([], log)
1767
transport.readv, 'a', [(12,2)])