~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

- add short-form of testaments

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 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
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
25
 
24
26
from pprint import pformat
25
27
 
26
 
from bzrlib import (
27
 
    errors,
28
 
    )
 
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
29
32
from bzrlib.osutils import sha_string
30
 
from bzrlib.tests import TestCase, TestCaseInTempDir
31
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
32
 
from bzrlib.weavefile import write_weave, read_weave
33
33
 
34
34
 
35
35
# texts for use in testing
38
38
          "A second line"]
39
39
 
40
40
 
 
41
 
41
42
class TestBase(TestCase):
42
 
 
43
43
    def check_read_write(self, k):
44
44
        """Check the weave k can be written & re-read."""
45
45
        from tempfile import TemporaryFile
59
59
            self.log('         %r' % k._parents)
60
60
            self.log('         %r' % k2._parents)
61
61
            self.log('')
 
62
 
 
63
            
62
64
            self.fail('read/write check failed')
63
 
 
64
 
 
65
 
class WeaveContains(TestBase):
66
 
    """Weave __contains__ operator"""
67
 
    def runTest(self):
68
 
        k = Weave(get_scope=lambda:None)
69
 
        self.assertFalse('foo' in k)
70
 
        k.add_lines('foo', [], TEXT_1)
71
 
        self.assertTrue('foo' in k)
 
65
        
 
66
        
72
67
 
73
68
 
74
69
class Easy(TestBase):
76
71
        k = Weave()
77
72
 
78
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
 
 
83
 
79
84
class AnnotateOne(TestBase):
80
85
    def runTest(self):
81
86
        k = Weave()
82
 
        k.add_lines('text0', [], TEXT_0)
83
 
        self.assertEqual(k.annotate('text0'),
84
 
                         [('text0', TEXT_0[0])])
 
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
 
85
115
 
86
116
 
87
117
class InvalidAdd(TestBase):
89
119
    def runTest(self):
90
120
        k = Weave()
91
121
 
