/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_versionedfile.py

  • Committer: Matt Nordhoff
  • Date: 2009-04-04 02:50:01 UTC
  • mfrom: (4253 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4256.
  • Revision ID: mnordhoff@mattnordhoff.com-20090404025001-z1403k0tatmc8l91
Merge bzr.dev, fixing conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005, 2009 Canonical Ltd
2
2
#
3
3
# Authors:
4
4
#   Johan Rydberg <jrydberg@gnu.org>
15
15
#
16
16
# You should have received a copy of the GNU General Public License
17
17
# along with this program; if not, write to the Free Software
18
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
19
 
20
20
 
21
21
# TODO: might be nice to create a versionedfile with some type of corruption
24
24
from itertools import chain, izip
25
25
from StringIO import StringIO
26
26
 
27
 
import bzrlib
28
27
from bzrlib import (
29
28
    errors,
 
29
    knit as _mod_knit,
30
30
    osutils,
31
31
    progress,
32
32
    )
35
35
                           RevisionAlreadyPresent,
36
36
                           WeaveParentMismatch
37
37
                           )
38
 
from bzrlib import knit as _mod_knit
39
38
from bzrlib.knit import (
40
39
    cleanup_pack_knit,
41
40
    make_file_factory,
43
42
    KnitAnnotateFactory,
44
43
    KnitPlainFactory,
45
44
    )
46
 
from bzrlib.symbol_versioning import one_four, one_five
47
45
from bzrlib.tests import (
48
46
    TestCase,
49
47
    TestCaseWithMemoryTransport,
50
48
    TestNotApplicable,
51
 
    TestScenarioApplier,
52
49
    TestSkipped,
53
50
    condition_isinstance,
54
51
    split_suite_by_condition,
55
 
    iter_suite_tests,
 
52
    multiply_tests,
56
53
    )
57
54
from bzrlib.tests.http_utils import TestCaseWithWebserver
58
55
from bzrlib.trace import mutter
76
73
    """Parameterize VersionedFiles tests for different implementations."""
77
74
    to_adapt, result = split_suite_by_condition(
78
75
        standard_tests, condition_isinstance(TestVersionedFiles))
79
 
    len_one_adapter = TestScenarioApplier()
80
 
    len_two_adapter = TestScenarioApplier()
81
76
    # We want to be sure of behaviour for:
82
77
    # weaves prefix layout (weave texts)
83
78
    # individually named weaves (weave inventories)
88
83
    # individual graph knits in packs (inventories)
89
84
    # individual graph nocompression knits in packs (revisions)
90
85
    # plain text knits in packs (texts)
