~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-09-05 13:41:59 UTC
  • mfrom: (1963.1.4 run-subprocess)
  • Revision ID: pqm@pqm.ubuntu.com-20060905134159-ac8e1b01ac29c518
(jam) run_bzr_subprocess can remove env vars

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
18
18
 
19
19
 
20
20
# TODO: tests regarding version names
21
 
 
22
 
 
 
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
22
#       if it fails.
23
23
 
24
24
"""test suite for weave algorithm"""
25
25
 
 
26
from pprint import pformat
26
27
 
27
 
import testsweet
28
 
from bzrlib.weave import Weave, WeaveFormatError
 
28
import bzrlib.errors as errors
 
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
29
30
from bzrlib.weavefile import write_weave, read_weave
30
 
from pprint import pformat
31
 
 
32
 
 
33
 
try:
34
 
    set
35
 
    frozenset
36
 
except NameError:
37
 
    from sets import Set, ImmutableSet
38
 
    set = Set
39
 
    frozenset = ImmutableSet
40
 
    del Set, ImmutableSet
41
 
 
 
31
from bzrlib.tests import TestCase
 
32
from bzrlib.osutils import sha_string
42
33
 
43
34
 
44
35
# texts for use in testing
47
38
          "A second line"]
48
39
 
49
40
 
50
 
 
51
 
class TestBase(testsweet.TestBase):
 
41
class TestBase(TestCase):
52
42
    def check_read_write(self, k):
53
43
        """Check the weave k can be written & re-read."""
54
44
        from tempfile import TemporaryFile
68
58
            self.log('         %r' % k._parents)
69
59
            self.log('         %r' % k2._parents)
70
60
            self.log('')
71
 
 
72
 
            
73
61
            self.fail('read/write check failed')
74
 
        
75
 
        
 
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)
76
71
 
77
72
 
78
73
class Easy(TestBase):
82
77
 
83
78
class StoreText(TestBase):
84
79
    """Store and retrieve a simple text."""
85
 
    def runTest(self):
 
80
 
 
81
    def test_storing_text(self):
86
82
        k = Weave()
87
 
        idx = k.add('text0', [], TEXT_0)
88
 
        self.assertEqual(k.get(idx), TEXT_0)
 
83
        idx = k.add_lines('text0', [], TEXT_0)
 
84
        self.assertEqual(k.get_lines(idx), TEXT_0)
89
85
        self.assertEqual(idx, 0)
90
86
 
91
87
 
92
 
 
93
88
class AnnotateOne(TestBase):
94
89
    def runTest(self):
95
90
        k = Weave()
96
 
        k.add('text0', [], TEXT_0)
97
 
        self.assertEqual(k.annotate(0),
98
 
                         [(0, TEXT_0[0])])
 
91
        k.add_lines('text0', [], TEXT_0)
 
92
        self.assertEqual(k.annotate('text0'),
 
93
                         [('text0', TEXT_0[0])])
99
94
 
100
95
 
101
96
class StoreTwo(TestBase):
102
97
    def runTest(self):
103
98
        k = Weave()
104
99
 
105
 
        idx = k.add('text0', [], TEXT_0)
 
100
        idx = k.add_lines('text0', [], TEXT_0)
106
101
        self.assertEqual(idx, 0)
107
102
 
108
 
        idx = k.add('text1', [], TEXT_1)
 
103
        idx = k.add_lines('text1', [], TEXT_1)
109
104
        self.assertEqual(idx, 1)
110
105
 
111
 
        self.assertEqual(k.get(0), TEXT_0)
112
 
        self.assertEqual(k.get(1), TEXT_1)
113
 
 
114
 
        k.dump(self.TEST_LOG)
115
 
 
116
 
 
 
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):
 
112
        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
                        
117
121
 
118
122
class InvalidAdd(TestBase):
119
123
    """Try to use invalid version number during add."""
120
124
    def runTest(self):
121
125
        k = Weave()
122
126
 
