/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005 Canonical Ltd
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
2
#
3
# Authors:
4
#   Johan Rydberg <jrydberg@gnu.org>
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
10
#
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
15
#
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
1704.2.15 by Martin Pool
Remove TODO about knit testing printed from test suite
21
# TODO: might be nice to create a versionedfile with some type of corruption
22
# considered typical and check that it can be detected/corrected.
23
1664.2.9 by Aaron Bentley
Ported weave merge test to versionedfile
24
from StringIO import StringIO
25
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
26
import bzrlib
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
27
from bzrlib import (
28
    errors,
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
29
    osutils,
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
30
    progress,
31
    )
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
32
from bzrlib.errors import (
33
                           RevisionNotPresent, 
34
                           RevisionAlreadyPresent,
35
                           WeaveParentMismatch
36
                           )
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
37
from bzrlib.knit import KnitVersionedFile, \
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
38
     KnitAnnotateFactory, KnitPlainFactory
2670.3.3 by Andrew Bennetts
Merge from bzr.dev.
39
from bzrlib.tests import TestCaseWithMemoryTransport, TestSkipped
1666.1.1 by Robert Collins
Add trivial http-using test for versioned files.
40
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
41
from bzrlib.trace import mutter
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
42
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
43
from bzrlib.transport.memory import MemoryTransport
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
44
from bzrlib.tsort import topo_sort
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
45
import bzrlib.versionedfile as versionedfile
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
46
from bzrlib.weave import WeaveFile
1664.2.9 by Aaron Bentley
Ported weave merge test to versionedfile
47
from bzrlib.weavefile import read_weave, write_weave
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
48
49
50
class VersionedFileTestMixIn(object):
51
    """A mixin test class for testing VersionedFiles.
52
53
    This is not an adaptor-style test at this point because
54
    theres no dynamic substitution of versioned file implementations,
55
    they are strictly controlled by their owning repositories.
56
    """
57
58
    def test_add(self):
59
        f = self.get_file()
60
        f.add_lines('r0', [], ['a\n', 'b\n'])
61
        f.add_lines('r1', ['r0'], ['b\n', 'c\n'])
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
62
        def verify_file(f):
63
            versions = f.versions()
64
            self.assertTrue('r0' in versions)
65
            self.assertTrue('r1' in versions)
66
            self.assertEquals(f.get_lines('r0'), ['a\n', 'b\n'])
67
            self.assertEquals(f.get_text('r0'), 'a\nb\n')
68
            self.assertEquals(f.get_lines('r1'), ['b\n', 'c\n'])
1563.2.18 by Robert Collins
get knit repositories really using knits for text storage.
69
            self.assertEqual(2, len(f))
70
            self.assertEqual(2, f.num_versions())
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
71
    
72
            self.assertRaises(RevisionNotPresent,
73
                f.add_lines, 'r2', ['foo'], [])
74
            self.assertRaises(RevisionAlreadyPresent,
75
                f.add_lines, 'r1', [], [])
76
        verify_file(f)
1666.1.6 by Robert Collins
Make knit the default format.
77
        # this checks that reopen with create=True does not break anything.
78
        f = self.reopen_file(create=True)
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
79
        verify_file(f)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
80
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
81
    def test_adds_with_parent_texts(self):
82
        f = self.get_file()
83
        parent_texts = {}
2776.1.1 by Robert Collins
* The ``add_lines`` methods on ``VersionedFile`` implementations has changed
84
        _, _, parent_texts['r0'] = f.add_lines('r0', [], ['a\n', 'b\n'])
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
85
        try:
2776.1.1 by Robert Collins
* The ``add_lines`` methods on ``VersionedFile`` implementations has changed
86
            _, _, parent_texts['r1'] = f.add_lines_with_ghosts('r1',
87
                ['r0', 'ghost'], ['b\n', 'c\n'], parent_texts=parent_texts)
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
88
        except NotImplementedError:
89
            # if the format doesn't support ghosts, just add normally.
2776.1.1 by Robert Collins
* The ``add_lines`` methods on ``VersionedFile`` implementations has changed
90
            _, _, parent_texts['r1'] = f.add_lines('r1',
91
                ['r0'], ['b\n', 'c\n'], parent_texts=parent_texts)
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
92
        f.add_lines('r2', ['r1'], ['c\n', 'd\n'], parent_texts=parent_texts)
93
        self.assertNotEqual(None, parent_texts['r0'])
94
        self.assertNotEqual(None, parent_texts['r1'])
95
        def verify_file(f):
96
            versions = f.versions()
97
            self.assertTrue('r0' in versions)
98
            self.assertTrue('r1' in versions)
99
            self.assertTrue('r2' in versions)
100
            self.assertEquals(f.get_lines('r0'), ['a\n', 'b\n'])
101
            self.assertEquals(f.get_lines('r1'), ['b\n', 'c\n'])
102
            self.assertEquals(f.get_lines('r2'), ['c\n', 'd\n'])
103
            self.assertEqual(3, f.num_versions())
104
            origins = f.annotate('r1')
105
            self.assertEquals(origins[0][0], 'r0')
106
            self.assertEquals(origins[1][0], 'r1')
107
            origins = f.annotate('r2')
108
            self.assertEquals(origins[0][0], 'r1')
109
            self.assertEquals(origins[1][0], 'r2')
110
111
        verify_file(f)
112
        f = self.reopen_file()
113
        verify_file(f)
114
1666.1.6 by Robert Collins
Make knit the default format.
115
    def test_add_unicode_content(self):
116
        # unicode content is not permitted in versioned files. 
117
        # versioned files version sequences of bytes only.
118
        vf = self.get_file()
119
        self.assertRaises(errors.BzrBadParameterUnicode,
120
            vf.add_lines, 'a', [], ['a\n', u'b\n', 'c\n'])
121
        self.assertRaises(
122
            (errors.BzrBadParameterUnicode, NotImplementedError),
123
            vf.add_lines_with_ghosts, 'a', [], ['a\n', u'b\n', 'c\n'])
124
2520.4.150 by Aaron Bentley
Test that non-Weave uses left_matching_blocks for add_lines
125
    def test_add_follows_left_matching_blocks(self):
126
        """If we change left_matching_blocks, delta changes
127
128
        Note: There are multiple correct deltas in this case, because
129
        we start with 1 "a" and we get 3.
130
        """
131
        vf = self.get_file()
132
        if isinstance(vf, WeaveFile):
133
            raise TestSkipped("WeaveFile ignores left_matching_blocks")
134
        vf.add_lines('1', [], ['a\n'])
135
        vf.add_lines('2', ['1'], ['a\n', 'a\n', 'a\n'],
136
                     left_matching_blocks=[(0, 0, 1), (1, 3, 0)])
137
        self.assertEqual([(1, 1, 2, [('2', 'a\n'), ('2', 'a\n')])],
138
                         vf.get_delta('2')[3])
139
        vf.add_lines('3', ['1'], ['a\n', 'a\n', 'a\n'],
140
                     left_matching_blocks=[(0, 2, 1), (1, 3, 0)])
141
        self.assertEqual([(0, 0, 2, [('3', 'a\n'), ('3', 'a\n')])],
142
                         vf.get_delta('3')[3])
143
1666.1.6 by Robert Collins
Make knit the default format.
144
    def test_inline_newline_throws(self):
145
        # \r characters are not permitted in lines being added
146
        vf = self.get_file()
147
        self.assertRaises(errors.BzrBadParameterContainsNewline, 
148
            vf.add_lines, 'a', [], ['a\n\n'])
149
        self.assertRaises(
150
            (errors.BzrBadParameterContainsNewline, NotImplementedError),
151
            vf.add_lines_with_ghosts, 'a', [], ['a\n\n'])
152
        # but inline CR's are allowed
