/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Robert Collins
  • Date: 2005-10-19 10:11:57 UTC
  • mfrom: (1185.16.78)
  • mto: This revision was merged to the branch mainline in revision 1470.
  • Revision ID: robertc@robertcollins.net-20051019101157-17438d311e746b4f
mergeĀ fromĀ upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011, 2016 Canonical Ltd
2
 
#
 
1
#! /usr/bin/python2.4
 
2
 
 
3
# Copyright (C) 2005 by Canonical Ltd
 
4
 
3
5
# This program is free software; you can redistribute it and/or modify
4
6
# it under the terms of the GNU General Public License as published by
5
7
# the Free Software Foundation; either version 2 of the License, or
6
8
# (at your option) any later version.
7
 
#
 
9
 
8
10
# This program is distributed in the hope that it will be useful,
9
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
13
# GNU General Public License for more details.
12
 
#
 
14
 
13
15
# You should have received a copy of the GNU General Public License
14
16
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
18
 
17
19
 
18
20
# TODO: tests regarding version names
19
 
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
 
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
20
22
#       if it fails.
21
23
 
22
24
"""test suite for weave algorithm"""
23
25
 
24
26
from pprint import pformat
25
27
 
26
 
from .. import (
27
 
    errors,
28
 
    )
29
 
from ..osutils import sha_string
30
 
from ..sixish import (
31
 
    BytesIO,
32
 
    )
33
 
from . import TestCase, TestCaseInTempDir
34
 
from ..bzr.weave import Weave, WeaveFormatError, WeaveInvalidChecksum
35
 
from ..bzr.weavefile import write_weave, read_weave
 
28
import bzrlib.errors as errors
 
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
 
30
from bzrlib.weavefile import write_weave, read_weave
 
31
from bzrlib.selftest import TestCase
 
32
from bzrlib.osutils import sha_string
36
33
 
37
34
 
38
35
# texts for use in testing
39
 
TEXT_0 = [b"Hello world"]
40
 
TEXT_1 = [b"Hello world",
41
 
          b"A second line"]
 
36
TEXT_0 = ["Hello world"]
 
37
TEXT_1 = ["Hello world",
 
38
          "A second line"]
 
39
 
42
40
 
43
41
 
44
42
class TestBase(TestCase):
45
 
 
46
43
    def check_read_write(self, k):
47
44
        """Check the weave k can be written & re-read."""
48
45
        from tempfile import TemporaryFile
62
59
            self.log('         %r' % k._parents)
63
60
            self.log('         %r' % k2._parents)
64
61
            self.log('')
 
62
 
 
63
            
65
64
            self.fail('read/write check failed')
66
 
 
67
 
 
68
 
class WeaveContains(TestBase):
69
 
    """Weave __contains__ operator"""
70
 
 
71
 
    def runTest(self):
72
 
        k = Weave(get_scope=lambda: None)
73
 
        self.assertFalse(b'foo' in k)
74
 
        k.add_lines(b'foo', [], TEXT_1)
75
 
        self.assertTrue(b'foo' in k)
 
65
        
 
66
        
76
67
 
77
68
 
78
69
class Easy(TestBase):
79
 
 
80
 
    def runTest(self):
81
 
        Weave()
 
70
    def runTest(self):
 
71
        k = Weave()
 
72
 
 
73
 
 
74
class StoreText(TestBase):
 
75
    """Store and retrieve a simple text."""
 
76
    def runTest(self):
 
77
        k = Weave()
 
78
        idx = k.add('text0', [], TEXT_0)
 
79
        self.assertEqual(k.get(idx), TEXT_0)
 
80
        self.assertEqual(idx, 0)
 
81
 
82
82
 
83
83
 
84
84
class AnnotateOne(TestBase):
85
 
 
86
 
    def runTest(self):
87
 
        k = Weave()
88
 
        k.add_lines(b'text0', [], TEXT_0)
89
 
        self.assertEqual(k.annotate(b'text0'),
90
 
                         [(b'text0', TEXT_0[0])])
 
85
    def runTest(self):
 
86
        k = Weave()
 
87
        k.add('text0', [], TEXT_0)
 
88
        self.assertEqual(k.annotate(0),
 
89
                         [(0, TEXT_0[0])])
 
90
 
 
91
 
 
92
class StoreTwo(TestBase):
 
93
    def runTest(self):
 
94
        k = Weave()
 
95
 
 
96
        idx = k.add('text0', [], TEXT_0)
 
97
        self.assertEqual(idx, 0)
 
98
 
 
99
        idx = k.add('text1', [], TEXT_1)
 
100
        self.assertEqual(idx, 1)
 
101
 
 
102
        self.assertEqual(k.get(0), TEXT_0)
 
103
        self.assertEqual(k.get(1), TEXT_1)
 
104
 
 
105
 
 
106
 
 
107
class AddWithGivenSha(TestBase):
 
108
    def runTest(self):
 
109
        """Add with caller-supplied SHA-1"""
 
110
        k = Weave()
 
111
 
 
112
        t = 'text0'
 
113
        k.add('text0', [], [t], sha1=sha_string(t))
 
114
 
91
115
 
92
116
 
93
117
class InvalidAdd(TestBase):
94
118
    """Try to use invalid version number during add."""
95
 
 
96
119
    def runTest(self):
97
120
        k = Weave()
98
121
 
99
 
        self.assertRaises(errors.RevisionNotPresent,
100
 
                          k.add_lines,
101
 
                          b'text0',
102
 
                          [b'69'],
103
 
                          [b'new text!'])
 
122
        self.assertRaises(IndexError,
 
123
                          k.add,
 
124
                          'text0',
 
125
                          [69],
 
126
                          ['new text!'])
104
127
 
105
128
 
106
129
class RepeatedAdd(TestBase):
107
130
    """Add the same version twice; harmless."""
108
 
 
109
 
    def test_duplicate_add(self):
 
131
    def runTest(self):
110
132
        k = Weave()
111
 
        idx = k.add_lines(b'text0', [], TEXT_0)
112
 
        idx2 = k.add_lines(b'text0', [], TEXT_0)
 
133
        idx = k.add('text0', [], TEXT_0)
 
134
        idx2 = k.add('text0', [], TEXT_0)
113
135
        self.assertEqual(idx, idx2)
114
136
 
115
137
 
 
138
 
116
139
class InvalidRepeatedAdd(TestBase):
117
 
 
118
140
    def runTest(self):
119
141
        k = Weave()
