/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Martin Pool
  • Date: 2007-05-04 08:46:39 UTC
  • mto: (2483.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2484.
  • Revision ID: mbp@sourcefrog.net-20070504084639-8v8mzetmr1y74xer
Rename push/pull back to 'run_hooks' (jameinel)

Reorganize Branch.push into some template methods: public push,
_push_with_bound_branches, and _basic_push.  This fixes the case 
where the destination of push is bound, but the source branch
format doesn't support binding.

Run push and pull hook tests with a local branch that does support binding,
rather than skipping if the branch can't be bound to another of the same
format.

(broken) because the hooks are given the wrong parameters when 
pushing into something bound to a remote branch.

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