92
 
        self.assertRaises(errors.RevisionNotPresent,
93
 
                          k.add_lines,
 
122
        self.assertRaises(IndexError,
 
123
                          k.add,
94
124
                          'text0',
95
 
                          ['69'],
 
125
                          [69],
96
126
                          ['new text!'])
97
127
 
98
128
 
99
129
class RepeatedAdd(TestBase):
100
130
    """Add the same version twice; harmless."""
101
 
 
102
 
    def test_duplicate_add(self):
 
131
    def runTest(self):
103
132
        k = Weave()
104
 
        idx = k.add_lines('text0', [], TEXT_0)
105
 
        idx2 = k.add_lines('text0', [], TEXT_0)
 
133
        idx = k.add('text0', [], TEXT_0)
 
134
        idx2 = k.add('text0', [], TEXT_0)
106
135
        self.assertEqual(idx, idx2)
107
136
 
108
137
 
 
138
 
109
139
class InvalidRepeatedAdd(TestBase):
110
140
    def runTest(self):
111
141
        k = Weave()
112
 
        k.add_lines('basis', [], TEXT_0)
113
 
        idx = k.add_lines('text0', [], TEXT_0)
114
 
        self.assertRaises(errors.RevisionAlreadyPresent,
115
 
                          k.add_lines,
 
142
        idx = k.add('text0', [], TEXT_0)
 
143
        self.assertRaises(WeaveError,
 
144
                          k.add,
116
145
                          'text0',
117
146
                          [],
118
147
                          ['not the same text'])
119
 
        self.assertRaises(errors.RevisionAlreadyPresent,
120
 
                          k.add_lines,
 
148
        self.assertRaises(WeaveError,
 
149
                          k.add,
121
150
                          'text0',
122
 
                          ['basis'],         # not the right parents
 
151
                          [12],         # not the right parents
123
152
                          TEXT_0)
124
153
        
125
154
 
 
155
 
126
156
class InsertLines(TestBase):
127
157
    """Store a revision that adds one line to the original.
128
158
 
131
161
    def runTest(self):
132
162
        k = Weave()
133
163
 
134
 
        k.add_lines('text0', [], ['line 1'])
135
 
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
136
 
 
137
 
        self.assertEqual(k.annotate('text0'),
138
 
                         [('text0', 'line 1')])
139
 
 
140
 
        self.assertEqual(k.get_lines(1),
 
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),
141
171
                         ['line 1',
142
172
                          'line 2'])
143
173
 
144
 
        self.assertEqual(k.annotate('text1'),
145
 
                         [('text0', 'line 1'),
146
 
                          ('text1', 'line 2')])
147
 
 
148
 
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
149
 
 
150
 
        self.assertEqual(k.annotate('text2'),
151
 
                         [('text0', 'line 1'),
152
 
                          ('text2', 'diverged line')])
 
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')])
153
183
 
154
184
        text3 = ['line 1', 'middle line', 'line 2']
155
 
        k.add_lines('text3',
156
 
              ['text0', 'text1'],
 
185
        k.add('text3',
 
186
              [0, 1],
157
187
              text3)
158
188
 
159
189
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
160
190
 
161
191
        self.log("k._weave=" + pformat(k._weave))
162
192
 
163
 
        self.assertEqual(k.annotate('text3'),
164
 
                         [('text0', 'line 1'),
165
 
                          ('text3', 'middle line'),
166
 
                          ('text1', 'line 2')])
 
193
        self.assertEqual(k.annotate(3),
 
194
                         [(0, 'line 1'),
 
195
                          (3, 'middle line'),
 
196
                          (1, 'line 2')])
167
197
 
168
198
        # now multiple insertions at different places
169
 
        k.add_lines('text4',
170
 
              ['text0', 'text1', 'text3'],
 
199
        k.add('text4',
 
200
              [0, 1, 3],
171
201
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
172
202
 
173
 
        self.assertEqual(k.annotate('text4'), 
174
 
                         [('text0', 'line 1'),
175
 
                          ('text4', 'aaa'),
176
 
                          ('text3', 'middle line'),
177
 
                          ('text4', 'bbb'),
178
 
                          ('text1', 'line 2'),
179
 
                          ('text4', 'ccc')])
 
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
 
180
211
 
181
212
 
182
213
class DeleteLines(TestBase):
188
219
 
189
220
        base_text = ['one', 'two', 'three', 'four']
190
221
 
191
 
        k.add_lines('text0', [], base_text)
 
222
        k.add('text0', [], base_text)
192
223
        
193
224
        texts = [['one', 'two', 'three'],
194
225
                 ['two', 'three', 'four'],
198
229
 
199
230
        i = 1
200
231
        for t in texts:
201
 
            ver = k.add_lines('text%d' % i,
202
 
                        ['text0'], t)
 
232
            ver = k.add('text%d' % i,
 
233
                        [0], t)
203
234
            i += 1
204
235
 
205
236
        self.log('final weave:')
206
237
        self.log('k._weave=' + pformat(k._weave))
207
238
 
208
239
        for i in range(len(texts)):
209
 
            self.assertEqual(k.get_lines(i+1),
 
240
            self.assertEqual(k.get(i+1),
210
241
                             texts[i])
 
242
            
 
243
 
211
244
 
212
245
 
213
246
class SuicideDelete(TestBase):
229
262
        return 
230
263
 
231
264
        self.assertRaises(WeaveFormatError,
232
 
                          k.get_lines,
 
265
                          k.get,
233
266
                          0)        
234
267
 
235
268
 
 
269
 
236
270
class CannedDelete(TestBase):
237
271
    """Unpack canned weave with deleted lines."""
238
272
    def runTest(self):
249
283
                'last line',
250
284
                ('}', 0),
251
285
                ]
252
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
253
 
                  , sha_string('first linelast line')]
254
286
 
255
 
        self.assertEqual(k.get_lines(0),
 
287
        self.assertEqual(k.get(0),
256
288
                         ['first line',
257
289
                          'line to be deleted',
258
290
                          'last line',
259
291
                          ])
260
292
 
261
 
        self.assertEqual(k.get_lines(1),
 
293
        self.assertEqual(k.get(1),
262
294
                         ['first line',
263
295
                          'last line',
264
296
                          ])
265
297
 
266
298
 
 
299
 
267
300
class CannedReplacement(TestBase):
268
301
    """Unpack canned weave with deleted lines."""
269
302
    def runTest(self):
283
316
                'last line',
284
317
                ('}', 0),
285
318
                ]
286
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
287
 
                  , sha_string('first linereplacement linelast line')]
288
319
 
289
 
        self.assertEqual(k.get_lines(0),
 
320
        self.assertEqual(k.get(0),
290
321
                         ['first line',
291
322
                          'line to be deleted',
292
323
                          'last line',
293
324
                          ])
294
325
 
295
 
        self.assertEqual(k.get_lines(1),
 
326
        self.assertEqual(k.get(1),
296
327
                         ['first line',
297
328
                          'replacement line',
298
329
                          'last line',
299
330
                          ])
300
331
 
301
332
 
 
333
 
302
334
class BadWeave(TestBase):
303
335
    """Test that we trap an insert which should not occur."""
304
336
    def runTest(self):
384
416
                '}',
385
417
                ('}', 0)]
386
418
 
387
 
        k._sha1s = [sha_string('foo {}')
388
 
                  , sha_string('foo {  added in version 1  also from v1}')
389
 
                  , sha_string('foo {  added in v2}')
390
 
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
391
 
                  ]
392
 
 
393
 
        self.assertEqual(k.get_lines(0),
 
419
        self.assertEqual(k.get(0),
394
420
                         ['foo {',
395
421
                          '}'])
396
422
 
397
 
        self.assertEqual(k.get_lines(1),
 
423
        self.assertEqual(k.get(1),
398
424
                         ['foo {',
399
425
                          '  added in version 1',
400
426
                          '  also from v1',
401
427
                          '}'])
402
428
                       
403
 
        self.assertEqual(k.get_lines(2),
 
429
        self.assertEqual(k.get(2),
404
430
                         ['foo {',
405
431
                          '  added in v2',
406
432
                          '}'])
407
433
 
408
 
        self.assertEqual(k.get_lines(3),
 
434
        self.assertEqual(k.get(3),
409
435
                         ['foo {',
410
436
                          '  added in version 1',
411
437
                          '  added in v2',
413
439
                          '}'])
414
440
                         
415
441
 
 
442
 
416
443
class DeleteLines2(TestBase):
417
444
    """Test recording revisions that delete lines.
418
445
 
421
448
    def runTest(self):
422
449
        k = Weave()
423
450
 
424
 
        k.add_lines('text0', [], ["line the first",
 
451
        k.add('text0', [], ["line the first",
425
452
                   "line 2",
426
453
                   "line 3",
427
454
                   "fine"])
428
455
 
429
 
        self.assertEqual(len(k.get_lines(0)), 4)
 
456
        self.assertEqual(len(k.get(0)), 4)
430
457
 
431
 
        k.add_lines('text1', ['text0'], ["line the first",
 
458
        k.add('text1', [0], ["line the first",
432
459
                   "fine"])
433
460
 
434
 
        self.assertEqual(k.get_lines(1),
 
461
        self.assertEqual(k.get(1),
435
462
                         ["line the first",
436
463
                          "fine"])
437
464
 
438
 
        self.assertEqual(k.annotate('text1'),
439
 
                         [('text0', "line the first"),
440
 
                          ('text0', "fine")])
 
465
        self.assertEqual(k.annotate(1),
 
466
                         [(0, "line the first"),
 
467
                          (0, "fine")])
 
468
 
441
469
 
442
470
 
443
471
class IncludeVersions(TestBase):
461
489
                "second line",
462
490
                ('}', 1)]
463
491
 
464
 
        k._sha1s = [sha_string('first line')
465
 
                  , sha_string('first linesecond line')]
466
 
 
467
 
        self.assertEqual(k.get_lines(1),
 
492
        self.assertEqual(k.get(1),
468
493
                         ["first line",
469
494
                          "second line"])
470
495
 
471
 
        self.assertEqual(k.get_lines(0),
 
496
        self.assertEqual(k.get(0),
472
497
                         ["first line"])
473
498
 
474
499
 
476
501
    """Weave with two diverged texts based on version 0.
477
502
    """
478
503
    def runTest(self):
479
 
        # FIXME make the weave, dont poke at it.
480
504
        k = Weave()
481
505
 
482
 
        k._names = ['0', '1', '2']
483
 
        k._name_map = {'0':0, '1':1, '2':2}
484
506
        k._parents = [frozenset(),
485
507
                frozenset([0]),
486
508
                frozenset([0]),
496
518
                ('}', 2),                
497
519
                ]
498
520
 
499
 
        k._sha1s = [sha_string('first line')
500
 
                  , sha_string('first linesecond line')
501
 
                  , sha_string('first linealternative second line')]
502
 
 
503
 
        self.assertEqual(k.get_lines(0),
 
521
        self.assertEqual(k.get(0),
504
522
                         ["first line"])
505
523
 
506
 
        self.assertEqual(k.get_lines(1),
 
524
        self.assertEqual(k.get(1),
507
525
                         ["first line",
508
526
                          "second line"])
509
527
 
510
 
        self.assertEqual(k.get_lines('2'),
 
528
        self.assertEqual(k.get(2),
511
529
                         ["first line",
512
530
                          "alternative second line"])
513
531
 
514
 
        self.assertEqual(list(k.get_ancestry(['2'])),
515
 
                         ['0', '2'])
 
532
        self.assertEqual(list(k.inclusions([2])),
 
533
                         [0, 2])
 
534
 
516
535
 
517
536
 
518
537
class ReplaceLine(TestBase):
522
541
        text0 = ['cheddar', 'stilton', 'gruyere']
523
542
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
524
543
        
525
 
        k.add_lines('text0', [], text0)
526
 
        k.add_lines('text1', ['text0'], text1)
 
544
        k.add('text0', [], text0)
 
545
        k.add('text1', [0], text1)
527
546
 
528
547
        self.log('k._weave=' + pformat(k._weave))
529
548
 
530
 
        self.assertEqual(k.get_lines(0), text0)
531
 
        self.assertEqual(k.get_lines(1), text1)
 
549
        self.assertEqual(k.get(0), text0)
 
550
        self.assertEqual(k.get(1), text1)
 
551
 
532
552
 
533
553
 
534
554
class Merge(TestBase):
542
562
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
543
563
                 ]
544
564
 
545
 
        k.add_lines('text0', [], texts[0])
546
 
        k.add_lines('text1', ['text0'], texts[1])
547
 
        k.add_lines('text2', ['text0'], texts[2])
548
 
        k.add_lines('merge', ['text0', 'text1', '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])
549
569
 
550
570
        for i, t in enumerate(texts):
551
 
            self.assertEqual(k.get_lines(i), t)
 
571
            self.assertEqual(k.get(i), t)
552
572
 
553
 
        self.assertEqual(k.annotate('merge'),
554
 
                         [('text0', 'header'),
555
 
                          ('text1', ''),
556
 
                          ('text1', 'line from 1'),
557
 
                          ('merge', 'fixup line'),
558
 
                          ('text2', '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'),
559
579
                          ])
560
580
 
561
 
        self.assertEqual(list(k.get_ancestry(['merge'])),
562
 
                         ['text0', 'text1', 'text2', 'merge'])
 
581
        self.assertEqual(list(k.inclusions([3])),
 
582
                         [0, 1, 2, 3])
563
583
 
564
584
        self.log('k._weave=' + pformat(k._weave))
565
585
 
576
596
        return  # NOT RUN
577
597
        k = Weave()
578
598
 
579
 
        k.add_lines([], ['aaa', 'bbb'])
580
 
        k.add_lines([0], ['aaa', '111', 'bbb'])
581
 
        k.add_lines([1], ['aaa', '222', 'bbb'])
 
599
        k.add([], ['aaa', 'bbb'])
 
600
        k.add([0], ['aaa', '111', 'bbb'])
 
601
        k.add([1], ['aaa', '222', 'bbb'])
582
602
 
583
603
        merged = k.merge([1, 2])
584
604
 
587
607
                           [['bbb']]])
588
608
 
589
609
 
 
610
 
590
611
class NonConflict(TestBase):
591
612
    """Two descendants insert compatible changes.
592
613
 
595
616
        return  # NOT RUN
596
617
        k = Weave()
597
618
 
598
 
        k.add_lines([], ['aaa', 'bbb'])
599
 
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
600
 
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '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
        
601
650
 
602
651
 
603
652
class Khayyam(TestBase):
604
653
    """Test changes to multi-line texts, and read/write"""
605
 
 
606
 
    def test_multi_line_merge(self):
 
654
    def runTest(self):
607
655
        rawtexts = [
608
656
            """A Book of Verses underneath the Bough,
609
657
            A Jug of Wine, a Loaf of Bread, -- and Thou
635
683
        parents = set()
636
684
        i = 0
637
685
        for t in texts:
638
 
            ver = k.add_lines('text%d' % i,
 
686
            ver = k.add('text%d' % i,
639
687
                        list(parents), t)
640
 
            parents.add('text%d' % i)
 
688
            parents.add(ver)
641
689
            i += 1
642
690
 
643
691
        self.log("k._weave=" + pformat(k._weave))
644
692
 
645
693
        for i, t in enumerate(texts):
646
 
            self.assertEqual(k.get_lines(i), t)
 
694
            self.assertEqual(k.get(i), t)
647
695
 
648
696
        self.check_read_write(k)
649
697
 
650
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
 
651
797
class JoinWeavesTests(TestBase):
652
798
    def setUp(self):
653
799
        super(JoinWeavesTests, self).setUp()
654
800
        self.weave1 = Weave()
655
801
        self.lines1 = ['hello\n']
656
802
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
657
 
        self.weave1.add_lines('v1', [], self.lines1)
658
 
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
659
 
        self.weave1.add_lines('v3', ['v2'], self.lines3)
660
 
 
661
 
    def test_written_detection(self):
662
 
        # Test detection of weave file corruption.
663
 
        #
664
 
        # Make sure that we can detect if a weave file has
665
 
        # been corrupted. This doesn't test all forms of corruption,
666
 
        # but it at least helps verify the data you get, is what you want.
667
 
        from cStringIO import StringIO
668
 
 
669
 
        w = Weave()
670
 
        w.add_lines('v1', [], ['hello\n'])
671
 
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
672
 
 
673
 
        tmpf = StringIO()
674
 
        write_weave(w, tmpf)
675
 
 
676
 
        # Because we are corrupting, we need to make sure we have the exact text
677
 
        self.assertEquals('# bzr weave file v5\n'
678
 
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
679
 
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
680
 
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
681
 
                          tmpf.getvalue())
682
 
 
683
 
        # Change a single letter
684
 
        tmpf = StringIO('# bzr weave file v5\n'
685
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
686
 
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
687
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
688
 
 
689
 
        w = read_weave(tmpf)
690
 
 
691
 
        self.assertEqual('hello\n', w.get_text('v1'))
692
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
693
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
694
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
695
 
 
696
 
        # Change the sha checksum
697
 
        tmpf = StringIO('# bzr weave file v5\n'
698
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
699
 
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
700
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
701
 
 
702
 
        w = read_weave(tmpf)
703
 
 
704
 
        self.assertEqual('hello\n', w.get_text('v1'))
705
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
706
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
707
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
708
 
 
709
 
 
710
 
class TestWeave(TestCase):
711
 
 
712
 
    def test_allow_reserved_false(self):
713
 
        w = Weave('name', allow_reserved=False)
714
 
        # Add lines is checked at the WeaveFile level, not at the Weave level
715
 
        w.add_lines('name:', [], TEXT_1)
716
 
        # But get_lines is checked at this level
717
 
        self.assertRaises(errors.ReservedId, w.get_lines, 'name:')
718
 
 
719
 
    def test_allow_reserved_true(self):
720
 
        w = Weave('name', allow_reserved=True)
721
 
        w.add_lines('name:', [], TEXT_1)
722
 
        self.assertEqual(TEXT_1, w.get_lines('name:'))
723
 
 
724
 
 
725
 
class InstrumentedWeave(Weave):
726
 
    """Keep track of how many times functions are called."""
727
 
    
728
 
    def __init__(self, weave_name=None):
729
 
        self._extract_count = 0
730
 
        Weave.__init__(self, weave_name=weave_name)
731
 
 
732
 
    def _extract(self, versions):
733
 
        self._extract_count += 1
734
 
        return Weave._extract(self, versions)
735
 
 
736
 
 
737
 
class TestNeedsReweave(TestCase):
738
 
    """Internal corner cases for when reweave is needed."""
739
 
 
740
 
    def test_compatible_parents(self):
741
 
        w1 = Weave('a')
742
 
        my_parents = set([1, 2, 3])
743
 
        # subsets are ok
744
 
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
745
 
        # same sets
746
 
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
747
 
        # same empty corner case
748
 
        self.assertTrue(w1._compatible_parents(set(), set()))
749
 
        # other cannot contain stuff my_parents does not
750
 
        self.assertFalse(w1._compatible_parents(set(), set([1])))
751
 
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
752
 
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
753
 
 
754
 
 
755
 
class TestWeaveFile(TestCaseInTempDir):
756
 
    
757
 
    def test_empty_file(self):
758
 
        f = open('empty.weave', 'wb+')
759
 
        try:
760
 
            self.assertRaises(errors.WeaveFormatError,
761
 
                              read_weave, f)
762
 
        finally:
763
 
            f.close()
 
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