/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for Knit data structure"""
18
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
19
from cStringIO import StringIO
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
20
import difflib
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
21
import gzip
22
import sha
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
23
import sys
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
24
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
25
from bzrlib import (
26
    errors,
2484.1.5 by John Arbash Meinel
Simplistic implementations of custom parsers for options and parents
27
    generate_ids,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
28
    knit,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
29
    pack,
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
30
    )
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
31
from bzrlib.errors import (
32
    RevisionAlreadyPresent,
33
    KnitHeaderError,
34
    RevisionNotPresent,
35
    NoSuchFile,
36
    )
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
37
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
38
from bzrlib.knit import (
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
39
    AnnotatedKnitContent,
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
40
    KnitContent,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
41
    KnitSequenceMatcher,
42
    KnitVersionedFiles,
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
43
    PlainKnitContent,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
44
    _DirectPackAccess,
45
    _KndxIndex,
46
    _KnitGraphIndex,
47
    _KnitKeyAccess,
48
    make_file_factory,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
49
    )
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
50
from bzrlib.osutils import split_lines
3316.2.13 by Robert Collins
* ``VersionedFile.annotate_iter`` is deprecated. While in principal this
51
from bzrlib.symbol_versioning import one_four
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
52
from bzrlib.tests import (
53
    Feature,
54
    TestCase,
55
    TestCaseWithMemoryTransport,
56
    TestCaseWithTransport,
57
    )
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
58
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
59
from bzrlib.transport.memory import MemoryTransport
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
60
from bzrlib.tuned_gzip import GzipFile
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
61
from bzrlib.versionedfile import ConstantMapper
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
62
63
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
64
class _CompiledKnitFeature(Feature):
65
66
    def _probe(self):
67
        try:
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
68
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
69
        except ImportError:
70
            return False
71
        return True
72
73
    def feature_name(self):
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
74
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
75
76
CompiledKnitFeature = _CompiledKnitFeature()
77
78
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
79
class KnitContentTestsMixin(object):
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
80
81
    def test_constructor(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
82
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
83
84
    def test_text(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
85
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
86
        self.assertEqual(content.text(), [])
87
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
88
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
89
        self.assertEqual(content.text(), ["text1", "text2"])
90
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
91
    def test_copy(self):
92
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
93
        copy = content.copy()
94
        self.assertIsInstance(copy, content.__class__)
95
        self.assertEqual(copy.annotate(), content.annotate())
96
97
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
98
        """Assert that the derived matching blocks match real output"""
99
        source_lines = source.splitlines(True)
100
        target_lines = target.splitlines(True)
101
        def nl(line):
102
            if noeol and not line.endswith('\n'):
103
                return line + '\n'
104
            else:
105
                return line
106
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
107
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
108
        line_delta = source_content.line_delta(target_content)
109
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
110
            source_lines, target_lines))
111
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
112
        matcher_blocks = list(list(matcher.get_matching_blocks()))
113
        self.assertEqual(matcher_blocks, delta_blocks)
114
115
    def test_get_line_delta_blocks(self):
116
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
117
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
118
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
119
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
120
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
121
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
122
        self.assertDerivedBlocksEqual(TEXT_1A, '')
123
        self.assertDerivedBlocksEqual('', TEXT_1A)
124
        self.assertDerivedBlocksEqual('', '')
125
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
126
127
    def test_get_line_delta_blocks_noeol(self):
128
        """Handle historical knit deltas safely
129
130
        Some existing knit deltas don't consider the last line to differ
131
        when the only difference whether it has a final newline.
132
133
        New knit deltas appear to always consider the last line to differ
134
        in this case.