123
 
        self.assertRaises(IndexError,
124
 
                          k.add,
 
127
        self.assertRaises(errors.RevisionNotPresent,
 
128
                          k.add_lines,
125
129
                          'text0',
126
 
                          [69],
 
130
                          ['69'],
127
131
                          ['new text!'])
128
132
 
129
133
 
 
134
class RepeatedAdd(TestBase):
 
135
    """Add the same version twice; harmless."""
 
136
    def runTest(self):
 
137
        k = Weave()
 
138
        idx = k.add_lines('text0', [], TEXT_0)
 
139
        idx2 = k.add_lines('text0', [], TEXT_0)
 
140
        self.assertEqual(idx, idx2)
 
141
 
 
142
 
 
143
class InvalidRepeatedAdd(TestBase):
 
144
    def runTest(self):
 
145
        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,
 
150
                          'text0',
 
151
                          [],
 
152
                          ['not the same text'])
 
153
        self.assertRaises(errors.RevisionAlreadyPresent,
 
154
                          k.add_lines,
 
155
                          'text0',
 
156
                          ['basis'],         # not the right parents
 
157
                          TEXT_0)
 
158
        
 
159
 
130
160
class InsertLines(TestBase):
131
161
    """Store a revision that adds one line to the original.
132
162
 
135
165
    def runTest(self):
136
166
        k = Weave()
137
167
 
138
 
        k.add('text0', [], ['line 1'])
139
 
        k.add('text1', [0], ['line 1', 'line 2'])
140
 
 
141
 
        self.assertEqual(k.annotate(0),
142
 
                         [(0, 'line 1')])
143
 
 
144
 
        self.assertEqual(k.get(1),
 
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),
145
175
                         ['line 1',
146
176
                          'line 2'])
147
177
 
148
 
        self.assertEqual(k.annotate(1),
149
 
                         [(0, 'line 1'),
150
 
                          (1, 'line 2')])
151
 
 
152
 
        k.add('text2', [0], ['line 1', 'diverged line'])
153
 
 
154
 
        self.assertEqual(k.annotate(2),
155
 
                         [(0, 'line 1'),
156
 
                          (2, 'diverged line')])
 
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')])
157
187
 
158
188
        text3 = ['line 1', 'middle line', 'line 2']
159
 
        k.add('text3',
160
 
              [0, 1],
 
189
        k.add_lines('text3',
 
190
              ['text0', 'text1'],
161
191
              text3)
162
192
 
163
193
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
164
194
 
165
195
        self.log("k._weave=" + pformat(k._weave))
166
196
 
167
 
        self.assertEqual(k.annotate(3),
168
 
                         [(0, 'line 1'),
169
 
                          (3, 'middle line'),
170
 
                          (1, 'line 2')])
 
197
        self.assertEqual(k.annotate('text3'),
 
198
                         [('text0', 'line 1'),
 
199
                          ('text3', 'middle line'),
 
200
                          ('text1', 'line 2')])
171
201
 
172
202
        # now multiple insertions at different places
173
 
        k.add('text4',
174
 
              [0, 1, 3],
 
203
        k.add_lines('text4',
 
204
              ['text0', 'text1', 'text3'],
175
205
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
176
206
 
177
 
        self.assertEqual(k.annotate(4), 
178
 
                         [(0, 'line 1'),
179
 
                          (4, 'aaa'),
180
 
                          (3, 'middle line'),
181
 
                          (4, 'bbb'),
182
 
                          (1, 'line 2'),
183
 
                          (4, 'ccc')])
184
 
 
 
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')])
185
214
 
186
215
 
187
216
class DeleteLines(TestBase):
193
222
 
194
223
        base_text = ['one', 'two', 'three', 'four']
195
224
 
196
 
        k.add('text0', [], base_text)
 
225
        k.add_lines('text0', [], base_text)
197
226
        
198
227
        texts = [['one', 'two', 'three'],
199
228
                 ['two', 'three', 'four'],
203
232
 
204
233
        i = 1
205
234
        for t in texts:
206
 
            ver = k.add('text%d' % i,
207
 
                        [0], t)
 
235
            ver = k.add_lines('text%d' % i,
 
236
                        ['text0'], t)
208
237
            i += 1
209
238
 
210
239
        self.log('final weave:')
211
240
        self.log('k._weave=' + pformat(k._weave))
212
241
 
213
242
        for i in range(len(texts)):
214
 
            self.assertEqual(k.get(i+1),
 
243
            self.assertEqual(k.get_lines(i+1),
215
244
                             texts[i])
216
 
            
217
 
 
218
245
 
219
246
 
220
247
class SuicideDelete(TestBase):
236
263
        return 
237
264
 
238
265
        self.assertRaises(WeaveFormatError,
239
 
                          k.get,
 
266
                          k.get_lines,
240
267
                          0)        
241
268
 
242
269
 
243
 
 
244
270
class CannedDelete(TestBase):
245
271
    """Unpack canned weave with deleted lines."""
246
272
    def runTest(self):
257
283
                'last line',
258
284
                ('}', 0),
259
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linelast line')]
260
288
 
261
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
262
290
                         ['first line',
263
291
                          'line to be deleted',
264
292
                          'last line',
265
293
                          ])
266
294
 
267
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
268
296
                         ['first line',
269
297
                          'last line',
270
298
                          ])
271
299
 
272
300
 
273
 
 
274
301
class CannedReplacement(TestBase):
275
302
    """Unpack canned weave with deleted lines."""
276
303
    def runTest(self):
290
317
                'last line',
291
318
                ('}', 0),
292
319
                ]
 
320
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
321
                  , sha_string('first linereplacement linelast line')]
293
322
 
294
 
        self.assertEqual(k.get(0),
 
323
        self.assertEqual(k.get_lines(0),
295
324
                         ['first line',
296
325
                          'line to be deleted',
297
326
                          'last line',
298
327
                          ])
299
328
 
300
 
        self.assertEqual(k.get(1),
 
329
        self.assertEqual(k.get_lines(1),
301
330
                         ['first line',
302
331
                          'replacement line',
303
332
                          'last line',
304
333
                          ])
305
334
 
306
335
 
307
 
 
308
336
class BadWeave(TestBase):
309
337
    """Test that we trap an insert which should not occur."""
310
338
    def runTest(self):
390
418
                '}',
391
419
                ('}', 0)]
392
420
 
393
 
        self.assertEqual(k.get(0),
 
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),
394
428
                         ['foo {',
395
429
                          '}'])
396
430
 
397
 
        self.assertEqual(k.get(1),
 
431
        self.assertEqual(k.get_lines(1),
398
432
                         ['foo {',
399
433
                          '  added in version 1',
400
434
                          '  also from v1',
401
435
                          '}'])
402
436
                       
403
 
        self.assertEqual(k.get(2),
 
437
        self.assertEqual(k.get_lines(2),
404
438
                         ['foo {',
405
439
                          '  added in v2',
406
440
                          '}'])
407
441
 
408
 
        self.assertEqual(k.get(3),
 
442
        self.assertEqual(k.get_lines(3),
409
443
                         ['foo {',
410
444
                          '  added in version 1',
411
445
                          '  added in v2',
413
447
                          '}'])
414
448
                         
415
449
 
416
 
 
417
450
class DeleteLines2(TestBase):
418
451
    """Test recording revisions that delete lines.
