/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 tools/testweave.py

  • Committer: Martin Pool
  • Date: 2005-07-07 10:31:36 UTC
  • Revision ID: mbp@sourcefrog.net-20050707103135-9b4d911d8df6e880
- fix pwk help

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
 
20
 
# TODO: tests regarding version names
21
 
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
22
 
#       if it fails.
 
20
 
23
21
 
24
22
"""test suite for weave algorithm"""
25
23
 
 
24
 
 
25
import testsweet
 
26
from weave import Weave, WeaveFormatError
26
27
from pprint import pformat
27
28
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
30
 
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.tests import TestCase
32
 
from bzrlib.osutils import sha_string
 
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
 
33
40
 
34
41
 
35
42
# texts for use in testing
38
45
          "A second line"]
39
46
 
40
47
 
41
 
class TestBase(TestCase):
 
48
 
 
49
class TestBase(testsweet.TestBase):
42
50
    def check_read_write(self, k):
43
51
        """Check the weave k can be written & re-read."""
44
52
        from tempfile import TemporaryFile
 
53
        from weavefile import write_weave, read_weave
45
54
        tf = TemporaryFile()
46
55
 
47
56
        write_weave(k, tf)
52
61
            tf.seek(0)
53
62
            self.log('serialized weave:')
54
63
            self.log(tf.read())
55
 
 
56
 
            self.log('')
57
 
            self.log('parents: %s' % (k._parents == k2._parents))
58
 
            self.log('         %r' % k._parents)
59
 
            self.log('         %r' % k2._parents)
60
 
            self.log('')
61
64
            self.fail('read/write check failed')
62
 
 
63
 
 
64
 
class WeaveContains(TestBase):
65
 
    """Weave __contains__ operator"""
66
 
    def runTest(self):
67
 
        k = Weave()
68
 
        self.assertFalse('foo' in k)
69
 
        k.add_lines('foo', [], TEXT_1)
70
 
        self.assertTrue('foo' in k)
 
65
        
 
66
        
71
67
 
72
68
 
73
69
class Easy(TestBase):
77
73
 
78
74
class StoreText(TestBase):
79
75
    """Store and retrieve a simple text."""
80
 
 
81
 
    def test_storing_text(self):
 
76
    def runTest(self):
82
77
        k = Weave()
