~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

- refactor handling of short option names

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#! /usr/bin/python2.4
2
2
 
3
3
# Copyright (C) 2005 by Canonical Ltd
4
 
#
 
4
 
5
5
# This program is free software; you can redistribute it and/or modify
6
6
# it under the terms of the GNU General Public License as published by
7
7
# the Free Software Foundation; either version 2 of the License, or
8
8
# (at your option) any later version.
9
 
#
 
9
 
10
10
# This program is distributed in the hope that it will be useful,
11
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
13
# GNU General Public License for more details.
14
 
#
 
14
 
15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
28
import bzrlib.errors as errors
29
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
30
30
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.tests import TestCase
 
31
from bzrlib.selftest import TestCase
32
32
from bzrlib.osutils import sha_string
33
33
 
34
34
 
38
38
          "A second line"]
39
39
 
40
40
 
 
41
 
41
42
class TestBase(TestCase):
42
43
    def check_read_write(self, k):
43
44
        """Check the weave k can be written & re-read."""
58
59
            self.log('         %r' % k._parents)
59
60
            self.log('         %r' % k2._parents)
60
61
            self.log('')
 
62
 
 
63
            
61
64
            self.fail('read/write check failed')
62
 
 
63
 
 
64
 
class WeaveContains(TestBase):
65
 
    """Weave __contains__ operator"""
66
 
    def runTest(self):
67
 
        k = Weave()
68
 
        self.assertFalse('foo' in k)
69
 
        k.add_lines('foo', [], TEXT_1)
70
 
        self.assertTrue('foo' in k)
 
65
        
 
66
        
71
67
 
72
68
 
73
69
class Easy(TestBase):
77
73
 
78
74
class StoreText(TestBase):
79
75
    """Store and retrieve a simple text."""
80
 
 
81
 
    def test_storing_text(self):
 
76
    def runTest(self):
82
77
        k = Weave()
