~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: 2006-08-17 07:52:09 UTC
  • mfrom: (1910.3.4 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20060817075209-e85a1f9e05ff8b87
(andrew) Trivial fixes to NotImplemented errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005 by Canonical Ltd
2
2
#
3
3
# Authors:
4
4
#   Johan Rydberg <jrydberg@gnu.org>
24
24
from StringIO import StringIO
25
25
 
26
26
import bzrlib
27
 
from bzrlib import (
28
 
    errors,
29
 
    osutils,
30
 
    progress,
31
 
    )
 
27
import bzrlib.errors as errors
32
28
from bzrlib.errors import (
33
29
                           RevisionNotPresent, 
34
30
                           RevisionAlreadyPresent,
35
31
                           WeaveParentMismatch
36
32
                           )
37
 
from bzrlib.knit import (
38
 
    KnitVersionedFile,
39
 
    KnitAnnotateFactory,
40
 
    KnitPlainFactory,
41
 
    )
42
 
from bzrlib.tests import TestCaseWithMemoryTransport, TestSkipped
 
33
from bzrlib.knit import KnitVersionedFile, \
 
34
     KnitAnnotateFactory
 
35
from bzrlib.tests import TestCaseWithTransport
43
36
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
44
37
from bzrlib.trace import mutter
45
38
from bzrlib.transport import get_transport
84
77
    def test_adds_with_parent_texts(self):
85
78
        f = self.get_file()
86
79
        parent_texts = {}
87
 
        _, _, parent_texts['r0'] = f.add_lines('r0', [], ['a\n', 'b\n'])
 
80
        parent_texts['r0'] = f.add_lines('r0', [], ['a\n', 'b\n'])
88
81
        try:
89
 
            _, _, parent_texts['r1'] = f.add_lines_with_ghosts('r1',
90
 
                ['r0', 'ghost'], ['b\n', 'c\n'], parent_texts=parent_texts)
 
82
            parent_texts['r1'] = f.add_lines_with_ghosts('r1',
 
83
                                                         ['r0', 'ghost'], 
 
84
                                                         ['b\n', 'c\n'],
 
85
                                                         parent_texts=parent_texts)
91
86
        except NotImplementedError:
92
87
            # if the format doesn't support ghosts, just add normally.
93
 
            _, _, parent_texts['r1'] = f.add_lines('r1',
94
 
                ['r0'], ['b\n', 'c\n'], parent_texts=parent_texts)
 
88
            parent_texts['r1'] = f.add_lines('r1',
 
89
                                             ['r0'], 
 
90
                                             ['b\n', 'c\n'],
 
91
                                             parent_texts=parent_texts)
95
92
        f.add_lines('r2', ['r1'], ['c\n', 'd\n'], parent_texts=parent_texts)
96
93
        self.assertNotEqual(None, parent_texts['r0'])
97
94
        self.assertNotEqual(None, parent_texts['r1'])
125
122
            (errors.BzrBadParameterUnicode, NotImplementedError),
126
123
            vf.add_lines_with_ghosts, 'a', [], ['a\n', u'b\n', 'c\n'])
127
124
 
128
 
    def test_add_follows_left_matching_blocks(self):
129
 
        """If we change left_matching_blocks, delta changes
130
 
 
131
 
        Note: There are multiple correct deltas in this case, because
132
 
        we start with 1 "a" and we get 3.
133
 
        """
134
 
        vf = self.get_file()
135
 
        if isinstance(vf, WeaveFile):
136
 
            raise TestSkipped("WeaveFile ignores left_matching_blocks")
137
 
        vf.add_lines('1', [], ['a\n'])
138
 
        vf.add_lines('2', ['1'], ['a\n', 'a\n', 'a\n'],
139
 
                     left_matching_blocks=[(0, 0, 1), (1, 3, 0)])
140
 
        self.assertEqual(['a\n', 'a\n', 'a\n'], vf.get_lines('2'))
141
 
        vf.add_lines('3', ['1'], ['a\n', 'a\n', 'a\n'],
142
 
                     left_matching_blocks=[(0, 2, 1), (1, 3, 0)])
143
 
        self.assertEqual(['a\n', 'a\n', 'a\n'], vf.get_lines('3'))
144
 
 
145
125
    def test_inline_newline_throws(self):
146
126
        # \r characters are not permitted in lines being added
147
127
        vf = self.get_file()
157
137
        except NotImplementedError:
158
138
            pass
159
139
 
160
 
    def test_add_reserved(self):
161
 
        vf = self.get_file()
162
 
        self.assertRaises(errors.ReservedId,
163
 
            vf.add_lines, 'a:', [], ['a\n', 'b\n', 'c\n'])
164
 
 
165
 
    def test_add_lines_nostoresha(self):