135
        """
136
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
137
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
138
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
139
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
140
141
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
142
TEXT_1 = """\
143
Banana cup cakes:
144
145
- bananas
146
- eggs
147
- broken tea cups
148
"""
149
150
TEXT_1A = """\
151
Banana cup cake recipe
152
(serves 6)
153
154
- bananas
155
- eggs
156
- broken tea cups
157
- self-raising flour
158
"""
159
160
TEXT_1B = """\
161
Banana cup cake recipe
162
163
- bananas (do not use plantains!!!)
164
- broken tea cups
165
- flour
166
"""
167
168
delta_1_1a = """\
169
0,1,2
170
Banana cup cake recipe
171
(serves 6)
172
5,5,1
173
- self-raising flour
174
"""
175
176
TEXT_2 = """\
177
Boeuf bourguignon
178
179
- beef
180
- red wine
181
- small onions
182
- carrot
183
- mushrooms
184
"""
185
186
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
187
class TestPlainKnitContent(TestCase, KnitContentTestsMixin):
188
189
    def _make_content(self, lines):
190
        annotated_content = AnnotatedKnitContent(lines)
191
        return PlainKnitContent(annotated_content.text(), 'bogus')
192
193
    def test_annotate(self):
194
        content = self._make_content([])
195
        self.assertEqual(content.annotate(), [])
196
197
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
198
        self.assertEqual(content.annotate(),
199
            [("bogus", "text1"), ("bogus", "text2")])
200
201
    def test_line_delta(self):
202
        content1 = self._make_content([("", "a"), ("", "b")])
203
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
204
        self.assertEqual(content1.line_delta(content2),
205
            [(1, 2, 2, ["a", "c"])])
206
207
    def test_line_delta_iter(self):
208
        content1 = self._make_content([("", "a"), ("", "b")])
209
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
210
        it = content1.line_delta_iter(content2)
211
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
212
        self.assertRaises(StopIteration, it.next)
213
214
215
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
216
217
    def _make_content(self, lines):
218
        return AnnotatedKnitContent(lines)
219
220
    def test_annotate(self):
221
        content = self._make_content([])
222
        self.assertEqual(content.annotate(), [])
223
224
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
225
        self.assertEqual(content.annotate(),
226
            [("origin1", "text1"), ("origin2", "text2")])
227
228
    def test_line_delta(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
229
        content1 = self._make_content([("", "a"), ("", "b")])
230
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
231
        self.assertEqual(content1.line_delta(content2),
232
            [(1, 2, 2, [("", "a"), ("", "c")])])
233
234
    def test_line_delta_iter(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
235
        content1 = self._make_content([("", "a"), ("", "b")])
236
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
237
        it = content1.line_delta_iter(content2)
238
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
239
        self.assertRaises(StopIteration, it.next)
240
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
241
242
class MockTransport(object):
243
244
    def __init__(self, file_lines=None):
245
        self.file_lines = file_lines
246
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
247
        # We have no base directory for the MockTransport
248
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
249
250
    def get(self, filename):
251
        if self.file_lines is None:
252
            raise NoSuchFile(filename)
253
        else:
254
            return StringIO("\n".join(self.file_lines))
255
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
256
    def readv(self, relpath, offsets):
257
        fp = self.get(relpath)
258
        for offset, size in offsets:
259
            fp.seek(offset)
260
            yield offset, fp.read(size)
261
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
262
    def __getattr__(self, name):
263
        def queue_call(*args, **kwargs):
264
            self.calls.append((name, args, kwargs))
265
        return queue_call
266
267
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
268
class KnitRecordAccessTestsMixin(object):
269
    """Tests for getting and putting knit records."""
270
271
    def test_add_raw_records(self):
272
        """Add_raw_records adds records retrievable later."""
273
        access = self.get_access()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
274
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
275
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
276
 
277
    def test_add_several_raw_records(self):
278
        """add_raw_records with many records and read some back."""
279
        access = self.get_access()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
280
        memos = access.add_raw_records([('key', 10), ('key2', 2), ('key3', 5)],
281
            '12345678901234567')
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
282
        self.assertEqual(['1234567890', '12', '34567'],
283
            list(access.get_raw_records(memos)))
284
        self.assertEqual(['1234567890'],
285
            list(access.get_raw_records(memos[0:1])))
286
        self.assertEqual(['12'],
287
            list(access.get_raw_records(memos[1:2])))
288
        self.assertEqual(['34567'],
289
            list(access.get_raw_records(memos[2:3])))
290
        self.assertEqual(['1234567890', '34567'],
291
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
292
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
293
294
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
295
    """Tests for the .kndx implementation."""
296
297
    def get_access(self):
298
        """Get a .knit style access instance."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
299
        mapper = ConstantMapper("foo")
300
        access = _KnitKeyAccess(self.get_transport(), mapper)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
301
        return access
302
    
303
304
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
305
    """Tests for the pack based access."""
306
307
    def get_access(self):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
308
        return self._get_access()[0]
309
310
    def _get_access(self, packname='packfile', index='FOO'):
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
311
        transport = self.get_transport()
312
        def write_data(bytes):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
313
            transport.append_bytes(packname, bytes)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
314
        writer = pack.ContainerWriter(write_data)
315
        writer.begin()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
316
        access = _DirectPackAccess({})
317
        access.set_writer(writer, index, (transport, packname))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
318
        return access, writer
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
319
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
320
    def test_read_from_several_packs(self):
321
        access, writer = self._get_access()