120
 
        k.add_lines(b'basis', [], TEXT_0)
121
 
        k.add_lines(b'text0', [], TEXT_0)
122
 
        self.assertRaises(errors.RevisionAlreadyPresent,
123
 
                          k.add_lines,
124
 
                          b'text0',
 
142
        idx = k.add('text0', [], TEXT_0)
 
143
        self.assertRaises(WeaveError,
 
144
                          k.add,
 
145
                          'text0',
125
146
                          [],
126
 
                          [b'not the same text'])
127
 
        self.assertRaises(errors.RevisionAlreadyPresent,
128
 
                          k.add_lines,
129
 
                          b'text0',
130
 
                          [b'basis'],         # not the right parents
 
147
                          ['not the same text'])
 
148
        self.assertRaises(WeaveError,
 
149
                          k.add,
 
150
                          'text0',
 
151
                          [12],         # not the right parents
131
152
                          TEXT_0)
 
153
        
132
154
 
133
155
 
134
156
class InsertLines(TestBase):
136
158
 
137
159
    Look at the annotations to make sure that the first line is matched
138
160
    and not stored repeatedly."""
139
 
 
140
161
    def runTest(self):
141
162
        k = Weave()
142
163
 
143
 
        k.add_lines(b'text0', [], [b'line 1'])
144
 
        k.add_lines(b'text1', [b'text0'], [b'line 1', b'line 2'])
145
 
 
146
 
        self.assertEqual(k.annotate(b'text0'),
147
 
                         [(b'text0', b'line 1')])
148
 
 
149
 
        self.assertEqual(k.get_lines(1),
150
 
                         [b'line 1',
151
 
                          b'line 2'])
152
 
 
153
 
        self.assertEqual(k.annotate(b'text1'),
154
 
                         [(b'text0', b'line 1'),
155
 
                          (b'text1', b'line 2')])
156
 
 
157
 
        k.add_lines(b'text2', [b'text0'], [b'line 1', b'diverged line'])
158
 
 
159
 
        self.assertEqual(k.annotate(b'text2'),
160
 
                         [(b'text0', b'line 1'),
161
 
                          (b'text2', b'diverged line')])
162
 
 
163
 
        text3 = [b'line 1', b'middle line', b'line 2']
164
 
        k.add_lines(b'text3',
165
 
                    [b'text0', b'text1'],
166
 
                    text3)
167
 
 
168
 
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]),
169
 
        # text3))))
 
164
        k.add('text0', [], ['line 1'])
 
165
        k.add('text1', [0], ['line 1', 'line 2'])
 
166
 
 
167
        self.assertEqual(k.annotate(0),
 
168
                         [(0, 'line 1')])
 
169
 
 
170
        self.assertEqual(k.get(1),
 
171
                         ['line 1',
 
172
                          'line 2'])
 
173
 
 
174
        self.assertEqual(k.annotate(1),
 
175
                         [(0, 'line 1'),
 
176
                          (1, 'line 2')])
 
177
 
 
178
        k.add('text2', [0], ['line 1', 'diverged line'])
 
179
 
 
180
        self.assertEqual(k.annotate(2),
 
181
                         [(0, 'line 1'),
 
182
                          (2, 'diverged line')])
 
183
 
 
184
        text3 = ['line 1', 'middle line', 'line 2']
 
185
        k.add('text3',
 
186
              [0, 1],
 
187
              text3)
 
188
 
 
189
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
170
190
 
171
191
        self.log("k._weave=" + pformat(k._weave))
172
192
 
173
 
        self.assertEqual(k.annotate(b'text3'),
174
 
                         [(b'text0', b'line 1'),
175
 
                          (b'text3', b'middle line'),
176
 
                          (b'text1', b'line 2')])
 
193
        self.assertEqual(k.annotate(3),
 
194
                         [(0, 'line 1'),
 
195
                          (3, 'middle line'),
 
196
                          (1, 'line 2')])
177
197
 
178
198
        # now multiple insertions at different places
179
 
        k.add_lines(
180
 
            b'text4', [b'text0', b'text1', b'text3'],
181
 
            [b'line 1', b'aaa', b'middle line', b'bbb', b'line 2', b'ccc'])
182
 
 
183
 
        self.assertEqual(k.annotate(b'text4'),
184
 
                         [(b'text0', b'line 1'),
185
 
                          (b'text4', b'aaa'),
186
 
                          (b'text3', b'middle line'),
187
 
                          (b'text4', b'bbb'),
188
 
                          (b'text1', b'line 2'),
189
 
                          (b'text4', b'ccc')])
 
199
        k.add('text4',
 
200
              [0, 1, 3],
 
201
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
 
202
 
 
203
        self.assertEqual(k.annotate(4), 
 
204
                         [(0, 'line 1'),
 
205
                          (4, 'aaa'),
 
206
                          (3, 'middle line'),
 
207
                          (4, 'bbb'),
 
208
                          (1, 'line 2'),
 
209
                          (4, 'ccc')])
 
210
 
190
211
 
191
212
 
192
213
class DeleteLines(TestBase):
193
214
    """Deletion of lines from existing text.
194
215
 
195
216
    Try various texts all based on a common ancestor."""
196
 
 
197
217
    def runTest(self):
198
218
        k = Weave()
199
219
 
200
 
        base_text = [b'one', b'two', b'three', b'four']
201
 
 
202
 
        k.add_lines(b'text0', [], base_text)
203
 
 
204
 
        texts = [[b'one', b'two', b'three'],
205
 
                 [b'two', b'three', b'four'],
206
 
                 [b'one', b'four'],
207
 
                 [b'one', b'two', b'three', b'four'],
 
220
        base_text = ['one', 'two', 'three', 'four']
 
221
 
 
222
        k.add('text0', [], base_text)
 
223
        
 
224
        texts = [['one', 'two', 'three'],
 
225
                 ['two', 'three', 'four'],
 
226
                 ['one', 'four'],
 
227
                 ['one', 'two', 'three', 'four'],
208
228
                 ]
209
229
 
210
230
        i = 1
211
231
        for t in texts:
212
 
            k.add_lines(b'text%d' % i, [b'text0'], t)
 
232
            ver = k.add('text%d' % i,
 
233
                        [0], t)
213
234
            i += 1
214
235
 
215
236
        self.log('final weave:')
216
237
        self.log('k._weave=' + pformat(k._weave))
217
238
 
218
239
        for i in range(len(texts)):
219
 
            self.assertEqual(k.get_lines(i + 1),
 
240
            self.assertEqual(k.get(i+1),
220
241
                             texts[i])
 
242
            
 
243
 
221
244
 
222
245
 
223
246
class SuicideDelete(TestBase):
224
247
    """Invalid weave which tries to add and delete simultaneously."""
225
 
 
226
248
    def runTest(self):
227
249
        k = Weave()
228
250
 
229
251
        k._parents = [(),
230
 
                      ]
231
 
        k._weave = [(b'{', 0),
232
 
                    b'first line',
233
 
                    (b'[', 0),
234
 
                    b'deleted in 0',
235
 
                    (b']', 0),
236
 
                    (b'}', 0),
237
 
                    ]
238
 
        # SKIPPED
 
252
                ]
 
253
        k._weave = [('{', 0),
 
254
                'first line',
 
255
                ('[', 0),
 
256
                'deleted in 0',
 
257
                (']', 0),
 
258
                ('}', 0),
 
259
                ]
 
260
        ################################### SKIPPED
239
261
        # Weave.get doesn't trap this anymore
240
 
        return
 
262
        return 
241
263
 
242
264
        self.assertRaises(WeaveFormatError,
243
 
                          k.get_lines,
244
 
                          0)
 
265
                          k.get,
 
266
                          0)        
 
267
 
245
268
 
246
269
 
247
270
class CannedDelete(TestBase):
248
271
    """Unpack canned weave with deleted lines."""
249
 
 
250
272
    def runTest(self):
251
273
        k = Weave()
252
274
 
253
275
        k._parents = [(),
254
 
                      frozenset([0]),
255
 
                      ]
256
 
        k._weave = [(b'{', 0),
257
 
                    b'first line',
258
 
                    (b'[', 1),
259
 
                    b'line to be deleted',
260
 
                    (b']', 1),
261
 
                    b'last line',
262
 
                    (b'}', 0),
263
 
                    ]
264
 
        k._sha1s = [
265
 
            sha_string(b'first lineline to be deletedlast line'),
266
 
            sha_string(b'first linelast line')]
267
 
 
268
 
        self.assertEqual(k.get_lines(0),
269
 
                         [b'first line',
270
 
                          b'line to be deleted',
271
 
                          b'last line',
272
 
                          ])
273
 
 
274
 
        self.assertEqual(k.get_lines(1),
275
 
                         [b'first line',
276
 
                          b'last line',
277
 
                          ])
 
276
                frozenset([0]),
 
277
                ]
 
278
        k._weave = [('{', 0),
 
279
                'first line',
 
280
                ('[', 1),
 
281
                'line to be deleted',
 
282
                (']', 1),
 
283
                'last line',
 
284
                ('}', 0),
 
285
                ]
 
286
 
 
287
        self.assertEqual(k.get(0),
 
288
                         ['first line',
 
289
                          'line to be deleted',
 
290
                          'last line',
 
291
                          ])
 
292
 
 
293
        self.assertEqual(k.get(1),
 
294
                         ['first line',
 
295
                          'last line',
 
296
                          ])
 
297
 
278
298
 
279
299
 
280
300
class CannedReplacement(TestBase):
281
301
    """Unpack canned weave with deleted lines."""
282
 
 
283
302
    def runTest(self):
284
303
        k = Weave()
285
304
 
286
305
        k._parents = [frozenset(),
287
 
                      frozenset([0]),
288
 
                      ]
289
 
        k._weave = [(b'{', 0),
290
 
                    b'first line',
291
 
                    (b'[', 1),
292
 
                    b'line to be deleted',
293
 
                    (b']', 1),
294
 
                    (b'{', 1),
295
 
                    b'replacement line',
296
 
                    (b'}', 1),
297
 
                    b'last line',
298
 
                    (b'}', 0),
299
 
                    ]
300
 
        k._sha1s = [
301
 
            sha_string(b'first lineline to be deletedlast line'),
302
 
            sha_string(b'first linereplacement linelast line')]
303
 
 
304
 
        self.assertEqual(k.get_lines(0),
305
 
                         [b'first line',
306
 
                          b'line to be deleted',
307
 
                          b'last line',
308
 
                          ])
309
 
 
310
 
        self.assertEqual(k.get_lines(1),
311
 
                         [b'first line',
312
 
                          b'replacement line',
313
 
                          b'last line',
314
 
                          ])
 
306
                frozenset([0]),
 
307
                ]
 
308
        k._weave = [('{', 0),
 
309
                'first line',
 
310
                ('[', 1),
 
311
                'line to be deleted',
 
312
                (']', 1),
 
313
                ('{', 1),
 
314
                'replacement line',                
 
315
                ('}', 1),
 
316
                'last line',
 
317
                ('}', 0),
 
318
                ]
 
319
 
 
320
        self.assertEqual(k.get(0),
 
321
                         ['first line',
 
322
                          'line to be deleted',
 
323
                          'last line',
 
324
                          ])
 
325
 
 
326
        self.assertEqual(k.get(1),
 
327
                         ['first line',
 
328
                          'replacement line',
 
329
                          'last line',
 
330
                          ])
 
331
 
315
332
 
316
333
 
317
334
class BadWeave(TestBase):
318
335
    """Test that we trap an insert which should not occur."""
319
 
 
320
336
    def runTest(self):
321
337
        k = Weave()
322
338
 
323
339
        k._parents = [frozenset(),
324
 
                      ]
325
 
        k._weave = [b'bad line',
326
 
                    (b'{', 0),
327
 
                    b'foo {',
328
 
                    (b'{', 1),
329
 
                    b'  added in version 1',
330
 
                    (b'{', 2),
331
 
                    b'  added in v2',
332
 
                    (b'}', 2),
333
 
                    b'  also from v1',
334
 
                    (b'}', 1),
335
 
                    b'}',
336
 
                    (b'}', 0)]
 
340
                ]
 
341
        k._weave = ['bad line',
 
342
                ('{', 0),
 
343
                'foo {',
 
344
                ('{', 1),
 
345
                '  added in version 1',
 
346
                ('{', 2),
 
347
                '  added in v2',
 
348
                ('}', 2),
 
349
                '  also from v1',
 
350
                ('}', 1),
 
351
                '}',
 
352
                ('}', 0)]
337
353
 
338
 
        # SKIPPED
 
354
        ################################### SKIPPED
339
355
        # Weave.get doesn't trap this anymore
340
 
        return
 
356
        return 
 
357
 
341
358
 
342
359
        self.assertRaises(WeaveFormatError,
343
360
                          k.get,
346
363
 
347
364
class BadInsert(TestBase):
348
365
    """Test that we trap an insert which should not occur."""
349
 
 
350
366
    def runTest(self):
351
367
        k = Weave()
352
368
 
353
369
        k._parents = [frozenset(),
354
 
                      frozenset([0]),
355
 
                      frozenset([0]),
356
 
                      frozenset([0, 1, 2]),
357
 
                      ]
358
 
        k._weave = [(b'{', 0),
359
 
                    b'foo {',
360
 
                    (b'{', 1),
361
 
                    b'  added in version 1',
362
 
                    (b'{', 1),
363
 
                    b'  more in 1',
364
 
                    (b'}', 1),
365
 
                    (b'}', 1),
366
 
                    (b'}', 0)]
 
370
                frozenset([0]),
 
371
                frozenset([0]),
 
372
                frozenset([0,1,2]),
 
373
                ]
 
374
        k._weave = [('{', 0),
 
375
                'foo {',
 
376
                ('{', 1),
 
377
                '  added in version 1',
 
378
                ('{', 1),
 
379
                '  more in 1',
 
380
                ('}', 1),
 
381
                ('}', 1),
 
382
                ('}', 0)]
 
383
 
367
384
 
368
385
        # this is not currently enforced by get
369
 
        return
 
386
        return  ##########################################
370
387
 
371
388
        self.assertRaises(WeaveFormatError,
372
389
                          k.get,
379
396
 
380
397
class InsertNested(TestBase):
381
398
    """Insertion with nested instructions."""
382
 
 
383
399
    def runTest(self):
384
400
        k = Weave()
385
401
 
386
402
        k._parents = [frozenset(),
387
 
                      frozenset([0]),
388
 
                      frozenset([0]),
389
 
                      frozenset([0, 1, 2]),
390
 
                      ]
391
 
        k._weave = [(b'{', 0),
392
 
                    b'foo {',
393
 
                    (b'{', 1),
394
 
                    b'  added in version 1',
395
 
                    (b'{', 2),
396
 
                    b'  added in v2',
397
 
                    (b'}', 2),
398
 
                    b'  also from v1',
399
 
                    (b'}', 1),
400
 
                    b'}',
401
 
                    (b'}', 0)]
402
 
 
403
 
        k._sha1s = [
404
 
            sha_string(b'foo {}'),
405
 
            sha_string(b'foo {  added in version 1  also from v1}'),
406
 
            sha_string(b'foo {  added in v2}'),
407
 
            sha_string(
408
 
                b'foo {  added in version 1  added in v2  also from v1}')
409
 
            ]
410
 
 
411
 
        self.assertEqual(k.get_lines(0),
412
 
                         [b'foo {',
413
 
                          b'}'])
414
 
 
415
 
        self.assertEqual(k.get_lines(1),
416
 
                         [b'foo {',
417
 
                          b'  added in version 1',
418
 
                          b'  also from v1',
419
 
                          b'}'])
420
 
 
421
 
        self.assertEqual(k.get_lines(2),
422
 
                         [b'foo {',
423
 
                          b'  added in v2',
424
 
                          b'}'])
425
 
 
426
 
        self.assertEqual(k.get_lines(3),
427
 
                         [b'foo {',
428
 
                          b'  added in version 1',
429
 
                          b'  added in v2',
430
 
                          b'  also from v1',
431
 
                          b'}'])
 
403
                frozenset([0]),
 
404
                frozenset([0]),
 
405
                frozenset([0,1,2]),
 
406
                ]
 
407
        k._weave = [('{', 0),
 
408
                'foo {',
 
409
                ('{', 1),
 
410
                '  added in version 1',
 
411
                ('{', 2),
 
412
                '  added in v2',
 
413
                ('}', 2),
 
414
                '  also from v1',
 
415
                ('}', 1),
 
416
                '}',
 
417
                ('}', 0)]
 
418
 
 
419
        self.assertEqual(k.get(0),
 
420
                         ['foo {',
 
421
                          '}'])
 
422
 
 
423
        self.assertEqual(k.get(1),
 
424
                         ['foo {',
 
425
                          '  added in version 1',
 
426
                          '  also from v1',
 
427
                          '}'])
 
428
                       
 
429
        self.assertEqual(k.get(2),
 
430
                         ['foo {',
 
431
                          '  added in v2',
 
432
                          '}'])
 
433
 
 
434
        self.assertEqual(k.get(3),
 
435
                         ['foo {',
 
436
                          '  added in version 1',
 
437
                          '  added in v2',
 
438
                          '  also from v1',
 
439
                          '}'])
 
440
                         
432
441
 
433
442
 
434
443
class DeleteLines2(TestBase):
436
445
 
437
446
    This relies on the weave having a way to represent lines knocked
438
447
    out by a later revision."""
439
 
 
440
448
    def runTest(self):
441
449
        k = Weave()
442
450
 
443
 
        k.add_lines(b'text0', [], [b"line the first",
444
 
                                   b"line 2",
445
 
                                   b"line 3",
446
 
                                   b"fine"])
447
 
 
448
 
        self.assertEqual(len(k.get_lines(0)), 4)
449
 
 
450
 
        k.add_lines(b'text1', [b'text0'], [b"line the first",
451
 
                                           b"fine"])
452
 
 
453
 
        self.assertEqual(k.get_lines(1),
454
 
                         [b"line the first",
455
 
                          b"fine"])
456
 
 
457
 
        self.assertEqual(k.annotate(b'text1'),
458
 
                         [(b'text0', b"line the first"),
459
 
                          (b'text0', b"fine")])
 
451
        k.add('text0', [], ["line the first",
 
452
                   "line 2",
 
453
                   "line 3",
 
454
                   "fine"])
 
455
 
 
456
        self.assertEqual(len(k.get(0)), 4)
 
457
 
 
458
        k.add('text1', [0], ["line the first",
 
459
                   "fine"])
 
460
 
 
461
        self.assertEqual(k.get(1),
 
462
                         ["line the first",
 
463
                          "fine"])
 
464
 
 
465
        self.assertEqual(k.annotate(1),
 
466
                         [(0, "line the first"),
 
467
                          (0, "fine")])
 
468
 
460
469
 
461
470
 
462
471
class IncludeVersions(TestBase):
473
482
        k = Weave()
474
483
 
475
484
        k._parents = [frozenset(), frozenset([0])]
476
 
        k._weave = [(b'{', 0),
477
 
                    b"first line",
478
 
                    (b'}', 0),
479
 
                    (b'{', 1),
480
 
                    b"second line",
481
 
                    (b'}', 1)]
482
 
 
483
 
        k._sha1s = [sha_string(b'first line'), sha_string(
484
 
            b'first linesecond line')]
485
 
 
486
 
        self.assertEqual(k.get_lines(1),
487
 
                         [b"first line",
488
 
                          b"second line"])
489
 
 
490
 
        self.assertEqual(k.get_lines(0),
491
 
                         [b"first line"])
 
485
        k._weave = [('{', 0),
 
486
                "first line",
 
487
                ('}', 0),
 
488
                ('{', 1),
 
489
                "second line",
 
490
                ('}', 1)]
 
491
 
 
492
        self.assertEqual(k.get(1),
 
493
                         ["first line",
 
494
                          "second line"])
 
495
 
 
496
        self.assertEqual(k.get(0),
 
497
                         ["first line"])
492
498
 
493
499
 
494
500
class DivergedIncludes(TestBase):
495
501
    """Weave with two diverged texts based on version 0.
496
502
    """
497
 
 
498
503
    def runTest(self):
499
 
        # FIXME make the weave, dont poke at it.
500
504
        k = Weave()
501
505
 
502
 
        k._names = [b'0', b'1', b'2']
503
 
        k._name_map = {b'0': 0, b'1': 1, b'2': 2}
504
506
        k._parents = [frozenset(),
505
 
                      frozenset([0]),
506
 
                      frozenset([0]),
507
 
                      ]
508
 
        k._weave = [(b'{', 0),
509
 
                    b"first line",
510
 
                    (b'}', 0),
511
 
                    (b'{', 1),
512
 
                    b"second line",
513
 
                    (b'}', 1),
514
 
                    (b'{', 2),
515
 
                    b"alternative second line",
516
 
                    (b'}', 2),
517
 
                    ]
518
 
 
519
 
        k._sha1s = [
520
 
            sha_string(b'first line'),
521
 
            sha_string(b'first linesecond line'),
522
 
            sha_string(b'first linealternative second line')]
523
 
 
524
 
        self.assertEqual(k.get_lines(0),
525
 
                         [b"first line"])
526
 
 
527
 
        self.assertEqual(k.get_lines(1),
528
 
                         [b"first line",
529
 
                          b"second line"])
530
 
 
531
 
        self.assertEqual(k.get_lines(b'2'),
532
 
                         [b"first line",
533
 
                          b"alternative second line"])
534
 
 
535
 
        self.assertEqual(list(k.get_ancestry([b'2'])),
536
 
                         [b'0', b'2'])
 
507
                frozenset([0]),
 
508
                frozenset([0]),
 
509
                ]
 