83
 
        idx = k.add_lines('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get_lines(idx), TEXT_0)
 
78
        idx = k.add([], TEXT_0)
 
79
        self.assertEqual(k.get(idx), TEXT_0)
85
80
        self.assertEqual(idx, 0)
86
81
 
87
82
 
 
83
 
88
84
class AnnotateOne(TestBase):
89
85
    def runTest(self):
90
86
        k = Weave()
91
 
        k.add_lines('text0', [], TEXT_0)
92
 
        self.assertEqual(k.annotate('text0'),
93
 
                         [('text0', TEXT_0[0])])
 
87
        k.add([], TEXT_0)
 
88
        self.assertEqual(k.annotate(0),
 
89
                         [(0, TEXT_0[0])])
94
90
 
95
91
 
96
92
class StoreTwo(TestBase):
97
93
    def runTest(self):
98
94
        k = Weave()
99
95
 
100
 
        idx = k.add_lines('text0', [], TEXT_0)
 
96
        idx = k.add([], TEXT_0)
101
97
        self.assertEqual(idx, 0)
102
98
 
103
 
        idx = k.add_lines('text1', [], TEXT_1)
 
99
        idx = k.add([], TEXT_1)
104
100
        self.assertEqual(idx, 1)
105
101
 
106
 
        self.assertEqual(k.get_lines(0), TEXT_0)
107
 
        self.assertEqual(k.get_lines(1), TEXT_1)
108
 
 
109
 
 
110
 
class GetSha1(TestBase):
111
 
    def test_get_sha1(self):
 
102
        self.assertEqual(k.get(0), TEXT_0)
 
103
        self.assertEqual(k.get(1), TEXT_1)
 
104
 
 
105
        k.dump(self.TEST_LOG)
 
106
 
 
107
 
 
108
 
 
109
class DeltaAdd(TestBase):
 
110
    """Detection of changes prior to inserting new revision."""
 
111
    def runTest(self):
112
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
 
                        
 
113
        k.add([], ['line 1'])
 
114
 
 
115
        self.assertEqual(k._l,
 
116
                         [('{', 0),
 
117
                          'line 1',
 
118
                          ('}', 0),
 
119
                          ])
 
120
 
 
121
        changes = list(k._delta(set([0]),
 
122
                                ['line 1',
 
123
                                 'new line']))
 
124
 
 
125
        self.log('raw changes: ' + pformat(changes))
 
126
 
 
127
        # currently there are 3 lines in the weave, and we insert after them
 
128
        self.assertEquals(changes,
 
129
                          [(3, 3, ['new line'])])
 
130
 
 
131
        changes = k._delta(set([0]),
 
132
                           ['top line',
 
133
                            'line 1'])
 
134
        
 
135
        self.assertEquals(list(changes),
 
136
                          [(1, 1, ['top line'])])
 
137
 
 
138
        self.check_read_write(k)
 
139
 
121
140
 
122
141
class InvalidAdd(TestBase):
123
142
    """Try to use invalid version number during add."""
124
143
    def runTest(self):
125
144
        k = Weave()
126
145
 
127
 
        self.assertRaises(errors.RevisionNotPresent,
128
 
                          k.add_lines,
129
 
                          'text0',
130
 
                          ['69'],
 
146
        self.assertRaises(IndexError,
 
147
                          k.add,
 
148
                          [69],
131
149
                          ['new text!'])
132
150
 
133
151
 
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
 
 
160
152
class InsertLines(TestBase):
161
153
    """Store a revision that adds one line to the original.
162
154
 
165
157
    def runTest(self):
166
158
        k = Weave()
167
159
 
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),
 
160
        k.add([], ['line 1'])
 
161
        k.add([0], ['line 1', 'line 2'])
 
162
 
 
163
        self.assertEqual(k.annotate(0),
 
164
                         [(0, 'line 1')])
 
165
 
 
166
        self.assertEqual(k.get(1),
175
167
                         ['line 1',
176
168
                          'line 2'])
177
169
 
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')])
 
170
        self.assertEqual(k.annotate(1),
 
171
                         [(0, 'line 1'),
 
172
                          (1, 'line 2')])
 
173
 
 
174
        k.add([0], ['line 1', 'diverged line'])
 
175
 
 
176
        self.assertEqual(k.annotate(2),
 
177
                         [(0, 'line 1'),
 
178
                          (2, 'diverged line')])
187
179
 
188
180
        text3 = ['line 1', 'middle line', 'line 2']
189
 
        k.add_lines('text3',
190
 
              ['text0', 'text1'],
 
181
        k.add([0, 1],
191
182
              text3)
192
183
 
193
 
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
194
 
 
195
 
        self.log("k._weave=" + pformat(k._weave))
196
 
 
197
 
        self.assertEqual(k.annotate('text3'),
198
 
                         [('text0', 'line 1'),
199
 
                          ('text3', 'middle line'),
200
 
                          ('text1', 'line 2')])
 
184
        self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
 
185
 
 
186
        self.log("k._l=" + pformat(k._l))
 
187
 
 
188
        self.assertEqual(k.annotate(3),
 
189
                         [(0, 'line 1'),
 
190
                          (3, 'middle line'),
 
191
                          (1, 'line 2')])
201
192
 
202
193
        # now multiple insertions at different places
203
 
        k.add_lines('text4',
204
 
              ['text0', 'text1', 'text3'],
 
194
        k.add([0, 1, 3],
205
195
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
206
196
 
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')])
 
197
        self.assertEqual(k.annotate(4), 
 
198
                         [(0, 'line 1'),
 
199
                          (4, 'aaa'),
 
200
                          (3, 'middle line'),
 
201
                          (4, 'bbb'),
 
202
                          (1, 'line 2'),
 
203
                          (4, 'ccc')])
 
204
 
214
205
 
215
206
 
216
207
class DeleteLines(TestBase):
222
213
 
223
214
        base_text = ['one', 'two', 'three', 'four']
224
215
 
225
 
        k.add_lines('text0', [], base_text)
 
216
        k.add([], base_text)
226
217
        
227
218
        texts = [['one', 'two', 'three'],
228
219
                 ['two', 'three', 'four'],
230
221
                 ['one', 'two', 'three', 'four'],
231
222
                 ]
232
223
 
233
 
        i = 1
234
224
        for t in texts:
235
 
            ver = k.add_lines('text%d' % i,
236
 
                        ['text0'], t)
237
 
            i += 1
 
225
            ver = k.add([0], t)
238
226
 
239
227
        self.log('final weave:')
240
 
        self.log('k._weave=' + pformat(k._weave))
 
228
        self.log('k._l=' + pformat(k._l))
241
229
 
242
230
        for i in range(len(texts)):
243
 
            self.assertEqual(k.get_lines(i+1),
 
231
            self.assertEqual(k.get(i+1),
244
232
                             texts[i])
 
233
            
 
234
 
245
235
 
246
236
 
247
237
class SuicideDelete(TestBase):
249
239
    def runTest(self):
250
240
        k = Weave()
251
241
 
252
 
        k._parents = [(),
 
242
        k._v = [(),
253
243
                ]
254
 
        k._weave = [('{', 0),
 
244
        k._l = [('{', 0),
255
245
                'first line',
256
246
                ('[', 0),
257
247
                'deleted in 0',
258
248
                (']', 0),
259
249
                ('}', 0),
260
250
                ]
261
 
        ################################### SKIPPED
262
 
        # Weave.get doesn't trap this anymore
263
 
        return 
264
251
 
265
252
        self.assertRaises(WeaveFormatError,
266
 
                          k.get_lines,
 
253
                          k.get,
267
254
                          0)        
268
255
 
269
256
 
 
257
 
270
258
class CannedDelete(TestBase):
271
259
    """Unpack canned weave with deleted lines."""
272
260
    def runTest(self):
273
261
        k = Weave()
274
262
 
275
 
        k._parents = [(),
 
263
        k._v = [(),
276
264
                frozenset([0]),
277
265
                ]
278
 
        k._weave = [('{', 0),
 
266
        k._l = [('{', 0),
279
267
                'first line',
280
268
                ('[', 1),
281
269
                'line to be deleted',
283
271
                'last line',
284
272
                ('}', 0),
285
273
                ]
286
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
287
 
                  , sha_string('first linelast line')]
288
274
 
289
 
        self.assertEqual(k.get_lines(0),
 
275
        self.assertEqual(k.get(0),
290
276
                         ['first line',
291
277
                          'line to be deleted',
292
278
                          'last line',
293
279
                          ])
294
280
 
295
 
        self.assertEqual(k.get_lines(1),
 
281
        self.assertEqual(k.get(1),
296
282
                         ['first line',
297
283
                          'last line',
298
284
                          ])
299
285
 
300
286
 
 
287
 
301
288
class CannedReplacement(TestBase):
302
289
    """Unpack canned weave with deleted lines."""
303
290
    def runTest(self):
304
291
        k = Weave()
305
292
 
306
 
        k._parents = [frozenset(),
 
293
        k._v = [frozenset(),
307
294
                frozenset([0]),
308
295
                ]
309
 
        k._weave = [('{', 0),
 
296
        k._l = [('{', 0),
310
297
                'first line',
311
298
                ('[', 1),
312
299
                'line to be deleted',
317
304
                'last line',
318
305
                ('}', 0),
319
306
                ]
320
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
321
 
                  , sha_string('first linereplacement linelast line')]
322
307
 
323
 
        self.assertEqual(k.get_lines(0),
 
308
        self.assertEqual(k.get(0),
324
309
                         ['first line',
325
310
                          'line to be deleted',
326
311
                          'last line',
327
312
                          ])
328
313
 
329
 
        self.assertEqual(k.get_lines(1),
 
314
        self.assertEqual(k.get(1),
330
315
                         ['first line',
331
316
                          'replacement line',
332
317
                          'last line',
333
318
                          ])
334
319
 
335
320
 
 
321
 
336
322
class BadWeave(TestBase):
337
323
    """Test that we trap an insert which should not occur."""
338
324
    def runTest(self):
339
325
        k = Weave()
340
326
 
341
 
        k._parents = [frozenset(),
 
327
        k._v = [frozenset(),
342
328
                ]
343
 
        k._weave = ['bad line',
 
329
        k._l = ['bad line',
344
330
                ('{', 0),
345
331
                'foo {',
346
332
                ('{', 1),
353
339
                '}',
354
340
                ('}', 0)]
355
341
 
356
 
        ################################### SKIPPED
357
 
        # Weave.get doesn't trap this anymore
358
 
        return 
359
 
 
360
 
 
361
342
        self.assertRaises(WeaveFormatError,
362
343
                          k.get,
363
344
                          0)
368
349
    def runTest(self):
369
350
        k = Weave()
370
351
 
371
 
        k._parents = [frozenset(),
 
352
        k._v = [frozenset(),
372
353
                frozenset([0]),
373
354
                frozenset([0]),
374
355
                frozenset([0,1,2]),
375
356
                ]
376
 
        k._weave = [('{', 0),
 
357
        k._l = [('{', 0),
377
358
                'foo {',
378
359
                ('{', 1),
379
360
                '  added in version 1',
383
364
                ('}', 1),
384
365
                ('}', 0)]
385
366
 
386
 
 
387
 
        # this is not currently enforced by get
388
 
        return  ##########################################
389
 
 
390
367
        self.assertRaises(WeaveFormatError,
391
368
                          k.get,
392
369
                          0)
401
378
    def runTest(self):
402
379
        k = Weave()
403
380
 
404
 
        k._parents = [frozenset(),
 
381
        k._v = [frozenset(),
405
382
                frozenset([0]),
406
383
                frozenset([0]),
407
384
                frozenset([0,1,2]),
408
385
                ]
409
 
        k._weave = [('{', 0),
 
386
        k._l = [('{', 0),
410
387
                'foo {',
411
388
                ('{', 1),
412
389
                '  added in version 1',
418
395
                '}',
419
396
                ('}', 0)]
420
397
 
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),
 
398
        self.assertEqual(k.get(0),
428
399
                         ['foo {',
429
400
                          '}'])
430
401
 
431
 
        self.assertEqual(k.get_lines(1),
 
402
        self.assertEqual(k.get(1),
432
403
                         ['foo {',
433
404
                          '  added in version 1',
434
405
                          '  also from v1',
435
406
                          '}'])
436
407
                       
437
 
        self.assertEqual(k.get_lines(2),
 
408
        self.assertEqual(k.get(2),
438
409
                         ['foo {',
439
410
                          '  added in v2',
440
411
                          '}'])
441
412
 
442
 
        self.assertEqual(k.get_lines(3),
 
413
        self.assertEqual(k.get(3),
443
414
                         ['foo {',
444
415
                          '  added in version 1',
445
416
                          '  added in v2',
447
418
                          '}'])
448
419
                         
449
420
 
 
421
 
450
422
class DeleteLines2(TestBase):
451
423
    """Test recording revisions that delete lines.
452
424
 
455
427
    def runTest(self):
456
428
        k = Weave()
457
429
 
458
 
        k.add_lines('text0', [], ["line the first",
 
430
        k.add([], ["line the first",
459
431
                   "line 2",
460
432
                   "line 3",
461
433
                   "fine"])
462
434
 
463
 
        self.assertEqual(len(k.get_lines(0)), 4)
 
435
        self.assertEqual(len(k.get(0)), 4)
464
436
 
465
 
        k.add_lines('text1', ['text0'], ["line the first",
 
437
        k.add([0], ["line the first",
466
438
                   "fine"])
467
439
 
468
 
        self.assertEqual(k.get_lines(1),
 
440
        self.assertEqual(k.get(1),
469
441
                         ["line the first",
470
442
                          "fine"])
471
443
 
472
 
        self.assertEqual(k.annotate('text1'),
473
 
                         [('text0', "line the first"),
474
 
                          ('text0', "fine")])
 
444
        self.assertEqual(k.annotate(1),
 
445
                         [(0, "line the first"),
 
446
                          (0, "fine")])
 
447
 
475
448
 
476
449
 
477
450
class IncludeVersions(TestBase):
487
460
    def runTest(self):
488
461
        k = Weave()
489
462
 
490
 
        k._parents = [frozenset(), frozenset([0])]
491
 
        k._weave = [('{', 0),
 
463
        k._v = [frozenset(), frozenset([0])]
 
464
        k._l = [('{', 0),
492
465
                "first line",
493
466
                ('}', 0),
494
467
                ('{', 1),
495
468
                "second line",
496
469
                ('}', 1)]
497
470
 
498
 
        k._sha1s = [sha_string('first line')
499
 
                  , sha_string('first linesecond line')]
500
 
 
501
 
        self.assertEqual(k.get_lines(1),
 
471
        self.assertEqual(k.get(1),
502
472
                         ["first line",
503
473
                          "second line"])
504
474
 
505
 
        self.assertEqual(k.get_lines(0),
 
475
        self.assertEqual(k.get(0),
506
476
                         ["first line"])
507
477
 
 
478
        k.dump(self.TEST_LOG)
 
479
 
508
480
 
509
481
class DivergedIncludes(TestBase):
510
482
    """Weave with two diverged texts based on version 0.
511
483
    """
512
484
    def runTest(self):
513
 
        # FIXME make the weave, dont poke at it.
514
485
        k = Weave()
515
486
 
516
 
        k._names = ['0', '1', '2']
517
 
        k._name_map = {'0':0, '1':1, '2':2}
518
 
        k._parents = [frozenset(),
 
487
        k._v = [frozenset(),
519
488
                frozenset([0]),
520
489
                frozenset([0]),
521
490
                ]
522
 
        k._weave = [('{', 0),
 
491
        k._l = [('{', 0),
523
492
                "first line",
524
493
                ('}', 0),
525
494
                ('{', 1),
530
499
                ('}', 2),                
531
500
                ]
532
501
 
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),
 
502
        self.assertEqual(k.get(0),
538
503
                         ["first line"])
539
504
 
540
 
        self.assertEqual(k.get_lines(1),
 
505
        self.assertEqual(k.get(1),
541
506
                         ["first line",
542
507
                          "second line"])
543
508
 
544
 
        self.assertEqual(k.get_lines('2'),
 
509
        self.assertEqual(k.get(2),
545
510
                         ["first line",
546
511
                          "alternative second line"])
547
512
 
548
 
        self.assertEqual(list(k.get_ancestry(['2'])),
549
 
                         ['0', '2'])
 
513
        self.assertEqual(k.inclusions([2]),
 
514
                         set([0, 2]))
 
515
 
550
516
 
551
517
 
552
518
class ReplaceLine(TestBase):
556
522
        text0 = ['cheddar', 'stilton', 'gruyere']
557
523
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
558
524
        
559
 
        k.add_lines('text0', [], text0)
560
 
        k.add_lines('text1', ['text0'], text1)
561
 
 
562
 
        self.log('k._weave=' + pformat(k._weave))
563
 
 
564
 
        self.assertEqual(k.get_lines(0), text0)
565
 
        self.assertEqual(k.get_lines(1), text1)
 
525
        k.add([], text0)
 
526
        k.add([0], text1)
 
527
 
 
528
        self.log('k._l=' + pformat(k._l))
 
529
 
 
530
        self.assertEqual(k.get(0), text0)
 
531
        self.assertEqual(k.get(1), text1)
 
532
 
566
533
 
567
534
 
568
535
class Merge(TestBase):
576
543
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
577
544
                 ]
578
545
 
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])
 
546
        k.add([], texts[0])
 
547
        k.add([0], texts[1])
 
548
        k.add([0], texts[2])
 
549
        k.add([0, 1, 2], texts[3])
583
550
 
584
551
        for i, t in enumerate(texts):
585
 
            self.assertEqual(k.get_lines(i), t)
 
552
            self.assertEqual(k.get(i), t)
586
553
 
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'),
 
554
        self.assertEqual(k.annotate(3),
 
555
                         [(0, 'header'),
 
556
                          (1, ''),
 
557
                          (1, 'line from 1'),
 
558
                          (3, 'fixup line'),
 
559
                          (2, 'line from 2'),
593
560
                          ])
594
561
 
595
 
        self.assertEqual(list(k.get_ancestry(['merge'])),
596
 
                         ['text0', 'text1', 'text2', 'merge'])
 
562
        self.assertEqual(k.inclusions([3]),
 
563
                         set([0, 1, 2, 3]))
597
564
 
598
 
        self.log('k._weave=' + pformat(k._weave))
 
565
        self.log('k._l=' + pformat(k._l))
599
566
 
600
567
        self.check_read_write(k)
601
568
 
610
577
        return  # NOT RUN
611
578
        k = Weave()
612
579
 
613
 
        k.add_lines([], ['aaa', 'bbb'])
614
 
        k.add_lines([0], ['aaa', '111', 'bbb'])
615
 
        k.add_lines([1], ['aaa', '222', 'bbb'])
 
580
        k.add([], ['aaa', 'bbb'])
 
581
        k.add([0], ['aaa', '111', 'bbb'])
 
582
        k.add([1], ['aaa', '222', 'bbb'])
616
583
 
617
584
        merged = k.merge([1, 2])
618
585
 
621
588
                           [['bbb']]])
622
589
 
623
590
 
 
591
 
624
592
class NonConflict(TestBase):
625
593
    """Two descendants insert compatible changes.
626
594
 
629
597
        return  # NOT RUN
630
598
        k = Weave()
631
599
 
632
 
        k.add_lines([], ['aaa', 'bbb'])
633
 
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
634
 
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
 
600
        k.add([], ['aaa', 'bbb'])
 
601
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
 
602
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
 
603
 
 
604
    
 
605
    
 
606
 
 
607
 
 
608
class AutoMerge(TestBase):
 
609
    def runTest(self):
 
610
        k = Weave()
 
611
 
 
612
        texts = [['header', 'aaa', 'bbb'],
 
613
                 ['header', 'aaa', 'line from 1', 'bbb'],
 
614
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
 
615
                 ]
 
616
 
 
617
        k.add([], texts[0])
 
618
        k.add([0], texts[1])
 
619
        k.add([0], texts[2])
 
620
 
 
621
        self.log('k._l=' + pformat(k._l))
 
622
 
 
623
        m = list(k.mash_iter([0, 1, 2]))
 
624
 
 
625
        self.assertEqual(m,
 
626
                         ['header', 'aaa',
 
627
                          'line from 1',
 
628
                          'bbb',
 
629
                          'line from 2', 'more from 2'])
 
630
        
635
631
 
636
632
 
637
633
class Khayyam(TestBase):
638
634
    """Test changes to multi-line texts, and read/write"""
639
 
 
640
 
    def test_multi_line_merge(self):
 
635
    def runTest(self):
641
636
        rawtexts = [
642
637
            """A Book of Verses underneath the Bough,
643
638
            A Jug of Wine, a Loaf of Bread, -- and Thou
667
662
 
668
663
        k = Weave()
669
664
        parents = set()
670
 
        i = 0
671
665
        for t in texts:
672
 
            ver = k.add_lines('text%d' % i,
673
 
                        list(parents), t)
674
 
            parents.add('text%d' % i)
675
 
            i += 1
 
666
            ver = k.add(list(parents), t)
 
667
            parents.add(ver)
676
668
 
677
 
        self.log("k._weave=" + pformat(k._weave))
 
669
        self.log("k._l=" + pformat(k._l))
678
670
 
679
671
        for i, t in enumerate(texts):
680
 
            self.assertEqual(k.get_lines(i), t)
 
672
            self.assertEqual(k.get(i), t)
681
673
 
682
674
        self.check_read_write(k)
683
675
 
684
676
 
685
 
class MergeCases(TestBase):
686
 
    def doMerge(self, base, a, b, mp):
687
 
        from cStringIO import StringIO
688
 
        from textwrap import dedent
689
 
 
690
 
        def addcrlf(x):
691
 
            return x + '\n'
692
 
        
693
 
        w = Weave()
694
 
        w.add_lines('text0', [], map(addcrlf, base))
695
 
        w.add_lines('text1', ['text0'], map(addcrlf, a))
696
 
        w.add_lines('text2', ['text0'], map(addcrlf, b))
697
 
 
698
 
        self.log('weave is:')
699
 
        tmpf = StringIO()
700
 
        write_weave(w, tmpf)
701
 
        self.log(tmpf.getvalue())
702
 
 
703
 
        self.log('merge plan:')
704
 
        p = list(w.plan_merge('text1', 'text2'))
705
 
        for state, line in p:
706
 
            if line:
707
 
                self.log('%12s | %s' % (state, line[:-1]))
708
 
 
709
 
        self.log('merge:')
710
 
        mt = StringIO()
711
 
        mt.writelines(w.weave_merge(p))
712
 
        mt.seek(0)
713
 
        self.log(mt.getvalue())
714
 
 
715
 
        mp = map(addcrlf, mp)
716
 
        self.assertEqual(mt.readlines(), mp)
717
 
        
718
 
        
719
 
    def testOneInsert(self):
720
 
        self.doMerge([],
721
 
                     ['aa'],
722
 
                     [],
723
 
                     ['aa'])
724
 
 
725
 
    def testSeparateInserts(self):
726
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
727
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
728
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
729
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
730
 
 
731
 
    def testSameInsert(self):
732
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
733
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
734
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
735
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
736
 
 
737
 
    def testOverlappedInsert(self):
738
 
        self.doMerge(['aaa', 'bbb'],
739
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
740
 
                     ['aaa', 'xxx', 'bbb'],
741
 
                     ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 'xxx', 
742
 
                      '>>>>>>> ', 'bbb'])
743
 
 
744
 
        # really it ought to reduce this to 
745
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
746
 
 
747
 
 
748
 
    def testClashReplace(self):
749
 
        self.doMerge(['aaa'],
750
 
                     ['xxx'],
751
 
                     ['yyy', 'zzz'],
752
 
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
753
 
                      '>>>>>>> '])
754
 
 
755
 
    def testNonClashInsert(self):
756
 
        self.doMerge(['aaa'],
757
 
                     ['xxx', 'aaa'],
758
 
                     ['yyy', 'zzz'],
759
 
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
760
 
                      '>>>>>>> '])
761
 
 
762
 
        self.doMerge(['aaa'],
763
 
                     ['aaa'],
764
 
                     ['yyy', 'zzz'],
765
 
                     ['yyy', 'zzz'])
766
 
 
767
 
 
768
 
    def testDeleteAndModify(self):
769
 
        """Clashing delete and modification.
770
 
 
771
 
        If one side modifies a region and the other deletes it then
772
 
        there should be a conflict with one side blank.
773
 
        """
774
 
 
775
 
        #######################################
776
 
        # skippd, not working yet
777
 
        return
778
 
        
779
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
780
 
                     ['aaa', 'ddd', 'ccc'],
781
 
                     ['aaa', 'ccc'],
782
 
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
783
 
 
784
 
    def _test_merge_from_strings(self, base, a, b, expected):
785
 
        w = Weave()
786
 
        w.add_lines('text0', [], base.splitlines(True))
787
 
        w.add_lines('text1', ['text0'], a.splitlines(True))
788
 
        w.add_lines('text2', ['text0'], b.splitlines(True))
789
 
        self.log('merge plan:')
790
 
        p = list(w.plan_merge('text1', 'text2'))
791
 
        for state, line in p:
792
 
            if line:
793
 
                self.log('%12s | %s' % (state, line[:-1]))
794
 
        self.log('merge result:')
795
 
        result_text = ''.join(w.weave_merge(p))
796
 
        self.log(result_text)
797
 
        self.assertEqualDiff(result_text, expected)
798
 
 
799
 
    def test_deletion_extended(self):
800
 
        """One side deletes, the other deletes more.
801
 
        """
802
 
        base = """\
803
 
            line 1
804
 
            line 2
805
 
            line 3
806
 
            """
807
 
        a = """\
808
 
            line 1
809
 
            line 2
810
 
            """
811
 
        b = """\
812
 
            line 1
813
 
            """
814
 
        result = """\
815
 
            line 1
816
 
            """
817
 
        self._test_merge_from_strings(base, a, b, result)
818
 
 
819
 
    def test_deletion_overlap(self):
820
 
        """Delete overlapping regions with no other conflict.
821
 
 
822
 
        Arguably it'd be better to treat these as agreement, rather than 
823
 
        conflict, but for now conflict is safer.
824
 
        """
825
 
        base = """\
826
 
            start context
827
 
            int a() {}
828
 
            int b() {}
829
 
            int c() {}
830
 
            end context
831
 
            """
832
 
        a = """\
833
 
            start context
834
 
            int a() {}
835
 
            end context
836
 
            """
837
 
        b = """\
838
 
            start context
839
 
            int c() {}
840
 
            end context
841
 
            """
842
 
        result = """\
843
 
            start context
844
 
<<<<<<< 
845
 
            int a() {}
846
 
=======
847
 
            int c() {}
848
 
>>>>>>> 
849
 
            end context
850
 
            """
851
 
        self._test_merge_from_strings(base, a, b, result)
852
 
 
853
 
    def test_agreement_deletion(self):
854
 
        """Agree to delete some lines, without conflicts."""
855
 
        base = """\
856
 
            start context
857
 
            base line 1
858
 
            base line 2
859
 
            end context
860
 
            """
861
 
        a = """\
862
 
            start context
863
 
            base line 1
864
 
            end context
865
 
            """
866
 
        b = """\
867
 
            start context
868
 
            base line 1
869
 
            end context
870
 
            """
871
 
        result = """\
872
 
            start context
873
 
            base line 1
874
 
            end context
875
 
            """
876
 
        self._test_merge_from_strings(base, a, b, result)
877
 
 
878
 
    def test_sync_on_deletion(self):
879
 
        """Specific case of merge where we can synchronize incorrectly.
880
 
        
881
 
        A previous version of the weave merge concluded that the two versions
882
 
        agreed on deleting line 2, and this could be a synchronization point.
883
 
        Line 1 was then considered in isolation, and thought to be deleted on 
884
 
        both sides.
885
 
 
886
 
        It's better to consider the whole thing as a disagreement region.
887
 
        """
888
 
        base = """\
889
 
            start context
890
 
            base line 1
891
 
            base line 2
892
 
            end context
893
 
            """
894
 
        a = """\
895
 
            start context
896
 
            base line 1
897
 
            a's replacement line 2
898
 
            end context
899
 
            """
900
 
        b = """\
901
 
            start context
902
 
            b replaces
903
 
            both lines
904
 
            end context
905
 
            """
906
 
        result = """\
907
 
            start context
908
 
<<<<<<< 
909
 
            base line 1
910
 
            a's replacement line 2
911
 
=======
912
 
            b replaces
913
 
            both lines
914
 
>>>>>>> 
915
 
            end context
916
 
            """
917
 
        self._test_merge_from_strings(base, a, b, result)
918
 
 
919
 
 
920
 
class JoinWeavesTests(TestBase):
921
 
    def setUp(self):
922
 
        super(JoinWeavesTests, self).setUp()
923
 
        self.weave1 = Weave()
924
 
        self.lines1 = ['hello\n']
925
 
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
926
 
        self.weave1.add_lines('v1', [], self.lines1)
927
 
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
928
 
        self.weave1.add_lines('v3', ['v2'], self.lines3)
929
 
        
930
 
    def test_join_empty(self):
931
 
        """Join two empty weaves."""
932
 
        eq = self.assertEqual
933
 
        w1 = Weave()
934
 
        w2 = Weave()
935
 
        w1.join(w2)
936
 
        eq(len(w1), 0)
937
 
        
938
 
    def test_join_empty_to_nonempty(self):
939
 
        """Join empty weave onto nonempty."""
940
 
        self.weave1.join(Weave())
941
 
        self.assertEqual(len(self.weave1), 3)
942
 
 
943
 
    def test_join_unrelated(self):
944
 
        """Join two weaves with no history in common."""
945
 
        wb = Weave()
946
 
        wb.add_lines('b1', [], ['line from b\n'])
947
 
        w1 = self.weave1
948
 
        w1.join(wb)
949
 
        eq = self.assertEqual
950
 
        eq(len(w1), 4)
951
 
        eq(sorted(w1.versions()),
952
 
           ['b1', 'v1', 'v2', 'v3'])
953
 
 
954
 
    def test_join_related(self):
955
 
        wa = self.weave1.copy()
956
 
        wb = self.weave1.copy()
957
 
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
958
 
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
959
 
        eq = self.assertEquals
960
 
        eq(len(wa), 4)
961
 
        eq(len(wb), 4)
962
 
        wa.join(wb)
963
 
        eq(len(wa), 5)
964
 
        eq(wa.get_lines('b1'),
965
 
           ['hello\n', 'pale blue\n', 'world\n'])
966
 
 
967
 
    def test_join_parent_disagreement(self):
968
 
        #join reconciles differening parents into a union.
969
 
        wa = Weave()
970
 
        wb = Weave()
971
 
        wa.add_lines('v1', [], ['hello\n'])
972
 
        wb.add_lines('v0', [], [])
973
 
        wb.add_lines('v1', ['v0'], ['hello\n'])
974
 
        wa.join(wb)
975
 
        self.assertEqual(['v0'], wa.get_parents('v1'))
976
 
 
977
 
    def test_join_text_disagreement(self):
978
 
        """Cannot join weaves with different texts for a version."""
979
 
        wa = Weave()
980
 
        wb = Weave()
981
 
        wa.add_lines('v1', [], ['hello\n'])
982
 
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
983
 
        self.assertRaises(WeaveError,
984
 
                          wa.join, wb)
985
 
 
986
 
    def test_join_unordered(self):
987
 
        """Join weaves where indexes differ.
988
 
        
989
 
        The source weave contains a different version at index 0."""
990
 
        wa = self.weave1.copy()
991
 
        wb = Weave()
992
 
        wb.add_lines('x1', [], ['line from x1\n'])
993
 
        wb.add_lines('v1', [], ['hello\n'])
994
 
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
995
 
        wa.join(wb)
996
 
        eq = self.assertEquals
997
 
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
998
 
        eq(wa.get_text('x1'), 'line from x1\n')
999
 
 
1000
 
    def test_written_detection(self):
1001
 
        # Test detection of weave file corruption.
1002
 
        #
1003
 
        # Make sure that we can detect if a weave file has
1004
 
        # been corrupted. This doesn't test all forms of corruption,
1005
 
        # but it at least helps verify the data you get, is what you want.
1006
 
        from cStringIO import StringIO
1007
 
 
1008
 
        w = Weave()
1009
 
        w.add_lines('v1', [], ['hello\n'])
1010
 
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
1011
 
 
1012
 
        tmpf = StringIO()
1013
 
        write_weave(w, tmpf)
1014
 
 
1015
 
        # Because we are corrupting, we need to make sure we have the exact text
1016
 
        self.assertEquals('# bzr weave file v5\n'
1017
 
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
1018
 
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
1019
 
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
1020
 
                          tmpf.getvalue())
1021
 
 
1022
 
        # Change a single letter
1023
 
        tmpf = StringIO('# bzr weave file v5\n'
1024
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
1025
 
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
1026
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
1027
 
 
1028
 
        w = read_weave(tmpf)
1029
 
 
1030
 
        self.assertEqual('hello\n', w.get_text('v1'))
1031
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
1032
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
1033
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
1034
 
 
1035
 
        # Change the sha checksum
1036
 
        tmpf = StringIO('# bzr weave file v5\n'
1037
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
1038
 
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
1039
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
1040
 
 
1041
 
        w = read_weave(tmpf)
1042
 
 
1043
 
        self.assertEqual('hello\n', w.get_text('v1'))
1044
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
1045
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
1046
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
1047
 
 
1048
 
 
1049
 
class InstrumentedWeave(Weave):
1050
 
    """Keep track of how many times functions are called."""
1051
 
    
1052
 
    def __init__(self, weave_name=None):
1053
 
        self._extract_count = 0
1054
 
        Weave.__init__(self, weave_name=weave_name)
1055
 
 
1056
 
    def _extract(self, versions):
1057
 
        self._extract_count += 1
1058
 
        return Weave._extract(self, versions)
1059
 
 
1060
 
 
1061
 
class JoinOptimization(TestCase):
1062
 
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
1063
 
 
1064
 
    def test_join(self):
1065
 
        w1 = InstrumentedWeave()
1066
 
        w2 = InstrumentedWeave()
1067
 
 
1068
 
        txt0 = ['a\n']
1069
 
        txt1 = ['a\n', 'b\n']
1070
 
        txt2 = ['a\n', 'c\n']
1071
 
        txt3 = ['a\n', 'b\n', 'c\n']
1072
 
 
1073
 
        w1.add_lines('txt0', [], txt0) # extract 1a
1074
 
        w2.add_lines('txt0', [], txt0) # extract 1b
1075
 
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
1076
 
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
1077
 
        w1.join(w2) # extract 3a to add txt2 
1078
 
        w2.join(w1) # extract 3b to add txt1 
1079
 
 
1080
 
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
1081
 
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
1082
 
        # These secretly have inverted parents
1083
 
 
1084
 
        # This should not have to do any extractions
1085
 
        w1.join(w2) # NO extract, texts already present with same parents
1086
 
        w2.join(w1) # NO extract, texts already present with same parents
1087
 
 
1088
 
        self.assertEqual(4, w1._extract_count)
1089
 
        self.assertEqual(4, w2._extract_count)
1090
 
 
1091
 
    def test_double_parent(self):
1092
 
        # It should not be considered illegal to add
1093
 
        # a revision with the same parent twice
1094
 
        w1 = InstrumentedWeave()
1095
 
        w2 = InstrumentedWeave()
1096
 
 
1097
 
        txt0 = ['a\n']
1098
 
        txt1 = ['a\n', 'b\n']
1099
 
        txt2 = ['a\n', 'c\n']
1100
 
        txt3 = ['a\n', 'b\n', 'c\n']
1101
 
 
1102
 
        w1.add_lines('txt0', [], txt0)
1103
 
        w2.add_lines('txt0', [], txt0)
1104
 
        w1.add_lines('txt1', ['txt0'], txt1)
1105
 
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
1106
 
        # Same text, effectively the same, because the
1107
 
        # parent is only repeated
1108
 
        w1.join(w2) # extract 3a to add txt2 
1109
 
        w2.join(w1) # extract 3b to add txt1 
1110
 
 
1111
 
 
1112
 
class TestNeedsReweave(TestCase):
1113
 
    """Internal corner cases for when reweave is needed."""
1114
 
 
1115
 
    def test_compatible_parents(self):
1116
 
        w1 = Weave('a')
1117
 
        my_parents = set([1, 2, 3])
1118
 
        # subsets are ok
1119
 
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
1120
 
        # same sets
1121
 
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
1122
 
        # same empty corner case
1123
 
        self.assertTrue(w1._compatible_parents(set(), set()))
1124
 
        # other cannot contain stuff my_parents does not
1125
 
        self.assertFalse(w1._compatible_parents(set(), set([1])))
1126
 
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
1127
 
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
 
677
def testweave():
 
678
    import testsweet
 
679
    from unittest import TestSuite, TestLoader
 
680
    import testweave
 
681
 
 
682
    tl = TestLoader()
 
683
    suite = TestSuite()
 
684
    suite.addTest(tl.loadTestsFromModule(testweave))
 
685
    
 
686
    return int(not testsweet.run_suite(suite)) # for shell 0=true
 
687
 
 
688
 
 
689
if __name__ == '__main__':
 
690
    import sys
 
691
    sys.exit(testweave())
 
692