322
        memos = []
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
323
        memos.extend(access.add_raw_records([('key', 10)], '1234567890'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
324
        writer.end()
325
        access, writer = self._get_access('pack2', 'FOOBAR')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
326
        memos.extend(access.add_raw_records([('key', 5)], '12345'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
327
        writer.end()
328
        access, writer = self._get_access('pack3', 'BAZ')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
329
        memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
330
        writer.end()
331
        transport = self.get_transport()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
332
        access = _DirectPackAccess({"FOO":(transport, 'packfile'),
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
333
            "FOOBAR":(transport, 'pack2'),
334
            "BAZ":(transport, 'pack3')})
335
        self.assertEqual(['1234567890', '12345', 'alpha'],
336
            list(access.get_raw_records(memos)))
337
        self.assertEqual(['1234567890'],
338
            list(access.get_raw_records(memos[0:1])))
339
        self.assertEqual(['12345'],
340
            list(access.get_raw_records(memos[1:2])))
341
        self.assertEqual(['alpha'],
342
            list(access.get_raw_records(memos[2:3])))
343
        self.assertEqual(['1234567890', 'alpha'],
344
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
345
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
346
    def test_set_writer(self):
347
        """The writer should be settable post construction."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
348
        access = _DirectPackAccess({})
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
349
        transport = self.get_transport()
350
        packname = 'packfile'
351
        index = 'foo'
352
        def write_data(bytes):
353
            transport.append_bytes(packname, bytes)
354
        writer = pack.ContainerWriter(write_data)
355
        writer.begin()
356
        access.set_writer(writer, index, (transport, packname))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
357
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
358
        writer.end()
359
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
360
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
361
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
362
class LowLevelKnitDataTests(TestCase):
363
364
    def create_gz_content(self, text):
365
        sio = StringIO()
366
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
367
        gz_file.write(text)
368
        gz_file.close()
369
        return sio.getvalue()
370
371
    def test_valid_knit_data(self):
372
        sha1sum = sha.new('foo\nbar\n').hexdigest()
373
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
374
                                        'foo\n'
375
                                        'bar\n'
376
                                        'end rev-id-1\n'
377
                                        % (sha1sum,))
378
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
379
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
380
        knit = KnitVersionedFiles(None, access)
381
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
382
383
        contents = list(knit._read_records_iter(records))
384
        self.assertEqual([(('rev-id-1',), ['foo\n', 'bar\n'],
385
            '4e48e2c9a3d2ca8a708cb0cc545700544efb5021')], contents)
386
387
        raw_contents = list(knit._read_records_iter_raw(records))
388
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
389
390
    def test_not_enough_lines(self):
391
        sha1sum = sha.new('foo\n').hexdigest()
392
        # record says 2 lines data says 1
393
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
394
                                        'foo\n'
395
                                        'end rev-id-1\n'
396
                                        % (sha1sum,))
397
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
398
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
399
        knit = KnitVersionedFiles(None, access)
400
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
401
        self.assertRaises(errors.KnitCorrupt, list,
402
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
403
404
        # read_records_iter_raw won't detect that sort of mismatch/corruption
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
405
        raw_contents = list(knit._read_records_iter_raw(records))
406
        self.assertEqual([(('rev-id-1',),  gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
407
408
    def test_too_many_lines(self):
409
        sha1sum = sha.new('foo\nbar\n').hexdigest()
410
        # record says 1 lines data says 2
411
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
412
                                        'foo\n'
413
                                        'bar\n'
414
                                        'end rev-id-1\n'
415
                                        % (sha1sum,))
416
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
417
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
418
        knit = KnitVersionedFiles(None, access)
419
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
420
        self.assertRaises(errors.KnitCorrupt, list,
421
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
422
423
        # read_records_iter_raw won't detect that sort of mismatch/corruption
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
424
        raw_contents = list(knit._read_records_iter_raw(records))
425
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
426
427
    def test_mismatched_version_id(self):
428
        sha1sum = sha.new('foo\nbar\n').hexdigest()
429
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
430
                                        'foo\n'
431
                                        'bar\n'
432
                                        'end rev-id-1\n'
433
                                        % (sha1sum,))
434
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
435
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
436
        knit = KnitVersionedFiles(None, access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
437
        # We are asking for rev-id-2, but the data is rev-id-1
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
438
        records = [(('rev-id-2',), (('rev-id-2',), 0, len(gz_txt)))]
439
        self.assertRaises(errors.KnitCorrupt, list,
440
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
441
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
442
        # read_records_iter_raw detects mismatches in the header
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
443
        self.assertRaises(errors.KnitCorrupt, list,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
444
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
445
446
    def test_uncompressed_data(self):
447
        sha1sum = sha.new('foo\nbar\n').hexdigest()
448
        txt = ('version rev-id-1 2 %s\n'
449
               'foo\n'
450
               'bar\n'
451
               'end rev-id-1\n'
452
               % (sha1sum,))
453
        transport = MockTransport([txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
454
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
455
        knit = KnitVersionedFiles(None, access)
456
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
457
458
        # We don't have valid gzip data ==> corrupt
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
459
        self.assertRaises(errors.KnitCorrupt, list,
460
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
461
462
        # read_records_iter_raw will notice the bad data
463
        self.assertRaises(errors.KnitCorrupt, list,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
464
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
465
466
    def test_corrupted_data(self):
467
        sha1sum = sha.new('foo\nbar\n').hexdigest()
468
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
469
                                        'foo\n'
470
                                        'bar\n'
471
                                        'end rev-id-1\n'
472
                                        % (sha1sum,))
473
        # Change 2 bytes in the middle to \xff
474
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
475
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
476
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
477
        knit = KnitVersionedFiles(None, access)
478
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
479
        self.assertRaises(errors.KnitCorrupt, list,
480
            knit._read_records_iter(records))
481
        # read_records_iter_raw will barf on bad gz data
482
        self.assertRaises(errors.KnitCorrupt, list,
483
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
484
485
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
486
class LowLevelKnitIndexTests(TestCase):
487
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
488
    def get_knit_index(self, transport, name, mode):
489
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
490
        orig = knit._load_data
491
        def reset():
492
            knit._load_data = orig
493
        self.addCleanup(reset)
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
494
        from bzrlib._knit_load_data_py import _load_data_py
495
        knit._load_data = _load_data_py
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
496
        allow_writes = lambda: 'w' in mode
497
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
498
499
    def test_create_file(self):
500
        transport = MockTransport()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
501
        index = self.get_knit_index(transport, "filename", "w")
502
        index.keys()
503
        call = transport.calls.pop(0)
504
        # call[1][1] is a StringIO - we can't test it by simple equality.
505
        self.assertEqual('put_file_non_atomic', call[0])
506
        self.assertEqual('filename.kndx', call[1][0])
507
        # With no history, _KndxIndex writes a new index:
508
        self.assertEqual(_KndxIndex.HEADER,
509
            call[1][1].getvalue())
510
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
511
512
    def test_read_utf8_version_id(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
513
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
514
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
515
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
516
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
517
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
518
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
519
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
520
        # _KndxIndex is a private class, and deals in utf8 revision_ids, not
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
521
        # Unicode revision_ids.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
522
        self.assertEqual({(utf8_revision_id,):()},
523
            index.get_parent_map(index.keys()))
524
        self.assertFalse((unicode_revision_id,) in index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
525
526
    def test_read_utf8_parents(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
527
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
528
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
529
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
530
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
531
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
532
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
533
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
534
        self.assertEqual({("version",):((utf8_revision_id,),)},
535
            index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
536
537
    def test_read_ignore_corrupted_lines(self):
538
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
539
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
540
            "corrupted",
541
            "corrupted options 0 1 .b .c ",
542
            "version options 0 1 :"
543
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
544
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
545
        self.assertEqual(1, len(index.keys()))
546
        self.assertEqual(set([("version",)]), index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
547
548
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
549
        transport = MockTransport(['not a bzr knit index header\n'])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
550
        index = self.get_knit_index(transport, "filename", "r")
551
        self.assertRaises(KnitHeaderError, index.keys)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
552
553
    def test_read_duplicate_entries(self):
554
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
555
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
556
            "parent options 0 1 :",
557
            "version options1 0 1 0 :",
558
            "version options2 1 2 .other :",
559
            "version options3 3 4 0 .other :"
560
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
561
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
562
        self.assertEqual(2, len(index.keys()))
2592.3.8 by Robert Collins
Remove unneeded pulib method lookup on private class _KnitIndex.
563
        # check that the index used is the first one written. (Specific
564
        # to KnitIndex style indices.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
565
        self.assertEqual("1", index._dictionary_compress([("version",)]))
566
        self.assertEqual((("version",), 3, 4), index.get_position(("version",)))
567
        self.assertEqual(["options3"], index.get_options(("version",)))
568
        self.assertEqual({("version",):(("parent",), ("other",))},
569
            index.get_parent_map([("version",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
570
571
    def test_read_compressed_parents(self):
572
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
573
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
574
            "a option 0 1 :",
575
            "b option 0 1 0 :",
576
            "c option 0 1 1 0 :",
577
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
578
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
579
        self.assertEqual({("b",):(("a",),), ("c",):(("b",), ("a",))},
580
            index.get_parent_map([("b",), ("c",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
581
582
    def test_write_utf8_version_id(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
583
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
584
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
585
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
586
            _KndxIndex.HEADER
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
587
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
588
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
589
        index.add_records([
590
            ((utf8_revision_id,), ["option"], ((utf8_revision_id,), 0, 1), [])])
591
        call = transport.calls.pop(0)
592
        # call[1][1] is a StringIO - we can't test it by simple equality.
593
        self.assertEqual('put_file_non_atomic', call[0])
594
        self.assertEqual('filename.kndx', call[1][0])
595
        # With no history, _KndxIndex writes a new index:
596
        self.assertEqual(_KndxIndex.HEADER +
597
            "\n%s option 0 1  :" % (utf8_revision_id,),
598
            call[1][1].getvalue())
599
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
600
601
    def test_write_utf8_parents(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
602
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
603
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
604
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
605
            _KndxIndex.HEADER
606
            ])
607
        index = self.get_knit_index(transport, "filename", "r")
608
        index.add_records([
609
            (("version",), ["option"], (("version",), 0, 1), [(utf8_revision_id,)])])
610
        call = transport.calls.pop(0)
611
        # call[1][1] is a StringIO - we can't test it by simple equality.
612
        self.assertEqual('put_file_non_atomic', call[0])
613
        self.assertEqual('filename.kndx', call[1][0])
614
        # With no history, _KndxIndex writes a new index:
615
        self.assertEqual(_KndxIndex.HEADER +
616
            "\nversion option 0 1 .%s :" % (utf8_revision_id,),
617
            call[1][1].getvalue())
618
        self.assertEqual({'create_parent_dir': True}, call[2])
619
620
    def test_keys(self):
621
        transport = MockTransport([
622
            _KndxIndex.HEADER
623
            ])
624
        index = self.get_knit_index(transport, "filename", "r")
625
626
        self.assertEqual(set(), index.keys())
627
628
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
629
        self.assertEqual(set([("a",)]), index.keys())
630
631
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
632
        self.assertEqual(set([("a",)]), index.keys())
633
634
        index.add_records([(("b",), ["option"], (("b",), 0, 1), [])])
635
        self.assertEqual(set([("a",), ("b",)]), index.keys())
636
637
    def add_a_b(self, index, random_id=None):
638
        kwargs = {}
639
        if random_id is not None:
640
            kwargs["random_id"] = random_id
641
        index.add_records([
642
            (("a",), ["option"], (("a",), 0, 1), [("b",)]),
643
            (("a",), ["opt"], (("a",), 1, 2), [("c",)]),
644
            (("b",), ["option"], (("b",), 2, 3), [("a",)])
645
            ], **kwargs)
646
647
    def assertIndexIsAB(self, index):
648
        self.assertEqual({
649
            ('a',): (('c',),),
650
            ('b',): (('a',),),
651
            },
652
            index.get_parent_map(index.keys()))
653
        self.assertEqual((("a",), 1, 2), index.get_position(("a",)))
654
        self.assertEqual((("b",), 2, 3), index.get_position(("b",)))
655
        self.assertEqual(["opt"], index.get_options(("a",)))
656
657
    def test_add_versions(self):
658
        transport = MockTransport([
659
            _KndxIndex.HEADER
660
            ])
661
        index = self.get_knit_index(transport, "filename", "r")
662
663
        self.add_a_b(index)
664
        call = transport.calls.pop(0)
665
        # call[1][1] is a StringIO - we can't test it by simple equality.
666
        self.assertEqual('put_file_non_atomic', call[0])
667
        self.assertEqual('filename.kndx', call[1][0])
668
        # With no history, _KndxIndex writes a new index:
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
669
        self.assertEqual(
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
670
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
671
            "\na option 0 1 .b :"
672
            "\na opt 1 2 .c :"
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
673
            "\nb option 2 3 0 :",
674
            call[1][1].getvalue())
675
        self.assertEqual({'create_parent_dir': True}, call[2])
676
        self.assertIndexIsAB(index)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
677
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
678
    def test_add_versions_random_id_is_accepted(self):
679
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
680
            _KndxIndex.HEADER
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
681
            ])
682
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
683
        self.add_a_b(index, random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
684
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
685
    def test_delay_create_and_add_versions(self):
686
        transport = MockTransport()
687
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
688
        index = self.get_knit_index(transport, "filename", "w")
689
        # dir_mode=0777)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
690
        self.assertEqual([], transport.calls)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
691
        self.add_a_b(index)
692
        #self.assertEqual(
693
        #[    {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
694
        #    kwargs)
695
        # Two calls: one during which we load the existing index (and when its
696
        # missing create it), then a second where we write the contents out.
697
        self.assertEqual(2, len(transport.calls))
698
        call = transport.calls.pop(0)
699
        self.assertEqual('put_file_non_atomic', call[0])
700
        self.assertEqual('filename.kndx', call[1][0])
701
        # With no history, _KndxIndex writes a new index:
702
        self.assertEqual(_KndxIndex.HEADER, call[1][1].getvalue())
703
        self.assertEqual({'create_parent_dir': True}, call[2])
704
        call = transport.calls.pop(0)
705
        # call[1][1] is a StringIO - we can't test it by simple equality.
706
        self.assertEqual('put_file_non_atomic', call[0])
707
        self.assertEqual('filename.kndx', call[1][0])
708
        # With no history, _KndxIndex writes a new index:
709
        self.assertEqual(
710
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
711
            "\na option 0 1 .b :"
712
            "\na opt 1 2 .c :"
713
            "\nb option 2 3 0 :",
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
714
            call[1][1].getvalue())
715
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
716
717
    def test_get_position(self):
718
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
719
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
720
            "a option 0 1 :",
721
            "b option 1 2 :"
722
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
723
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
724
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
725
        self.assertEqual((("a",), 0, 1), index.get_position(("a",)))
726
        self.assertEqual((("b",), 1, 2), index.get_position(("b",)))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
727
728
    def test_get_method(self):
729
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
730
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
731
            "a fulltext,unknown 0 1 :",
732
            "b unknown,line-delta 1 2 :",
733
            "c bad 3 4 :"
734
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
735
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
736
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
737
        self.assertEqual("fulltext", index.get_method("a"))
738
        self.assertEqual("line-delta", index.get_method("b"))
739
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
740
741
    def test_get_options(self):
742
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
743
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
744
            "a opt1 0 1 :",
745
            "b opt2,opt3 1 2 :"
746
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
747
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
748
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
749
        self.assertEqual(["opt1"], index.get_options("a"))
750
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
751
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
752
    def test_get_parent_map(self):
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
753
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
754
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
755
            "a option 0 1 :",
756
            "b option 1 2 0 .c :",
757
            "c option 1 2 1 0 .e :"
758
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
759
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
760
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
761
        self.assertEqual({
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
762
            ("a",):(),
763
            ("b",):(("a",), ("c",)),
764
            ("c",):(("b",), ("a",), ("e",)),
765
            }, index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
766
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
767
    def test_impossible_parent(self):
768
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
769
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
770
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
771
            "a option 0 1 :",
772
            "b option 0 1 4 :"  # We don't have a 4th record
773
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
774
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
775
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
776
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
777
        except TypeError, e:
778
            if (str(e) == ('exceptions must be strings, classes, or instances,'
779
                           ' not exceptions.IndexError')
780
                and sys.version_info[0:2] >= (2,5)):
781
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
782
                                  ' raising new style exceptions with python'
783
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
784
            else:
785
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
786
787
    def test_corrupted_parent(self):
788
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
789
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
790
            "a option 0 1 :",
791
            "b option 0 1 :",
792
            "c option 0 1 1v :", # Can't have a parent of '1v'
793
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
794
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
795
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
796
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
797
        except TypeError, e:
798
            if (str(e) == ('exceptions must be strings, classes, or instances,'
799
                           ' not exceptions.ValueError')
800
                and sys.version_info[0:2] >= (2,5)):
801
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
802
                                  ' raising new style exceptions with python'
803
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
804
            else:
805
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
806
807
    def test_corrupted_parent_in_list(self):
808
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
809
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
810
            "a option 0 1 :",
811
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
812
            "c option 0 1 1 v :", # Can't have a parent of 'v'
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
813
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
814
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
815
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
816
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
817
        except TypeError, e:
818
            if (str(e) == ('exceptions must be strings, classes, or instances,'
819
                           ' not exceptions.ValueError')
820
                and sys.version_info[0:2] >= (2,5)):
821
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
822
                                  ' raising new style exceptions with python'
823
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
824
            else:
825
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
826
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
827
    def test_invalid_position(self):
828
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
829
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
830
            "a option 1v 1 :",
831
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
832
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
833
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
834
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
835
        except TypeError, e:
836
            if (str(e) == ('exceptions must be strings, classes, or instances,'
837
                           ' not exceptions.ValueError')
838
                and sys.version_info[0:2] >= (2,5)):
839
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
840
                                  ' raising new style exceptions with python'
841
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
842
            else:
843
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
844
845
    def test_invalid_size(self):
846
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
847
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
848
            "a option 1 1v :",
849
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
850
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
851
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
852
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
853
        except TypeError, e:
854
            if (str(e) == ('exceptions must be strings, classes, or instances,'
855
                           ' not exceptions.ValueError')
856
                and sys.version_info[0:2] >= (2,5)):
857
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
858
                                  ' raising new style exceptions with python'
859
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
860
            else:
861
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
862
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
863
    def test_short_line(self):
864
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
865
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
866
            "a option 0 10  :",
867
            "b option 10 10 0", # This line isn't terminated, ignored
868
            ])
869
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
870
        self.assertEqual(set([('a',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
871
872
    def test_skip_incomplete_record(self):
873
        # A line with bogus data should just be skipped
874
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
875
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
876
            "a option 0 10  :",
877
            "b option 10 10 0", # This line isn't terminated, ignored
878
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
879
            ])
880
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
881
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
882
883
    def test_trailing_characters(self):
884
        # A line with bogus data should just be skipped
885
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
886
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
887
            "a option 0 10  :",
888
            "b option 10 10 0 :a", # This line has extra trailing characters
889
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
890
            ])
891
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
892
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
893
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
894
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
895
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
896
897
    _test_needs_features = [CompiledKnitFeature]
898
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
899
    def get_knit_index(self, transport, name, mode):
900
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
901
        orig = knit._load_data
902
        def reset():
903
            knit._load_data = orig
904
        self.addCleanup(reset)
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
905
        from bzrlib._knit_load_data_c import _load_data_c
906
        knit._load_data = _load_data_c
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
907
        allow_writes = lambda: mode == 'w'
908
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
909
910
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
911
class KnitTests(TestCaseWithTransport):
912
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
913
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
914
    def make_test_knit(self, annotate=False, name='test'):
915
        mapper = ConstantMapper(name)
916
        return make_file_factory(annotate, mapper)(self.get_transport())
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
917
918
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
919
class TestKnitIndex(KnitTests):
920
921
    def test_add_versions_dictionary_compresses(self):
922
        """Adding versions to the index should update the lookup dict"""
923
        knit = self.make_test_knit()
924
        idx = knit._index
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
925
        idx.add_records([(('a-1',), ['fulltext'], (('a-1',), 0, 0), [])])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
926
        self.check_file_contents('test.kndx',
927
            '# bzr knit index 8\n'
928
            '\n'
929
            'a-1 fulltext 0 0  :'
930
            )
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
931
        idx.add_records([
932
            (('a-2',), ['fulltext'], (('a-2',), 0, 0), [('a-1',)]),
933
            (('a-3',), ['fulltext'], (('a-3',), 0, 0), [('a-2',)]),
934
            ])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
935
        self.check_file_contents('test.kndx',
936
            '# bzr knit index 8\n'
937
            '\n'
938
            'a-1 fulltext 0 0  :\n'
939
            'a-2 fulltext 0 0 0 :\n'
940
            'a-3 fulltext 0 0 1 :'
941
            )
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
942
        self.assertEqual(set([('a-3',), ('a-1',), ('a-2',)]), idx.keys())
943
        self.assertEqual({
944
            ('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False)),
945
            ('a-2',): ((('a-2',), 0, 0), None, (('a-1',),), ('fulltext', False)),
946
            ('a-3',): ((('a-3',), 0, 0), None, (('a-2',),), ('fulltext', False)),
947
            }, idx.get_build_details(idx.keys()))
948
        self.assertEqual({('a-1',):(),
949
            ('a-2',):(('a-1',),),
950
            ('a-3',):(('a-2',),),},
951
            idx.get_parent_map(idx.keys()))
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
952
953
    def test_add_versions_fails_clean(self):
954
        """If add_versions fails in the middle, it restores a pristine state.
955
956
        Any modifications that are made to the index are reset if all versions
957
        cannot be added.
958
        """
959
        # This cheats a little bit by passing in a generator which will
960
        # raise an exception before the processing finishes
961
        # Other possibilities would be to have an version with the wrong number
962
        # of entries, or to make the backing transport unable to write any
963
        # files.
964
965
        knit = self.make_test_knit()
966
        idx = knit._index
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
967
        idx.add_records([(('a-1',), ['fulltext'], (('a-1',), 0, 0), [])])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
968
969
        class StopEarly(Exception):
970
            pass
971
972
        def generate_failure():
973
            """Add some entries and then raise an exception"""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
974
            yield (('a-2',), ['fulltext'], (None, 0, 0), ('a-1',))
975
            yield (('a-3',), ['fulltext'], (None, 0, 0), ('a-2',))
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
976
            raise StopEarly()
977
978
        # Assert the pre-condition
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
979
        def assertA1Only():
980
            self.assertEqual(set([('a-1',)]), set(idx.keys()))
981
            self.assertEqual(
982
                {('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False))},
983
                idx.get_build_details([('a-1',)]))
984
            self.assertEqual({('a-1',):()}, idx.get_parent_map(idx.keys()))
985
986
        assertA1Only()
987
        self.assertRaises(StopEarly, idx.add_records, generate_failure())
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
988
        # And it shouldn't be modified
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
989
        assertA1Only()
2171.1.1 by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt.
990
991
    def test_knit_index_ignores_empty_files(self):
992
        # There was a race condition in older bzr, where a ^C at the right time
993
        # could leave an empty .kndx file, which bzr would later claim was a
994
        # corrupted file since the header was not present. In reality, the file
995
        # just wasn't created, so it should be ignored.
996
        t = get_transport('.')
997
        t.put_bytes('test.kndx', '')
998
999
        knit = self.make_test_knit()
1000
1001
    def test_knit_index_checks_header(self):
1002
        t = get_transport('.')
1003
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1004
        k = self.make_test_knit()
1005
        self.assertRaises(KnitHeaderError, k.keys)
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1006
1007
1008
class TestGraphIndexKnit(KnitTests):
1009
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
1010
1011
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1012
        builder = GraphIndexBuilder(ref_lists)
1013
        for node, references, value in nodes:
1014
            builder.add_node(node, references, value)
1015
        stream = builder.finish()
1016
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1017
        size = trans.put_file(name, stream)
1018
        return GraphIndex(trans, name, size)
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1019
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1020
    def two_graph_index(self, deltas=False, catch_adds=False):
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1021
        """Build a two-graph index.
1022
1023
        :param deltas: If true, use underlying indices with two node-ref
1024
            lists and 'parent' set to a delta-compressed against tail.
1025
        """
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1026
        # build a complex graph across several indices.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1027
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1028
            # delta compression inn the index
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1029
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1030
                (('tip', ), 'N0 100', ([('parent', )], [], )),
1031
                (('tail', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1032
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1033
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
1034
                (('separate', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1035
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1036
            # just blob location and graph in the index.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1037
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1038
                (('tip', ), 'N0 100', ([('parent', )], )),
1039
                (('tail', ), '', ([], ))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1040
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1041
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
1042
                (('separate', ), '', ([], ))])
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1043
        combined_index = CombinedGraphIndex([index1, index2])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1044
        if catch_adds:
1045
            self.combined_index = combined_index
1046
            self.caught_entries = []
1047
            add_callback = self.catch_add
1048
        else:
1049
            add_callback = None
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1050
        return _KnitGraphIndex(combined_index, lambda:True, deltas=deltas,
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1051
            add_callback=add_callback)
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
1052
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1053
    def test_keys(self):
1054
        index = self.two_graph_index()
1055
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1056
            set(index.keys()))
2592.3.9 by Robert Collins
Implement KnitGraphIndex.has_version.
1057
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1058
    def test_get_position(self):
1059
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1060
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position(('tip',)))
1061
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position(('parent',)))
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1062
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1063
    def test_get_method_deltas(self):
1064
        index = self.two_graph_index(deltas=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1065
        self.assertEqual('fulltext', index.get_method(('tip',)))
1066
        self.assertEqual('line-delta', index.get_method(('parent',)))
2592.3.11 by Robert Collins
Implement KnitGraphIndex.get_method.
1067
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1068
    def test_get_method_no_deltas(self):
1069
        # check that the parent-history lookup is ignored with deltas=False.
1070
        index = self.two_graph_index(deltas=False)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1071
        self.assertEqual('fulltext', index.get_method(('tip',)))
1072
        self.assertEqual('fulltext', index.get_method(('parent',)))
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1073
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1074
    def test_get_options_deltas(self):
1075
        index = self.two_graph_index(deltas=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1076
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1077
        self.assertEqual(['line-delta'], index.get_options(('parent',)))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1078
1079
    def test_get_options_no_deltas(self):
1080
        # check that the parent-history lookup is ignored with deltas=False.
1081
        index = self.two_graph_index(deltas=False)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1082
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1083
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1084
1085
    def test_get_parent_map(self):
1086
        index = self.two_graph_index()
1087
        self.assertEqual({('parent',):(('tail',), ('ghost',))},
1088
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1089
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1090
    def catch_add(self, entries):
1091
        self.caught_entries.append(entries)
1092
1093
    def test_add_no_callback_errors(self):
1094
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1095
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1096
            [(('new',), 'fulltext,no-eol', (None, 50, 60), ['separate'])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1097
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1098
    def test_add_version_smoke(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1099
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1100
        index.add_records([(('new',), 'fulltext,no-eol', (None, 50, 60),
1101
            [('separate',)])])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1102
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1103
            self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1104
1105
    def test_add_version_delta_not_delta_index(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1106
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1107
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1108
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1109
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1110
1111
    def test_add_version_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1112
        index = self.two_graph_index(catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1113
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1114
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
1115
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
1116
        # position/length are ignored (because each pack could have fulltext or
1117
        # delta, and be at a different position.
1118
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1119
            [('parent',)])])
1120
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1121
            [('parent',)])])
1122
        # but neither should have added data:
1123
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1124
        
1125
    def test_add_version_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1126
        index = self.two_graph_index(deltas=True, catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1127
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1128
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1129
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1130
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1131
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1132
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1133
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1134
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1135
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1136
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1137
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1138
        
1139
    def test_add_versions_nodeltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1140
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1141
        index.add_records([
1142
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1143
                (('new2',), 'fulltext', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1144
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1145
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
1146
            (('new2', ), ' 0 6', ((('new',),),))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1147
            sorted(self.caught_entries[0]))
1148
        self.assertEqual(1, len(self.caught_entries))
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1149
1150
    def test_add_versions_deltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1151
        index = self.two_graph_index(deltas=True, catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1152
        index.add_records([
1153
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1154
                (('new2',), 'line-delta', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1155
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1156
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
1157
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1158
            sorted(self.caught_entries[0]))
1159
        self.assertEqual(1, len(self.caught_entries))
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1160
1161
    def test_add_versions_delta_not_delta_index(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1162
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1163
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1164
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1165
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1166
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1167
    def test_add_versions_random_id_accepted(self):
1168
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1169
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1170
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1171
    def test_add_versions_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1172
        index = self.two_graph_index(catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1173
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1174
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100),
1175
            [('parent',)])])
1176
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100),
1177
            [('parent',)])])
1178
        # position/length are ignored (because each pack could have fulltext or
1179
        # delta, and be at a different position.
1180
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1181
            [('parent',)])])
1182
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1183
            [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1184
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1185
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1186
        
1187
    def test_add_versions_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1188
        index = self.two_graph_index(deltas=True, catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1189
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1190
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1191
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1192
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1193
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1194
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1195
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1196
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1197
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1198
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1199
        # change options in the second record
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1200
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1201
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)]),
1202
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1203
        self.assertEqual([], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1204
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1205
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1206
class TestNoParentsGraphIndexKnit(KnitTests):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1207
    """Tests for knits using _KnitGraphIndex with no parents."""
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1208
1209
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1210
        builder = GraphIndexBuilder(ref_lists)
1211
        for node, references in nodes:
1212
            builder.add_node(node, references)
1213
        stream = builder.finish()
1214
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1215
        size = trans.put_file(name, stream)
1216
        return GraphIndex(trans, name, size)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1217
1218
    def test_parents_deltas_incompatible(self):
1219
        index = CombinedGraphIndex([])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1220
        self.assertRaises(errors.KnitError, _KnitGraphIndex, lambda:True,
1221
            index, deltas=True, parents=False)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1222
1223
    def two_graph_index(self, catch_adds=False):
1224
        """Build a two-graph index.
1225
1226
        :param deltas: If true, use underlying indices with two node-ref
1227
            lists and 'parent' set to a delta-compressed against tail.
1228
        """
1229
        # put several versions in the index.
1230
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1231
            (('tip', ), 'N0 100'),
1232
            (('tail', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1233
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1234
            (('parent', ), ' 100 78'),
1235
            (('separate', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1236
        combined_index = CombinedGraphIndex([index1, index2])
1237
        if catch_adds:
1238
            self.combined_index = combined_index
1239
            self.caught_entries = []
1240
            add_callback = self.catch_add
1241
        else:
1242
            add_callback = None
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1243
        return _KnitGraphIndex(combined_index, lambda:True, parents=False,
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1244
            add_callback=add_callback)
1245
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1246
    def test_keys(self):
1247
        index = self.two_graph_index()
1248
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1249
            set(index.keys()))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1250
1251
    def test_get_position(self):
1252
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1253
        self.assertEqual((index._graph_index._indices[0], 0, 100),
1254
            index.get_position(('tip',)))
1255
        self.assertEqual((index._graph_index._indices[1], 100, 78),
1256
            index.get_position(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1257
1258
    def test_get_method(self):
1259
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1260
        self.assertEqual('fulltext', index.get_method(('tip',)))
1261
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1262
1263
    def test_get_options(self):
1264
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1265
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1266
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1267
1268
    def test_get_parent_map(self):
1269
        index = self.two_graph_index()
1270
        self.assertEqual({('parent',):None},
1271
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1272
1273
    def catch_add(self, entries):
1274
        self.caught_entries.append(entries)
1275
1276
    def test_add_no_callback_errors(self):
1277
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1278
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1279
            [(('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1280
1281
    def test_add_version_smoke(self):
1282
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1283
        index.add_records([(('new',), 'fulltext,no-eol', (None, 50, 60), [])])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1284
        self.assertEqual([[(('new', ), 'N50 60')]],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1285
            self.caught_entries)
1286
1287
    def test_add_version_delta_not_delta_index(self):
1288
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1289
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1290
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1291
        self.assertEqual([], self.caught_entries)
1292
1293
    def test_add_version_same_dup(self):
1294
        index = self.two_graph_index(catch_adds=True)
1295
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1296
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1297
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1298
        # position/length are ignored (because each pack could have fulltext or
1299
        # delta, and be at a different position.
1300
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1301
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1302
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1303
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1304
        
1305
    def test_add_version_different_dup(self):
1306
        index = self.two_graph_index(catch_adds=True)
1307
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1308
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1309
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1310
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1311
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1312
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1313
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1314
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1315
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1316
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1317
        self.assertEqual([], self.caught_entries)
1318
        
1319
    def test_add_versions(self):
1320
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1321
        index.add_records([
1322
                (('new',), 'fulltext,no-eol', (None, 50, 60), []),
1323
                (('new2',), 'fulltext', (None, 0, 6), []),
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1324
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1325
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1326
            sorted(self.caught_entries[0]))
1327
        self.assertEqual(1, len(self.caught_entries))
1328
1329
    def test_add_versions_delta_not_delta_index(self):
1330
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1331
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1332
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1333
        self.assertEqual([], self.caught_entries)
1334
1335
    def test_add_versions_parents_not_parents_index(self):
1336
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1337
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1338
            [(('new',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1339
        self.assertEqual([], self.caught_entries)
1340
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1341
    def test_add_versions_random_id_accepted(self):
1342
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1343
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1344
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1345
    def test_add_versions_same_dup(self):
1346
        index = self.two_graph_index(catch_adds=True)
1347
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1348
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1349
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1350
        # position/length are ignored (because each pack could have fulltext or
1351
        # delta, and be at a different position.
1352
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1353
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1354
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1355
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1356
        
1357
    def test_add_versions_different_dup(self):
1358
        index = self.two_graph_index(catch_adds=True)
1359
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1360
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1361
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1362
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1363
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1364
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1365
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1366
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1367
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1368
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1369
        # change options in the second record
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1370
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1371
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), []),
1372
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1373
        self.assertEqual([], self.caught_entries)