510
        k._weave = [('{', 0),
 
511
                "first line",
 
512
                ('}', 0),
 
513
                ('{', 1),
 
514
                "second line",
 
515
                ('}', 1),
 
516
                ('{', 2),
 
517
                "alternative second line",
 
518
                ('}', 2),                
 
519
                ]
 
520
 
 
521
        self.assertEqual(k.get(0),
 
522
                         ["first line"])
 
523
 
 
524
        self.assertEqual(k.get(1),
 
525
                         ["first line",
 
526
                          "second line"])
 
527
 
 
528
        self.assertEqual(k.get(2),
 
529
                         ["first line",
 
530
                          "alternative second line"])
 
531
 
 
532
        self.assertEqual(list(k.inclusions([2])),
 
533
                         [0, 2])
 
534
 
537
535
 
538
536
 
539
537
class ReplaceLine(TestBase):
540
538
    def runTest(self):
541
539
        k = Weave()
542
540
 
543
 
        text0 = [b'cheddar', b'stilton', b'gruyere']
544
 
        text1 = [b'cheddar', b'blue vein', b'neufchatel', b'chevre']
545
 
 
546
 
        k.add_lines(b'text0', [], text0)
547
 
        k.add_lines(b'text1', [b'text0'], text1)
 
541
        text0 = ['cheddar', 'stilton', 'gruyere']
 