166
 
        """When nostore_sha is supplied using old content raises."""
167
 
        vf = self.get_file()
168
 
        empty_text = ('a', [])
169
 
        sample_text_nl = ('b', ["foo\n", "bar\n"])
170
 
        sample_text_no_nl = ('c', ["foo\n", "bar"])
171
 
        shas = []
172
 
        for version, lines in (empty_text, sample_text_nl, sample_text_no_nl):
173
 
            sha, _, _ = vf.add_lines(version, [], lines)
174
 
            shas.append(sha)
175
 
        # we now have a copy of all the lines in the vf.
176
 
        for sha, (version, lines) in zip(
177
 
            shas, (empty_text, sample_text_nl, sample_text_no_nl)):
178
 
            self.assertRaises(errors.ExistingContent,
179
 
                vf.add_lines, version + "2", [], lines,
180
 
                nostore_sha=sha)
181
 
            # and no new version should have been added.
182
 
            self.assertRaises(errors.RevisionNotPresent, vf.get_lines,
183
 
                version + "2")
184
 
 
185
 
    def test_add_lines_with_ghosts_nostoresha(self):
186
 
        """When nostore_sha is supplied using old content raises."""
187
 
        vf = self.get_file()
188
 
        empty_text = ('a', [])
189
 
        sample_text_nl = ('b', ["foo\n", "bar\n"])
190
 
        sample_text_no_nl = ('c', ["foo\n", "bar"])
191
 
        shas = []
192
 
        for version, lines in (empty_text, sample_text_nl, sample_text_no_nl):
193
 
            sha, _, _ = vf.add_lines(version, [], lines)
194
 
            shas.append(sha)
195
 
        # we now have a copy of all the lines in the vf.
196
 
        # is the test applicable to this vf implementation?
197
 
        try:
198
 
            vf.add_lines_with_ghosts('d', [], [])
199
 
        except NotImplementedError:
200
 
            raise TestSkipped("add_lines_with_ghosts is optional")
201
 
        for sha, (version, lines) in zip(
202
 
            shas, (empty_text, sample_text_nl, sample_text_no_nl)):
203
 
            self.assertRaises(errors.ExistingContent,
204
 
                vf.add_lines_with_ghosts, version + "2", [], lines,
205
 
                nostore_sha=sha)
206
 
            # and no new version should have been added.
207
 
            self.assertRaises(errors.RevisionNotPresent, vf.get_lines,
208
 
                version + "2")
209
 
 
210
 
    def test_add_lines_return_value(self):
211
 
        # add_lines should return the sha1 and the text size.
212
 
        vf = self.get_file()
213
 
        empty_text = ('a', [])
214
 
        sample_text_nl = ('b', ["foo\n", "bar\n"])
215
 
        sample_text_no_nl = ('c', ["foo\n", "bar"])
216
 
        # check results for the three cases:
217
 
        for version, lines in (empty_text, sample_text_nl, sample_text_no_nl):
218
 
            # the first two elements are the same for all versioned files:
219
 
            # - the digest and the size of the text. For some versioned files
220
 
            #   additional data is returned in additional tuple elements.
221
 
            result = vf.add_lines(version, [], lines)
222
 
            self.assertEqual(3, len(result))
223
 
            self.assertEqual((osutils.sha_strings(lines), sum(map(len, lines))),
224
 
                result[0:2])
225
 
        # parents should not affect the result:
226
 
        lines = sample_text_nl[1]
227
 
        self.assertEqual((osutils.sha_strings(lines), sum(map(len, lines))),
228
 
            vf.add_lines('d', ['b', 'c'], lines)[0:2])
229
 
 
230
 
    def test_get_reserved(self):
231
 
        vf = self.get_file()
232
 
        self.assertRaises(errors.ReservedId, vf.get_texts, ['b:'])
233
 
        self.assertRaises(errors.ReservedId, vf.get_lines, 'b:')
234
 
        self.assertRaises(errors.ReservedId, vf.get_text, 'b:')
235
 
 
236
 
    def test_make_mpdiffs(self):
237
 
        from bzrlib import multiparent
238
 
        vf = self.get_file('foo')
239
 
        sha1s = self._setup_for_deltas(vf)
240
 
        new_vf = self.get_file('bar')
241
 
        for version in multiparent.topo_iter(vf):
242
 
            mpdiff = vf.make_mpdiffs([version])[0]
243
 
            new_vf.add_mpdiffs([(version, vf.get_parents(version),
244
 
                                 vf.get_sha1(version), mpdiff)])
245
 
            self.assertEqualDiff(vf.get_text(version),
246
 
                                 new_vf.get_text(version))
 
140
    def test_get_delta(self):
 
141
        f = self.get_file()
 