83
 
        idx = k.add_lines('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get_lines(idx), TEXT_0)
 
78
        idx = k.add('text0', [], TEXT_0)
 
79
        self.assertEqual(k.get(idx), TEXT_0)
85
80
        self.assertEqual(idx, 0)
86
81
 
87
82
 
 
83
 
88
84
class AnnotateOne(TestBase):
89
85
    def runTest(self):
90
86
        k = Weave()
91
 
        k.add_lines('text0', [], TEXT_0)
92
 
        self.assertEqual(k.annotate('text0'),
93
 
                         [('text0', TEXT_0[0])])
 
87
        k.add('text0', [], TEXT_0)
 
88
        self.assertEqual(k.annotate(0),
 
89
                         [(0, TEXT_0[0])])
94
90
 
95
91
 
96
92
class StoreTwo(TestBase):
97
93
    def runTest(self):
98
94
        k = Weave()
99
95
 
100
 
        idx = k.add_lines('text0', [], TEXT_0)
 
96
        idx = k.add('text0', [], TEXT_0)
101
97
        self.assertEqual(idx, 0)
102
98
 
103
 
        idx = k.add_lines('text1', [], TEXT_1)
 
99
        idx = k.add('text1', [], TEXT_1)
104
100
        self.assertEqual(idx, 1)
105
101
 
106
 
        self.assertEqual(k.get_lines(0), TEXT_0)
107
 
        self.assertEqual(k.get_lines(1), TEXT_1)
108
 
 
109
 
 
110
 
class GetSha1(TestBase):
111
 
    def test_get_sha1(self):
 
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"""
112
110
        k = Weave()
113
 
        k.add_lines('text0', [], 'text0')
114
 
        self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
115
 
                         k.get_sha1('text0'))
116
 
        self.assertRaises(errors.RevisionNotPresent,
117
 
                          k.get_sha1, 0)
118
 
        self.assertRaises(errors.RevisionNotPresent,
119
 
                          k.get_sha1, 'text1')
120
 
                        
 
111
 
 
112
        t = 'text0'
 
113
        k.add('text0', [], [t], sha1=sha_string(t))
 
114
 
 
115
 
121
116
 
122
117
class InvalidAdd(TestBase):
123
118
    """Try to use invalid version number during add."""
124
119
    def runTest(self):
125
120
        k = Weave()
126
121
 
127
 
        self.assertRaises(errors.RevisionNotPresent,
128
 
                          k.add_lines,
 
122
        self.assertRaises(IndexError,
 
123
                          k.add,
129
124
                          'text0',
130
 
                          ['69'],
 
125
                          [69],
131
126
                          ['new text!'])
132
127
 
133
128
 
135
130
    """Add the same version twice; harmless."""
136
131
    def runTest(self):
137
132
        k = Weave()
138
 
        idx = k.add_lines('text0', [], TEXT_0)
139
 
        idx2 = k.add_lines('text0', [], TEXT_0)
 
133
        idx = k.add('text0', [], TEXT_0)
 
134
        idx2 = k.add('text0', [], TEXT_0)
140
135
        self.assertEqual(idx, idx2)
141
136
 
142
137
 
 
138
 
143
139
class InvalidRepeatedAdd(TestBase):
144
140
    def runTest(self):
145
141
        k = Weave()
146
 
        k.add_lines('basis', [], TEXT_0)
147
 
        idx = k.add_lines('text0', [], TEXT_0)
148
 
        self.assertRaises(errors.RevisionAlreadyPresent,
149
 
                          k.add_lines,
 
142
        idx = k.add('text0', [], TEXT_0)
 
143
        self.assertRaises(WeaveError,
 
144
                          k.add,
150
145
                          'text0',
151
146
                          [],
152
147
                          ['not the same text'])
153
 
        self.assertRaises(errors.RevisionAlreadyPresent,
154
 
                          k.add_lines,
 
148
        self.assertRaises(WeaveError,
 
149
                          k.add,
155
150
                          'text0',
156
 
                          ['basis'],         # not the right parents
 
151
                          [12],         # not the right parents
157
152
                          TEXT_0)
158
153
        
159
154
 
 
155
 
160
156
class InsertLines(TestBase):
161
157
    """Store a revision that adds one line to the original.
162
158
 
165
161
    def runTest(self):
166
162
        k = Weave()
167
163
 
168
 
        k.add_lines('text0', [], ['line 1'])
169
 
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
170
 
 
171
 
        self.assertEqual(k.annotate('text0'),
172
 
                         [('text0', 'line 1')])
173
 
 
174
 
        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),
175
171
                         ['line 1',
176
172
                          'line 2'])
177
173
 
178
 
        self.assertEqual(k.annotate('text1'),
179
 
                         [('text0', 'line 1'),
180
 
                          ('text1', 'line 2')])
181
 
 
182
 
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
183
 
 
184
 
        self.assertEqual(k.annotate('text2'),
185
 
                         [('text0', 'line 1'),
186
 
                          ('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')])
187
183
 
188
184
        text3 = ['line 1', 'middle line', 'line 2']
