~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Martin Pool
  • Date: 2006-11-02 10:20:19 UTC
  • mfrom: (2114 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2119.
  • Revision ID: mbp@sourcefrog.net-20061102102019-9a5a02f485dff6f6
merge bzr.dev and reconcile several changes, also some test fixes

Show diffs side-by-side

added added

removed removed

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