~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_versionedfile.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-05-12 03:07:05 UTC
  • mfrom: (3350.3.22 data_stream_revamp)
  • Revision ID: pqm@pqm.ubuntu.com-20080512030705-nvl2q1tuls904eru
Deprecate bzrlib.versionedfiles.VersionedFile.join.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
# TODO: might be nice to create a versionedfile with some type of corruption
22
22
# considered typical and check that it can be detected/corrected.
23
23
 
 
24
from itertools import chain
24
25
from StringIO import StringIO
25
26
 
26
27
import bzrlib
34
35
                           RevisionAlreadyPresent,
35
36
                           WeaveParentMismatch
36
37
                           )
 
38
from bzrlib import knit as _mod_knit
37
39
from bzrlib.knit import (
38
40
    make_file_knit,
39
41
    KnitAnnotateFactory,
40
42
    KnitPlainFactory,
41
43
    )
42
 
from bzrlib.symbol_versioning import one_four
 
44
from bzrlib.symbol_versioning import one_four, one_five
43
45
from bzrlib.tests import TestCaseWithMemoryTransport, TestSkipped
44
46
from bzrlib.tests.http_utils import TestCaseWithWebserver
45
47
from bzrlib.trace import mutter
46
48
from bzrlib.transport import get_transport
47
49
from bzrlib.transport.memory import MemoryTransport
48
50
from bzrlib.tsort import topo_sort
 
51
from bzrlib.tuned_gzip import GzipFile
49
52
import bzrlib.versionedfile as versionedfile
50
53
from bzrlib.weave import WeaveFile
51
54
from bzrlib.weavefile import read_weave, write_weave
52
55
 
53
56
 
 
57
def get_diamond_vf(f, trailing_eol=True, left_only=False):
 
58
    """Get a diamond graph to exercise deltas and merges.
 
59
    
 
60
    :param trailing_eol: If True end the last line with \n.
 
61
    """
 
62
    parents = {
 
63
        'origin': (),
 
64
        'base': (('origin',),),
 
65
        'left': (('base',),),
 
66
        'right': (('base',),),
 
67
        'merged': (('left',), ('right',)),
 
68
        }
 
69
    # insert a diamond graph to exercise deltas and merges.
 
70
    if trailing_eol:
 
71
        last_char = '\n'
 
72
    else:
 
73
        last_char = ''
 
74
    f.add_lines('origin', [], ['origin' + last_char])
 
75
    f.add_lines('base', ['origin'], ['base' + last_char])
 
76
    f.add_lines('left', ['base'], ['base\n', 'left' + last_char])
 
77
    if not left_only:
 
78
        f.add_lines('right', ['base'],
 
79
            ['base\n', 'right' + last_char])
 
80
        f.add_lines('merged', ['left', 'right'],
 
81
            ['base\n', 'left\n', 'right\n', 'merged' + last_char])
 
82
    return f, parents
 
83
 
 
84
 
