1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2006-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
"""Tests for Knit data structure"""
19
19
from cStringIO import StringIO
24
23
from bzrlib import (
33
32
from bzrlib.errors import (
34
RevisionAlreadyPresent,
39
36
from bzrlib.index import *
40
37
from bzrlib.knit import (
41
38
AnnotatedKnitContent,
44
40
KnitVersionedFiles,
46
42
_VFContentMapGenerator,
53
from bzrlib.repofmt import pack_repo
48
from bzrlib.patiencediff import PatienceSequenceMatcher
49
from bzrlib.repofmt import (
54
53
from bzrlib.tests import (
58
55
TestCaseWithMemoryTransport,
59
56
TestCaseWithTransport,
62
from bzrlib.transport import get_transport
63
from bzrlib.transport.memory import MemoryTransport
64
from bzrlib.tuned_gzip import GzipFile
65
59
from bzrlib.versionedfile import (
66
60
AbsentContentFactory,
106
100
line_delta = source_content.line_delta(target_content)
107
101
delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
108
102
source_lines, target_lines))
109
matcher = KnitSequenceMatcher(None, source_lines, target_lines)
110
matcher_blocks = list(list(matcher.get_matching_blocks()))
103
matcher = PatienceSequenceMatcher(None, source_lines, target_lines)
104
matcher_blocks = list(matcher.get_matching_blocks())
111
105
self.assertEqual(matcher_blocks, delta_blocks)
113
107
def test_get_line_delta_blocks(self):
333
327
transport.append_bytes(packname, bytes)
334
328
writer = pack.ContainerWriter(write_data)
336
access = _DirectPackAccess({})
330
access = pack_repo._DirectPackAccess({})
337
331
access.set_writer(writer, index, (transport, packname))
338
332
return access, writer
343
def test_pack_collection_pack_retries(self):
344
"""An explicit pack of a pack collection succeeds even when a
345
concurrent pack happens.
347
builder = self.make_branch_builder('.')
348
builder.start_series()
349
builder.build_snapshot('rev-1', None, [
350
('add', ('', 'root-id', 'directory', None)),
351
('add', ('file', 'file-id', 'file', 'content\nrev 1\n')),
353
builder.build_snapshot('rev-2', ['rev-1'], [
354
('modify', ('file-id', 'content\nrev 2\n')),
356
builder.build_snapshot('rev-3', ['rev-2'], [
357
('modify', ('file-id', 'content\nrev 3\n')),
359
self.addCleanup(builder.finish_series)
360
b = builder.get_branch()
361
self.addCleanup(b.lock_write().unlock)
363
collection = repo._pack_collection
364
# Concurrently repack the repo.
365
reopened_repo = repo.bzrdir.open_repository()
349
370
def make_vf_for_retrying(self):
350
371
"""Create 3 packs and a reload function.
378
399
collection = repo._pack_collection
379
400
collection.ensure_loaded()
380
401
orig_packs = collection.packs
381
packer = pack_repo.Packer(collection, orig_packs, '.testpack')
402
packer = knitpack_repo.KnitPacker(collection, orig_packs, '.testpack')
382
403
new_pack = packer.pack()
383
404
# forget about the new pack
384
405
collection.reset()
437
458
memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
439
460
transport = self.get_transport()
440
access = _DirectPackAccess({"FOO":(transport, 'packfile'),
461
access = pack_repo._DirectPackAccess({"FOO":(transport, 'packfile'),
441
462
"FOOBAR":(transport, 'pack2'),
442
463
"BAZ":(transport, 'pack3')})
443
464
self.assertEqual(['1234567890', '12345', 'alpha'],
454
475
def test_set_writer(self):
455
476
"""The writer should be settable post construction."""
456
access = _DirectPackAccess({})
477
access = pack_repo._DirectPackAccess({})
457
478
transport = self.get_transport()
458
479
packname = 'packfile'
471
492
transport = self.get_transport()
472
493
reload_called, reload_func = self.make_reload_func()
473
494
# Note that the index key has changed from 'foo' to 'bar'
474
access = _DirectPackAccess({'bar':(transport, 'packname')},
495
access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')},
475
496
reload_func=reload_func)
476
497
e = self.assertListRaises(errors.RetryWithNewPacks,
477
498
access.get_raw_records, memos)
486
507
memos = self.make_pack_file()
487
508
transport = self.get_transport()
488
509
# Note that the index key has changed from 'foo' to 'bar'
489
access = _DirectPackAccess({'bar':(transport, 'packname')})
510
access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')})
490
511
e = self.assertListRaises(KeyError, access.get_raw_records, memos)
492
513
def test_missing_file_raises_retry(self):
494
515
transport = self.get_transport()
495
516
reload_called, reload_func = self.make_reload_func()
496
517
# Note that the 'filename' has been changed to 'different-packname'
497
access = _DirectPackAccess({'foo':(transport, 'different-packname')},
498
reload_func=reload_func)
518
access = pack_repo._DirectPackAccess(
519
{'foo':(transport, 'different-packname')},
520
reload_func=reload_func)
499
521
e = self.assertListRaises(errors.RetryWithNewPacks,
500
522
access.get_raw_records, memos)
501
523
# The file has gone missing, so we assume we need to reload
509
531
memos = self.make_pack_file()
510
532
transport = self.get_transport()
511
533
# Note that the 'filename' has been changed to 'different-packname'
512
access = _DirectPackAccess({'foo':(transport, 'different-packname')})
534
access = pack_repo._DirectPackAccess(
535
{'foo': (transport, 'different-packname')})
513
536
e = self.assertListRaises(errors.NoSuchFile,
514
537
access.get_raw_records, memos)
519
542
failing_transport = MockReadvFailingTransport(
520
543
[transport.get_bytes('packname')])
521
544
reload_called, reload_func = self.make_reload_func()
522
access = _DirectPackAccess({'foo':(failing_transport, 'packname')},
523
reload_func=reload_func)
545
access = pack_repo._DirectPackAccess(
546
{'foo': (failing_transport, 'packname')},
547
reload_func=reload_func)
524
548
# Asking for a single record will not trigger the Mock failure
525
549
self.assertEqual(['1234567890'],
526
550
list(access.get_raw_records(memos[:1])))
542
566
failing_transport = MockReadvFailingTransport(
543
567
[transport.get_bytes('packname')])
544
568
reload_called, reload_func = self.make_reload_func()
545
access = _DirectPackAccess({'foo':(failing_transport, 'packname')})
569
access = pack_repo._DirectPackAccess(
570
{'foo':(failing_transport, 'packname')})
546
571
# Asking for a single record will not trigger the Mock failure
547
572
self.assertEqual(['1234567890'],
548
573
list(access.get_raw_records(memos[:1])))
553
578
access.get_raw_records, memos)
555
580
def test_reload_or_raise_no_reload(self):
556
access = _DirectPackAccess({}, reload_func=None)
581
access = pack_repo._DirectPackAccess({}, reload_func=None)
557
582
retry_exc = self.make_retry_exception()
558
583
# Without a reload_func, we will just re-raise the original exception
559
584
self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
561
586
def test_reload_or_raise_reload_changed(self):
562
587
reload_called, reload_func = self.make_reload_func(return_val=True)
563
access = _DirectPackAccess({}, reload_func=reload_func)
588
access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
564
589
retry_exc = self.make_retry_exception()
565
590
access.reload_or_raise(retry_exc)
566
591
self.assertEqual([1], reload_called)
571
596
def test_reload_or_raise_reload_no_change(self):
572
597
reload_called, reload_func = self.make_reload_func(return_val=False)
573
access = _DirectPackAccess({}, reload_func=reload_func)
598
access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
574
599
retry_exc = self.make_retry_exception()
575
600
# If reload_occurred is False, then we consider it an error to have
576
601
# reload_func() return False (no changes).
1167
1192
self.assertRaises(errors.KnitCorrupt, index.keys)
1168
1193
except TypeError, e:
1169
1194
if (str(e) == ('exceptions must be strings, classes, or instances,'
1170
' not exceptions.IndexError')
1171
and sys.version_info[0:2] >= (2,5)):
1195
' not exceptions.IndexError')):
1172
1196
self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1173
1197
' raising new style exceptions with python'
1187
1211
self.assertRaises(errors.KnitCorrupt, index.keys)
1188
1212
except TypeError, e:
1189
1213
if (str(e) == ('exceptions must be strings, classes, or instances,'
1190
' not exceptions.ValueError')
1191
and sys.version_info[0:2] >= (2,5)):
1214
' not exceptions.ValueError')):
1192
1215
self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1193
1216
' raising new style exceptions with python'
1207
1230
self.assertRaises(errors.KnitCorrupt, index.keys)
1208
1231
except TypeError, e:
1209
1232
if (str(e) == ('exceptions must be strings, classes, or instances,'
1210
' not exceptions.ValueError')
1211
and sys.version_info[0:2] >= (2,5)):
1233
' not exceptions.ValueError')):
1212
1234
self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1213
1235
' raising new style exceptions with python'
1225
1247
self.assertRaises(errors.KnitCorrupt, index.keys)
1226
1248
except TypeError, e:
1227
1249
if (str(e) == ('exceptions must be strings, classes, or instances,'
1228
' not exceptions.ValueError')
1229
and sys.version_info[0:2] >= (2,5)):
1250
' not exceptions.ValueError')):
1230
1251
self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1231
1252
' raising new style exceptions with python'
1243
1264
self.assertRaises(errors.KnitCorrupt, index.keys)
1244
1265
except TypeError, e:
1245
1266
if (str(e) == ('exceptions must be strings, classes, or instances,'
1246
' not exceptions.ValueError')
1247
and sys.version_info[0:2] >= (2,5)):
1267
' not exceptions.ValueError')):
1248
1268
self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1249
1269
' raising new style exceptions with python'
1579
1599
# could leave an empty .kndx file, which bzr would later claim was a
1580
1600
# corrupted file since the header was not present. In reality, the file
1581
1601
# just wasn't created, so it should be ignored.
1582
t = get_transport('.')
1602
t = transport.get_transport('.')
1583
1603
t.put_bytes('test.kndx', '')
1585
1605
knit = self.make_test_knit()
1587
1607
def test_knit_index_checks_header(self):
1588
t = get_transport('.')
1608
t = transport.get_transport('.')
1589
1609
t.put_bytes('test.kndx', '# not really a knit header\n\n')
1590
1610
k = self.make_test_knit()
1591
1611
self.assertRaises(KnitHeaderError, k.keys)