153
        vf.add_lines('a', [], ['a\r\n'])
154
        try:
155
            vf.add_lines_with_ghosts('b', [], ['a\r\n'])
156
        except NotImplementedError:
157
            pass
158
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
159
    def test_add_reserved(self):
160
        vf = self.get_file()
161
        self.assertRaises(errors.ReservedId,
162
            vf.add_lines, 'a:', [], ['a\n', 'b\n', 'c\n'])
163
164
        self.assertRaises(errors.ReservedId,
165
            vf.add_delta, 'a:', [], None, 'sha1', False, ((0, 0, 0, []),))
166
2776.1.1 by Robert Collins
* The ``add_lines`` methods on ``VersionedFile`` implementations has changed
167
    def test_add_lines_return_value(self):
168
        # add_lines should return the sha1 and the text size.
169
        vf = self.get_file()
170
        empty_text = ('a', [])
171
        sample_text_nl = ('b', ["foo\n", "bar\n"])
172
        sample_text_no_nl = ('c', ["foo\n", "bar"])
173
        # check results for the three cases:
174
        for version, lines in (empty_text, sample_text_nl, sample_text_no_nl):
175
            # the first two elements are the same for all versioned files:
176
            # - the digest and the size of the text. For some versioned files
177
            #   additional data is returned in additional tuple elements.
178
            result = vf.add_lines(version, [], lines)
179
            self.assertEqual(3, len(result))
180
            self.assertEqual((osutils.sha_strings(lines), sum(map(len, lines))),
181
                result[0:2])
182
        # parents should not affect the result:
183
        lines = sample_text_nl[1]
184
        self.assertEqual((osutils.sha_strings(lines), sum(map(len, lines))),
185
            vf.add_lines('d', ['b', 'c'], lines)[0:2])
186
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
187
    def test_get_reserved(self):
188
        vf = self.get_file()
189
        self.assertRaises(errors.ReservedId, vf.get_delta, 'b:')
190
        self.assertRaises(errors.ReservedId, vf.get_texts, ['b:'])
191
        self.assertRaises(errors.ReservedId, vf.get_lines, 'b:')
192
        self.assertRaises(errors.ReservedId, vf.get_text, 'b:')
193
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
194
    def test_get_delta(self):
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
195
        f = self.get_file()
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
196
        sha1s = self._setup_for_deltas(f)
197
        expected_delta = (None, '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
198
                          [(0, 0, 1, [('base', 'line\n')])])
199
        self.assertEqual(expected_delta, f.get_delta('base'))
200
        next_parent = 'base'
201
        text_name = 'chain1-'
202
        for depth in range(26):
203
            new_version = text_name + '%s' % depth