189
 
        k.add_lines('text3',
190
 
              ['text0', 'text1'],
 
185
        k.add('text3',
 
186
              [0, 1],
191
187
              text3)
192
188
 
193
189
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
194
190
 
195
191
        self.log("k._weave=" + pformat(k._weave))
196
192
 
197
 
        self.assertEqual(k.annotate('text3'),
198
 
                         [('text0', 'line 1'),
199
 
                          ('text3', 'middle line'),
200
 
                          ('text1', 'line 2')])
 
193
        self.assertEqual(k.annotate(3),
 
194
                         [(0, 'line 1'),
 
195
                          (3, 'middle line'),
 
196
                          (1, 'line 2')])
201
197
 
202
198
        # now multiple insertions at different places
203
 
        k.add_lines('text4',
204
 
              ['text0', 'text1', 'text3'],
 
199
        k.add('text4',
 
200
              [0, 1, 3],
205
201
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
206
202
 
207
 
        self.assertEqual(k.annotate('text4'), 
208
 
                         [('text0', 'line 1'),
209
 
                          ('text4', 'aaa'),
210
 
                          ('text3', 'middle line'),
211
 
                          ('text4', 'bbb'),
212
 
                          ('text1', 'line 2'),
213
 
                          ('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
 
214
211
 
215
212
 
216
213
class DeleteLines(TestBase):
222
219
 
223
220
        base_text = ['one', 'two', 'three', 'four']
224
221
 
225
 
        k.add_lines('text0', [], base_text)
 
222
        k.add('text0', [], base_text)
226
223
        
227
224
        texts = [['one', 'two', 'three'],
228
225
                 ['two', 'three', 'four'],
232
229
 
233
230
        i = 1
234
231
        for t in texts:
235
 
            ver = k.add_lines('text%d' % i,
236
 
                        ['text0'], t)
 
232
            ver = k.add('text%d' % i,
 
233
                        [0], t)
237
234
            i += 1
238
235
 
239
236
        self.log('final weave:')
240
237
        self.log('k._weave=' + pformat(k._weave))
241
238
 
242
239
        for i in range(len(texts)):
243
 
            self.assertEqual(k.get_lines(i+1),
 
240
            self.assertEqual(k.get(i+1),
244
241
                             texts[i])
 
242
            
 
243
 
245
244
 
246
245
 
247
246
class SuicideDelete(TestBase):
263
262
        return 
264
263
 
265
264
        self.assertRaises(WeaveFormatError,
266
 
                          k.get_lines,
 
265
                          k.get,
267
266
                          0)        
268
267
 
269
268
 
 
269
 
270
270
class CannedDelete(TestBase):
271
271
    """Unpack canned weave with deleted lines."""
272
272
    def runTest(self):
283
283
                'last line',
284
284
                ('}', 0),
285
285
                ]
286
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
287
 
                  , sha_string('first linelast line')]
288
286
 
289
 
        self.assertEqual(k.get_lines(0),
 
287
        self.assertEqual(k.get(0),
290
288
                         ['first line',
291
289
                          'line to be deleted',
292
290
                          'last line',
293
291
                          ])
294
292
 
295
 
        self.assertEqual(k.get_lines(1),
 
293
        self.assertEqual(k.get(1),
296
294
                         ['first line',
297
295
                          'last line',
298
296
                          ])
299
297
 
300
298
 
 
299
 
301
300
class CannedReplacement(TestBase):
302
301
    """Unpack canned weave with deleted lines."""
303
302
    def runTest(self):
317
316
                'last line',
318
317
                ('}', 0),
319
318
                ]
320
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
321
 
                  , sha_string('first linereplacement linelast line')]
322
319
 
323
 
        self.assertEqual(k.get_lines(0),
 
320
        self.assertEqual(k.get(0),
324
321
                         ['first line',
325
322
                          'line to be deleted',
326
323
                          'last line',
327
324
                          ])
328
325
 
329
 
        self.assertEqual(k.get_lines(1),
 
326
        self.assertEqual(k.get(1),
330
327
                         ['first line',
331
328
                          'replacement line',
332
329
                          'last line',
333
330
                          ])
334
331
 
335
332
 
 
333
 
336
334
class BadWeave(TestBase):
337
335
    """Test that we trap an insert which should not occur."""
338
336
    def runTest(self):
418
416
                '}',
419
417
                ('}', 0)]
420
418
 
421
 
        k._sha1s = [sha_string('foo {}')
422
 
                  , sha_string('foo {  added in version 1  also from v1}')
423
 
                  , sha_string('foo {  added in v2}')
424
 
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
425
 
                  ]
426
 
 
427
 
        self.assertEqual(k.get_lines(0),
 
419
        self.assertEqual(k.get(0),
428
420
                         ['foo {',
429
421
                          '}'])
430
422
 
431
 
        self.assertEqual(k.get_lines(1),
 
423
        self.assertEqual(k.get(1),
432
424
                         ['foo {',
433
425
                          '  added in version 1',
434
426
                          '  also from v1',
435
427
                          '}'])
436
428
                       
437
 
        self.assertEqual(k.get_lines(2),
 
429
        self.assertEqual(k.get(2),
438
430
                         ['foo {',
439
431
                          '  added in v2',
440
432
                          '}'])
441
433
 
442
 
        self.assertEqual(k.get_lines(3),
 
434
        self.assertEqual(k.get(3),
443
435
                         ['foo {',
444
436
                          '  added in version 1',
445
437
                          '  added in v2',
447
439
                          '}'])
448
440
                         
449
441
 
 
442
 
450
443
class DeleteLines2(TestBase):
451
444
    """Test recording revisions that delete lines.
452
445
 
455
448
    def runTest(self):
456
449
        k = Weave()
457
450
 
458
 
        k.add_lines('text0', [], ["line the first",
 
451
        k.add('text0', [], ["line the first",
459
452
                   "line 2",
460
453
                   "line 3",
461
454
                   "fine"])
462
455
 
463
 
        self.assertEqual(len(k.get_lines(0)), 4)
 
456
        self.assertEqual(len(k.get(0)), 4)
464
457
 
465
 
        k.add_lines('text1', ['text0'], ["line the first",
 
458
        k.add('text1', [0], ["line the first",
466
459
                   "fine"])
467
460
 
468
 
        self.assertEqual(k.get_lines(1),
 
461
        self.assertEqual(k.get(1),
469
462
                         ["line the first",
470
463
                          "fine"])
471
464
 
472
 
        self.assertEqual(k.annotate('text1'),
473
 
                         [('text0', "line the first"),
474
 
                          ('text0', "fine")])
 
465
        self.assertEqual(k.annotate(1),
 
466
                         [(0, "line the first"),
 
467
                          (0, "fine")])
 
468
 
475
469
 
476
470
 
477
471
class IncludeVersions(TestBase):
495
489
                "second line",
496
490
                ('}', 1)]
497
491
 
498
 
        k._sha1s = [sha_string('first line')
499
 
                  , sha_string('first linesecond line')]
500
 
 
501
 
        self.assertEqual(k.get_lines(1),
 
492
        self.assertEqual(k.get(1),
502
493
                         ["first line",
503
494
                          "second line"])
504
495
 
505
 
        self.assertEqual(k.get_lines(0),
 
496
        self.assertEqual(k.get(0),
506
497
                         ["first line"])
507
498
 
508
499
 
510
501
    """Weave with two diverged texts based on version 0.
511
502
    """
512
503
    def runTest(self):
513
 
        # FIXME make the weave, dont poke at it.
514
504
        k = Weave()
515
505
 
516
 
        k._names = ['0', '1', '2']
517
 
        k._name_map = {'0':0, '1':1, '2':2}
518
506
        k._parents = [frozenset(),
519
507
                frozenset([0]),
520
508
                frozenset([0]),
530
518
                ('}', 2),                
531
519
                ]
532
520
 
533
 
        k._sha1s = [sha_string('first line')
534
 
                  , sha_string('first linesecond line')
535
 
                  , sha_string('first linealternative second line')]
536
 
 
537
 
        self.assertEqual(k.get_lines(0),
 
521
        self.assertEqual(k.get(0),
538
522
                         ["first line"])
539
523
 
540
 
        self.assertEqual(k.get_lines(1),
 
524
        self.assertEqual(k.get(1),
541
525
                         ["first line",
542
526
                          "second line"])
543
527
 
544
 
        self.assertEqual(k.get_lines('2'),
 
528
        self.assertEqual(k.get(2),
545
529
                         ["first line",
546
530
                          "alternative second line"])
547
531
 
548
 
        self.assertEqual(list(k.get_ancestry(['2'])),
549
 
                         ['0', '2'])
 
532
        self.assertEqual(list(k.inclusions([2])),
 
533
                         [0, 2])
 
534
 
550
535
 
551
536
 
552
537
class ReplaceLine(TestBase):
556
541
        text0 = ['cheddar', 'stilton', 'gruyere']
557
542
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
558
543
        
559
 
        k.add_lines('text0', [], text0)
560
 
        k.add_lines('text1', ['text0'], text1)
 
544
        k.add('text0', [], text0)
 
545
        k.add('text1', [0], text1)
561
546
 
562
547
        self.log('k._weave=' + pformat(k._weave))
563
548
 
564
 
        self.assertEqual(k.get_lines(0), text0)
565
 
        self.assertEqual(k.get_lines(1), text1)
 
549
        self.assertEqual(k.get(0), text0)
 
550
        self.assertEqual(k.get(1), text1)
 
551
 
566
552
 
567
553
 
568
554
class Merge(TestBase):
576
562
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
577
563
                 ]
578
564
 
579
 
        k.add_lines('text0', [], texts[0])
580
 
        k.add_lines('text1', ['text0'], texts[1])
581
 
        k.add_lines('text2', ['text0'], texts[2])
582
 
        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])
583
569
 
584
570
        for i, t in enumerate(texts):
585
 
            self.assertEqual(k.get_lines(i), t)
 
571
            self.assertEqual(k.get(i), t)
586
572
 
587
 
        self.assertEqual(k.annotate('merge'),
588
 
                         [('text0', 'header'),
589
 
                          ('text1', ''),
590
 
                          ('text1', 'line from 1'),
591
 
                          ('merge', 'fixup line'),
592
 
                          ('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'),
593
579
                          ])
594
580
 
595
 
        self.assertEqual(list(k.get_ancestry(['merge'])),
596
 
                         ['text0', 'text1', 'text2', 'merge'])
 
581
        self.assertEqual(list(k.inclusions([3])),
 
582
                         [0, 1, 2, 3])
597
583
 
598
584
        self.log('k._weave=' + pformat(k._weave))
599
585
 
610
596
        return  # NOT RUN
611
597
        k = Weave()
612
598
 
613
 
        k.add_lines([], ['aaa', 'bbb'])
614
 
        k.add_lines([0], ['aaa', '111', 'bbb'])
615
 
        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'])
616
602
 
617
603
        merged = k.merge([1, 2])
618
604
 
621
607
                           [['bbb']]])
622
608
 
623
609
 
 
610
 
624
611
class NonConflict(TestBase):
625
612
    """Two descendants insert compatible changes.
626
613
 
629
616
        return  # NOT RUN
630
617
        k = Weave()
631
618
 
632
 
        k.add_lines([], ['aaa', 'bbb'])
633
 
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
634
 
        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
        
635
650
 
636
651
 
637
652
class Khayyam(TestBase):
638
653
    """Test changes to multi-line texts, and read/write"""
639
 
 
640
 
    def test_multi_line_merge(self):
 
654
    def runTest(self):
641
655
        rawtexts = [
642
656
            """A Book of Verses underneath the Bough,
643
657
            A Jug of Wine, a Loaf of Bread, -- and Thou
669
683
        parents = set()
670
684
        i = 0
671
685
        for t in texts:
672
 
            ver = k.add_lines('text%d' % i,
 
686
            ver = k.add('text%d' % i,
673
687
                        list(parents), t)
674
 
            parents.add('text%d' % i)
 
688
            parents.add(ver)
675
689
            i += 1
676
690
 
677
691
        self.log("k._weave=" + pformat(k._weave))
678
692
 
679
693
        for i, t in enumerate(texts):
680
 
            self.assertEqual(k.get_lines(i), t)
 
694
            self.assertEqual(k.get(i), t)
681
695
 
682
696
        self.check_read_write(k)
683
697
 
684
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
 
685
797
class JoinWeavesTests(TestBase):
686
798
    def setUp(self):
687
799
        super(JoinWeavesTests, self).setUp()
688
800
        self.weave1 = Weave()
689
801
        self.lines1 = ['hello\n']
690
802
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
691
 
        self.weave1.add_lines('v1', [], self.lines1)
692
 
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
693
 
        self.weave1.add_lines('v3', ['v2'], self.lines3)
 
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)
694
806
        
695
807
    def test_join_empty(self):
696
808
        """Join two empty weaves."""
698
810
        w1 = Weave()
699
811
        w2 = Weave()
700
812
        w1.join(w2)
701
 
        eq(len(w1), 0)
 
813
        eq(w1.numversions(), 0)
702
814
        
703
815
    def test_join_empty_to_nonempty(self):
704
816
        """Join empty weave onto nonempty."""
708
820
    def test_join_unrelated(self):
709
821
        """Join two weaves with no history in common."""
710
822
        wb = Weave()
711
 
        wb.add_lines('b1', [], ['line from b\n'])
 
823
        wb.add('b1', [], ['line from b\n'])
712
824
        w1 = self.weave1
713
825
        w1.join(wb)
714
826
        eq = self.assertEqual
715
827
        eq(len(w1), 4)
716
 
        eq(sorted(w1.versions()),
 
828
        eq(sorted(list(w1.iter_names())),
717
829
           ['b1', 'v1', 'v2', 'v3'])
718
830
 
719
831
    def test_join_related(self):
720
832
        wa = self.weave1.copy()
721
833
        wb = self.weave1.copy()
722
 
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
723
 
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
834
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
835
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
724
836
        eq = self.assertEquals
725
837
        eq(len(wa), 4)
726
838
        eq(len(wb), 4)
730
842
           ['hello\n', 'pale blue\n', 'world\n'])
731
843
 
732
844
    def test_join_parent_disagreement(self):
733
 
        #join reconciles differening parents into a union.
 
845
        """Cannot join weaves with different parents for a version."""
734
846
        wa = Weave()
735
847
        wb = Weave()
736
 
        wa.add_lines('v1', [], ['hello\n'])
737
 
        wb.add_lines('v0', [], [])
738
 
        wb.add_lines('v1', ['v0'], ['hello\n'])
739
 
        wa.join(wb)
740
 
        self.assertEqual(['v0'], wa.get_parents('v1'))
 
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)
741
853
 
742
854
    def test_join_text_disagreement(self):
743
855
        """Cannot join weaves with different texts for a version."""
744
856
        wa = Weave()
745
857
        wb = Weave()
746
 
        wa.add_lines('v1', [], ['hello\n'])
747
 
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
 
858
        wa.add('v1', [], ['hello\n'])
 
859
        wb.add('v1', [], ['not\n', 'hello\n'])
748
860
        self.assertRaises(WeaveError,
749
861
                          wa.join, wb)
750
862
 
754
866
        The source weave contains a different version at index 0."""
755
867
        wa = self.weave1.copy()
756
868
        wb = Weave()
757
 
        wb.add_lines('x1', [], ['line from x1\n'])
758
 
        wb.add_lines('v1', [], ['hello\n'])
759
 
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
869
        wb.add('x1', [], ['line from x1\n'])
 
870
        wb.add('v1', [], ['hello\n'])
 
871
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
760
872
        wa.join(wb)
761
873
        eq = self.assertEquals
762
 
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
 
874
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
763
875
        eq(wa.get_text('x1'), 'line from x1\n')
764
876
 
765
 
    def test_written_detection(self):
766
 
        # Test detection of weave file corruption.
767
 
        #
768
 
        # Make sure that we can detect if a weave file has
769
 
        # been corrupted. This doesn't test all forms of corruption,
770
 
        # but it at least helps verify the data you get, is what you want.
771
 
        from cStringIO import StringIO
772
 
 
773
 
        w = Weave()
774
 
        w.add_lines('v1', [], ['hello\n'])
775
 
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
776
 
 
777
 
        tmpf = StringIO()
778
 
        write_weave(w, tmpf)
779
 
 
780
 
        # Because we are corrupting, we need to make sure we have the exact text
781
 
        self.assertEquals('# bzr weave file v5\n'
782
 
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
783
 
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
784
 
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
785
 
                          tmpf.getvalue())
786
 
 
787
 
        # Change a single letter
788
 
        tmpf = StringIO('# bzr weave file v5\n'
789
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
790
 
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
791
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
792
 
 
793
 
        w = read_weave(tmpf)
794
 
 
795
 
        self.assertEqual('hello\n', w.get_text('v1'))
796
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
797
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
798
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
799
 
 
800
 
        # Change the sha checksum
801
 
        tmpf = StringIO('# bzr weave file v5\n'
802
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
803
 
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
804
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
805
 
 
806
 
        w = read_weave(tmpf)
807
 
 
808
 
        self.assertEqual('hello\n', w.get_text('v1'))
809
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
810
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
811
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
812
 
 
813
 
 
814
 
class InstrumentedWeave(Weave):
815
 
    """Keep track of how many times functions are called."""
 
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())
816
921
    
817
 
    def __init__(self, weave_name=None):
818
 
        self._extract_count = 0
819
 
        Weave.__init__(self, weave_name=weave_name)
820
 
 
821
 
    def _extract(self, versions):
822
 
        self._extract_count += 1
823
 
        return Weave._extract(self, versions)
824
 
 
825
 
 
826
 
class JoinOptimization(TestCase):
827
 
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
828
 
 
829
 
    def test_join(self):
830
 
        w1 = InstrumentedWeave()
831
 
        w2 = InstrumentedWeave()
832
 
 
833
 
        txt0 = ['a\n']
834
 
        txt1 = ['a\n', 'b\n']
835
 
        txt2 = ['a\n', 'c\n']
836
 
        txt3 = ['a\n', 'b\n', 'c\n']
837
 
 
838
 
        w1.add_lines('txt0', [], txt0) # extract 1a
839
 
        w2.add_lines('txt0', [], txt0) # extract 1b
840
 
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
841
 
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
842
 
        w1.join(w2) # extract 3a to add txt2 
843
 
        w2.join(w1) # extract 3b to add txt1 
844
 
 
845
 
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
846
 
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
847
 
        # These secretly have inverted parents
848
 
 
849
 
        # This should not have to do any extractions
850
 
        w1.join(w2) # NO extract, texts already present with same parents
851
 
        w2.join(w1) # NO extract, texts already present with same parents
852
 
 
853
 
        self.assertEqual(4, w1._extract_count)
854
 
        self.assertEqual(4, w2._extract_count)
855
 
 
856
 
    def test_double_parent(self):
857
 
        # It should not be considered illegal to add
858
 
        # a revision with the same parent twice
859
 
        w1 = InstrumentedWeave()
860
 
        w2 = InstrumentedWeave()
861
 
 
862
 
        txt0 = ['a\n']
863
 
        txt1 = ['a\n', 'b\n']
864
 
        txt2 = ['a\n', 'c\n']
865
 
        txt3 = ['a\n', 'b\n', 'c\n']
866
 
 
867
 
        w1.add_lines('txt0', [], txt0)
868
 
        w2.add_lines('txt0', [], txt0)
869
 
        w1.add_lines('txt1', ['txt0'], txt1)
870
 
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
871
 
        # Same text, effectively the same, because the
872
 
        # parent is only repeated
873
 
        w1.join(w2) # extract 3a to add txt2 
874
 
        w2.join(w1) # extract 3b to add txt1 
875
 
 
876
 
 
877
 
class TestNeedsReweave(TestCase):
878
 
    """Internal corner cases for when reweave is needed."""
879
 
 
880
 
    def test_compatible_parents(self):
881
 
        w1 = Weave('a')
882
 
        my_parents = set([1, 2, 3])
883
 
        # subsets are ok
884
 
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
885
 
        # same sets
886
 
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
887
 
        # same empty corner case
888
 
        self.assertTrue(w1._compatible_parents(set(), set()))
889
 
        # other cannot contain stuff my_parents does not
890
 
        self.assertFalse(w1._compatible_parents(set(), set([1])))
891
 
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
892
 
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))