142
        sha1s = self._setup_for_deltas(f)
 
143
        expected_delta = (None, '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
 
144
                          [(0, 0, 1, [('base', 'line\n')])])
 
145
        self.assertEqual(expected_delta, f.get_delta('base'))
 
146
        next_parent = 'base'
 
147
        text_name = 'chain1-'
 
148
        for depth in range(26):
 
149
            new_version = text_name + '%s' % depth
 
150
            expected_delta = (next_parent, sha1s[depth], 
 
151
                              False,
 
152
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
 
153
            self.assertEqual(expected_delta, f.get_delta(new_version))
 
154
            next_parent = new_version
 
155
        next_parent = 'base'
 
156
        text_name = 'chain2-'
 
157
        for depth in range(26):
 
158
            new_version = text_name + '%s' % depth
 
159
            expected_delta = (next_parent, sha1s[depth], False,
 
160
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
 
161
            self.assertEqual(expected_delta, f.get_delta(new_version))
 
162
            next_parent = new_version
 
163
        # smoke test for eol support
 
164
        expected_delta = ('base', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, [])
 
165
        self.assertEqual(['line'], f.get_lines('noeol'))
 
166
        self.assertEqual(expected_delta, f.get_delta('noeol'))
 
167
 
 
168
    def test_get_deltas(self):
 
169
        f = self.get_file()
 
170
        sha1s = self._setup_for_deltas(f)
 
171
        deltas = f.get_deltas(f.versions())
 
172
        expected_delta = (None, '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
 
173
                          [(0, 0, 1, [('base', 'line\n')])])
 
174
        self.assertEqual(expected_delta, deltas['base'])
 
175
        next_parent = 'base'
 
176
        text_name = 'chain1-'
 
177
        for depth in range(26):
 
178
            new_version = text_name + '%s' % depth
 
179
            expected_delta = (next_parent, sha1s[depth], 
 
180
                              False,
 
181
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
 
182
            self.assertEqual(expected_delta, deltas[new_version])
 
183
            next_parent = new_version
 
184
        next_parent = 'base'
 
185
        text_name = 'chain2-'
 
186
        for depth in range(26):
 
187
            new_version = text_name + '%s' % depth
 
188
            expected_delta = (next_parent, sha1s[depth], False,
 
189
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
 
190
            self.assertEqual(expected_delta, deltas[new_version])
 
191
            next_parent = new_version
 
192
        # smoke tests for eol support
 
193
        expected_delta = ('base', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, [])
 
194
        self.assertEqual(['line'], f.get_lines('noeol'))
 
195
        self.assertEqual(expected_delta, deltas['noeol'])
 
196
        # smoke tests for eol support - two noeol in a row same content
 
197
        expected_deltas = (('noeol', '3ad7ee82dbd8f29ecba073f96e43e414b3f70a4d', True, 
 
198
                          [(0, 1, 2, [(u'noeolsecond', 'line\n'), (u'noeolsecond', 'line\n')])]),
 
199
                          ('noeol', '3ad7ee82dbd8f29ecba073f96e43e414b3f70a4d', True, 
 
200
                           [(0, 0, 1, [('noeolsecond', 'line\n')]), (1, 1, 0, [])]))
 
201
        self.assertEqual(['line\n', 'line'], f.get_lines('noeolsecond'))
 
202
        self.assertTrue(deltas['noeolsecond'] in expected_deltas)
 
203
        # two no-eol in a row, different content
 
204
        expected_delta = ('noeolsecond', '8bb553a84e019ef1149db082d65f3133b195223b', True, 
 
205
                          [(1, 2, 1, [(u'noeolnotshared', 'phone\n')])])
 
206
        self.assertEqual(['line\n', 'phone'], f.get_lines('noeolnotshared'))
 
207
        self.assertEqual(expected_delta, deltas['noeolnotshared'])
 
208
        # eol folling a no-eol with content change
 
209
        expected_delta = ('noeol', 'a61f6fb6cfc4596e8d88c34a308d1e724caf8977', False, 
 
210
                          [(0, 1, 1, [(u'eol', 'phone\n')])])
 
211
        self.assertEqual(['phone\n'], f.get_lines('eol'))
 
212
        self.assertEqual(expected_delta, deltas['eol'])
 
213
        # eol folling a no-eol with content change
 
214
        expected_delta = ('noeol', '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
 
215
                          [(0, 1, 1, [(u'eolline', 'line\n')])])
 
216
        self.assertEqual(['line\n'], f.get_lines('eolline'))
 
217
        self.assertEqual(expected_delta, deltas['eolline'])
 
218
        # eol with no parents
 
219
        expected_delta = (None, '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, 
 
220
                          [(0, 0, 1, [(u'noeolbase', 'line\n')])])
 
221
        self.assertEqual(['line'], f.get_lines('noeolbase'))
 
222
        self.assertEqual(expected_delta, deltas['noeolbase'])
 
223
        # eol with two parents, in inverse insertion order
 
224
        expected_deltas = (('noeolbase', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True,
 
225
                            [(0, 1, 1, [(u'eolbeforefirstparent', 'line\n')])]),
 
226
                           ('noeolbase', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True,
 
227
                            [(0, 1, 1, [(u'eolbeforefirstparent', 'line\n')])]))
 
228
        self.assertEqual(['line'], f.get_lines('eolbeforefirstparent'))
 
229
        #self.assertTrue(deltas['eolbeforefirstparent'] in expected_deltas)
247
230
 
248
231
    def _setup_for_deltas(self, f):
249
 
        self.assertFalse(f.has_version('base'))
 
232
        self.assertRaises(errors.RevisionNotPresent, f.get_delta, 'base')
250
233
        # add texts that should trip the knit maximum delta chain threshold
251
234
        # as well as doing parallel chains of data in knits.
252
235
        # this is done by two chains of 25 insertions
315
298
            next_parent = new_version
316
299
        return sha1s
317
300
 
 
301
    def test_add_delta(self):
 
302
        # tests for the add-delta facility.
 
303
        # at this point, optimising for speed, we assume no checks when deltas are inserted.
 
304
        # this may need to be revisited.
 
305
        source = self.get_file('source')
 
306
        source.add_lines('base', [], ['line\n'])
 
307
        next_parent = 'base'
 
308
        text_name = 'chain1-'
 
309
        text = ['line\n']
 
310
        for depth in range(26):
 
311
            new_version = text_name + '%s' % depth
 
312
            text = text + ['line\n']
 
313
            source.add_lines(new_version, [next_parent], text)
 
314
            next_parent = new_version
 
315
        next_parent = 'base'
 
316
        text_name = 'chain2-'
 
317
        text = ['line\n']
 
318
        for depth in range(26):
 
319
            new_version = text_name + '%s' % depth
 
320
            text = text + ['line\n']
 
321
            source.add_lines(new_version, [next_parent], text)
 
322
            next_parent = new_version
 
323
        source.add_lines('noeol', ['base'], ['line'])
 
324
        
 
325
        target = self.get_file('target')
 
326
        for version in source.versions():
 
327
            parent, sha1, noeol, delta = source.get_delta(version)
 
328
            target.add_delta(version,
 
329
                             source.get_parents(version),
 
330
                             parent,
 
331
                             sha1,
 
332
                             noeol,
 
333
                             delta)
 
334
        self.assertRaises(RevisionAlreadyPresent,
 
335
                          target.add_delta, 'base', [], None, '', False, [])
 
336
        for version in source.versions():
 
337
            self.assertEqual(source.get_lines(version),
 
338
                             target.get_lines(version))
 
339
 
318
340
    def test_ancestry(self):
319
341
        f = self.get_file()
320
342
        self.assertEqual([], f.get_ancestry([]))
343
365
        self.assertRaises(RevisionNotPresent,
344
366
            f.get_ancestry, ['rM', 'rX'])
345
367
 
346
 
        self.assertEqual(set(f.get_ancestry('rM')),
347
 
            set(f.get_ancestry('rM', topo_sorted=False)))
348
 
 
349
368
    def test_mutate_after_finish(self):
350
369
        f = self.get_file()
351
370
        f.transaction_finished()
 
371
        self.assertRaises(errors.OutSideTransaction, f.add_delta, '', [], '', '', False, [])
352
372
        self.assertRaises(errors.OutSideTransaction, f.add_lines, '', [], [])
353
373
        self.assertRaises(errors.OutSideTransaction, f.add_lines_with_ghosts, '', [], [])
 
374
        self.assertRaises(errors.OutSideTransaction, f.fix_parents, '', [])
354
375
        self.assertRaises(errors.OutSideTransaction, f.join, '')
355
376
        self.assertRaises(errors.OutSideTransaction, f.clone_text, 'base', 'bar', ['foo'])
356
377
        
410
431
    def test_get_graph(self):
411
432
        f = self.get_file()
412
433
        graph = {
413
 
            'v1': (),
414
 
            'v2': ('v1', ),
415
 
            'v3': ('v2', )}
 
434
            'v1': [],
 
435
            'v2': ['v1'],
 
436
            'v3': ['v2']}
416
437
        self.build_graph(f, graph)
417
438
        self.assertEqual(graph, f.get_graph())
418
439
    
420
441
        f = self.get_file()
421
442
        complex_graph = {}
422
443
        simple_a = {
423
 
            'c': (),
424
 
            'b': ('c', ),
425
 
            'a': ('b', ),
 
444
            'c': [],
 
445
            'b': ['c'],
 
446
            'a': ['b'],
426
447
            }
427
448
        complex_graph.update(simple_a)
428
449
        simple_b = {
429
 
            'c': (),
430
 
            'b': ('c', ),
 
450
            'c': [],
 
451
            'b': ['c'],
431
452
            }
432
453
        complex_graph.update(simple_b)
433
454
        simple_gam = {
434
 
            'c': (),
435
 
            'oo': (),
436
 
            'bar': ('oo', 'c'),
437
 
            'gam': ('bar', ),
 
455
            'c': [],
 
456
            'oo': [],
 
457
            'bar': ['oo', 'c'],
 
458
            'gam': ['bar'],
438
459
            }
439
460
        complex_graph.update(simple_gam)
440
461
        simple_b_gam = {}
469
490
        self.assertRaises(RevisionNotPresent,
470
491
            f.annotate, 'foo')
471
492
 
 
493
    def test_walk(self):
 
494
        # tests that walk returns all the inclusions for the requested
 
495
        # revisions as well as the revisions changes themselves.
 
496
        f = self.get_file('1')
 
497
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
498
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
 
499
        f.add_lines('rX', ['r1'], ['d\n', 'b\n'])
 
500
        f.add_lines('rY', ['r1'], ['c\n', 'e\n'])
 
501
 
 
502
        lines = {}
 
503
        for lineno, insert, dset, text in f.walk(['rX', 'rY']):
 
504
            lines[text] = (insert, dset)
 
505
 
 
506
        self.assertTrue(lines['a\n'], ('r0', set(['r1'])))
 
507
        self.assertTrue(lines['b\n'], ('r0', set(['rY'])))
 
508
        self.assertTrue(lines['c\n'], ('r1', set(['rX'])))
 
509
        self.assertTrue(lines['d\n'], ('rX', set([])))
 
510
        self.assertTrue(lines['e\n'], ('rY', set([])))
 
511
 
472
512
    def test_detection(self):
473
513
        # Test weaves detect corruption.
474
514
        #
498
538
        """Open the versioned file from disk again."""
499
539
        raise NotImplementedError(self.reopen_file)
500
540
 
501
 
    def test_iter_parents(self):
502
 
        """iter_parents returns the parents for many nodes."""
503
 
        f = self.get_file()
504
 
        # sample data:
505
 
        # no parents
506
 
        f.add_lines('r0', [], ['a\n', 'b\n'])
507
 
        # 1 parents
508
 
        f.add_lines('r1', ['r0'], ['a\n', 'b\n'])
509
 
        # 2 parents
510
 
        f.add_lines('r2', ['r1', 'r0'], ['a\n', 'b\n'])
511
 
        # XXX TODO a ghost
512
 
        # cases: each sample data individually:
513
 
        self.assertEqual(set([('r0', ())]),
514
 
            set(f.iter_parents(['r0'])))
515
 
        self.assertEqual(set([('r1', ('r0', ))]),
516
 
            set(f.iter_parents(['r1'])))
517
 
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
518
 
            set(f.iter_parents(['r2'])))
519
 
        # no nodes returned for a missing node
520
 
        self.assertEqual(set(),
521
 
            set(f.iter_parents(['missing'])))
522
 
        # 1 node returned with missing nodes skipped
523
 
        self.assertEqual(set([('r1', ('r0', ))]),
524
 
            set(f.iter_parents(['ghost1', 'r1', 'ghost'])))
525
 
        # 2 nodes returned
526
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
527
 
            set(f.iter_parents(['r0', 'r1'])))
528
 
        # 2 nodes returned, missing skipped
529
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
530
 
            set(f.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
531
 
 
532
541
    def test_iter_lines_added_or_present_in_versions(self):
533
542
        # test that we get at least an equalset of the lines added by
534
543
        # versions in the weave 
535
544
        # the ordering here is to make a tree so that dumb searches have
536
545
        # more changes to muck up.
537
 
 
538
 
        class InstrumentedProgress(progress.DummyProgress):
539
 
 
540
 
            def __init__(self):
541
 
 
542
 
                progress.DummyProgress.__init__(self)
543
 
                self.updates = []
544
 
 
545
 
            def update(self, msg=None, current=None, total=None):
546
 
                self.updates.append((msg, current, total))
547
 
 
548
546
        vf = self.get_file()
549
547
        # add a base to get included
550
548
        vf.add_lines('base', [], ['base\n'])
558
556
        vf.add_lines('otherchild',
559
557
                     ['lancestor', 'base'],
560
558
                     ['base\n', 'lancestor\n', 'otherchild\n'])
561
 
        def iter_with_versions(versions, expected):
 
559
        def iter_with_versions(versions):
562
560
            # now we need to see what lines are returned, and how often.
563
561
            lines = {'base\n':0,
564
562
                     'lancestor\n':0,
566
564
                     'child\n':0,
567
565
                     'otherchild\n':0,
568
566
                     }
569
 
            progress = InstrumentedProgress()
570
567
            # iterate over the lines
571
 
            for line in vf.iter_lines_added_or_present_in_versions(versions, 
572
 
                pb=progress):
 
568
            for line in vf.iter_lines_added_or_present_in_versions(versions):
573
569
                lines[line] += 1
574
 
            if []!= progress.updates: 
575
 
                self.assertEqual(expected, progress.updates)
576
570
            return lines
577
 
        lines = iter_with_versions(['child', 'otherchild'],
578
 
                                   [('Walking content.', 0, 2),
579
 
                                    ('Walking content.', 1, 2),
580
 
                                    ('Walking content.', 2, 2)])
 
571
        lines = iter_with_versions(['child', 'otherchild'])
581
572
        # we must see child and otherchild
582
573
        self.assertTrue(lines['child\n'] > 0)
583
574
        self.assertTrue(lines['otherchild\n'] > 0)
584
575
        # we dont care if we got more than that.
585
576
        
586
577
        # test all lines
587
 
        lines = iter_with_versions(None, [('Walking content.', 0, 5),
588
 
                                          ('Walking content.', 1, 5),
589
 
                                          ('Walking content.', 2, 5),
590
 
                                          ('Walking content.', 3, 5),
591
 
                                          ('Walking content.', 4, 5),
592
 
                                          ('Walking content.', 5, 5)])
 
578
        lines = iter_with_versions(None)
593
579
        # all lines must be seen at least once
594
580
        self.assertTrue(lines['base\n'] > 0)
595
581
        self.assertTrue(lines['lancestor\n'] > 0)
597
583
        self.assertTrue(lines['child\n'] > 0)
598
584
        self.assertTrue(lines['otherchild\n'] > 0)
599
585
 
 
586
    def test_fix_parents(self):
 
587
        # some versioned files allow incorrect parents to be corrected after
 
588
        # insertion - this may not fix ancestry..
 
589
        # if they do not supported, they just do not implement it.
 
590
        # we test this as an interface test to ensure that those that *do*
 
591
        # implementent it get it right.
 
592
        vf = self.get_file()
 
593
        vf.add_lines('notbase', [], [])
 
594
        vf.add_lines('base', [], [])
 
595
        try:
 
596
            vf.fix_parents('notbase', ['base'])
 
597
        except NotImplementedError:
 
598
            return
 
599
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
600
        # open again, check it stuck.
 
601
        vf = self.get_file()
 
602
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
603
 
 
604
    def test_fix_parents_with_ghosts(self):
 
605
        # when fixing parents, ghosts that are listed should not be ghosts
 
606
        # anymore.
 
607
        vf = self.get_file()
 
608
 
 
609
        try:
 
610
            vf.add_lines_with_ghosts('notbase', ['base', 'stillghost'], [])
 
611
        except NotImplementedError:
 
612
            return
 
613
        vf.add_lines('base', [], [])
 
614
        vf.fix_parents('notbase', ['base', 'stillghost'])
 
615
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
616
        # open again, check it stuck.
 
617
        vf = self.get_file()
 
618
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
619
        # and check the ghosts
 
620
        self.assertEqual(['base', 'stillghost'],
 
621
                         vf.get_parents_with_ghosts('notbase'))
 
622
 
600
623
    def test_add_lines_with_ghosts(self):
601
624
        # some versioned file formats allow lines to be added with parent
602
625
        # information that is > than that in the format. Formats that do
604
627
        # add_lines_with_ghosts api.
605
628
        vf = self.get_file()
606
629
        # add a revision with ghost parents
607
 
        # The preferred form is utf8, but we should translate when needed
608
 
        parent_id_unicode = u'b\xbfse'
609
 
        parent_id_utf8 = parent_id_unicode.encode('utf8')
610
630
        try:
611
 
            vf.add_lines_with_ghosts('notbxbfse', [parent_id_utf8], [])
 
631
            vf.add_lines_with_ghosts(u'notbxbfse', [u'b\xbfse'], [])
612
632
        except NotImplementedError:
613
633
            # check the other ghost apis are also not implemented
614
634
            self.assertRaises(NotImplementedError, vf.has_ghost, 'foo')
616
636
            self.assertRaises(NotImplementedError, vf.get_parents_with_ghosts, 'foo')
617
637
            self.assertRaises(NotImplementedError, vf.get_graph_with_ghosts)
618
638
            return
619
 
        vf = self.reopen_file()
620
639
        # test key graph related apis: getncestry, _graph, get_parents
621
640
        # has_version
622
641
        # - these are ghost unaware and must not be reflect ghosts
623
 
        self.assertEqual(['notbxbfse'], vf.get_ancestry('notbxbfse'))
624
 
        self.assertEqual([], vf.get_parents('notbxbfse'))
625
 
        self.assertEqual({'notbxbfse':()}, vf.get_graph())
626
 
        self.assertFalse(vf.has_version(parent_id_utf8))
 
642
        self.assertEqual([u'notbxbfse'], vf.get_ancestry(u'notbxbfse'))
 
643
        self.assertEqual([], vf.get_parents(u'notbxbfse'))
 
644
        self.assertEqual({u'notbxbfse':[]}, vf.get_graph())
 
645
        self.assertFalse(vf.has_version(u'b\xbfse'))
627
646
        # we have _with_ghost apis to give us ghost information.
628
 
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry_with_ghosts(['notbxbfse']))
629
 
        self.assertEqual([parent_id_utf8], vf.get_parents_with_ghosts('notbxbfse'))
630
 
        self.assertEqual({'notbxbfse':[parent_id_utf8]}, vf.get_graph_with_ghosts())
631
 
        self.assertTrue(vf.has_ghost(parent_id_utf8))
 
647
        self.assertEqual([u'b\xbfse', u'notbxbfse'], vf.get_ancestry_with_ghosts([u'notbxbfse']))
 
648
        self.assertEqual([u'b\xbfse'], vf.get_parents_with_ghosts(u'notbxbfse'))
 
649
        self.assertEqual({u'notbxbfse':[u'b\xbfse']}, vf.get_graph_with_ghosts())
 
650
        self.assertTrue(vf.has_ghost(u'b\xbfse'))
632
651
        # if we add something that is a ghost of another, it should correct the
633
652
        # results of the prior apis
634
 
        vf.add_lines(parent_id_utf8, [], [])
635
 
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry(['notbxbfse']))
636
 
        self.assertEqual([parent_id_utf8], vf.get_parents('notbxbfse'))