54
85
class VersionedFileTestMixIn(object):
55
86
    """A mixin test class for testing VersionedFiles.
56
87
 
87
118
        f = self.reopen_file(create=True)
88
119
        verify_file(f)
89
120
 
 
121
    def test_get_record_stream_empty(self):
 
122
        """get_record_stream is a replacement for get_data_stream."""
 
123
        f = self.get_file()
 
124
        entries = f.get_record_stream([], 'unordered', False)
 
125
        self.assertEqual([], list(entries))
 
126
 
 
127
    def assertValidStorageKind(self, storage_kind):
 
128
        """Assert that storage_kind is a valid storage_kind."""
 
129
        self.assertSubset([storage_kind],
 
130
            ['mpdiff', 'knit-annotated-ft', 'knit-annotated-delta',
 
131
             'knit-ft', 'knit-delta', 'fulltext', 'knit-annotated-ft-gz',
 
132
             'knit-annotated-delta-gz', 'knit-ft-gz', 'knit-delta-gz'])
 
133
 
 
134
    def capture_stream(self, f, entries, on_seen, parents):
 
135
        """Capture a stream for testing."""
 
136
        for factory in entries:
 
137
            on_seen(factory.key)
 
138
            self.assertValidStorageKind(factory.storage_kind)
 
139
            self.assertEqual(f.get_sha1s([factory.key[0]])[0], factory.sha1)
 
140
            self.assertEqual(parents[factory.key[0]], factory.parents)
 
141
            self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
 
142
                str)
 
143
 
 
144
    def test_get_record_stream_interface(self):
 
145
        """Each item in a stream has to provide a regular interface."""
 
146
        f, parents = get_diamond_vf(self.get_file())
 
147
        entries = f.get_record_stream(['merged', 'left', 'right', 'base'],
 
148
            'unordered', False)
 
149
        seen = set()
 
150
        self.capture_stream(f, entries, seen.add, parents)
 
151
        self.assertEqual(set([('base',), ('left',), ('right',), ('merged',)]),
 
152
            seen)
 
153
 
 
154
    def test_get_record_stream_interface_ordered(self):
 
155
        """Each item in a stream has to provide a regular interface."""
 
156
        f, parents = get_diamond_vf(self.get_file())
 
157
        entries = f.get_record_stream(['merged', 'left', 'right', 'base'],
 
158
            'topological', False)
 
159
        seen = []
 
160
        self.capture_stream(f, entries, seen.append, parents)
 
161
        self.assertSubset([tuple(seen)],
 
162
            (
 
163
             (('base',), ('left',), ('right',), ('merged',)),
 
164
             (('base',), ('right',), ('left',), ('merged',)),
 
165
            ))
 
166
 
 
167
    def test_get_record_stream_interface_ordered_with_delta_closure(self):
 
168
        """Each item in a stream has to provide a regular interface."""
 
169
        f, parents = get_diamond_vf(self.get_file())
 
170
        entries = f.get_record_stream(['merged', 'left', 'right', 'base'],
 
171
            'topological', True)
 
172
        seen = []
 
173
        for factory in entries:
 
174
            seen.append(factory.key)
 
175
            self.assertValidStorageKind(factory.storage_kind)
 
176
            self.assertEqual(f.get_sha1s([factory.key[0]])[0], factory.sha1)
 
177
            self.assertEqual(parents[factory.key[0]], factory.parents)
 
178
            self.assertEqual(f.get_text(factory.key[0]),
 
179
                factory.get_bytes_as('fulltext'))
 
180
            self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
 
181
                str)
 
182
        self.assertSubset([tuple(seen)],
 
183
            (
 
184
             (('base',), ('left',), ('right',), ('merged',)),
 
185
             (('base',), ('right',), ('left',), ('merged',)),
 
186
            ))
 
187
 
 
188
    def test_get_record_stream_unknown_storage_kind_raises(self):
 
189
        """Asking for a storage kind that the stream cannot supply raises."""
 
190
        f, parents = get_diamond_vf(self.get_file())
 
191
        entries = f.get_record_stream(['merged', 'left', 'right', 'base'],
 
192
            'unordered', False)
 
193
        # We track the contents because we should be able to try, fail a
 
194
        # particular kind and then ask for one that works and continue.
 
195
        seen = set()
 
196
        for factory in entries:
 
197
            seen.add(factory.key)
 
198
            self.assertValidStorageKind(factory.storage_kind)
 
199
            self.assertEqual(f.get_sha1s([factory.key[0]])[0], factory.sha1)
 
200
            self.assertEqual(parents[factory.key[0]], factory.parents)
 
201
            # currently no stream emits mpdiff
 
202
            self.assertRaises(errors.UnavailableRepresentation,
 
203
                factory.get_bytes_as, 'mpdiff')
 
204
            self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
 
205
                str)
 
206
        self.assertEqual(set([('base',), ('left',), ('right',), ('merged',)]),
 
207
            seen)
 
208
 
 
209
    def test_get_record_stream_missing_records_are_absent(self):
 
210
        f, parents = get_diamond_vf(self.get_file())
 
211
        entries = f.get_record_stream(['merged', 'left', 'right', 'or', 'base'],
 
212
            'unordered', False)
 
213
        self.assertAbsentRecord(f, parents, entries)
 
214
        entries = f.get_record_stream(['merged', 'left', 'right', 'or', 'base'],
 
215
            'topological', False)
 
216
        self.assertAbsentRecord(f, parents, entries)
 
217
 
 
218
    def assertAbsentRecord(self, f, parents, entries):
 
219
        """Helper for test_get_record_stream_missing_records_are_absent."""
 
220
        seen = set()
 
221
        for factory in entries:
 
222
            seen.add(factory.key)
 
223
            if factory.key == ('or',):
 
224
                self.assertEqual('absent', factory.storage_kind)
 
225
                self.assertEqual(None, factory.sha1)
 
226
                self.assertEqual(None, factory.parents)
 
227
            else:
 
228
                self.assertValidStorageKind(factory.storage_kind)
 
229
                self.assertEqual(f.get_sha1s([factory.key[0]])[0], factory.sha1)
 
230
                self.assertEqual(parents[factory.key[0]], factory.parents)
 
231
                self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
 
232
                    str)
 
233
        self.assertEqual(
 
234
            set([('base',), ('left',), ('right',), ('merged',), ('or',)]),
 
235
            seen)
 
236
 
 
237
    def test_filter_absent_records(self):
 
238
        """Requested missing records can be filter trivially."""
 
239
        f, parents = get_diamond_vf(self.get_file())
 
240
        entries = f.get_record_stream(['merged', 'left', 'right', 'extra', 'base'],
 
241
            'unordered', False)
 
242
        seen = set()
 
243
        self.capture_stream(f, versionedfile.filter_absent(entries), seen.add,
 
244
            parents)
 
245
        self.assertEqual(set([('base',), ('left',), ('right',), ('merged',)]),
 
246
            seen)
 
247
 
 
248
    def test_insert_record_stream_empty(self):
 
249
        """Inserting an empty record stream should work."""
 
250
        f = self.get_file()
 
251
        stream = []
 
252
        f.insert_record_stream([])
 
253
 
 
254
    def assertIdenticalVersionedFile(self, left, right):
 
255
        """Assert that left and right have the same contents."""
 
256
        self.assertEqual(set(left.versions()), set(right.versions()))
 
257
        self.assertEqual(left.get_parent_map(left.versions()),
 
258
            right.get_parent_map(right.versions()))
 
259
        for v in left.versions():
 
260
            self.assertEqual(left.get_text(v), right.get_text(v))
 
261
 
 
262
    def test_insert_record_stream_fulltexts(self):
 
263
        """Any file should accept a stream of fulltexts."""
 
264
        f = self.get_file()
 
265
        weave_vf = WeaveFile('source', get_transport(self.get_url('.')),
 
266
            create=True, get_scope=self.get_transaction)
 
267
        source, _ = get_diamond_vf(weave_vf)
 
268
        stream = source.get_record_stream(source.versions(), 'topological',
 
269
            False)
 
270
        f.insert_record_stream(stream)
 
271
        self.assertIdenticalVersionedFile(f, source)
 
272
 
 
273
    def test_insert_record_stream_fulltexts_noeol(self):
 
274
        """Any file should accept a stream of fulltexts."""
 
275
        f = self.get_file()
 
276
        weave_vf = WeaveFile('source', get_transport(self.get_url('.')),
 
277
            create=True, get_scope=self.get_transaction)
 
278
        source, _ = get_diamond_vf(weave_vf, trailing_eol=False)
 
279
        stream = source.get_record_stream(source.versions(), 'topological',
 
280
            False)
 
281
        f.insert_record_stream(stream)
 
282
        self.assertIdenticalVersionedFile(f, source)
 
283
 
 
284
    def test_insert_record_stream_annotated_knits(self):
 
285
        """Any file should accept a stream from plain knits."""
 
286
        f = self.get_file()
 
287
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
288
            create=True)
 
289
        get_diamond_vf(source)
 
290
        stream = source.get_record_stream(source.versions(), 'topological',
 
291
            False)
 
292
        f.insert_record_stream(stream)
 
293
        self.assertIdenticalVersionedFile(f, source)
 
294
 
 
295
    def test_insert_record_stream_annotated_knits_noeol(self):
 
296
        """Any file should accept a stream from plain knits."""
 
297
        f = self.get_file()
 
298
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
299
            create=True)
 
300
        get_diamond_vf(source, trailing_eol=False)
 
301
        stream = source.get_record_stream(source.versions(), 'topological',
 
302
            False)
 
303
        f.insert_record_stream(stream)
 
304
        self.assertIdenticalVersionedFile(f, source)
 
305
 
 
306
    def test_insert_record_stream_plain_knits(self):
 
307
        """Any file should accept a stream from plain knits."""
 
308
        f = self.get_file()
 
309
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
310
            create=True, factory=KnitPlainFactory())
 
311
        get_diamond_vf(source)
 
312
        stream = source.get_record_stream(source.versions(), 'topological',
 
313
            False)
 
314
        f.insert_record_stream(stream)
 
315
        self.assertIdenticalVersionedFile(f, source)
 
316
 
 
317
    def test_insert_record_stream_plain_knits_noeol(self):
 
318
        """Any file should accept a stream from plain knits."""
 
319
        f = self.get_file()
 
320
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
321
            create=True, factory=KnitPlainFactory())
 
322
        get_diamond_vf(source, trailing_eol=False)
 
323
        stream = source.get_record_stream(source.versions(), 'topological',
 
324
            False)
 
325
        f.insert_record_stream(stream)
 
326
        self.assertIdenticalVersionedFile(f, source)
 
327
 
 
328
    def test_insert_record_stream_existing_keys(self):
 
329
        """Inserting keys already in a file should not error."""
 
330
        f = self.get_file()
 
331
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
332
            create=True, factory=KnitPlainFactory())
 
333
        get_diamond_vf(source)
 
334
        # insert some keys into f.
 
335
        get_diamond_vf(f, left_only=True)
 
336
        stream = source.get_record_stream(source.versions(), 'topological',
 
337
            False)
 
338
        f.insert_record_stream(stream)
 
339
        self.assertIdenticalVersionedFile(f, source)
 
340
 
 
341
    def test_insert_record_stream_missing_keys(self):
 
342
        """Inserting a stream with absent keys should raise an error."""
 
343
        f = self.get_file()
 
344
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
345
            create=True, factory=KnitPlainFactory())
 
346
        stream = source.get_record_stream(['missing'], 'topological',
 
347
            False)
 
348
        self.assertRaises(errors.RevisionNotPresent, f.insert_record_stream,
 
349
            stream)
 
350
 
 
351
    def test_insert_record_stream_out_of_order(self):
 
352
        """An out of order stream can either error or work."""
 
353
        f, parents = get_diamond_vf(self.get_file())
 
354
        origin_entries = f.get_record_stream(['origin'], 'unordered', False)
 
355
        end_entries = f.get_record_stream(['merged', 'left'],
 
356
            'topological', False)
 
357
        start_entries = f.get_record_stream(['right', 'base'],
 
358
            'topological', False)
 
359
        entries = chain(origin_entries, end_entries, start_entries)
 
360
        target = self.get_file('target')
 
361
        try:
 
362
            target.insert_record_stream(entries)
 
363
        except RevisionNotPresent:
 
364
            # Must not have corrupted the file.
 
365
            target.check()
 
366
        else:
 
367
            self.assertIdenticalVersionedFile(f, target)
 
368
 
 
369
    def test_insert_record_stream_delta_missing_basis_no_corruption(self):
 
370
        """Insertion where a needed basis is not included aborts safely."""
 
371
        # Annotated source - deltas can be used in any knit.
 
372
        source = make_file_knit('source', get_transport(self.get_url('.')),
 
373
            create=True)
 
374
        get_diamond_vf(source)
 
375
        entries = source.get_record_stream(['origin', 'merged'], 'unordered', False)
 
376
        f = self.get_file()
 
377
        self.assertRaises(RevisionNotPresent, f.insert_record_stream, entries)
 
378
        f.check()
 
379
        self.assertFalse(f.has_version('merged'))
 
380
 
90
381
    def test_adds_with_parent_texts(self):
91
382
        f = self.get_file()
92
383
        parent_texts = {}
358
649
        self._transaction = 'after'
359
650
        self.assertRaises(errors.OutSideTransaction, f.add_lines, '', [], [])
360
651
        self.assertRaises(errors.OutSideTransaction, f.add_lines_with_ghosts, '', [], [])
361
 
        self.assertRaises(errors.OutSideTransaction, f.join, '')
 
652
        self.assertRaises(errors.OutSideTransaction, self.applyDeprecated,
 
653
            one_five, f.join, '')
362
654
        
363
655
    def test_clone_text(self):
364
656
        f = self.get_file()
693
985
                          'base',
694
986
                          [],
695
987
                          [])
696
 
        self.assertRaises(errors.ReadOnlyError, vf.join, 'base')
 
988
        self.assertRaises(errors.ReadOnlyError, self.applyDeprecated, one_five,
 
989
            vf.join, 'base')
697
990
    
698
991
    def test_get_sha1s(self):
699
992
        # check the sha1 data is available
778
1071
 
779
1072
class TestKnit(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
780
1073
 
781
 
    def get_file(self, name='foo'):
782
 
        return self.get_factory()(name, get_transport(self.get_url('.')),
 
1074
    def get_file(self, name='foo', create=True):
 
1075
        return make_file_knit(name, get_transport(self.get_url('.')),
783
1076
            delta=True, create=True, get_scope=self.get_transaction)
784
1077
 
785
1078
    def get_factory(self):
792
1085
        return knit
793
1086
 
794
1087
    def reopen_file(self, name='foo', create=False):
795
 
        return self.get_factory()(name, get_transport(self.get_url('.')),
796
 
            delta=True,
797
 
            create=create)
 
1088
        return self.get_file(name, create)
798
1089
 
799
1090
    def test_detection(self):
800
1091
        knit = self.get_file()
808
1099
class TestPlaintextKnit(TestKnit):
809
1100
    """Test a knit with no cached annotations"""
810
1101
 
811
 
    def get_factory(self):
812
 
        return make_file_knit
 
1102
    def get_file(self, name='foo', create=True):
 
1103
        return make_file_knit(name, get_transport(self.get_url('.')),
 
1104
            delta=True, create=create, get_scope=self.get_transaction,
 
1105
            factory=_mod_knit.KnitPlainFactory())
813
1106
 
814
1107
 
815
1108
class TestPlanMergeVersionedFile(TestCaseWithMemoryTransport):
1243
1536
 
1244
1537
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
1245
1538
                                'xxx', '>>>>>>> ', 'bbb']
 
1539
 
 
1540
 
 
1541
class TestContentFactoryAdaption(TestCaseWithMemoryTransport):
 
1542
 
 
1543
    def test_select_adaptor(self):
 
1544
        """Test expected adapters exist."""
 
1545
        # One scenario for each lookup combination we expect to use.
 
1546
        # Each is source_kind, requested_kind, adapter class
 
1547
        scenarios = [
 
1548
            ('knit-delta-gz', 'fulltext', _mod_knit.DeltaPlainToFullText),
 
1549
            ('knit-ft-gz', 'fulltext', _mod_knit.FTPlainToFullText),
 
1550
            ('knit-annotated-delta-gz', 'knit-delta-gz',
 
1551
                _mod_knit.DeltaAnnotatedToUnannotated),
 
1552
            ('knit-annotated-delta-gz', 'fulltext',
 
1553
                _mod_knit.DeltaAnnotatedToFullText),
 
1554
            ('knit-annotated-ft-gz', 'knit-ft-gz',
 
1555
                _mod_knit.FTAnnotatedToUnannotated),
 
1556
            ('knit-annotated-ft-gz', 'fulltext',
 
1557
                _mod_knit.FTAnnotatedToFullText),
 
1558
            ]
 
1559
        for source, requested, klass in scenarios:
 
1560
            adapter_factory = versionedfile.adapter_registry.get(
 
1561
                (source, requested))
 
1562
            adapter = adapter_factory(None)
 
1563
            self.assertIsInstance(adapter, klass)
 
1564
 
 
1565
    def get_knit(self, annotated=True):
 
1566
        if annotated:
 
1567
            factory = KnitAnnotateFactory()
 
1568
        else:
 
1569
            factory = KnitPlainFactory()
 
1570
        return make_file_knit('knit', self.get_transport('.'), delta=True,
 
1571
            create=True, factory=factory)
 
1572
 
 
1573
    def helpGetBytes(self, f, ft_adapter, delta_adapter):
 
1574
        """Grab the interested adapted texts for tests."""
 
1575
        # origin is a fulltext
 
1576
        entries = f.get_record_stream(['origin'], 'unordered', False)
 
1577
        base = entries.next()
 
1578
        ft_data = ft_adapter.get_bytes(base, base.get_bytes_as(base.storage_kind))
 
1579
        # merged is both a delta and multiple parents.
 
1580
        entries = f.get_record_stream(['merged'], 'unordered', False)
 
1581
        merged = entries.next()
 
1582
        delta_data = delta_adapter.get_bytes(merged,
 
1583
            merged.get_bytes_as(merged.storage_kind))
 
1584
        return ft_data, delta_data
 
1585
 
 
1586
    def test_deannotation_noeol(self):
 
1587
        """Test converting annotated knits to unannotated knits."""
 
1588
        # we need a full text, and a delta
 
1589
        f, parents = get_diamond_vf(self.get_knit(), trailing_eol=False)
 
1590
        ft_data, delta_data = self.helpGetBytes(f,
 
1591
            _mod_knit.FTAnnotatedToUnannotated(None),
 
1592
            _mod_knit.DeltaAnnotatedToUnannotated(None))
 
1593
        self.assertEqual(
 
1594
            'version origin 1 b284f94827db1fa2970d9e2014f080413b547a7e\n'
 
1595
            'origin\n'
 
1596
            'end origin\n',
 
1597
            GzipFile(mode='rb', fileobj=StringIO(ft_data)).read())
 
1598
        self.assertEqual(
 
1599
            'version merged 4 32c2e79763b3f90e8ccde37f9710b6629c25a796\n'
 
1600
            '1,2,3\nleft\nright\nmerged\nend merged\n',
 
1601
            GzipFile(mode='rb', fileobj=StringIO(delta_data)).read())
 
1602
 
 
1603
    def test_deannotation(self):
 
1604
        """Test converting annotated knits to unannotated knits."""
 
1605
        # we need a full text, and a delta
 
1606
        f, parents = get_diamond_vf(self.get_knit())
 
1607
        ft_data, delta_data = self.helpGetBytes(f,
 
1608
            _mod_knit.FTAnnotatedToUnannotated(None),
 
1609
            _mod_knit.DeltaAnnotatedToUnannotated(None))
 
1610
        self.assertEqual(
 
1611
            'version origin 1 00e364d235126be43292ab09cb4686cf703ddc17\n'
 
1612
            'origin\n'
 
1613
            'end origin\n',
 
1614
            GzipFile(mode='rb', fileobj=StringIO(ft_data)).read())
 
1615
        self.assertEqual(
 
1616
            'version merged 3 ed8bce375198ea62444dc71952b22cfc2b09226d\n'
 
1617
            '2,2,2\nright\nmerged\nend merged\n',
 
1618
            GzipFile(mode='rb', fileobj=StringIO(delta_data)).read())
 
1619
 
 
1620
    def test_annotated_to_fulltext_no_eol(self):
 
1621
        """Test adapting annotated knits to full texts (for -> weaves)."""
 
1622
        # we need a full text, and a delta
 
1623
        f, parents = get_diamond_vf(self.get_knit(), trailing_eol=False)
 
1624
        # Reconstructing a full text requires a backing versioned file, and it
 
1625
        # must have the base lines requested from it.
 
1626
        logged_vf = versionedfile.RecordingVersionedFileDecorator(f)
 
1627
        ft_data, delta_data = self.helpGetBytes(f,
 
1628
            _mod_knit.FTAnnotatedToFullText(None),
 
1629
            _mod_knit.DeltaAnnotatedToFullText(logged_vf))
 
1630
        self.assertEqual('origin', ft_data)
 
1631
        self.assertEqual('base\nleft\nright\nmerged', delta_data)
 
1632
        self.assertEqual([('get_lines', 'left')], logged_vf.calls)
 
1633
 
 
1634
    def test_annotated_to_fulltext(self):
 
1635
        """Test adapting annotated knits to full texts (for -> weaves)."""
 
1636
        # we need a full text, and a delta
 
1637
        f, parents = get_diamond_vf(self.get_knit())
 
1638
        # Reconstructing a full text requires a backing versioned file, and it
 
1639
        # must have the base lines requested from it.
 
1640
        logged_vf = versionedfile.RecordingVersionedFileDecorator(f)
 
1641
        ft_data, delta_data = self.helpGetBytes(f,
 
1642
            _mod_knit.FTAnnotatedToFullText(None),
 
1643
            _mod_knit.DeltaAnnotatedToFullText(logged_vf))
 
1644
        self.assertEqual('origin\n', ft_data)
 
1645
        self.assertEqual('base\nleft\nright\nmerged\n', delta_data)
 
1646
        self.assertEqual([('get_lines', 'left')], logged_vf.calls)
 
1647
 
 
1648
    def test_unannotated_to_fulltext(self):
 
1649
        """Test adapting unannotated knits to full texts.
 