91
 
    len_one_adapter.scenarios = [
 
86
    len_one_scenarios = [
92
87
        ('weave-named', {
93
88
            'cleanup':None,
94
89
            'factory':make_versioned_files_factory(WeaveFile,
126
121
            'support_partial_insertion': False,
127
122
            }),
128
123
        ]
129
 
    len_two_adapter.scenarios = [
 
124
    len_two_scenarios = [
130
125
        ('weave-prefix', {
131
126
            'cleanup':None,
132
127
            'factory':make_versioned_files_factory(WeaveFile,
150
145
            'support_partial_insertion': True,
151
146
            }),
152
147
        ]
153
 
    for test in iter_suite_tests(to_adapt):
154
 
        result.addTests(len_one_adapter.adapt(test))
155
 
        result.addTests(len_two_adapter.adapt(test))
156
 
    return result
 
148
    scenarios = len_one_scenarios + len_two_scenarios
 
149
    return multiply_tests(to_adapt, scenarios, result)
157
150
 
158
151
 
159
152
def get_diamond_vf(f, trailing_eol=True, left_only=False):
160
153
    """Get a diamond graph to exercise deltas and merges.
161
 
    
 
154
 
162
155
    :param trailing_eol: If True end the last line with \n.
163
156
    """
164
157
    parents = {
185
178
 
186
179
 
187
180
def get_diamond_files(files, key_length, trailing_eol=True, left_only=False,
188
 
    nograph=False):
 
181
    nograph=False, nokeys=False):
189
182
    """Get a diamond graph to exercise deltas and merges.
190
183
 
191
184
    This creates a 5-node graph in files. If files supports 2-length keys two
192
185
    graphs are made to exercise the support for multiple ids.
193
 
    
 
186
 
194
187
    :param trailing_eol: If True end the last line with \n.
195
188
    :param key_length: The length of keys in files. Currently supports length 1
196
189
        and 2 keys.
198
191
    :param nograph: If True, do not provide parents to the add_lines calls;
199
192
        this is useful for tests that need inserted data but have graphless
200
193
        stores.
 
194
    :param nokeys: If True, pass None is as the key for all insertions.
 
195
        Currently implies nograph.
201
196
    :return: The results of the add_lines calls.
202
197
    """
 
198
    if nokeys:
 
199
        nograph = True
203
200
    if key_length == 1:
204
201
        prefixes = [()]
205
202
    else:
216
213
        else:
217
214
            result = [prefix + suffix for suffix in suffix_list]
218
215
            return result
 
216
    def get_key(suffix):
 
217
        if nokeys:
 
218
            return (None, )
 
219
        else:
 
220
            return (suffix,)
219
221
    # we loop over each key because that spreads the inserts across prefixes,
220
222
    # which is how commit operates.
221
223
    for prefix in prefixes:
222
 
        result.append(files.add_lines(prefix + ('origin',), (),
 
224
        result.append(files.add_lines(prefix + get_key('origin'), (),
223
225
            ['origin' + last_char]))
224
226
    for prefix in prefixes:
225
 
        result.append(files.add_lines(prefix + ('base',),
 
227
        result.append(files.add_lines(prefix + get_key('base'),
226
228
            get_parents([('origin',)]), ['base' + last_char]))
227
229
    for prefix in prefixes:
228
 
        result.append(files.add_lines(prefix + ('left',),
 
230
        result.append(files.add_lines(prefix + get_key('left'),
229
231
            get_parents([('base',)]),
230
232
            ['base\n', 'left' + last_char]))
231
233
    if not left_only:
232
234
        for prefix in prefixes:
233
 
            result.append(files.add_lines(prefix + ('right',),
 
235
            result.append(files.add_lines(prefix + get_key('right'),
234
236
                get_parents([('base',)]),
235
237
                ['base\n', 'right' + last_char]))
236
238
        for prefix in prefixes:
237
 
            result.append(files.add_lines(prefix + ('merged',),
 
239
            result.append(files.add_lines(prefix + get_key('merged'),
238
240
                get_parents([('left',), ('right',)]),
239
241
                ['base\n', 'left\n', 'right\n', 'merged' + last_char]))
240
242
    return result
266
268
            self.assertEquals(f.get_lines('r1'), ['b\n', 'c\n'])
267
269
            self.assertEqual(2, len(f))
268
270
            self.assertEqual(2, f.num_versions())
269
 
    
 
271
 
270
272
            self.assertRaises(RevisionNotPresent,
271
273
                f.add_lines, 'r2', ['foo'], [])
272
274
            self.assertRaises(RevisionAlreadyPresent,
311
313
        verify_file(f)
312
314
 
313
315
    def test_add_unicode_content(self):
314
 
        # unicode content is not permitted in versioned files. 
 
316
        # unicode content is not permitted in versioned files.
315
317
        # versioned files version sequences of bytes only.
316
318
        vf = self.get_file()
317
319
        self.assertRaises(errors.BzrBadParameterUnicode,
340
342
    def test_inline_newline_throws(self):
341
343
        # \r characters are not permitted in lines being added
342
344
        vf = self.get_file()
343
 
        self.assertRaises(errors.BzrBadParameterContainsNewline, 
 
345
        self.assertRaises(errors.BzrBadParameterContainsNewline,
344
346
            vf.add_lines, 'a', [], ['a\n\n'])
345
347
        self.assertRaises(
346
348
            (errors.BzrBadParameterContainsNewline, NotImplementedError),
545
547
        f.add_lines('noeolbase', [], ['line'])
546
548
        # noeol preceeding its leftmost parent in the output:
547
549
        # this is done by making it a merge of two parents with no common
548
 
        # anestry: noeolbase and noeol with the 
 
550
        # anestry: noeolbase and noeol with the
549
551
        # later-inserted parent the leftmost.
550
552
        f.add_lines('eolbeforefirstparent', ['noeolbase', 'noeol'], ['line'])
551
553
        # two identical eol texts
632
634
        self._transaction = 'after'
633
635
        self.assertRaises(errors.OutSideTransaction, f.add_lines, '', [], [])
634
636
        self.assertRaises(errors.OutSideTransaction, f.add_lines_with_ghosts, '', [], [])
635
 
        
 
637
 
636
638
    def test_copy_to(self):
637
639
        f = self.get_file()
638
640
        f.add_lines('0', [], ['a\n'])
711
713
 
712
714
    def test_iter_lines_added_or_present_in_versions(self):
713
715
        # test that we get at least an equalset of the lines added by
714
 
        # versions in the weave 
 
716
        # versions in the weave
715
717
        # the ordering here is to make a tree so that dumb searches have
716
718
        # more changes to muck up.
717
719
 
751
753
                self.assertEqual(expected, progress.updates)
752
754
            return lines
753
755
        lines = iter_with_versions(['child', 'otherchild'],
754
 
                                   [('Walking content.', 0, 2),
755
 
                                    ('Walking content.', 1, 2),
756
 
                                    ('Walking content.', 2, 2)])
 
756
                                   [('Walking content', 0, 2),
 
757
                                    ('Walking content', 1, 2),
 
758
                                    ('Walking content', 2, 2)])
757
759
        # we must see child and otherchild
758
760
        self.assertTrue(lines[('child\n', 'child')] > 0)
759
761
        self.assertTrue(lines[('otherchild\n', 'otherchild')] > 0)
760
762
        # we dont care if we got more than that.
761
 
        
 
763
 
762
764
        # test all lines
763
 
        lines = iter_with_versions(None, [('Walking content.', 0, 5),
764
 
                                          ('Walking content.', 1, 5),
765
 
                                          ('Walking content.', 2, 5),
766
 
                                          ('Walking content.', 3, 5),
767
 
                                          ('Walking content.', 4, 5),
768
 
                                          ('Walking content.', 5, 5)])
 
765
        lines = iter_with_versions(None, [('Walking content', 0, 5),
 
766
                                          ('Walking content', 1, 5),
 
767
                                          ('Walking content', 2, 5),
 
768
                                          ('Walking content', 3, 5),
 
769
                                          ('Walking content', 4, 5),
 
770
                                          ('Walking content', 5, 5)])
769
771
        # all lines must be seen at least once
770
772
        self.assertTrue(lines[('base\n', 'base')] > 0)
771
773
        self.assertTrue(lines[('lancestor\n', 'lancestor')] > 0)
841
843
                          'base',
842
844
                          [],
843
845
                          [])
844
 
    
 
846
 
845
847
    def test_get_sha1s(self):
846
848
        # check the sha1 data is available
847
849
        vf = self.get_file()
857
859
            'b': '3f786850e387550fdab836ed7e6dc881de23001b',
858
860
            },
859
861
            vf.get_sha1s(['a', 'c', 'b']))
860
 
        
 
862
 
861
863
 
862
864
class TestWeave(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
863
865
 
870
872
            get_scope=self.get_transaction)
871
873
        w.add_lines('v1', [], ['hello\n'])
872
874
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
873
 
        
 
875
 
874
876
        # We are going to invasively corrupt the text
875
877
        # Make sure the internals of weave are the same
876
878
        self.assertEqual([('{', 0)
880
882
                        , 'there\n'
881
883
                        , ('}', None)
882
884
                        ], w._weave)
883
 
        
 
885
 
884
886
        self.assertEqual(['f572d396fae9206628714fb2ce00f72e94f2258f'
885
887
                        , '90f265c6e75f1c8f9ab76dcf85528352c5f215ef'
886
888
                        ], w._sha1s)
887
889
        w.check()
888
 
        
 
890
 
889
891
        # Corrupted
890
892
        w._weave[4] = 'There\n'
891
893
        return w
895
897
        # Corrected
896
898
        w._weave[4] = 'there\n'
897
899
        self.assertEqual('hello\nthere\n', w.get_text('v2'))
898
 
        
 
900
 
899
901
        #Invalid checksum, first digit changed
900
902
        w._sha1s[1] =  'f0f265c6e75f1c8f9ab76dcf85528352c5f215ef'
901
903
        return w
1010
1012
 
1011
1013
        def addcrlf(x):
1012
1014
            return x + '\n'
1013
 
        
 
1015
 
1014
1016
        w = self.get_file()
1015
1017
        w.add_lines('text0', [], map(addcrlf, base))
1016
1018
        w.add_lines('text1', ['text0'], map(addcrlf, a))
1032
1034
 
1033
1035
        mp = map(addcrlf, mp)
1034
1036
        self.assertEqual(mt.readlines(), mp)
1035
 
        
1036
 
        
 
1037
 
 
1038
 
1037
1039
    def testOneInsert(self):
1038
1040
        self.doMerge([],
1039
1041
                     ['aa'],
1057
1059
                     ['aaa', 'xxx', 'yyy', 'bbb'],
1058
1060
                     ['aaa', 'xxx', 'bbb'], self.overlappedInsertExpected)
1059
1061
 
1060
 
        # really it ought to reduce this to 
 
1062
        # really it ought to reduce this to
1061
1063
        # ['aaa', 'xxx', 'yyy', 'bbb']
1062
1064
 
1063
1065
 
1065
1067
        self.doMerge(['aaa'],
1066
1068
                     ['xxx'],
1067
1069
                     ['yyy', 'zzz'],
1068
 
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
 
1070
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz',
1069
1071
                      '>>>>>>> '])
1070
1072
 
1071
1073
    def testNonClashInsert1(self):
1072
1074
        self.doMerge(['aaa'],
1073
1075
                     ['xxx', 'aaa'],
1074
1076
                     ['yyy', 'zzz'],
1075
 
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
1077
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz',
1076
1078
                      '>>>>>>> '])
1077
1079
 
1078
1080
    def testNonClashInsert2(self):
1092
1094
        #######################################
1093
1095
        # skippd, not working yet
1094
1096
        return
1095
 
        
 
1097
 
1096
1098
        self.doMerge(['aaa', 'bbb', 'ccc'],
1097
1099
                     ['aaa', 'ddd', 'ccc'],
1098
1100
                     ['aaa', 'ccc'],
1141
1143
    def test_deletion_overlap(self):
1142
1144
        """Delete overlapping regions with no other conflict.
1143
1145
 
1144
 
        Arguably it'd be better to treat these as agreement, rather than 
 
1146
        Arguably it'd be better to treat these as agreement, rather than
1145
1147
        conflict, but for now conflict is safer.
1146
1148
        """
1147
1149
        base = """\
1163
1165
            """
1164
1166
        result = """\
1165
1167
            start context
1166
 
<<<<<<< 
 
1168
<<<<<<<\x20
1167
1169
            int a() {}
1168
1170
=======
1169
1171
            int c() {}
1170
 
>>>>>>> 
 
1172
>>>>>>>\x20
1171
1173
            end context
1172
1174
            """
1173
1175
        self._test_merge_from_strings(base, a, b, result)
1199
1201
 
1200
1202
    def test_sync_on_deletion(self):
1201
1203
        """Specific case of merge where we can synchronize incorrectly.
1202
 
        
 
1204
 
1203
1205
        A previous version of the weave merge concluded that the two versions
1204
1206
        agreed on deleting line 2, and this could be a synchronization point.
1205
 
        Line 1 was then considered in isolation, and thought to be deleted on 
 
1207
        Line 1 was then considered in isolation, and thought to be deleted on
1206
1208
        both sides.
1207
1209
 
1208
1210
        It's better to consider the whole thing as a disagreement region.
1227
1229
            """
1228
1230
        result = """\
1229
1231
            start context
1230
 
<<<<<<< 
 
1232
<<<<<<<\x20
1231
1233
            base line 1
1232
1234
            a's replacement line 2
1233
1235
=======
1234
1236
            b replaces
1235
1237
            both lines
1236
 
>>>>>>> 
 
1238
>>>>>>>\x20
1237
1239
            end context
1238
1240
            """
1239
1241
        self._test_merge_from_strings(base, a, b, result)
1250
1252
        write_weave(w, tmpf)
1251
1253
        self.log(tmpf.getvalue())
1252
1254
 
1253
 
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
 
1255
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======',
1254
1256
                                'xxx', '>>>>>>> ', 'bbb']
1255
1257
 
1256
1258
 
1365
1367
 
1366
1368
    def test_unannotated_to_fulltext(self):
1367
1369
        """Test adapting unannotated knits to full texts.
1368
 
        
 
1370
 
1369
1371
        This is used for -> weaves, and for -> annotated knits.
1370
1372
        """
1371
1373
        # we need a full text, and a delta
1384
1386
 
1385
1387
    def test_unannotated_to_fulltext_no_eol(self):
1386
1388
        """Test adapting unannotated knits to full texts.
1387
 
        
 
1389
 
1388
1390
        This is used for -> weaves, and for -> annotated knits.
1389
1391
        """
1390
1392
        # we need a full text, and a delta
1493
1495
        """Each parameterised test can be constructed on a transport."""
1494
1496
        files = self.get_versionedfiles()
1495
1497
 
1496
 
    def get_diamond_files(self, files, trailing_eol=True, left_only=False):
 
1498
    def get_diamond_files(self, files, trailing_eol=True, left_only=False,
 
1499
        nokeys=False):
1497
1500
        return get_diamond_files(files, self.key_length,
1498
1501
            trailing_eol=trailing_eol, nograph=not self.graph,
1499
 
            left_only=left_only)
 
1502
            left_only=left_only, nokeys=nokeys)
 
1503
 
 
1504
    def test_add_lines_nostoresha(self):
 
1505
        """When nostore_sha is supplied using old content raises."""
 
1506
        vf = self.get_versionedfiles()
 
1507
        empty_text = ('a', [])
 
1508
        sample_text_nl = ('b', ["foo\n", "bar\n"])
 
1509
        sample_text_no_nl = ('c', ["foo\n", "bar"])
 
1510
        shas = []
 
1511
        for version, lines in (empty_text, sample_text_nl, sample_text_no_nl):
 
1512
            sha, _, _ = vf.add_lines(self.get_simple_key(version), [], lines)
 
1513
            shas.append(sha)
 
1514
        # we now have a copy of all the lines in the vf.
 
1515
        for sha, (version, lines) in zip(
 
1516
            shas, (empty_text, sample_text_nl, sample_text_no_nl)):
 
1517
            new_key = self.get_simple_key(version + "2")
 
1518
            self.assertRaises(errors.ExistingContent,
 
1519
                vf.add_lines, new_key, [], lines,
 
1520
                nostore_sha=sha)
 
1521
            # and no new version should have been added.
 
1522
            record = vf.get_record_stream([new_key], 'unordered', True).next()
 
1523
            self.assertEqual('absent', record.storage_kind)
1500
1524
 
1501
1525
    def test_add_lines_return(self):
1502
1526
        files = self.get_versionedfiles()
1529
1553
                ('ed8bce375198ea62444dc71952b22cfc2b09226d', 23)],
1530
1554
                results)
1531
1555
 
 
1556
    def test_add_lines_no_key_generates_chk_key(self):
 
1557
        files = self.get_versionedfiles()
 
1558
        # save code by using the stock data insertion helper.
 
1559
        adds = self.get_diamond_files(files, nokeys=True)
 
1560
        results = []
 
1561
        # We can only validate the first 2 elements returned from add_lines.
 
1562
        for add in adds:
 
1563
            self.assertEqual(3, len(add))
 
1564
            results.append(add[:2])
 
1565
        if self.key_length == 1:
 
1566
            self.assertEqual([
 
1567
                ('00e364d235126be43292ab09cb4686cf703ddc17', 7),
 
1568
                ('51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44', 5),
 
1569
                ('a8478686da38e370e32e42e8a0c220e33ee9132f', 10),
 
1570
                ('9ef09dfa9d86780bdec9219a22560c6ece8e0ef1', 11),
 
1571
                ('ed8bce375198ea62444dc71952b22cfc2b09226d', 23)],
 
1572
                results)
 
1573
            # Check the added items got CHK keys.
 
1574
            self.assertEqual(set([
 
1575
                ('sha1:00e364d235126be43292ab09cb4686cf703ddc17',),
 
1576
                ('sha1:51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44',),
 
1577
                ('sha1:9ef09dfa9d86780bdec9219a22560c6ece8e0ef1',),
 
1578
                ('sha1:a8478686da38e370e32e42e8a0c220e33ee9132f',),
 
1579
                ('sha1:ed8bce375198ea62444dc71952b22cfc2b09226d',),
 
1580
                ]),
 
1581
                files.keys())
 
1582
        elif self.key_length == 2:
 
1583
            self.assertEqual([
 
1584
                ('00e364d235126be43292ab09cb4686cf703ddc17', 7),
 
1585
                ('00e364d235126be43292ab09cb4686cf703ddc17', 7),
 
1586
                ('51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44', 5),
 
1587
                ('51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44', 5),
 
1588
                ('a8478686da38e370e32e42e8a0c220e33ee9132f', 10),
 
1589
                ('a8478686da38e370e32e42e8a0c220e33ee9132f', 10),
 
1590
                ('9ef09dfa9d86780bdec9219a22560c6ece8e0ef1', 11),
 
1591
                ('9ef09dfa9d86780bdec9219a22560c6ece8e0ef1', 11),
 
1592
                ('ed8bce375198ea62444dc71952b22cfc2b09226d', 23),
 
1593
                ('ed8bce375198ea62444dc71952b22cfc2b09226d', 23)],
 
1594
                results)
 
1595
            # Check the added items got CHK keys.
 
1596
            self.assertEqual(set([
 
1597
                ('FileA', 'sha1:00e364d235126be43292ab09cb4686cf703ddc17'),
 
1598
                ('FileA', 'sha1:51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44'),
 
1599
                ('FileA', 'sha1:9ef09dfa9d86780bdec9219a22560c6ece8e0ef1'),
 
1600
                ('FileA', 'sha1:a8478686da38e370e32e42e8a0c220e33ee9132f'),
 
1601
                ('FileA', 'sha1:ed8bce375198ea62444dc71952b22cfc2b09226d'),
 
1602
                ('FileB', 'sha1:00e364d235126be43292ab09cb4686cf703ddc17'),
 
1603
                ('FileB', 'sha1:51c64a6f4fc375daf0d24aafbabe4d91b6f4bb44'),
 
1604
                ('FileB', 'sha1:9ef09dfa9d86780bdec9219a22560c6ece8e0ef1'),
 
1605
                ('FileB', 'sha1:a8478686da38e370e32e42e8a0c220e33ee9132f'),
 
1606
                ('FileB', 'sha1:ed8bce375198ea62444dc71952b22cfc2b09226d'),
 
1607
                ]),
 
1608
                files.keys())
 
1609
 
1532
1610
    def test_empty_lines(self):
1533
1611
        """Empty files can be stored."""
1534
1612
        f = self.get_versionedfiles()
1576
1654
        for factory in entries:
1577
1655
            on_seen(factory.key)
1578
1656
            self.assertValidStorageKind(factory.storage_kind)
1579
 
            self.assertEqual(f.get_sha1s([factory.key])[factory.key],
1580
 
                factory.sha1)
 
1657
            if factory.sha1 is not None:
 
1658
                self.assertEqual(f.get_sha1s([factory.key])[factory.key],
 
1659
                    factory.sha1)
1581
1660
            self.assertEqual(parents[factory.key], factory.parents)
1582
1661
            self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
1583
1662
                str)
1620
1699
                }
1621
1700
        return keys, sort_order
1622
1701
 
 
1702
    def get_keys_and_groupcompress_sort_order(self):
 
1703
        """Get diamond test keys list, and their groupcompress sort ordering."""
 
1704
        if self.key_length == 1:
 
1705
            keys = [('merged',), ('left',), ('right',), ('base',)]
 
1706
            sort_order = {('merged',):0, ('left',):1, ('right',):1, ('base',):2}
 
1707
        else:
 
1708
            keys = [
 
1709
                ('FileA', 'merged'), ('FileA', 'left'), ('FileA', 'right'),
 
1710
                ('FileA', 'base'),
 
1711
                ('FileB', 'merged'), ('FileB', 'left'), ('FileB', 'right'),
 
1712
                ('FileB', 'base'),
 
1713
                ]
 
1714
            sort_order = {
 
1715
                ('FileA', 'merged'):0, ('FileA', 'left'):1, ('FileA', 'right'):1,
 
1716
                ('FileA', 'base'):2,
 
1717
                ('FileB', 'merged'):3, ('FileB', 'left'):4, ('FileB', 'right'):4,
 
1718
                ('FileB', 'base'):5,
 
1719
                }
 
1720
        return keys, sort_order
 
1721
 
1623
1722
    def test_get_record_stream_interface_ordered(self):
1624
1723
        """each item in a stream has to provide a regular interface."""
1625
1724
        files = self.get_versionedfiles()
1653
1752
 
1654
1753
        self.assertStreamOrder(sort_order, seen, keys)
1655
1754
 
 
1755
    def test_get_record_stream_interface_groupcompress(self):
 
1756
        """each item in a stream has to provide a regular interface."""
 
1757
        files = self.get_versionedfiles()
 
1758
        self.get_diamond_files(files)
 
1759
        keys, sort_order = self.get_keys_and_groupcompress_sort_order()
 
1760
        parent_map = files.get_parent_map(keys)
 
1761
        entries = files.get_record_stream(keys, 'groupcompress', False)
 
1762
        seen = []
 
1763
        self.capture_stream(files, entries, seen.append, parent_map)
 
1764
        self.assertStreamOrder(sort_order, seen, keys)
 
1765
 
1656
1766
    def assertStreamOrder(self, sort_order, seen, keys):
1657
1767
        self.assertEqual(len(set(seen)), len(keys))
1658
1768
        if self.key_length == 1:
1689
1799
        for factory in entries:
1690
1800
            seen.add(factory.key)
1691
1801
            self.assertValidStorageKind(factory.storage_kind)
1692
 
            self.assertEqual(files.get_sha1s([factory.key])[factory.key],
1693
 
                factory.sha1)
 
1802
            if factory.sha1 is not None:
 
1803
                self.assertEqual(files.get_sha1s([factory.key])[factory.key],
 
1804
                                 factory.sha1)
1694
1805
            self.assertEqual(parent_map[factory.key], factory.parents)
1695
1806
            # currently no stream emits mpdiff
1696
1807
            self.assertRaises(errors.UnavailableRepresentation,
1751
1862
 
1752
1863
    def assertStreamMetaEqual(self, records, expected, stream):
1753
1864
        """Assert that streams expected and stream have the same records.
1754
 
        
 
1865
 
1755
1866
        :param records: A list to collect the seen records.
1756
1867
        :return: A generator of the records in stream.
1757
1868
        """
1770
1881
 
1771
1882
        :param skipped_records: A list with one element to increment when a
1772
1883
            record is skipped.
1773
 
        :param full_texts: A dict from key->fulltext representation, for 
 
1884
        :param full_texts: A dict from key->fulltext representation, for
1774
1885
            checking chunked or fulltext stored records.
1775
1886
        :param stream: A record_stream.
1776
1887
        :return: An iterator over the bytes of each record.
1894
2005
                self.assertEqual(None, factory.parents)
1895
2006
            else:
1896
2007
                self.assertValidStorageKind(factory.storage_kind)
1897
 
                self.assertEqual(files.get_sha1s([factory.key])[factory.key],
1898
 
                    factory.sha1)
 
2008
                if factory.sha1 is not None:
 
2009
                    sha1 = files.get_sha1s([factory.key])[factory.key]
 
2010
                    self.assertEqual(sha1, factory.sha1)
1899
2011
                self.assertEqual(parents[factory.key], factory.parents)
1900
2012
                self.assertIsInstance(factory.get_bytes_as(factory.storage_kind),
1901
2013
                    str)
1991
2103
            keys[4]: '9ef09dfa9d86780bdec9219a22560c6ece8e0ef1',
1992
2104
            },
1993
2105
            files.get_sha1s(keys))
1994
 
        
 
2106
 
1995
2107
    def test_insert_record_stream_empty(self):
1996
2108
        """Inserting an empty record stream should work."""
1997
2109
        files = self.get_versionedfiles()
2257
2369
            return lines
2258
2370
        lines = iter_with_keys(
2259
2371
            [self.get_simple_key('child'), self.get_simple_key('otherchild')],
2260
 
            [('Walking content.', 0, 2),
2261
 
             ('Walking content.', 1, 2),
2262
 
             ('Walking content.', 2, 2)])
 
2372
            [('Walking content', 0, 2),
 
2373
             ('Walking content', 1, 2),
 
2374
             ('Walking content', 2, 2)])
2263
2375
        # we must see child and otherchild
2264
2376
        self.assertTrue(lines[('child\n', self.get_simple_key('child'))] > 0)
2265
2377
        self.assertTrue(
2266
2378
            lines[('otherchild\n', self.get_simple_key('otherchild'))] > 0)
2267
2379
        # we dont care if we got more than that.
2268
 
        
 
2380
 
2269
2381
        # test all lines
2270
2382
        lines = iter_with_keys(files.keys(),
2271
 
            [('Walking content.', 0, 5),
2272
 
             ('Walking content.', 1, 5),
2273
 
             ('Walking content.', 2, 5),
2274
 
             ('Walking content.', 3, 5),
2275
 
             ('Walking content.', 4, 5),
2276
 
             ('Walking content.', 5, 5)])
 
2383
            [('Walking content', 0, 5),
 
2384
             ('Walking content', 1, 5),
 
2385
             ('Walking content', 2, 5),
 
2386
             ('Walking content', 3, 5),
 
2387
             ('Walking content', 4, 5),
 
2388
             ('Walking content', 5, 5)])
2277
2389
        # all lines must be seen at least once
2278
2390
        self.assertTrue(lines[('base\n', self.get_simple_key('base'))] > 0)
2279
2391
        self.assertTrue(
2312
2424
        files.add_lines(self.get_simple_key('noeolbase'), [], ['line'])
2313
2425
        # noeol preceeding its leftmost parent in the output:
2314
2426
        # this is done by making it a merge of two parents with no common
2315
 
        # anestry: noeolbase and noeol with the 
 
2427
        # anestry: noeolbase and noeol with the
2316
2428
        # later-inserted parent the leftmost.
2317
2429
        files.add_lines(self.get_simple_key('eolbeforefirstparent'),
2318
2430
            self.get_parents([self.get_simple_key('noeolbase'),
2404
2516
        TestCase.setUp(self)
2405
2517
        self._lines = {}
2406
2518
        self._parent_map = {}
2407
 
        self.texts = VirtualVersionedFiles(self._get_parent_map, 
 
2519
        self.texts = VirtualVersionedFiles(self._get_parent_map,
2408
2520
                                           self._lines.get)
2409
2521
 
2410
2522
    def test_add_lines(self):
2411
 
        self.assertRaises(NotImplementedError, 
 
2523
        self.assertRaises(NotImplementedError,
2412
2524
                self.texts.add_lines, "foo", [], [])
2413
2525
 
2414
2526
    def test_add_mpdiffs(self):
2415
 
        self.assertRaises(NotImplementedError, 
 
2527
        self.assertRaises(NotImplementedError,
2416
2528
                self.texts.add_mpdiffs, [])
2417
2529
 
2418
2530
    def test_check(self):
2432
2544
 
2433
2545
    def test_get_parent_map(self):
2434
2546
        self._parent_map = {"G": ("A", "B")}
2435
 
        self.assertEquals({("G",): (("A",),("B",))}, 
 
2547
        self.assertEquals({("G",): (("A",),("B",))},
2436
2548
                          self.texts.get_parent_map([("G",), ("L",)]))
2437
2549
 
2438
2550
    def test_get_record_stream(self):
2453
2565
        self._lines["B"] = ["HEY"]
2454
2566
        self._lines["C"] = ["Alberta"]
2455
2567
        it = self.texts.iter_lines_added_or_present_in_keys([("A",), ("B",)])
2456
 
        self.assertEquals(sorted([("FOO", "A"), ("BAR", "A"), ("HEY", "B")]), 
 
2568
        self.assertEquals(sorted([("FOO", "A"), ("BAR", "A"), ("HEY", "B")]),
2457
2569
            sorted(list(it)))
2458
2570
 
2459
2571