637
 
        self.assertEqual({parent_id_utf8:(),
638
 
                          'notbxbfse':(parent_id_utf8, ),
 
653
        vf.add_lines(u'b\xbfse', [], [])
 
654
        self.assertEqual([u'b\xbfse', u'notbxbfse'], vf.get_ancestry([u'notbxbfse']))
 
655
        self.assertEqual([u'b\xbfse'], vf.get_parents(u'notbxbfse'))
 
656
        self.assertEqual({u'b\xbfse':[],
 
657
                          u'notbxbfse':[u'b\xbfse'],
639
658
                          },
640
659
                         vf.get_graph())
641
 
        self.assertTrue(vf.has_version(parent_id_utf8))
 
660
        self.assertTrue(vf.has_version(u'b\xbfse'))
642
661
        # we have _with_ghost apis to give us ghost information.
643
 
        self.assertEqual([parent_id_utf8, 'notbxbfse'],
644
 
            vf.get_ancestry_with_ghosts(['notbxbfse']))
645
 
        self.assertEqual([parent_id_utf8], vf.get_parents_with_ghosts('notbxbfse'))
646
 
        self.assertEqual({parent_id_utf8:[],
647
 
                          'notbxbfse':[parent_id_utf8],
 
662
        self.assertEqual([u'b\xbfse', u'notbxbfse'], vf.get_ancestry_with_ghosts([u'notbxbfse']))
 
663
        self.assertEqual([u'b\xbfse'], vf.get_parents_with_ghosts(u'notbxbfse'))
 
664
        self.assertEqual({u'b\xbfse':[],
 
665
                          u'notbxbfse':[u'b\xbfse'],
648
666
                          },
649
667
                         vf.get_graph_with_ghosts())
650
 
        self.assertFalse(vf.has_ghost(parent_id_utf8))
 
668
        self.assertFalse(vf.has_ghost(u'b\xbfse'))
651
669
 
652
670
    def test_add_lines_with_ghosts_after_normal_revs(self):
653
671
        # some versioned file formats allow lines to be added with parent
674
692
        factory = self.get_factory()
675
693
        vf = factory('id', transport, 0777, create=True, access_mode='w')
676
694
        vf = factory('id', transport, access_mode='r')
 
695
        self.assertRaises(errors.ReadOnlyError, vf.add_delta, '', [], '', '', False, [])
677
696
        self.assertRaises(errors.ReadOnlyError, vf.add_lines, 'base', [], [])
678
697
        self.assertRaises(errors.ReadOnlyError,
679
698
                          vf.add_lines_with_ghosts,
680
699
                          'base',
681
700
                          [],
682
701
                          [])
 
702
        self.assertRaises(errors.ReadOnlyError, vf.fix_parents, 'base', [])
683
703
        self.assertRaises(errors.ReadOnlyError, vf.join, 'base')
684
704
        self.assertRaises(errors.ReadOnlyError, vf.clone_text, 'base', 'bar', ['foo'])
685
705
    
698
718
            '3f786850e387550fdab836ed7e6dc881de23001b', vf.get_sha1('b'))
699
719
        self.assertEqual(
700
720
            '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', vf.get_sha1('c'))
701
 
 
702
 
        self.assertEqual(['3f786850e387550fdab836ed7e6dc881de23001b',
703
 
                          '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8',
704
 
                          '3f786850e387550fdab836ed7e6dc881de23001b'],
705
 
                          vf.get_sha1s(['a', 'c', 'b']))
706
721
        
707
722
 
708
 
class TestWeave(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
 
723
class TestWeave(TestCaseWithTransport, VersionedFileTestMixIn):
709
724
 
710
725
    def get_file(self, name='foo'):
711
726
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
757
772
        return WeaveFile
758
773
 
759
774
 
760
 
class TestKnit(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
 
775
class TestKnit(TestCaseWithTransport, VersionedFileTestMixIn):
761
776
 
762
777
    def get_file(self, name='foo'):
763
 
        return self.get_factory()(name, get_transport(self.get_url('.')),
764
 
                                  delta=True, create=True)
 
778
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
 
779
                                 delta=True, create=True)
765
780
 
766
781
    def get_factory(self):
767
782
        return KnitVersionedFile
773
788
        return knit
774
789
 
775
790
    def reopen_file(self, name='foo', create=False):
776
 
        return self.get_factory()(name, get_transport(self.get_url('.')),
 
791
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
777
792
            delta=True,
778
793
            create=create)
779
794
 
788
803
                          get_transport(self.get_url('.')))
789
804
 
790
805
 
791
 
class TestPlaintextKnit(TestKnit):
792
 
    """Test a knit with no cached annotations"""
793
 
 
794
 
    def _factory(self, name, transport, file_mode=None, access_mode=None,
795
 
                 delta=True, create=False):
796
 
        return KnitVersionedFile(name, transport, file_mode, access_mode,
797
 
                                 KnitPlainFactory(), delta=delta,
798
 
                                 create=create)
799
 
 
800
 
    def get_factory(self):
801
 
        return self._factory
802
 
 
803
 
 
804
806
class InterString(versionedfile.InterVersionedFile):
805
807
    """An inter-versionedfile optimised code path for strings.
806
808
 
819
821
# if we make the registry a separate class though we still need to 
820
822
# test the behaviour in the active registry to catch failure-to-handle-
821
823
# stange-objects
822
 
class TestInterVersionedFile(TestCaseWithMemoryTransport):
 
824
class TestInterVersionedFile(TestCaseWithTransport):
823
825
 
824
826
    def test_get_default_inter_versionedfile(self):
825
827
        # test that the InterVersionedFile.get(a, b) probes
1139
1141
        self._test_merge_from_strings(base, a, b, result)
1140
1142
 
1141
1143
 
1142
 
class TestKnitMerge(TestCaseWithMemoryTransport, MergeCasesMixin):
 
1144
class TestKnitMerge(TestCaseWithTransport, MergeCasesMixin):
1143
1145
 
1144
1146
    def get_file(self, name='foo'):
1145
1147
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
1149
1151
        pass
1150
1152
 
1151
1153
 
1152
 
class TestWeaveMerge(TestCaseWithMemoryTransport, MergeCasesMixin):
 
1154
class TestWeaveMerge(TestCaseWithTransport, MergeCasesMixin):
1153
1155
 
1154
1156
    def get_file(self, name='foo'):
1155
1157
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
1162
1164
 
1163
1165
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
1164
1166
                                'xxx', '>>>>>>> ', 'bbb']
1165
 
 
1166
 
 
1167
 
class TestFormatSignatures(TestCaseWithMemoryTransport):
1168
 
 
1169
 
    def get_knit_file(self, name, annotated):
1170
 
        if annotated:
1171
 
            factory = KnitAnnotateFactory()
1172
 
        else:
1173
 
            factory = KnitPlainFactory()
1174
 
        return KnitVersionedFile(
1175
 
            name, get_transport(self.get_url('.')), create=True,
1176
 
            factory=factory)
1177
 
 
1178
 
    def test_knit_format_signatures(self):
1179
 
        """Different formats of knit have different signature strings."""
1180
 
        knit = self.get_knit_file('a', True)
1181
 
        self.assertEqual('knit-annotated', knit.get_format_signature())
1182
 
        knit = self.get_knit_file('p', False)
1183
 
        self.assertEqual('knit-plain', knit.get_format_signature())
1184