1650
        
 
1651
        This is used for -> weaves, and for -> annotated knits.
 
1652
        """
 
1653
        # we need a full text, and a delta
 
1654
        f, parents = get_diamond_vf(self.get_knit(annotated=False))
 
1655
        # Reconstructing a full text requires a backing versioned file, and it
 
1656
        # must have the base lines requested from it.
 
1657
        logged_vf = versionedfile.RecordingVersionedFileDecorator(f)
 
1658
        ft_data, delta_data = self.helpGetBytes(f,
 
1659
            _mod_knit.FTPlainToFullText(None),
 
1660
            _mod_knit.DeltaPlainToFullText(logged_vf))
 
1661
        self.assertEqual('origin\n', ft_data)
 
1662
        self.assertEqual('base\nleft\nright\nmerged\n', delta_data)
 
1663
        self.assertEqual([('get_lines', 'left')], logged_vf.calls)
 
1664
 
 
1665
    def test_unannotated_to_fulltext_no_eol(self):
 
1666
        """Test adapting unannotated knits to full texts.
 
1667
        
 
1668
        This is used for -> weaves, and for -> annotated knits.
 
1669
        """
 
1670
        # we need a full text, and a delta
 
1671
        f, parents = get_diamond_vf(self.get_knit(annotated=False),
 
1672
            trailing_eol=False)
 
1673
        # Reconstructing a full text requires a backing versioned file, and it
 
1674
        # must have the base lines requested from it.
 
1675
        logged_vf = versionedfile.RecordingVersionedFileDecorator(f)
 
1676
        ft_data, delta_data = self.helpGetBytes(f,
 
1677
            _mod_knit.FTPlainToFullText(None),
 
1678
            _mod_knit.DeltaPlainToFullText(logged_vf))
 
1679
        self.assertEqual('origin', ft_data)
 
1680
        self.assertEqual('base\nleft\nright\nmerged', delta_data)
 
1681
        self.assertEqual([('get_lines', 'left')], logged_vf.calls)
 
1682