542
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
 
543
        
 
544
        k.add('text0', [], text0)
 
545
        k.add('text1', [0], text1)
548
546
 
549
547
        self.log('k._weave=' + pformat(k._weave))
550
548
 
551
 
        self.assertEqual(k.get_lines(0), text0)
552
 
        self.assertEqual(k.get_lines(1), text1)
 
549
        self.assertEqual(k.get(0), text0)
 
550
        self.assertEqual(k.get(1), text1)
 
551
 
553
552
 
554
553
 
555
554
class Merge(TestBase):
556
555
    """Storage of versions that merge diverged parents"""
557
 
 
558
556
    def runTest(self):
559
557
        k = Weave()
560
558
 
561
 
        texts = [
562
 
            [b'header'],
563
 
            [b'header', b'', b'line from 1'],
564
 
            [b'header', b'', b'line from 2', b'more from 2'],
565
 
            [b'header', b'', b'line from 1', b'fixup line', b'line from 2'],
566
 
            ]
 
559
        texts = [['header'],
 
560
                 ['header', '', 'line from 1'],
 
561
                 ['header', '', 'line from 2', 'more from 2'],
 
562
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
 
563
                 ]
567
564
 
568
 
        k.add_lines(b'text0', [], texts[0])
569
 
        k.add_lines(b'text1', [b'text0'], texts[1])