419
452
 
422
455
    def runTest(self):
423
456
        k = Weave()
424
457
 
425
 
        k.add('text0', [], ["line the first",
 
458
        k.add_lines('text0', [], ["line the first",
426
459
                   "line 2",
427
460
                   "line 3",
428
461
                   "fine"])
429
462
 
430
 
        self.assertEqual(len(k.get(0)), 4)
 
463
        self.assertEqual(len(k.get_lines(0)), 4)
431
464
 
432
 
        k.add('text1', [0], ["line the first",
 
465
        k.add_lines('text1', ['text0'], ["line the first",
433
466
                   "fine"])
434
467
 
435
 
        self.assertEqual(k.get(1),
 
468
        self.assertEqual(k.get_lines(1),
436
469
                         ["line the first",
437
470
                          "fine"])
438
471
 
439
 
        self.assertEqual(k.annotate(1),
440
 
                         [(0, "line the first"),
441
 
                          (0, "fine")])
442
 
 
 
472
        self.assertEqual(k.annotate('text1'),
 
473
                         [('text0', "line the first"),
 
474
                          ('text0', "fine")])
443
475
 
444
476
 
445
477
class IncludeVersions(TestBase):
463
495
                "second line",
464
496
                ('}', 1)]
465
497
 
466
 
        self.assertEqual(k.get(1),
 
498
        k._sha1s = [sha_string('first line')
 
499
                  , sha_string('first linesecond line')]
 
500
 
 
501
        self.assertEqual(k.get_lines(1),
467
502
                         ["first line",
468
503
                          "second line"])
469
504
 
470
 
        self.assertEqual(k.get(0),
 
505
        self.assertEqual(k.get_lines(0),
471
506
                         ["first line"])
472
507
 
473
 
        k.dump(self.TEST_LOG)
474
 
 
475
508
 
476
509
class DivergedIncludes(TestBase):
477
510
    """Weave with two diverged texts based on version 0.
478
511
    """
479
512
    def runTest(self):
 
513
        # FIXME make the weave, dont poke at it.
480
514
        k = Weave()
481
515
 
 
516
        k._names = ['0', '1', '2']
 
517
        k._name_map = {'0':0, '1':1, '2':2}
482
518
        k._parents = [frozenset(),
483
519
                frozenset([0]),
484
520
                frozenset([0]),
494
530
                ('}', 2),                
495
531
                ]
496
532
 
497
 
        self.assertEqual(k.get(0),
 
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),
498
538
                         ["first line"])
499
539
 
500
 
        self.assertEqual(k.get(1),
 
540
        self.assertEqual(k.get_lines(1),
501
541
                         ["first line",
502
542
                          "second line"])
503
543
 
504
 
        self.assertEqual(k.get(2),
 
544
        self.assertEqual(k.get_lines('2'),
505
545
                         ["first line",
506
546
                          "alternative second line"])
507
547
 
508
 
        self.assertEqual(list(k.inclusions([2])),
509
 
                         [0, 2])
510
 
 
 
548
        self.assertEqual(list(k.get_ancestry(['2'])),
 
549
                         ['0', '2'])
511
550
 
512
551
 
513
552
class ReplaceLine(TestBase):
517
556
        text0 = ['cheddar', 'stilton', 'gruyere']
518
557
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
519
558
        
520
 
        k.add('text0', [], text0)
521
 
        k.add('text1', [0], text1)
 
559
        k.add_lines('text0', [], text0)
 
560
        k.add_lines('text1', ['text0'], text1)
522
561
 
523
562
        self.log('k._weave=' + pformat(k._weave))
524
563
 
525
 
        self.assertEqual(k.get(0), text0)
526
 
        self.assertEqual(k.get(1), text1)
527
 
 
 
564
        self.assertEqual(k.get_lines(0), text0)
 
565
        self.assertEqual(k.get_lines(1), text1)
528
566
 
529
567
 
530
568
class Merge(TestBase):
538
576
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
539
577
                 ]
540
578
 
541
 
        k.add('text0', [], texts[0])
542
 
        k.add('text1', [0], texts[1])
543
 
        k.add('text2', [0], texts[2])
544
 
        k.add('merge', [0, 1, 2], texts[3])
 
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])
545
583
 
546
584
        for i, t in enumerate(texts):
547
 
            self.assertEqual(k.get(i), t)
 
585
            self.assertEqual(k.get_lines(i), t)
548
586
 
549
 
        self.assertEqual(k.annotate(3),
550
 
                         [(0, 'header'),
551
 
                          (1, ''),
552
 
                          (1, 'line from 1'),
553
 
                          (3, 'fixup line'),
554
 
                          (2, 'line from 2'),
 
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'),
555
593
                          ])
556
594
 
557
 
        self.assertEqual(list(k.inclusions([3])),
558
 
                         [0, 1, 2, 3])
 
595
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
596
                         ['text0', 'text1', 'text2', 'merge'])
559
597
 
560
598
        self.log('k._weave=' + pformat(k._weave))
561
599
 
572
610
        return  # NOT RUN
573
611
        k = Weave()
574
612
 
575
 
        k.add([], ['aaa', 'bbb'])
576
 
        k.add([0], ['aaa', '111', 'bbb'])
577
 
        k.add([1], ['aaa', '222', 'bbb'])
 
613
        k.add_lines([], ['aaa', 'bbb'])
 
614
        k.add_lines([0], ['aaa', '111', 'bbb'])
 
615
        k.add_lines([1], ['aaa', '222', 'bbb'])
578
616
 
579
617
        merged = k.merge([1, 2])
580
618
 
583
621
                           [['bbb']]])
584
622
 
585
623
 
586
 
 
587
624
class NonConflict(TestBase):
588
625
    """Two descendants insert compatible changes.
589
626
 
592
629
        return  # NOT RUN
593
630
        k = Weave()
594
631
 
595
 
        k.add([], ['aaa', 'bbb'])
596
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
597
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
598
 
 
599
 
    
600
 
    
601
 
 
602
 
 
603
 
class AutoMerge(TestBase):
604
 
    def runTest(self):
605
 
        k = Weave()
606
 
 
607
 
        texts = [['header', 'aaa', 'bbb'],
608
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
609
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
610
 
                 ]
611
 
 
612
 
        k.add('text0', [], texts[0])
613
 
        k.add('text1', [0], texts[1])
614
 
        k.add('text2', [0], texts[2])
615
 
 
616
 
        self.log('k._weave=' + pformat(k._weave))
617
 
 
618
 
        m = list(k.mash_iter([0, 1, 2]))
619
 
 
620
 
        self.assertEqual(m,
621
 
                         ['header', 'aaa',
622
 
                          'line from 1',
623
 
                          'bbb',
624
 
                          'line from 2', 'more from 2'])
625
 
        
 
632
        k.add_lines([], ['aaa', 'bbb'])
 
633
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
634
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
626
635
 
627
636
 
628
637
class Khayyam(TestBase):
629
638
    """Test changes to multi-line texts, and read/write"""
630
 
    def runTest(self):
 
639
 
 
640
    def test_multi_line_merge(self):
631
641
        rawtexts = [
632
642
            """A Book of Verses underneath the Bough,
633
643
            A Jug of Wine, a Loaf of Bread, -- and Thou
659
669
        parents = set()
660
670
        i = 0
661
671
        for t in texts:
662
 
            ver = k.add('text%d' % i,
 
672
            ver = k.add_lines('text%d' % i,
663
673
                        list(parents), t)
664
 
            parents.add(ver)
 
674
            parents.add('text%d' % i)
665
675
            i += 1
666
676
 
667
677
        self.log("k._weave=" + pformat(k._weave))
668
678
 
669
679
        for i, t in enumerate(texts):
670
 
            self.assertEqual(k.get(i), t)
 
680
            self.assertEqual(k.get_lines(i), t)
671
681
 
672
682
        self.check_read_write(k)
673
683
 
674
684
 
675
 
 
676
 
class MergeCases(TestBase):
677
 
    def doMerge(self, base, a, b, mp):
 
685
class JoinWeavesTests(TestBase):
 
686
    def setUp(self):
 
687
        super(JoinWeavesTests, self).setUp()
 
688
        self.weave1 = Weave()
 
689
        self.lines1 = ['hello\n']
 
690
        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)
 
694
        
 
695
    def test_join_empty(self):
 
696
        """Join two empty weaves."""
 
697
        eq = self.assertEqual
 
698
        w1 = Weave()
 
699
        w2 = Weave()
 
700
        w1.join(w2)
 
701
        eq(len(w1), 0)
 
702
        
 
703
    def test_join_empty_to_nonempty(self):
 
704
        """Join empty weave onto nonempty."""
 
705
        self.weave1.join(Weave())
 
706
        self.assertEqual(len(self.weave1), 3)
 
707
 
 
708
    def test_join_unrelated(self):
 
709
        """Join two weaves with no history in common."""
 
710
        wb = Weave()
 
711
        wb.add_lines('b1', [], ['line from b\n'])
 
712
        w1 = self.weave1
 
713
        w1.join(wb)
 
714
        eq = self.assertEqual
 
715
        eq(len(w1), 4)
 
716
        eq(sorted(w1.versions()),
 
717
           ['b1', 'v1', 'v2', 'v3'])
 
718
 
 
719
    def test_join_related(self):
 
720
        wa = self.weave1.copy()
 
721
        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'])
 
724
        eq = self.assertEquals
 
725
        eq(len(wa), 4)
 
726
        eq(len(wb), 4)
 
727
        wa.join(wb)
 
728
        eq(len(wa), 5)
 
729
        eq(wa.get_lines('b1'),
 
730
           ['hello\n', 'pale blue\n', 'world\n'])
 
731
 
 
732
    def test_join_parent_disagreement(self):
 
733
        #join reconciles differening parents into a union.
 
734
        wa = Weave()
 
735
        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'))
 
741
 
 
742
    def test_join_text_disagreement(self):
 
743
        """Cannot join weaves with different texts for a version."""
 
744
        wa = Weave()
 
745
        wb = Weave()
 
746
        wa.add_lines('v1', [], ['hello\n'])
 
747
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
 
748
        self.assertRaises(WeaveError,
 
749
                          wa.join, wb)
 
750
 
 
751
    def test_join_unordered(self):
 
752
        """Join weaves where indexes differ.
 
753
        
 
754
        The source weave contains a different version at index 0."""
 
755
        wa = self.weave1.copy()
 
756
        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'])
 
760
        wa.join(wb)
 
761
        eq = self.assertEquals
 
762
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
 
763
        eq(wa.get_text('x1'), 'line from x1\n')
 
764
 
 
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.
678
771
        from cStringIO import StringIO
679
 
        from textwrap import dedent
680
772
 
681
 
        def addcrlf(x):
682
 
            return x + '\n'
683
 
        
684
773
        w = Weave()
685
 
        w.add('text0', [], map(addcrlf, base))
686
 
        w.add('text1', [0], map(addcrlf, a))
687
 
        w.add('text2', [0], map(addcrlf, b))
 
774
        w.add_lines('v1', [], ['hello\n'])
 
775
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
688
776
 
689
 
        self.log('weave is:')
690
777
        tmpf = StringIO()
691
778
        write_weave(w, tmpf)
692
 
        self.log(tmpf.getvalue())
693
 
 
694
 
        self.log('merge plan:')
695
 
        p = list(w.plan_merge(1, 2))
696
 
        for state, line in p:
697
 
            if line:
698
 
                self.log('%12s | %s' % (state, line[:-1]))
699
 
 
700
 
        self.log('merge:')
701
 
        mt = StringIO()
702
 
        mt.writelines(w.weave_merge(p))
703
 
        mt.seek(0)
704
 
        self.log(mt.getvalue())
705
 
 
706
 
        mp = map(addcrlf, mp)
707
 
        self.assertEqual(mt.readlines(), mp)
708
 
        
709
 
        
710
 
    def testOneInsert(self):
711
 
        self.doMerge([],
712
 
                     ['aa'],
713
 
                     [],
714
 
                     ['aa'])
715
 
 
716
 
    def testSeparateInserts(self):
717
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
718
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
719
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
720
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
721
 
 
722
 
    def testSameInsert(self):
723
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
724
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
725
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
726
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
727
 
 
728
 
    def testOverlappedInsert(self):
729
 
        self.doMerge(['aaa', 'bbb'],
730
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
731
 
                     ['aaa', 'xxx', 'bbb'],
732
 
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
733
 
 
734
 
        # really it ought to reduce this to 
735
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
736
 
 
737
 
 
738
 
    def testClashReplace(self):
739
 
        self.doMerge(['aaa'],
740
 
                     ['xxx'],
741
 
                     ['yyy', 'zzz'],
742
 
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
743
 
 
744
 
    def testNonClashInsert(self):
745
 
        self.doMerge(['aaa'],
746
 
                     ['xxx', 'aaa'],
747
 
                     ['yyy', 'zzz'],
748
 
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
749
 
 
750
 
        self.doMerge(['aaa'],
751
 
                     ['aaa'],
752
 
                     ['yyy', 'zzz'],
753
 
                     ['yyy', 'zzz'])
754
 
 
755
 
 
756
 
    def testDeleteAndModify(self):
757
 
        """Clashing delete and modification.
758
 
 
759
 
        If one side modifies a region and the other deletes it then
760
 
        there should be a conflict with one side blank.
761
 
        """
762
 
 
763
 
        #######################################
764
 
        # skippd, not working yet
765
 
        return
766
 
        
767
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
768
 
                     ['aaa', 'ddd', 'ccc'],
769
 
                     ['aaa', 'ccc'],
770
 
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
771
 
    
772
 
 
773
 
 
774
 
def testweave():
775
 
    import testsweet
776
 
    from unittest import TestSuite, TestLoader
777
 
    import testweave
778
 
 
779
 
    tl = TestLoader()
780
 
    suite = TestSuite()
781
 
    suite.addTest(tl.loadTestsFromModule(testweave))
782
 
    
783
 
    return int(not testsweet.run_suite(suite)) # for shell 0=true
784
 
 
785
 
 
786
 
if __name__ == '__main__':
787
 
    import sys
788
 
    sys.exit(testweave())
789
 
    
 
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."""
 
816
    
 
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])))