204
            expected_delta = (next_parent, sha1s[depth], 
205
                              False,
206
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
207
            self.assertEqual(expected_delta, f.get_delta(new_version))
208
            next_parent = new_version
209
        next_parent = 'base'
210
        text_name = 'chain2-'
211
        for depth in range(26):
212
            new_version = text_name + '%s' % depth
213
            expected_delta = (next_parent, sha1s[depth], False,
214
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
215
            self.assertEqual(expected_delta, f.get_delta(new_version))
216
            next_parent = new_version
217
        # smoke test for eol support
218
        expected_delta = ('base', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, [])
219
        self.assertEqual(['line'], f.get_lines('noeol'))
220
        self.assertEqual(expected_delta, f.get_delta('noeol'))
221
222
    def test_get_deltas(self):
223
        f = self.get_file()
224
        sha1s = self._setup_for_deltas(f)
225
        deltas = f.get_deltas(f.versions())
226
        expected_delta = (None, '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
227
                          [(0, 0, 1, [('base', 'line\n')])])
228
        self.assertEqual(expected_delta, deltas['base'])
229
        next_parent = 'base'
230
        text_name = 'chain1-'
231
        for depth in range(26):
232
            new_version = text_name + '%s' % depth
233
            expected_delta = (next_parent, sha1s[depth], 
234
                              False,
235
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
236
            self.assertEqual(expected_delta, deltas[new_version])
237
            next_parent = new_version
238
        next_parent = 'base'
239
        text_name = 'chain2-'
240
        for depth in range(26):
241
            new_version = text_name + '%s' % depth
242
            expected_delta = (next_parent, sha1s[depth], False,
243
                              [(depth + 1, depth + 1, 1, [(new_version, 'line\n')])])
244
            self.assertEqual(expected_delta, deltas[new_version])
245
            next_parent = new_version
246
        # smoke tests for eol support
247
        expected_delta = ('base', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, [])
248
        self.assertEqual(['line'], f.get_lines('noeol'))
249
        self.assertEqual(expected_delta, deltas['noeol'])
250
        # smoke tests for eol support - two noeol in a row same content
251
        expected_deltas = (('noeol', '3ad7ee82dbd8f29ecba073f96e43e414b3f70a4d', True, 
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
252
                          [(0, 1, 2, [('noeolsecond', 'line\n'), ('noeolsecond', 'line\n')])]),
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
253
                          ('noeol', '3ad7ee82dbd8f29ecba073f96e43e414b3f70a4d', True, 
254
                           [(0, 0, 1, [('noeolsecond', 'line\n')]), (1, 1, 0, [])]))
255
        self.assertEqual(['line\n', 'line'], f.get_lines('noeolsecond'))
256
        self.assertTrue(deltas['noeolsecond'] in expected_deltas)
257
        # two no-eol in a row, different content
258
        expected_delta = ('noeolsecond', '8bb553a84e019ef1149db082d65f3133b195223b', True, 
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
259
                          [(1, 2, 1, [('noeolnotshared', 'phone\n')])])
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
260
        self.assertEqual(['line\n', 'phone'], f.get_lines('noeolnotshared'))
261
        self.assertEqual(expected_delta, deltas['noeolnotshared'])
262
        # eol folling a no-eol with content change
263
        expected_delta = ('noeol', 'a61f6fb6cfc4596e8d88c34a308d1e724caf8977', False, 
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
264
                          [(0, 1, 1, [('eol', 'phone\n')])])
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
265
        self.assertEqual(['phone\n'], f.get_lines('eol'))
266
        self.assertEqual(expected_delta, deltas['eol'])
267
        # eol folling a no-eol with content change
268
        expected_delta = ('noeol', '6bfa09d82ce3e898ad4641ae13dd4fdb9cf0d76b', False, 
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
269
                          [(0, 1, 1, [('eolline', 'line\n')])])
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
270
        self.assertEqual(['line\n'], f.get_lines('eolline'))
271
        self.assertEqual(expected_delta, deltas['eolline'])
272
        # eol with no parents
273
        expected_delta = (None, '264f39cab871e4cfd65b3a002f7255888bb5ed97', True, 
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
274
                          [(0, 0, 1, [('noeolbase', 'line\n')])])
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
275
        self.assertEqual(['line'], f.get_lines('noeolbase'))
276
        self.assertEqual(expected_delta, deltas['noeolbase'])
277
        # eol with two parents, in inverse insertion order
278
        expected_deltas = (('noeolbase', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
279
                            [(0, 1, 1, [('eolbeforefirstparent', 'line\n')])]),
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
280
                           ('noeolbase', '264f39cab871e4cfd65b3a002f7255888bb5ed97', True,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
281
                            [(0, 1, 1, [('eolbeforefirstparent', 'line\n')])]))
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
282
        self.assertEqual(['line'], f.get_lines('eolbeforefirstparent'))
283
        #self.assertTrue(deltas['eolbeforefirstparent'] in expected_deltas)
284
2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
285
    def test_make_mpdiffs(self):
2520.4.3 by Aaron Bentley
Implement plain strategy for extracting and installing multiparent diffs
286
        from bzrlib import multiparent
287
        vf = self.get_file('foo')
288
        sha1s = self._setup_for_deltas(vf)
289
        new_vf = self.get_file('bar')
290
        for version in multiparent.topo_iter(vf):
2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
291
            mpdiff = vf.make_mpdiffs([version])[0]
292
            new_vf.add_mpdiffs([(version, vf.get_parents(version),
293
                                 vf.get_sha1(version), mpdiff)])
2520.4.3 by Aaron Bentley
Implement plain strategy for extracting and installing multiparent diffs
294
            self.assertEqualDiff(vf.get_text(version),
295
                                 new_vf.get_text(version))
296
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
297
    def _setup_for_deltas(self, f):
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
298
        self.assertRaises(errors.RevisionNotPresent, f.get_delta, 'base')
299
        # add texts that should trip the knit maximum delta chain threshold
300
        # as well as doing parallel chains of data in knits.
301
        # this is done by two chains of 25 insertions
302
        f.add_lines('base', [], ['line\n'])
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
303
        f.add_lines('noeol', ['base'], ['line'])
304
        # detailed eol tests:
305
        # shared last line with parent no-eol
306
        f.add_lines('noeolsecond', ['noeol'], ['line\n', 'line'])
307
        # differing last line with parent, both no-eol
308
        f.add_lines('noeolnotshared', ['noeolsecond'], ['line\n', 'phone'])
309
        # add eol following a noneol parent, change content
310
        f.add_lines('eol', ['noeol'], ['phone\n'])
311
        # add eol following a noneol parent, no change content
312
        f.add_lines('eolline', ['noeol'], ['line\n'])
313
        # noeol with no parents:
314
        f.add_lines('noeolbase', [], ['line'])
315
        # noeol preceeding its leftmost parent in the output:
316
        # this is done by making it a merge of two parents with no common
317
        # anestry: noeolbase and noeol with the 
318
        # later-inserted parent the leftmost.
319
        f.add_lines('eolbeforefirstparent', ['noeolbase', 'noeol'], ['line'])
320
        # two identical eol texts
321
        f.add_lines('noeoldup', ['noeol'], ['line'])
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
322
        next_parent = 'base'
323
        text_name = 'chain1-'
324
        text = ['line\n']
325
        sha1s = {0 :'da6d3141cb4a5e6f464bf6e0518042ddc7bfd079',
326
                 1 :'45e21ea146a81ea44a821737acdb4f9791c8abe7',
327
                 2 :'e1f11570edf3e2a070052366c582837a4fe4e9fa',
328
                 3 :'26b4b8626da827088c514b8f9bbe4ebf181edda1',
329
                 4 :'e28a5510be25ba84d31121cff00956f9970ae6f6',
330
                 5 :'d63ec0ce22e11dcf65a931b69255d3ac747a318d',
331
                 6 :'2c2888d288cb5e1d98009d822fedfe6019c6a4ea',
332
                 7 :'95c14da9cafbf828e3e74a6f016d87926ba234ab',
333
                 8 :'779e9a0b28f9f832528d4b21e17e168c67697272',
334
                 9 :'1f8ff4e5c6ff78ac106fcfe6b1e8cb8740ff9a8f',
335
                 10:'131a2ae712cf51ed62f143e3fbac3d4206c25a05',
336
                 11:'c5a9d6f520d2515e1ec401a8f8a67e6c3c89f199',
337
                 12:'31a2286267f24d8bedaa43355f8ad7129509ea85',
338
                 13:'dc2a7fe80e8ec5cae920973973a8ee28b2da5e0a',
339
                 14:'2c4b1736566b8ca6051e668de68650686a3922f2',
340
                 15:'5912e4ecd9b0c07be4d013e7e2bdcf9323276cde',
341
                 16:'b0d2e18d3559a00580f6b49804c23fea500feab3',
342
                 17:'8e1d43ad72f7562d7cb8f57ee584e20eb1a69fc7',
343
                 18:'5cf64a3459ae28efa60239e44b20312d25b253f3',
344
                 19:'1ebed371807ba5935958ad0884595126e8c4e823',
345
                 20:'2aa62a8b06fb3b3b892a3292a068ade69d5ee0d3',
346
                 21:'01edc447978004f6e4e962b417a4ae1955b6fe5d',
347
                 22:'d8d8dc49c4bf0bab401e0298bb5ad827768618bb',
348
                 23:'c21f62b1c482862983a8ffb2b0c64b3451876e3f',
349
                 24:'c0593fe795e00dff6b3c0fe857a074364d5f04fc',
350
                 25:'dd1a1cf2ba9cc225c3aff729953e6364bf1d1855',
351
                 }
352
        for depth in range(26):
353
            new_version = text_name + '%s' % depth
354
            text = text + ['line\n']
355
            f.add_lines(new_version, [next_parent], text)
356
            next_parent = new_version
357
        next_parent = 'base'
358
        text_name = 'chain2-'
359
        text = ['line\n']
360
        for depth in range(26):
361
            new_version = text_name + '%s' % depth
362
            text = text + ['line\n']
363
            f.add_lines(new_version, [next_parent], text)
364
            next_parent = new_version
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
365
        return sha1s
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
366
367
    def test_add_delta(self):
368
        # tests for the add-delta facility.
369
        # at this point, optimising for speed, we assume no checks when deltas are inserted.
370
        # this may need to be revisited.
371
        source = self.get_file('source')
372
        source.add_lines('base', [], ['line\n'])
373
        next_parent = 'base'
374
        text_name = 'chain1-'
375
        text = ['line\n']
376
        for depth in range(26):
377
            new_version = text_name + '%s' % depth
378
            text = text + ['line\n']
379
            source.add_lines(new_version, [next_parent], text)
380
            next_parent = new_version
381
        next_parent = 'base'
382
        text_name = 'chain2-'
383
        text = ['line\n']
384
        for depth in range(26):
385
            new_version = text_name + '%s' % depth
386
            text = text + ['line\n']
387
            source.add_lines(new_version, [next_parent], text)
388
            next_parent = new_version
389
        source.add_lines('noeol', ['base'], ['line'])
390
        
391
        target = self.get_file('target')
392
        for version in source.versions():
393
            parent, sha1, noeol, delta = source.get_delta(version)
394
            target.add_delta(version,
395
                             source.get_parents(version),
396
                             parent,
397
                             sha1,
398
                             noeol,
399
                             delta)
400
        self.assertRaises(RevisionAlreadyPresent,
401
                          target.add_delta, 'base', [], None, '', False, [])
402
        for version in source.versions():
403
            self.assertEqual(source.get_lines(version),
404
                             target.get_lines(version))
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
405
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
406
    def test_ancestry(self):
407
        f = self.get_file()
1563.2.29 by Robert Collins
Remove all but fetch references to repository.revision_store.
408
        self.assertEqual([], f.get_ancestry([]))
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
409
        f.add_lines('r0', [], ['a\n', 'b\n'])
410
        f.add_lines('r1', ['r0'], ['b\n', 'c\n'])
411
        f.add_lines('r2', ['r0'], ['b\n', 'c\n'])
412
        f.add_lines('r3', ['r2'], ['b\n', 'c\n'])
413
        f.add_lines('rM', ['r1', 'r2'], ['b\n', 'c\n'])
1563.2.29 by Robert Collins
Remove all but fetch references to repository.revision_store.
414
        self.assertEqual([], f.get_ancestry([]))
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
415
        versions = f.get_ancestry(['rM'])
416
        # there are some possibilities:
417
        # r0 r1 r2 rM r3
418
        # r0 r1 r2 r3 rM
419
        # etc
420
        # so we check indexes
421
        r0 = versions.index('r0')
422
        r1 = versions.index('r1')
423
        r2 = versions.index('r2')
424
        self.assertFalse('r3' in versions)
425
        rM = versions.index('rM')
426
        self.assertTrue(r0 < r1)
427
        self.assertTrue(r0 < r2)
428
        self.assertTrue(r1 < rM)
429
        self.assertTrue(r2 < rM)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
430
431
        self.assertRaises(RevisionNotPresent,
432
            f.get_ancestry, ['rM', 'rX'])
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
433
2530.1.1 by Aaron Bentley
Make topological sorting optional for get_ancestry
434
        self.assertEqual(set(f.get_ancestry('rM')),
435
            set(f.get_ancestry('rM', topo_sorted=False)))
436
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
437
    def test_mutate_after_finish(self):
438
        f = self.get_file()
439
        f.transaction_finished()
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
440
        self.assertRaises(errors.OutSideTransaction, f.add_delta, '', [], '', '', False, [])
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
441
        self.assertRaises(errors.OutSideTransaction, f.add_lines, '', [], [])
442
        self.assertRaises(errors.OutSideTransaction, f.add_lines_with_ghosts, '', [], [])
443
        self.assertRaises(errors.OutSideTransaction, f.fix_parents, '', [])
444
        self.assertRaises(errors.OutSideTransaction, f.join, '')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
445
        self.assertRaises(errors.OutSideTransaction, f.clone_text, 'base', 'bar', ['foo'])
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
446
        
447
    def test_clear_cache(self):
448
        f = self.get_file()
449
        # on a new file it should not error
450
        f.clear_cache()
451
        # and after adding content, doing a clear_cache and a get should work.
452
        f.add_lines('0', [], ['a'])
453
        f.clear_cache()
454
        self.assertEqual(['a'], f.get_lines('0'))
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
455
456
    def test_clone_text(self):
457
        f = self.get_file()
458
        f.add_lines('r0', [], ['a\n', 'b\n'])
1563.2.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
459
        f.clone_text('r1', 'r0', ['r0'])
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
460
        def verify_file(f):
461
            self.assertEquals(f.get_lines('r1'), f.get_lines('r0'))
462
            self.assertEquals(f.get_lines('r1'), ['a\n', 'b\n'])
463
            self.assertEquals(f.get_parents('r1'), ['r0'])
464
    
465
            self.assertRaises(RevisionNotPresent,
466
                f.clone_text, 'r2', 'rX', [])
467
            self.assertRaises(RevisionAlreadyPresent,
468
                f.clone_text, 'r1', 'r0', [])
469
        verify_file(f)
470
        verify_file(self.reopen_file())
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
471
1563.2.13 by Robert Collins
InterVersionedFile implemented.
472
    def test_create_empty(self):
473
        f = self.get_file()
474
        f.add_lines('0', [], ['a\n'])
475
        new_f = f.create_empty('t', MemoryTransport())
476
        # smoke test, specific types should check it is honoured correctly for
477
        # non type attributes
478
        self.assertEqual([], new_f.versions())
479
        self.assertTrue(isinstance(new_f, f.__class__))
480
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
481
    def test_copy_to(self):
482
        f = self.get_file()
483
        f.add_lines('0', [], ['a\n'])
484
        t = MemoryTransport()
485
        f.copy_to('foo', t)
486
        for suffix in f.__class__.get_suffixes():
487
            self.assertTrue(t.has('foo' + suffix))
488
489
    def test_get_suffixes(self):
490
        f = self.get_file()
491
        # should be the same
492
        self.assertEqual(f.__class__.get_suffixes(), f.__class__.get_suffixes())
493
        # and should be a list
494
        self.assertTrue(isinstance(f.__class__.get_suffixes(), list))
495
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
496
    def build_graph(self, file, graph):
497
        for node in topo_sort(graph.items()):
498
            file.add_lines(node, graph[node], [])
499
1563.2.13 by Robert Collins
InterVersionedFile implemented.
500
    def test_get_graph(self):
501
        f = self.get_file()
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
502
        graph = {
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
503
            'v1': (),
504
            'v2': ('v1', ),
505
            'v3': ('v2', )}
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
506
        self.build_graph(f, graph)
507
        self.assertEqual(graph, f.get_graph())
508
    
509
    def test_get_graph_partial(self):
510
        f = self.get_file()
511
        complex_graph = {}
512
        simple_a = {
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
513
            'c': (),
514
            'b': ('c', ),
515
            'a': ('b', ),
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
516
            }
517
        complex_graph.update(simple_a)
518
        simple_b = {
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
519
            'c': (),
520
            'b': ('c', ),
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
521
            }
522
        complex_graph.update(simple_b)
523
        simple_gam = {
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
524
            'c': (),
525
            'oo': (),
526
            'bar': ('oo', 'c'),
527
            'gam': ('bar', ),
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
528
            }
529
        complex_graph.update(simple_gam)
530
        simple_b_gam = {}
531
        simple_b_gam.update(simple_gam)
532
        simple_b_gam.update(simple_b)
533
        self.build_graph(f, complex_graph)
534
        self.assertEqual(simple_a, f.get_graph(['a']))
535
        self.assertEqual(simple_b, f.get_graph(['b']))
536
        self.assertEqual(simple_gam, f.get_graph(['gam']))
537
        self.assertEqual(simple_b_gam, f.get_graph(['b', 'gam']))
1563.2.13 by Robert Collins
InterVersionedFile implemented.
538
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
539
    def test_get_parents(self):
540
        f = self.get_file()
541
        f.add_lines('r0', [], ['a\n', 'b\n'])
542
        f.add_lines('r1', [], ['a\n', 'b\n'])
543
        f.add_lines('r2', [], ['a\n', 'b\n'])
544
        f.add_lines('r3', [], ['a\n', 'b\n'])
545
        f.add_lines('m', ['r0', 'r1', 'r2', 'r3'], ['a\n', 'b\n'])
546
        self.assertEquals(f.get_parents('m'), ['r0', 'r1', 'r2', 'r3'])
547
548
        self.assertRaises(RevisionNotPresent,
549
            f.get_parents, 'y')
550
551
    def test_annotate(self):
552
        f = self.get_file()
553
        f.add_lines('r0', [], ['a\n', 'b\n'])
554
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
555
        origins = f.annotate('r1')
556
        self.assertEquals(origins[0][0], 'r1')
557
        self.assertEquals(origins[1][0], 'r0')
558
559
        self.assertRaises(RevisionNotPresent,
560
            f.annotate, 'foo')
561
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
562
    def test_detection(self):
563
        # Test weaves detect corruption.
564
        #
565
        # Weaves contain a checksum of their texts.
566
        # When a text is extracted, this checksum should be
567
        # verified.
568
569
        w = self.get_file_corrupted_text()
570
571
        self.assertEqual('hello\n', w.get_text('v1'))
572
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
573
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
574
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
575
576
        w = self.get_file_corrupted_checksum()
577
578
        self.assertEqual('hello\n', w.get_text('v1'))
579
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
580
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
581
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
582
583
    def get_file_corrupted_text(self):
584
        """Return a versioned file with corrupt text but valid metadata."""
585
        raise NotImplementedError(self.get_file_corrupted_text)
586
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
587
    def reopen_file(self, name='foo'):
588
        """Open the versioned file from disk again."""
589
        raise NotImplementedError(self.reopen_file)
590
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
591
    def test_iter_parents(self):
592
        """iter_parents returns the parents for many nodes."""
593
        f = self.get_file()
594
        # sample data:
595
        # no parents
596
        f.add_lines('r0', [], ['a\n', 'b\n'])
597
        # 1 parents
598
        f.add_lines('r1', ['r0'], ['a\n', 'b\n'])
599
        # 2 parents
600
        f.add_lines('r2', ['r1', 'r0'], ['a\n', 'b\n'])
601
        # XXX TODO a ghost
602
        # cases: each sample data individually:
603
        self.assertEqual(set([('r0', ())]),
604
            set(f.iter_parents(['r0'])))
605
        self.assertEqual(set([('r1', ('r0', ))]),
606
            set(f.iter_parents(['r1'])))
607
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
608
            set(f.iter_parents(['r2'])))
609
        # no nodes returned for a missing node
610
        self.assertEqual(set(),
611
            set(f.iter_parents(['missing'])))
612
        # 1 node returned with missing nodes skipped
613
        self.assertEqual(set([('r1', ('r0', ))]),
614
            set(f.iter_parents(['ghost1', 'r1', 'ghost'])))
615
        # 2 nodes returned
616
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
617
            set(f.iter_parents(['r0', 'r1'])))
618
        # 2 nodes returned, missing skipped
619
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
620
            set(f.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
621
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
622
    def test_iter_lines_added_or_present_in_versions(self):
623
        # test that we get at least an equalset of the lines added by
624
        # versions in the weave 
625
        # the ordering here is to make a tree so that dumb searches have
626
        # more changes to muck up.
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
627
628
        class InstrumentedProgress(progress.DummyProgress):
629
630
            def __init__(self):
631
632
                progress.DummyProgress.__init__(self)
633
                self.updates = []
634
635
            def update(self, msg=None, current=None, total=None):
636
                self.updates.append((msg, current, total))
637
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
638
        vf = self.get_file()
639
        # add a base to get included
640
        vf.add_lines('base', [], ['base\n'])
641
        # add a ancestor to be included on one side
642
        vf.add_lines('lancestor', [], ['lancestor\n'])
643
        # add a ancestor to be included on the other side
644
        vf.add_lines('rancestor', ['base'], ['rancestor\n'])
645
        # add a child of rancestor with no eofile-nl
646
        vf.add_lines('child', ['rancestor'], ['base\n', 'child\n'])
647
        # add a child of lancestor and base to join the two roots
648
        vf.add_lines('otherchild',
649
                     ['lancestor', 'base'],
650
                     ['base\n', 'lancestor\n', 'otherchild\n'])
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
651
        def iter_with_versions(versions, expected):
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
652
            # now we need to see what lines are returned, and how often.
653
            lines = {'base\n':0,
654
                     'lancestor\n':0,
655
                     'rancestor\n':0,
656
                     'child\n':0,
657
                     'otherchild\n':0,
658
                     }
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
659
            progress = InstrumentedProgress()
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
660
            # iterate over the lines
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
661
            for line in vf.iter_lines_added_or_present_in_versions(versions, 
662
                pb=progress):
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
663
                lines[line] += 1
2039.1.2 by Aaron Bentley
Tweak test to avoid catching assert
664
            if []!= progress.updates: 
665
                self.assertEqual(expected, progress.updates)
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
666
            return lines
2147.1.3 by John Arbash Meinel
In knit.py we were re-using a variable in 2 loops, causing bogus progress messages to be generated.
667
        lines = iter_with_versions(['child', 'otherchild'],
668
                                   [('Walking content.', 0, 2),
669
                                    ('Walking content.', 1, 2),
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
670
                                    ('Walking content.', 2, 2)])
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
671
        # we must see child and otherchild
672
        self.assertTrue(lines['child\n'] > 0)
673
        self.assertTrue(lines['otherchild\n'] > 0)
674
        # we dont care if we got more than that.
675
        
676
        # test all lines
2147.1.3 by John Arbash Meinel
In knit.py we were re-using a variable in 2 loops, causing bogus progress messages to be generated.
677
        lines = iter_with_versions(None, [('Walking content.', 0, 5),
678
                                          ('Walking content.', 1, 5),
679
                                          ('Walking content.', 2, 5),
680
                                          ('Walking content.', 3, 5),
681
                                          ('Walking content.', 4, 5),
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
682
                                          ('Walking content.', 5, 5)])
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
683
        # all lines must be seen at least once
684
        self.assertTrue(lines['base\n'] > 0)
685
        self.assertTrue(lines['lancestor\n'] > 0)
686
        self.assertTrue(lines['rancestor\n'] > 0)
687
        self.assertTrue(lines['child\n'] > 0)
688
        self.assertTrue(lines['otherchild\n'] > 0)
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
689
690
    def test_fix_parents(self):
691
        # some versioned files allow incorrect parents to be corrected after
692
        # insertion - this may not fix ancestry..
693
        # if they do not supported, they just do not implement it.
1594.2.8 by Robert Collins
add ghost aware apis to knits.
694
        # we test this as an interface test to ensure that those that *do*
695
        # implementent it get it right.
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
696
        vf = self.get_file()
697
        vf.add_lines('notbase', [], [])
698
        vf.add_lines('base', [], [])
699
        try:
700
            vf.fix_parents('notbase', ['base'])
701
        except NotImplementedError:
702
            return
703
        self.assertEqual(['base'], vf.get_parents('notbase'))
704
        # open again, check it stuck.
705
        vf = self.get_file()
706
        self.assertEqual(['base'], vf.get_parents('notbase'))
707
1594.2.8 by Robert Collins
add ghost aware apis to knits.
708
    def test_fix_parents_with_ghosts(self):
709
        # when fixing parents, ghosts that are listed should not be ghosts
710
        # anymore.
711
        vf = self.get_file()
712
713
        try:
714
            vf.add_lines_with_ghosts('notbase', ['base', 'stillghost'], [])
715
        except NotImplementedError:
716
            return
717
        vf.add_lines('base', [], [])
718
        vf.fix_parents('notbase', ['base', 'stillghost'])
719
        self.assertEqual(['base'], vf.get_parents('notbase'))
720
        # open again, check it stuck.
721
        vf = self.get_file()
722
        self.assertEqual(['base'], vf.get_parents('notbase'))
723
        # and check the ghosts
724
        self.assertEqual(['base', 'stillghost'],
725
                         vf.get_parents_with_ghosts('notbase'))
726
727
    def test_add_lines_with_ghosts(self):
728
        # some versioned file formats allow lines to be added with parent
729
        # information that is > than that in the format. Formats that do
730
        # not support this need to raise NotImplementedError on the
731
        # add_lines_with_ghosts api.
732
        vf = self.get_file()
733
        # add a revision with ghost parents
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
734
        # The preferred form is utf8, but we should translate when needed
735
        parent_id_unicode = u'b\xbfse'
736
        parent_id_utf8 = parent_id_unicode.encode('utf8')
1594.2.8 by Robert Collins
add ghost aware apis to knits.
737
        try:
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
738
            vf.add_lines_with_ghosts('notbxbfse', [parent_id_utf8], [])
1594.2.8 by Robert Collins
add ghost aware apis to knits.
739
        except NotImplementedError:
740
            # check the other ghost apis are also not implemented
741
            self.assertRaises(NotImplementedError, vf.has_ghost, 'foo')
742
            self.assertRaises(NotImplementedError, vf.get_ancestry_with_ghosts, ['foo'])
743
            self.assertRaises(NotImplementedError, vf.get_parents_with_ghosts, 'foo')
744
            self.assertRaises(NotImplementedError, vf.get_graph_with_ghosts)
745
            return
2150.2.1 by Robert Collins
Correctly decode utf8 revision ids from knits when parsing, fixes a regression where a unicode revision id is stored correctly, but then indexed by the utf8 value on the next invocation of bzr, rather than the unicode value.
746
        vf = self.reopen_file()
1594.2.8 by Robert Collins
add ghost aware apis to knits.
747
        # test key graph related apis: getncestry, _graph, get_parents
748
        # has_version
749
        # - these are ghost unaware and must not be reflect ghosts
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
750
        self.assertEqual(['notbxbfse'], vf.get_ancestry('notbxbfse'))
751
        self.assertEqual([], vf.get_parents('notbxbfse'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
752
        self.assertEqual({'notbxbfse':()}, vf.get_graph())
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
753
        self.assertFalse(self.callDeprecated([osutils._revision_id_warning],
754
                         vf.has_version, parent_id_unicode))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
755
        self.assertFalse(vf.has_version(parent_id_utf8))
1594.2.8 by Robert Collins
add ghost aware apis to knits.
756
        # we have _with_ghost apis to give us ghost information.
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
757
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry_with_ghosts(['notbxbfse']))
758
        self.assertEqual([parent_id_utf8], vf.get_parents_with_ghosts('notbxbfse'))
759
        self.assertEqual({'notbxbfse':[parent_id_utf8]}, vf.get_graph_with_ghosts())
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
760
        self.assertTrue(self.callDeprecated([osutils._revision_id_warning],
761
                        vf.has_ghost, parent_id_unicode))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
762
        self.assertTrue(vf.has_ghost(parent_id_utf8))
1594.2.8 by Robert Collins
add ghost aware apis to knits.
763
        # if we add something that is a ghost of another, it should correct the
764
        # results of the prior apis
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
765
        self.callDeprecated([osutils._revision_id_warning],
766
                            vf.add_lines, parent_id_unicode, [], [])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
767
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry(['notbxbfse']))
768
        self.assertEqual([parent_id_utf8], vf.get_parents('notbxbfse'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
769
        self.assertEqual({parent_id_utf8:(),
770
                          'notbxbfse':(parent_id_utf8, ),
1594.2.8 by Robert Collins
add ghost aware apis to knits.
771
                          },
772
                         vf.get_graph())
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
773
        self.assertTrue(self.callDeprecated([osutils._revision_id_warning],
774
                        vf.has_version, parent_id_unicode))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
775
        self.assertTrue(vf.has_version(parent_id_utf8))
1594.2.8 by Robert Collins
add ghost aware apis to knits.
776
        # we have _with_ghost apis to give us ghost information.
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
777
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry_with_ghosts(['notbxbfse']))
778
        self.assertEqual([parent_id_utf8], vf.get_parents_with_ghosts('notbxbfse'))
779
        self.assertEqual({parent_id_utf8:[],
780
                          'notbxbfse':[parent_id_utf8],
1594.2.8 by Robert Collins
add ghost aware apis to knits.
781
                          },
782
                         vf.get_graph_with_ghosts())
2309.4.7 by John Arbash Meinel
Update VersionedFile tests to ensure that they can take Unicode,
783
        self.assertFalse(self.callDeprecated([osutils._revision_id_warning],
784
                         vf.has_ghost, parent_id_unicode))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
785
        self.assertFalse(vf.has_ghost(parent_id_utf8))
1594.2.8 by Robert Collins
add ghost aware apis to knits.
786
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
787
    def test_add_lines_with_ghosts_after_normal_revs(self):
788
        # some versioned file formats allow lines to be added with parent
789
        # information that is > than that in the format. Formats that do
790
        # not support this need to raise NotImplementedError on the
791
        # add_lines_with_ghosts api.
792
        vf = self.get_file()
793
        # probe for ghost support
794
        try:
795
            vf.has_ghost('hoo')
796
        except NotImplementedError:
797
            return
798
        vf.add_lines_with_ghosts('base', [], ['line\n', 'line_b\n'])
799
        vf.add_lines_with_ghosts('references_ghost',
800
                                 ['base', 'a_ghost'],
801
                                 ['line\n', 'line_b\n', 'line_c\n'])
802
        origins = vf.annotate('references_ghost')
803
        self.assertEquals(('base', 'line\n'), origins[0])
804
        self.assertEquals(('base', 'line_b\n'), origins[1])
805
        self.assertEquals(('references_ghost', 'line_c\n'), origins[2])
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
806
807
    def test_readonly_mode(self):
808
        transport = get_transport(self.get_url('.'))
809
        factory = self.get_factory()
810
        vf = factory('id', transport, 0777, create=True, access_mode='w')
811
        vf = factory('id', transport, access_mode='r')
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
812
        self.assertRaises(errors.ReadOnlyError, vf.add_delta, '', [], '', '', False, [])
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
813
        self.assertRaises(errors.ReadOnlyError, vf.add_lines, 'base', [], [])
814
        self.assertRaises(errors.ReadOnlyError,
815
                          vf.add_lines_with_ghosts,
816
                          'base',
817
                          [],
818
                          [])
819
        self.assertRaises(errors.ReadOnlyError, vf.fix_parents, 'base', [])
820
        self.assertRaises(errors.ReadOnlyError, vf.join, 'base')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
821
        self.assertRaises(errors.ReadOnlyError, vf.clone_text, 'base', 'bar', ['foo'])
1666.1.6 by Robert Collins
Make knit the default format.
822
    
823
    def test_get_sha1(self):
824
        # check the sha1 data is available
825
        vf = self.get_file()
826
        # a simple file
827
        vf.add_lines('a', [], ['a\n'])
828
        # the same file, different metadata
829
        vf.add_lines('b', ['a'], ['a\n'])
830
        # a file differing only in last newline.
831
        vf.add_lines('c', [], ['a'])
832
        self.assertEqual(
833
            '3f786850e387550fdab836ed7e6dc881de23001b', vf.get_sha1('a'))
834
        self.assertEqual(
835
            '3f786850e387550fdab836ed7e6dc881de23001b', vf.get_sha1('b'))
836
        self.assertEqual(
837
            '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', vf.get_sha1('c'))
2520.4.89 by Aaron Bentley
Add get_sha1s to weaves
838
839
        self.assertEqual(['3f786850e387550fdab836ed7e6dc881de23001b',
840
                          '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8',
841
                          '3f786850e387550fdab836ed7e6dc881de23001b'],
842
                          vf.get_sha1s(['a', 'c', 'b']))
1594.2.9 by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all.
843
        
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
844
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
845
class TestWeave(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
846
847
    def get_file(self, name='foo'):
1563.2.25 by Robert Collins
Merge in upstream.
848
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
849
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
850
    def get_file_corrupted_text(self):
1563.2.25 by Robert Collins
Merge in upstream.
851
        w = WeaveFile('foo', get_transport(self.get_url('.')), create=True)
1563.2.13 by Robert Collins
InterVersionedFile implemented.
852
        w.add_lines('v1', [], ['hello\n'])
853
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
854
        
855
        # We are going to invasively corrupt the text
856
        # Make sure the internals of weave are the same
857
        self.assertEqual([('{', 0)
858
                        , 'hello\n'
859
                        , ('}', None)
860
                        , ('{', 1)
861
                        , 'there\n'
862
                        , ('}', None)
863
                        ], w._weave)
864
        
865
        self.assertEqual(['f572d396fae9206628714fb2ce00f72e94f2258f'
866
                        , '90f265c6e75f1c8f9ab76dcf85528352c5f215ef'
867
                        ], w._sha1s)
868
        w.check()
869
        
870
        # Corrupted
871
        w._weave[4] = 'There\n'
872
        return w
873
874
    def get_file_corrupted_checksum(self):
875
        w = self.get_file_corrupted_text()
876
        # Corrected
877
        w._weave[4] = 'there\n'
878
        self.assertEqual('hello\nthere\n', w.get_text('v2'))
879
        
880
        #Invalid checksum, first digit changed
881
        w._sha1s[1] =  'f0f265c6e75f1c8f9ab76dcf85528352c5f215ef'
882
        return w
883
1666.1.6 by Robert Collins
Make knit the default format.
884
    def reopen_file(self, name='foo', create=False):
885
        return WeaveFile(name, get_transport(self.get_url('.')), create=create)
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
886
1563.2.25 by Robert Collins
Merge in upstream.
887
    def test_no_implicit_create(self):
888
        self.assertRaises(errors.NoSuchFile,
889
                          WeaveFile,
890
                          'foo',
891
                          get_transport(self.get_url('.')))
892
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
893
    def get_factory(self):
894
        return WeaveFile
895
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
896
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
897
class TestKnit(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
898
899
    def get_file(self, name='foo'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
900
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
1563.2.25 by Robert Collins
Merge in upstream.
901
                                 delta=True, create=True)
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
902
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
903
    def get_factory(self):
904
        return KnitVersionedFile
905
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
906
    def get_file_corrupted_text(self):
907
        knit = self.get_file()
908
        knit.add_lines('v1', [], ['hello\n'])
909
        knit.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
910
        return knit
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
911
1666.1.6 by Robert Collins
Make knit the default format.
912
    def reopen_file(self, name='foo', create=False):
913
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
914
            delta=True,
915
            create=create)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
916
917
    def test_detection(self):
1563.2.19 by Robert Collins
stub out a check for knits.
918
        knit = self.get_file()
919
        knit.check()
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
920
1563.2.25 by Robert Collins
Merge in upstream.
921
    def test_no_implicit_create(self):
922
        self.assertRaises(errors.NoSuchFile,
923
                          KnitVersionedFile,
924
                          'foo',
925
                          get_transport(self.get_url('.')))
926
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
927
928
class InterString(versionedfile.InterVersionedFile):
929
    """An inter-versionedfile optimised code path for strings.
930
931
    This is for use during testing where we use strings as versionedfiles
932
    so that none of the default regsitered interversionedfile classes will
933
    match - which lets us test the match logic.
934
    """
935
936
    @staticmethod
937
    def is_compatible(source, target):
938
        """InterString is compatible with strings-as-versionedfiles."""
939
        return isinstance(source, str) and isinstance(target, str)
940
941
942
# TODO this and the InterRepository core logic should be consolidatable
943
# if we make the registry a separate class though we still need to 
944
# test the behaviour in the active registry to catch failure-to-handle-
945
# stange-objects
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
946
class TestInterVersionedFile(TestCaseWithMemoryTransport):
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
947
948
    def test_get_default_inter_versionedfile(self):
949
        # test that the InterVersionedFile.get(a, b) probes
950
        # for a class where is_compatible(a, b) returns
951
        # true and returns a default interversionedfile otherwise.
952
        # This also tests that the default registered optimised interversionedfile
953
        # classes do not barf inappropriately when a surprising versionedfile type
954
        # is handed to them.
955
        dummy_a = "VersionedFile 1."
956
        dummy_b = "VersionedFile 2."
957
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)
958
959
    def assertGetsDefaultInterVersionedFile(self, a, b):
960
        """Asserts that InterVersionedFile.get(a, b) -> the default."""
961
        inter = versionedfile.InterVersionedFile.get(a, b)
962
        self.assertEqual(versionedfile.InterVersionedFile,
963
                         inter.__class__)
964
        self.assertEqual(a, inter.source)
965
        self.assertEqual(b, inter.target)
966
967
    def test_register_inter_versionedfile_class(self):
968
        # test that a optimised code path provider - a
969
        # InterVersionedFile subclass can be registered and unregistered
970
        # and that it is correctly selected when given a versionedfile
971
        # pair that it returns true on for the is_compatible static method
972
        # check
973
        dummy_a = "VersionedFile 1."
974
        dummy_b = "VersionedFile 2."
975
        versionedfile.InterVersionedFile.register_optimiser(InterString)
976
        try:
977
            # we should get the default for something InterString returns False
978
            # to
979
            self.assertFalse(InterString.is_compatible(dummy_a, None))
980
            self.assertGetsDefaultInterVersionedFile(dummy_a, None)
981
            # and we should get an InterString for a pair it 'likes'
982
            self.assertTrue(InterString.is_compatible(dummy_a, dummy_b))
983
            inter = versionedfile.InterVersionedFile.get(dummy_a, dummy_b)
984
            self.assertEqual(InterString, inter.__class__)
985
            self.assertEqual(dummy_a, inter.source)
986
            self.assertEqual(dummy_b, inter.target)
987
        finally:
988
            versionedfile.InterVersionedFile.unregister_optimiser(InterString)
989
        # now we should get the default InterVersionedFile object again.
990
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)
1666.1.1 by Robert Collins
Add trivial http-using test for versioned files.
991
992
993
class TestReadonlyHttpMixin(object):
994
995
    def test_readonly_http_works(self):
996
        # we should be able to read from http with a versioned file.
997
        vf = self.get_file()
1666.1.6 by Robert Collins
Make knit the default format.
998
        # try an empty file access
999
        readonly_vf = self.get_factory()('foo', get_transport(self.get_readonly_url('.')))
1000
        self.assertEqual([], readonly_vf.versions())
1001
        # now with feeling.
1666.1.1 by Robert Collins
Add trivial http-using test for versioned files.
1002
        vf.add_lines('1', [], ['a\n'])
1003
        vf.add_lines('2', ['1'], ['b\n', 'a\n'])
1004
        readonly_vf = self.get_factory()('foo', get_transport(self.get_readonly_url('.')))
1666.1.6 by Robert Collins
Make knit the default format.
1005
        self.assertEqual(['1', '2'], vf.versions())
1666.1.1 by Robert Collins
Add trivial http-using test for versioned files.
1006
        for version in readonly_vf.versions():
1007
            readonly_vf.get_lines(version)
1008
1009
1010
class TestWeaveHTTP(TestCaseWithWebserver, TestReadonlyHttpMixin):
1011
1012
    def get_file(self):
1013
        return WeaveFile('foo', get_transport(self.get_url('.')), create=True)
1014
1015
    def get_factory(self):
1016
        return WeaveFile
1017
1018
1019
class TestKnitHTTP(TestCaseWithWebserver, TestReadonlyHttpMixin):
1020
1021
    def get_file(self):
1022
        return KnitVersionedFile('foo', get_transport(self.get_url('.')),
1023
                                 delta=True, create=True)
1024
1025
    def get_factory(self):
1026
        return KnitVersionedFile
1664.2.9 by Aaron Bentley
Ported weave merge test to versionedfile
1027
1028
1029
class MergeCasesMixin(object):
1030
1031
    def doMerge(self, base, a, b, mp):
1032
        from cStringIO import StringIO
1033
        from textwrap import dedent
1034
1035
        def addcrlf(x):
1036
            return x + '\n'
1037
        
1038
        w = self.get_file()
1039
        w.add_lines('text0', [], map(addcrlf, base))
1040
        w.add_lines('text1', ['text0'], map(addcrlf, a))
1041
        w.add_lines('text2', ['text0'], map(addcrlf, b))
1042
1043
        self.log_contents(w)
1044
1045
        self.log('merge plan:')
1046
        p = list(w.plan_merge('text1', 'text2'))
1047
        for state, line in p:
1048
            if line:
1049
                self.log('%12s | %s' % (state, line[:-1]))
1050
1051
        self.log('merge:')
1052
        mt = StringIO()
1053
        mt.writelines(w.weave_merge(p))
1054
        mt.seek(0)
1055
        self.log(mt.getvalue())
1056
1057
        mp = map(addcrlf, mp)
1058
        self.assertEqual(mt.readlines(), mp)
1059
        
1060
        
1061
    def testOneInsert(self):
1062
        self.doMerge([],
1063
                     ['aa'],
1064
                     [],
1065
                     ['aa'])
1066
1067
    def testSeparateInserts(self):
1068
        self.doMerge(['aaa', 'bbb', 'ccc'],
1069
                     ['aaa', 'xxx', 'bbb', 'ccc'],
1070
                     ['aaa', 'bbb', 'yyy', 'ccc'],
1071
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
1072
1073
    def testSameInsert(self):
1074
        self.doMerge(['aaa', 'bbb', 'ccc'],
1075
                     ['aaa', 'xxx', 'bbb', 'ccc'],
1076
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
1077
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
1078
    overlappedInsertExpected = ['aaa', 'xxx', 'yyy', 'bbb']
1079
    def testOverlappedInsert(self):
1080
        self.doMerge(['aaa', 'bbb'],
1081
                     ['aaa', 'xxx', 'yyy', 'bbb'],
1082
                     ['aaa', 'xxx', 'bbb'], self.overlappedInsertExpected)
1083
1084
        # really it ought to reduce this to 
1085
        # ['aaa', 'xxx', 'yyy', 'bbb']
1086
1087
1088
    def testClashReplace(self):
1089
        self.doMerge(['aaa'],
1090
                     ['xxx'],
1091
                     ['yyy', 'zzz'],
1092
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
1093
                      '>>>>>>> '])
1094
1095
    def testNonClashInsert1(self):
1096
        self.doMerge(['aaa'],
1097
                     ['xxx', 'aaa'],
1098
                     ['yyy', 'zzz'],
1099
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
1100
                      '>>>>>>> '])
1101
1102
    def testNonClashInsert2(self):
1103
        self.doMerge(['aaa'],
1104
                     ['aaa'],
1105
                     ['yyy', 'zzz'],
1106
                     ['yyy', 'zzz'])
1107
1108
1109
    def testDeleteAndModify(self):
1110
        """Clashing delete and modification.
1111
1112
        If one side modifies a region and the other deletes it then
1113
        there should be a conflict with one side blank.
1114
        """
1115
1116
        #######################################
1117
        # skippd, not working yet
1118
        return
1119
        
1120
        self.doMerge(['aaa', 'bbb', 'ccc'],
1121
                     ['aaa', 'ddd', 'ccc'],
1122
                     ['aaa', 'ccc'],
1123
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
1124
1125
    def _test_merge_from_strings(self, base, a, b, expected):
1126
        w = self.get_file()
1127
        w.add_lines('text0', [], base.splitlines(True))
1128
        w.add_lines('text1', ['text0'], a.splitlines(True))
1129
        w.add_lines('text2', ['text0'], b.splitlines(True))
1130
        self.log('merge plan:')
1131
        p = list(w.plan_merge('text1', 'text2'))
1132
        for state, line in p:
1133
            if line:
1134
                self.log('%12s | %s' % (state, line[:-1]))
1135
        self.log('merge result:')
1136
        result_text = ''.join(w.weave_merge(p))
1137
        self.log(result_text)
1138
        self.assertEqualDiff(result_text, expected)
1139
1140
    def test_weave_merge_conflicts(self):
1141
        # does weave merge properly handle plans that end with unchanged?
1142
        result = ''.join(self.get_file().weave_merge([('new-a', 'hello\n')]))
1143
        self.assertEqual(result, 'hello\n')
1144
1145
    def test_deletion_extended(self):
1146
        """One side deletes, the other deletes more.
1147
        """
1148
        base = """\
1149
            line 1
1150
            line 2
1151
            line 3
1152
            """
1153
        a = """\
1154
            line 1
1155
            line 2
1156
            """
1157
        b = """\
1158
            line 1
1159
            """
1160
        result = """\
1161
            line 1
1162
            """
1163
        self._test_merge_from_strings(base, a, b, result)
1164
1165
    def test_deletion_overlap(self):
1166
        """Delete overlapping regions with no other conflict.
1167
1168
        Arguably it'd be better to treat these as agreement, rather than 
1169
        conflict, but for now conflict is safer.
1170
        """
1171
        base = """\
1172
            start context
1173
            int a() {}
1174
            int b() {}
1175
            int c() {}
1176
            end context
1177
            """
1178
        a = """\
1179
            start context
1180
            int a() {}
1181
            end context
1182
            """
1183
        b = """\
1184
            start context
1185
            int c() {}
1186
            end context
1187
            """
1188
        result = """\
1189
            start context
1190
<<<<<<< 
1191
            int a() {}
1192
=======
1193
            int c() {}
1194
>>>>>>> 
1195
            end context
1196
            """
1197
        self._test_merge_from_strings(base, a, b, result)
1198
1199
    def test_agreement_deletion(self):
1200
        """Agree to delete some lines, without conflicts."""
1201
        base = """\
1202
            start context
1203
            base line 1
1204
            base line 2
1205
            end context
1206
            """
1207
        a = """\
1208
            start context
1209
            base line 1
1210
            end context
1211
            """
1212
        b = """\
1213
            start context
1214
            base line 1
1215
            end context
1216
            """
1217
        result = """\
1218
            start context
1219
            base line 1
1220
            end context
1221
            """
1222
        self._test_merge_from_strings(base, a, b, result)
1223
1224
    def test_sync_on_deletion(self):
1225
        """Specific case of merge where we can synchronize incorrectly.
1226
        
1227
        A previous version of the weave merge concluded that the two versions
1228
        agreed on deleting line 2, and this could be a synchronization point.
1229
        Line 1 was then considered in isolation, and thought to be deleted on 
1230
        both sides.
1231
1232
        It's better to consider the whole thing as a disagreement region.
1233
        """
1234
        base = """\
1235
            start context
1236
            base line 1
1237
            base line 2
1238
            end context
1239
            """
1240
        a = """\
1241
            start context
1242
            base line 1
1243
            a's replacement line 2
1244
            end context
1245
            """
1246
        b = """\
1247
            start context
1248
            b replaces
1249
            both lines
1250
            end context
1251
            """
1252
        result = """\
1253
            start context
1254
<<<<<<< 
1255
            base line 1
1256
            a's replacement line 2
1257
=======
1258
            b replaces
1259
            both lines
1260
>>>>>>> 
1261
            end context
1262
            """
1263
        self._test_merge_from_strings(base, a, b, result)
1264
1265
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
1266
class TestKnitMerge(TestCaseWithMemoryTransport, MergeCasesMixin):
1664.2.9 by Aaron Bentley
Ported weave merge test to versionedfile
1267
1268
    def get_file(self, name='foo'):
1269
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
1270
                                 delta=True, create=True)
1271
1272
    def log_contents(self, w):
1273
        pass
1274
1275
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
1276
class TestWeaveMerge(TestCaseWithMemoryTransport, MergeCasesMixin):
1664.2.9 by Aaron Bentley
Ported weave merge test to versionedfile
1277
1278
    def get_file(self, name='foo'):
1279
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
1280
1281
    def log_contents(self, w):
1282
        self.log('weave is:')
1283
        tmpf = StringIO()
1284
        write_weave(w, tmpf)
1285
        self.log(tmpf.getvalue())
1286
1287
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
1288
                                'xxx', '>>>>>>> ', 'bbb']
2670.3.1 by Andrew Bennetts
Add get_data_stream/insert_data_stream to KnitVersionedFile.
1289
1290
1291
class TestFormatSignatures(TestCaseWithMemoryTransport):
1292
1293
    def get_knit_file(self, name, annotated):
1294
        if annotated:
1295
            factory = KnitAnnotateFactory()
1296
        else:
1297
            factory = KnitPlainFactory()
1298
        return KnitVersionedFile(
1299
            name, get_transport(self.get_url('.')), create=True,
1300
            factory=factory)
1301
1302
    def test_knit_format_signatures(self):
1303
        """Different formats of knit have different signature strings."""
1304
        knit = self.get_knit_file('a', True)
1305
        self.assertEqual('knit-annotated', knit.get_format_signature())
1306
        knit = self.get_knit_file('p', False)
1307
        self.assertEqual('knit-plain', knit.get_format_signature())
1308