570
 
        k.add_lines(b'text2', [b'text0'], texts[2])
571
 
        k.add_lines(b'merge', [b'text0', b'text1', b'text2'], texts[3])
 
565
        k.add('text0', [], texts[0])
 
566
        k.add('text1', [0], texts[1])
 
567
        k.add('text2', [0], texts[2])
 
568
        k.add('merge', [0, 1, 2], texts[3])
572
569
 
573
570
        for i, t in enumerate(texts):
574
 
            self.assertEqual(k.get_lines(i), t)
 
571
            self.assertEqual(k.get(i), t)
575
572
 
576
 
        self.assertEqual(k.annotate(b'merge'),
577
 
                         [(b'text0', b'header'),
578
 
                          (b'text1', b''),
579
 
                          (b'text1', b'line from 1'),
580
 
                          (b'merge', b'fixup line'),
581
 
                          (b'text2', b'line from 2'),
 
573
        self.assertEqual(k.annotate(3),
 
574
                         [(0, 'header'),
 
575
                          (1, ''),
 
576
                          (1, 'line from 1'),
 
577
                          (3, 'fixup line'),
 
578
                          (2, 'line from 2'),
582
579
                          ])
583
580
 
584
 
        self.assertEqual(list(k.get_ancestry([b'merge'])),
585
 
                         [b'text0', b'text1', b'text2', b'merge'])
 
581
        self.assertEqual(list(k.inclusions([3])),
 
582
                         [0, 1, 2, 3])
586
583
 
587
584
        self.log('k._weave=' + pformat(k._weave))
588
585
 
595
592
    A base version is inserted, then two descendents try to
596
593
    insert different lines in the same place.  These should be
597
594
    reported as a possible conflict and forwarded to the user."""
598
 
 
599
595
    def runTest(self):
600
596
        return  # NOT RUN
601
597
        k = Weave()
602
598
 
603
 
        k.add_lines([], [b'aaa', b'bbb'])
604
 
        k.add_lines([0], [b'aaa', b'111', b'bbb'])
605
 
        k.add_lines([1], [b'aaa', b'222', b'bbb'])
606
 
 
607
 
        k.merge([1, 2])
608
 
 
609
 
        self.assertEqual([[[b'aaa']],
610
 
                          [[b'111'], [b'222']],
611
 
                          [[b'bbb']]])
 
599
        k.add([], ['aaa', 'bbb'])
 
600
        k.add([0], ['aaa', '111', 'bbb'])
 
601
        k.add([1], ['aaa', '222', 'bbb'])
 
602
 
 
603
        merged = k.merge([1, 2])
 
604
 
 
605
        self.assertEquals([[['aaa']],
 
606
                           [['111'], ['222']],
 
607
                           [['bbb']]])
 
608
 
612
609
 
613
610
 
614
611
class NonConflict(TestBase):
615
612
    """Two descendants insert compatible changes.
616
613
 
617
614
    No conflict should be reported."""
618
 
 
619
615
    def runTest(self):
620
616
        return  # NOT RUN
621
617
        k = Weave()
622
618
 
623
 
        k.add_lines([], [b'aaa', b'bbb'])
624
 
        k.add_lines([0], [b'111', b'aaa', b'ccc', b'bbb'])
625
 
        k.add_lines([1], [b'aaa', b'ccc', b'bbb', b'222'])
 
619
        k.add([], ['aaa', 'bbb'])
 
620
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
 
621
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
 
622
 
 
623
    
 
624
    
 
625
 
 
626
 
 
627
class AutoMerge(TestBase):
 
628
    def runTest(self):
 
629
        k = Weave()
 
630
 
 
631
        texts = [['header', 'aaa', 'bbb'],
 
632
                 ['header', 'aaa', 'line from 1', 'bbb'],
 
633
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
 
634
                 ]
 
635
 
 
636
        k.add('text0', [], texts[0])
 
637
        k.add('text1', [0], texts[1])
 
638
        k.add('text2', [0], texts[2])
 
639
 
 
640
        self.log('k._weave=' + pformat(k._weave))
 
641
 
 
642
        m = list(k.mash_iter([0, 1, 2]))
 
643
 
 
644
        self.assertEqual(m,
 
645
                         ['header', 'aaa',
 
646
                          'line from 1',
 
647
                          'bbb',
 
648
                          'line from 2', 'more from 2'])
 
649
        
626
650
 
627
651
 
628
652
class Khayyam(TestBase):
629
653
    """Test changes to multi-line texts, and read/write"""
630
 
 
631
 
    def test_multi_line_merge(self):
 
654
    def runTest(self):
632
655
        rawtexts = [
633
 
            b"""A Book of Verses underneath the Bough,
 
656
            """A Book of Verses underneath the Bough,
634
657
            A Jug of Wine, a Loaf of Bread, -- and Thou
635
658
            Beside me singing in the Wilderness --
636
659
            Oh, Wilderness were Paradise enow!""",
637
 
 
638
 
            b"""A Book of Verses underneath the Bough,
 
660
            
 
661
            """A Book of Verses underneath the Bough,
639
662
            A Jug of Wine, a Loaf of Bread, -- and Thou
640
663
            Beside me singing in the Wilderness --
641
664
            Oh, Wilderness were Paradise now!""",
642
665
 
643
 
            b"""A Book of poems underneath the tree,
 
666
            """A Book of poems underneath the tree,
644
667
            A Jug of Wine, a Loaf of Bread,
645
668
            and Thou
646
669
            Beside me singing in the Wilderness --
648
671
 
649
672
            -- O. Khayyam""",
650
673
 
651
 
            b"""A Book of Verses underneath the Bough,
 
674
            """A Book of Verses underneath the Bough,
652
675
            A Jug of Wine, a Loaf of Bread,
653
676
            and Thou
654
677
            Beside me singing in the Wilderness --
655
678
            Oh, Wilderness were Paradise now!""",
656
679
            ]
657
 
        texts = [[l.strip() for l in t.split(b'\n')] for t in rawtexts]
 
680
        texts = [[l.strip() for l in t.split('\n')] for t in rawtexts]
658
681
 
659
682
        k = Weave()
660
683
        parents = set()
661
684
        i = 0
662
685
        for t in texts:
663
 
            k.add_lines(b'text%d' % i, list(parents), t)
664
 
            parents.add(b'text%d' % i)
 
686
            ver = k.add('text%d' % i,
 
687
                        list(parents), t)
 
688
            parents.add(ver)
665
689
            i += 1
666
690
 
667
691
        self.log("k._weave=" + pformat(k._weave))
668
692
 
669
693
        for i, t in enumerate(texts):
670
 
            self.assertEqual(k.get_lines(i), t)
 
694
            self.assertEqual(k.get(i), t)
671
695
 
672
696
        self.check_read_write(k)
673
697
 
674
698
 
 
699
 
 
700
class MergeCases(TestBase):
 
701
    def doMerge(self, base, a, b, mp):
 
702
        from cStringIO import StringIO
 
703
        from textwrap import dedent
 
704
 
 
705
        def addcrlf(x):
 
706
            return x + '\n'
 
707
        
 
708
        w = Weave()
 
709
        w.add('text0', [], map(addcrlf, base))
 
710
        w.add('text1', [0], map(addcrlf, a))
 
711
        w.add('text2', [0], map(addcrlf, b))
 
712
 
 
713
        self.log('weave is:')
 
714
        tmpf = StringIO()
 
715
        write_weave(w, tmpf)
 
716
        self.log(tmpf.getvalue())
 
717
 
 
718
        self.log('merge plan:')
 
719
        p = list(w.plan_merge(1, 2))
 
720
        for state, line in p:
 
721
            if line:
 
722
                self.log('%12s | %s' % (state, line[:-1]))
 
723
 
 
724
        self.log('merge:')
 
725
        mt = StringIO()
 
726
        mt.writelines(w.weave_merge(p))
 
727
        mt.seek(0)
 
728
        self.log(mt.getvalue())
 
729
 
 
730
        mp = map(addcrlf, mp)
 
731
        self.assertEqual(mt.readlines(), mp)
 
732
        
 
733
        
 
734
    def testOneInsert(self):
 
735
        self.doMerge([],
 
736
                     ['aa'],
 
737
                     [],
 
738
                     ['aa'])
 
739
 
 
740
    def testSeparateInserts(self):
 
741
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
742
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
743
                     ['aaa', 'bbb', 'yyy', 'ccc'],
 
744
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
745
 
 
746
    def testSameInsert(self):
 
747
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
748
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
749
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
 
750
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
751
 
 
752
    def testOverlappedInsert(self):
 
753
        self.doMerge(['aaa', 'bbb'],
 
754
                     ['aaa', 'xxx', 'yyy', 'bbb'],
 
755
                     ['aaa', 'xxx', 'bbb'],
 
756
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
757
 
 
758
        # really it ought to reduce this to 
 
759
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
760
 
 
761
 
 
762
    def testClashReplace(self):
 
763
        self.doMerge(['aaa'],
 
764
                     ['xxx'],
 
765
                     ['yyy', 'zzz'],
 
766
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
767
 
 
768
    def testNonClashInsert(self):
 
769
        self.doMerge(['aaa'],
 
770
                     ['xxx', 'aaa'],
 
771
                     ['yyy', 'zzz'],
 
772
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
773
 
 
774
        self.doMerge(['aaa'],
 
775
                     ['aaa'],
 
776
                     ['yyy', 'zzz'],
 
777
                     ['yyy', 'zzz'])
 
778
 
 
779
 
 
780
    def testDeleteAndModify(self):
 
781
        """Clashing delete and modification.
 
782
 
 
783
        If one side modifies a region and the other deletes it then
 
784
        there should be a conflict with one side blank.
 
785
        """
 
786
 
 
787
        #######################################
 
788
        # skippd, not working yet
 
789
        return
 
790
        
 
791
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
792
                     ['aaa', 'ddd', 'ccc'],
 
793
                     ['aaa', 'ccc'],
 
794
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
795
 
 
796
 
675
797
class JoinWeavesTests(TestBase):
676
 
 
677
798
    def setUp(self):
678
799
        super(JoinWeavesTests, self).setUp()
679
800
        self.weave1 = Weave()
680
 
        self.lines1 = [b'hello\n']
681
 
        self.lines3 = [b'hello\n', b'cruel\n', b'world\n']
682
 
        self.weave1.add_lines(b'v1', [], self.lines1)
683
 
        self.weave1.add_lines(b'v2', [b'v1'], [b'hello\n', b'world\n'])
684
 
        self.weave1.add_lines(b'v3', [b'v2'], self.lines3)
685
 
 
686
 
    def test_written_detection(self):
687
 
        # Test detection of weave file corruption.
688
 
        #
689
 
        # Make sure that we can detect if a weave file has
690
 
        # been corrupted. This doesn't test all forms of corruption,
691
 
        # but it at least helps verify the data you get, is what you want.
692
 
 
693
 
        w = Weave()
694
 
        w.add_lines(b'v1', [], [b'hello\n'])
695
 
        w.add_lines(b'v2', [b'v1'], [b'hello\n', b'there\n'])
696
 
 
697
 
        tmpf = BytesIO()
698
 
        write_weave(w, tmpf)
699
 
 
700
 
        # Because we are corrupting, we need to make sure we have the exact
701
 
        # text
702
 
        self.assertEqual(
703
 
            b'# bzr weave file v5\n'
704
 
            b'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
705
 
            b'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
706
 
            b'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
707
 
            tmpf.getvalue())
708
 
 
709
 
        # Change a single letter
710
 
        tmpf = BytesIO(
711
 
            b'# bzr weave file v5\n'
712
 
            b'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
713
 
            b'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
714
 
            b'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
715
 
 
716
 
        w = read_weave(tmpf)
717
 
 
718
 
        self.assertEqual(b'hello\n', w.get_text(b'v1'))
719
 
        self.assertRaises(WeaveInvalidChecksum, w.get_text, b'v2')
720
 
        self.assertRaises(WeaveInvalidChecksum, w.get_lines, b'v2')
721
 
        self.assertRaises(WeaveInvalidChecksum, w.check)
722
 
 
723
 
        # Change the sha checksum
724
 
        tmpf = BytesIO(
725
 
            b'# bzr weave file v5\n'
726
 
            b'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
727
 
            b'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
728
 
            b'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
729
 
 
730
 
        w = read_weave(tmpf)
731
 
 
732
 
        self.assertEqual(b'hello\n', w.get_text(b'v1'))
733
 
        self.assertRaises(WeaveInvalidChecksum, w.get_text, b'v2')
734
 
        self.assertRaises(WeaveInvalidChecksum, w.get_lines, b'v2')
735
 
        self.assertRaises(WeaveInvalidChecksum, w.check)
736
 
 
737
 
 
738
 
class TestWeave(TestCase):
739
 
 
740
 
    def test_allow_reserved_false(self):
741
 
        w = Weave('name', allow_reserved=False)
742
 
        # Add lines is checked at the WeaveFile level, not at the Weave level
743
 
        w.add_lines(b'name:', [], TEXT_1)
744
 
        # But get_lines is checked at this level
745
 
        self.assertRaises(errors.ReservedId, w.get_lines, b'name:')
746
 
 
747
 
    def test_allow_reserved_true(self):
748
 
        w = Weave('name', allow_reserved=True)
749
 
        w.add_lines(b'name:', [], TEXT_1)
750
 
        self.assertEqual(TEXT_1, w.get_lines(b'name:'))
751
 
 
752
 
 
753
 
class InstrumentedWeave(Weave):
754
 
    """Keep track of how many times functions are called."""
755
 
 
756
 
    def __init__(self, weave_name=None):
757
 
        self._extract_count = 0
758
 
        Weave.__init__(self, weave_name=weave_name)
759
 
 
760
 
    def _extract(self, versions):
761
 
        self._extract_count += 1
762
 
        return Weave._extract(self, versions)
763
 
 
764
 
 
765
 
class TestNeedsReweave(TestCase):
766
 
    """Internal corner cases for when reweave is needed."""
767
 
 
768
 
    def test_compatible_parents(self):
769
 
        w1 = Weave('a')
770
 
        my_parents = {1, 2, 3}
771
 
        # subsets are ok
772
 
        self.assertTrue(w1._compatible_parents(my_parents, {3}))
773
 
        # same sets
774
 
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
775
 
        # same empty corner case
776
 
        self.assertTrue(w1._compatible_parents(set(), set()))
777
 
        # other cannot contain stuff my_parents does not
778
 
        self.assertFalse(w1._compatible_parents(set(), {1}))
779
 
        self.assertFalse(w1._compatible_parents(my_parents, {1, 2, 3, 4}))
780
 
        self.assertFalse(w1._compatible_parents(my_parents, {4}))
781
 
 
782
 
 
783
 
class TestWeaveFile(TestCaseInTempDir):
784
 
 
785
 
    def test_empty_file(self):
786
 
        with open('empty.weave', 'wb+') as f:
787
 
            self.assertRaises(WeaveFormatError, read_weave, f)
 
801
        self.lines1 = ['hello\n']
 
802
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
 
803
        self.weave1.add('v1', [], self.lines1)
 
804
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
 
805
        self.weave1.add('v3', [1], self.lines3)
 
806
        
 
807
    def test_join_empty(self):
 
808
        """Join two empty weaves."""
 
809
        eq = self.assertEqual
 
810
        w1 = Weave()
 
811
        w2 = Weave()
 
812
        w1.join(w2)
 
813
        eq(w1.numversions(), 0)
 
814
        
 
815
    def test_join_empty_to_nonempty(self):
 
816
        """Join empty weave onto nonempty."""
 
817
        self.weave1.join(Weave())
 
818
        self.assertEqual(len(self.weave1), 3)
 
819
 
 
820
    def test_join_unrelated(self):
 
821
        """Join two weaves with no history in common."""
 
822
        wb = Weave()
 
823
        wb.add('b1', [], ['line from b\n'])
 
824
        w1 = self.weave1
 
825
        w1.join(wb)
 
826
        eq = self.assertEqual
 
827
        eq(len(w1), 4)
 
828
        eq(sorted(list(w1.iter_names())),
 
829
           ['b1', 'v1', 'v2', 'v3'])
 
830
 
 
831
    def test_join_related(self):
 
832
        wa = self.weave1.copy()
 
833
        wb = self.weave1.copy()
 
834
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
835
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
836
        eq = self.assertEquals
 
837
        eq(len(wa), 4)
 
838
        eq(len(wb), 4)
 
839
        wa.join(wb)
 
840
        eq(len(wa), 5)
 
841
        eq(wa.get_lines('b1'),
 
842
           ['hello\n', 'pale blue\n', 'world\n'])
 
843
 
 
844
    def test_join_parent_disagreement(self):
 
845
        """Cannot join weaves with different parents for a version."""
 
846
        wa = Weave()
 
847
        wb = Weave()
 
848
        wa.add('v1', [], ['hello\n'])
 
849
        wb.add('v0', [], [])
 
850
        wb.add('v1', ['v0'], ['hello\n'])
 
851
        self.assertRaises(WeaveError,
 
852
                          wa.join, wb)
 
853
 
 
854
    def test_join_text_disagreement(self):
 
855
        """Cannot join weaves with different texts for a version."""
 
856
        wa = Weave()
 
857
        wb = Weave()
 
858
        wa.add('v1', [], ['hello\n'])
 
859
        wb.add('v1', [], ['not\n', 'hello\n'])
 
860
        self.assertRaises(WeaveError,
 
861
                          wa.join, wb)
 
862
 
 
863
    def test_join_unordered(self):
 
864
        """Join weaves where indexes differ.
 
865
        
 
866
        The source weave contains a different version at index 0."""
 
867
        wa = self.weave1.copy()
 
868
        wb = Weave()
 
869
        wb.add('x1', [], ['line from x1\n'])
 
870
        wb.add('v1', [], ['hello\n'])
 
871
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
 
872
        wa.join(wb)
 
873
        eq = self.assertEquals
 
874
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
875
        eq(wa.get_text('x1'), 'line from x1\n')
 
876
 
 
877
    def test_reweave_with_empty(self):
 
878
        wb = Weave()
 
879
        wr = reweave(self.weave1, wb)
 
880
        eq = self.assertEquals
 
881
        eq(sorted(wr.iter_names()), ['v1', 'v2', 'v3'])
 
882
        eq(wr.get_lines('v3'), ['hello\n', 'cruel\n', 'world\n'])
 
883
        self.weave1.reweave(wb)
 
884
        self.assertEquals(wr, self.weave1)
 
885
 
 
886
    def test_join_with_ghosts_raises_parent_mismatch(self):
 
887
        wa = self.weave1.copy()
 
888
        wb = Weave()
 
889
        wb.add('x1', [], ['line from x1\n'])
 
890
        wb.add('v1', [], ['hello\n'])
 
891
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
 
892
        self.assertRaises(errors.WeaveParentMismatch, wa.join, wb)
 
893
 
 
894
    def test_reweave_with_ghosts(self):
 
895
        """Join that inserts parents of an existing revision.
 
896
 
 
897
        This can happen when merging from another branch who
 
898
        knows about revisions the destination does not.  In 
 
899
        this test the second weave knows of an additional parent of 
 
900
        v2.  Any revisions which are in common still have to have the 
 
901
        same text."""
 
902
        wa = self.weave1.copy()
 
903
        wb = Weave()
 
904
        wb.add('x1', [], ['line from x1\n'])
 
905
        wb.add('v1', [], ['hello\n'])
 
906
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
 
907
        wc = reweave(wa, wb)
 
908
        eq = self.assertEquals
 
909
        eq(sorted(wc.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
910
        eq(wc.get_text('x1'), 'line from x1\n')
 
911
        eq(wc.get_lines('v2'), ['hello\n', 'world\n'])
 
912
        eq(wc.parent_names('v2'), ['v1', 'x1'])
 
913
        self.weave1.reweave(wb)
 
914
        self.assertEquals(wc, self.weave1)
 
915
 
 
916
 
 
917
if __name__ == '__main__':
 
918
    import sys
 
919
    import unittest
 
920
    sys.exit(unittest.main())
 
921