/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
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
22
import sys
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
23
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
24
from bzrlib import (
25
    errors,
2484.1.5 by John Arbash Meinel
Simplistic implementations of custom parsers for options and parents
26
    generate_ids,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
27
    knit,
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
28
    multiparent,
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
29
    osutils,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
30
    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
31
    )
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
32
from bzrlib.errors import (
33
    RevisionAlreadyPresent,
34
    KnitHeaderError,
35
    RevisionNotPresent,
36
    NoSuchFile,
37
    )
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
38
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
39
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.
40
    AnnotatedKnitContent,
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
41
    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.
42
    KnitSequenceMatcher,
43
    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.
44
    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.
45
    _DirectPackAccess,
46
    _KndxIndex,
47
    _KnitGraphIndex,
48
    _KnitKeyAccess,
49
    make_file_factory,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
50
    )
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
51
from bzrlib.tests import (
52
    Feature,
3350.8.8 by Robert Collins
Stacking and knits don't play nice for annotation yet.
53
    KnownFailure,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
54
    TestCase,
55
    TestCaseWithMemoryTransport,
56
    TestCaseWithTransport,
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
57
    TestNotApplicable,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
58
    )
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
59
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
60
from bzrlib.transport.memory import MemoryTransport
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
61
from bzrlib.tuned_gzip import GzipFile
3350.8.2 by Robert Collins
stacked get_parent_map.
62
from bzrlib.versionedfile import (
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
63
    AbsentContentFactory,
3350.8.2 by Robert Collins
stacked get_parent_map.
64
    ConstantMapper,
65
    RecordingVersionedFilesDecorator,
66
    )
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
67
68
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
69
class _CompiledKnitFeature(Feature):
70
71
    def _probe(self):
72
        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
73
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
74
        except ImportError:
75
            return False
76
        return True
77
78
    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
79
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
80
81
CompiledKnitFeature = _CompiledKnitFeature()
82
83
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.
84
class KnitContentTestsMixin(object):
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
85
86
    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.
87
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
88
89
    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.
90
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
91
        self.assertEqual(content.text(), [])
92
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.
93
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
94
        self.assertEqual(content.text(), ["text1", "text2"])
95
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.
96
    def test_copy(self):
97
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
98
        copy = content.copy()
99
        self.assertIsInstance(copy, content.__class__)
100
        self.assertEqual(copy.annotate(), content.annotate())
101
102
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
103
        """Assert that the derived matching blocks match real output"""
104
        source_lines = source.splitlines(True)
105
        target_lines = target.splitlines(True)
106
        def nl(line):
107
            if noeol and not line.endswith('\n'):
108
                return line + '\n'
109
            else:
110
                return line
111
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
112
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
113
        line_delta = source_content.line_delta(target_content)
114
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
115
            source_lines, target_lines))
116
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
117
        matcher_blocks = list(list(matcher.get_matching_blocks()))
118
        self.assertEqual(matcher_blocks, delta_blocks)
119
120
    def test_get_line_delta_blocks(self):
121
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
122
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
123
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
124
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
125
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
126
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
127
        self.assertDerivedBlocksEqual(TEXT_1A, '')
128
        self.assertDerivedBlocksEqual('', TEXT_1A)
129
        self.assertDerivedBlocksEqual('', '')
130
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
131
132
    def test_get_line_delta_blocks_noeol(self):
133
        """Handle historical knit deltas safely
134
135
        Some existing knit deltas don't consider the last line to differ
136
        when the only difference whether it has a final newline.
137
138
        New knit deltas appear to always consider the last line to differ
139
        in this case.
140
        """
141
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
142
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
143
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
144
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
145
146
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.
147
TEXT_1 = """\
148
Banana cup cakes:
149
150
- bananas
151
- eggs
152
- broken tea cups
153
"""
154
155
TEXT_1A = """\
156
Banana cup cake recipe
157
(serves 6)
158
159
- bananas
160
- eggs
161
- broken tea cups
162
- self-raising flour
163
"""
164
165
TEXT_1B = """\
166
Banana cup cake recipe
167
168
- bananas (do not use plantains!!!)
169
- broken tea cups
170
- flour
171
"""
172
173
delta_1_1a = """\
174
0,1,2
175
Banana cup cake recipe
176
(serves 6)
177
5,5,1
178
- self-raising flour
179
"""
180
181
TEXT_2 = """\
182
Boeuf bourguignon
183
184
- beef
185
- red wine
186
- small onions
187
- carrot
188
- mushrooms
189
"""
190
191
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.
192
class TestPlainKnitContent(TestCase, KnitContentTestsMixin):
193
194
    def _make_content(self, lines):
195
        annotated_content = AnnotatedKnitContent(lines)
196
        return PlainKnitContent(annotated_content.text(), 'bogus')
197
198
    def test_annotate(self):
199
        content = self._make_content([])
200
        self.assertEqual(content.annotate(), [])
201
202
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
203
        self.assertEqual(content.annotate(),
204
            [("bogus", "text1"), ("bogus", "text2")])
205
206
    def test_line_delta(self):
207
        content1 = self._make_content([("", "a"), ("", "b")])
208
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
209
        self.assertEqual(content1.line_delta(content2),
210
            [(1, 2, 2, ["a", "c"])])
211
212
    def test_line_delta_iter(self):
213
        content1 = self._make_content([("", "a"), ("", "b")])
214
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
215
        it = content1.line_delta_iter(content2)
216
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
217
        self.assertRaises(StopIteration, it.next)
218
219
220
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
221
222
    def _make_content(self, lines):
223
        return AnnotatedKnitContent(lines)
224
225
    def test_annotate(self):
226
        content = self._make_content([])
227
        self.assertEqual(content.annotate(), [])
228
229
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
230
        self.assertEqual(content.annotate(),
231
            [("origin1", "text1"), ("origin2", "text2")])
232
233
    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.
234
        content1 = self._make_content([("", "a"), ("", "b")])
235
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
236
        self.assertEqual(content1.line_delta(content2),
237
            [(1, 2, 2, [("", "a"), ("", "c")])])
238
239
    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.
240
        content1 = self._make_content([("", "a"), ("", "b")])
241
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
242
        it = content1.line_delta_iter(content2)
243
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
244
        self.assertRaises(StopIteration, it.next)
245
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
246
247
class MockTransport(object):
248
249
    def __init__(self, file_lines=None):
250
        self.file_lines = file_lines
251
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
252
        # We have no base directory for the MockTransport
253
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
254
255
    def get(self, filename):
256
        if self.file_lines is None:
257
            raise NoSuchFile(filename)
258
        else:
259
            return StringIO("\n".join(self.file_lines))
260
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
261
    def readv(self, relpath, offsets):
262
        fp = self.get(relpath)
263
        for offset, size in offsets:
264
            fp.seek(offset)
265
            yield offset, fp.read(size)
266
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
267
    def __getattr__(self, name):
268
        def queue_call(*args, **kwargs):
269
            self.calls.append((name, args, kwargs))
270
        return queue_call
271
272
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
273
class KnitRecordAccessTestsMixin(object):
274
    """Tests for getting and putting knit records."""
275
276
    def test_add_raw_records(self):
277
        """Add_raw_records adds records retrievable later."""
278
        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.
279
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
280
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
281
 
282
    def test_add_several_raw_records(self):
283
        """add_raw_records with many records and read some back."""
284
        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.
285
        memos = access.add_raw_records([('key', 10), ('key2', 2), ('key3', 5)],
286
            '12345678901234567')
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
287
        self.assertEqual(['1234567890', '12', '34567'],
288
            list(access.get_raw_records(memos)))
289
        self.assertEqual(['1234567890'],
290
            list(access.get_raw_records(memos[0:1])))
291
        self.assertEqual(['12'],
292
            list(access.get_raw_records(memos[1:2])))
293
        self.assertEqual(['34567'],
294
            list(access.get_raw_records(memos[2:3])))
295
        self.assertEqual(['1234567890', '34567'],
296
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
297
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
298
299
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
300
    """Tests for the .kndx implementation."""
301
302
    def get_access(self):
303
        """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.
304
        mapper = ConstantMapper("foo")
305
        access = _KnitKeyAccess(self.get_transport(), mapper)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
306
        return access
307
    
308
309
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
310
    """Tests for the pack based access."""
311
312
    def get_access(self):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
313
        return self._get_access()[0]
314
315
    def _get_access(self, packname='packfile', index='FOO'):
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
316
        transport = self.get_transport()
317
        def write_data(bytes):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
318
            transport.append_bytes(packname, bytes)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
319
        writer = pack.ContainerWriter(write_data)
320
        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.
321
        access = _DirectPackAccess({})
322
        access.set_writer(writer, index, (transport, packname))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
323
        return access, writer
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
324
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
325
    def test_read_from_several_packs(self):
326
        access, writer = self._get_access()
327
        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.
328
        memos.extend(access.add_raw_records([('key', 10)], '1234567890'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
329
        writer.end()
330
        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.
331
        memos.extend(access.add_raw_records([('key', 5)], '12345'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
332
        writer.end()
333
        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.
334
        memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
335
        writer.end()
336
        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.
337
        access = _DirectPackAccess({"FOO":(transport, 'packfile'),
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
338
            "FOOBAR":(transport, 'pack2'),
339
            "BAZ":(transport, 'pack3')})
340
        self.assertEqual(['1234567890', '12345', 'alpha'],
341
            list(access.get_raw_records(memos)))
342
        self.assertEqual(['1234567890'],
343
            list(access.get_raw_records(memos[0:1])))
344
        self.assertEqual(['12345'],
345
            list(access.get_raw_records(memos[1:2])))
346
        self.assertEqual(['alpha'],
347
            list(access.get_raw_records(memos[2:3])))
348
        self.assertEqual(['1234567890', 'alpha'],
349
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
350
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
351
    def test_set_writer(self):
352
        """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.
353
        access = _DirectPackAccess({})
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
354
        transport = self.get_transport()
355
        packname = 'packfile'
356
        index = 'foo'
357
        def write_data(bytes):
358
            transport.append_bytes(packname, bytes)
359
        writer = pack.ContainerWriter(write_data)
360
        writer.begin()
361
        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.
362
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
363
        writer.end()
364
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
365
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
366
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
367
class LowLevelKnitDataTests(TestCase):
368
369
    def create_gz_content(self, text):
370
        sio = StringIO()
371
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
372
        gz_file.write(text)
373
        gz_file.close()
374
        return sio.getvalue()
375
376
    def test_valid_knit_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
377
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
378
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
379
                                        'foo\n'
380
                                        'bar\n'
381
                                        'end rev-id-1\n'
382
                                        % (sha1sum,))
383
        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.
384
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
385
        knit = KnitVersionedFiles(None, access)
386
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
387
388
        contents = list(knit._read_records_iter(records))
389
        self.assertEqual([(('rev-id-1',), ['foo\n', 'bar\n'],
390
            '4e48e2c9a3d2ca8a708cb0cc545700544efb5021')], contents)
391
392
        raw_contents = list(knit._read_records_iter_raw(records))
393
        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.
394
395
    def test_not_enough_lines(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
396
        sha1sum = osutils.sha('foo\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
397
        # record says 2 lines data says 1
398
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
399
                                        'foo\n'
400
                                        'end rev-id-1\n'
401
                                        % (sha1sum,))
402
        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.
403
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
404
        knit = KnitVersionedFiles(None, access)
405
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
406
        self.assertRaises(errors.KnitCorrupt, list,
407
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
408
409
        # 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.
410
        raw_contents = list(knit._read_records_iter_raw(records))
411
        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.
412
413
    def test_too_many_lines(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
414
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
415
        # record says 1 lines data says 2
416
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
417
                                        'foo\n'
418
                                        'bar\n'
419
                                        'end rev-id-1\n'
420
                                        % (sha1sum,))
421
        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.
422
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
423
        knit = KnitVersionedFiles(None, access)
424
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
425
        self.assertRaises(errors.KnitCorrupt, list,
426
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
427
428
        # 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.
429
        raw_contents = list(knit._read_records_iter_raw(records))
430
        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.
431
432
    def test_mismatched_version_id(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
433
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
434
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
435
                                        'foo\n'
436
                                        'bar\n'
437
                                        'end rev-id-1\n'
438
                                        % (sha1sum,))
439
        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.
440
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
441
        knit = KnitVersionedFiles(None, access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
442
        # 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.
443
        records = [(('rev-id-2',), (('rev-id-2',), 0, len(gz_txt)))]
444
        self.assertRaises(errors.KnitCorrupt, list,
445
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
446
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.
447
        # 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.
448
        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.
449
            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.
450
451
    def test_uncompressed_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
452
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
453
        txt = ('version rev-id-1 2 %s\n'
454
               'foo\n'
455
               'bar\n'
456
               'end rev-id-1\n'
457
               % (sha1sum,))
458
        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.
459
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
460
        knit = KnitVersionedFiles(None, access)
461
        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.
462
463
        # 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.
464
        self.assertRaises(errors.KnitCorrupt, list,
465
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
466
467
        # read_records_iter_raw will notice the bad data
468
        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.
469
            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.
470
471
    def test_corrupted_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
472
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
473
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
474
                                        'foo\n'
475
                                        'bar\n'
476
                                        'end rev-id-1\n'
477
                                        % (sha1sum,))
478
        # Change 2 bytes in the middle to \xff
479
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
480
        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.
481
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
482
        knit = KnitVersionedFiles(None, access)
483
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
484
        self.assertRaises(errors.KnitCorrupt, list,
485
            knit._read_records_iter(records))
486
        # read_records_iter_raw will barf on bad gz data
487
        self.assertRaises(errors.KnitCorrupt, list,
488
            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.
489
490
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
491
class LowLevelKnitIndexTests(TestCase):
492
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.
493
    def get_knit_index(self, transport, name, mode):
494
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
495
        orig = knit._load_data
496
        def reset():
497
            knit._load_data = orig
498
        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
499
        from bzrlib._knit_load_data_py import _load_data_py
500
        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.
501
        allow_writes = lambda: 'w' in mode
502
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
503
504
    def test_create_file(self):
505
        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.
506
        index = self.get_knit_index(transport, "filename", "w")
507
        index.keys()
508
        call = transport.calls.pop(0)
509
        # call[1][1] is a StringIO - we can't test it by simple equality.
510
        self.assertEqual('put_file_non_atomic', call[0])
511
        self.assertEqual('filename.kndx', call[1][0])
512
        # With no history, _KndxIndex writes a new index:
513
        self.assertEqual(_KndxIndex.HEADER,
514
            call[1][1].getvalue())
515
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
516
517
    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
518
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
519
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
520
        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.
521
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
522
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
523
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
524
        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.
525
        # _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
526
        # 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.
527
        self.assertEqual({(utf8_revision_id,):()},
528
            index.get_parent_map(index.keys()))
529
        self.assertFalse((unicode_revision_id,) in index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
530
531
    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
532
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
533
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
534
        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.
535
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
536
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
537
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
538
        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.
539
        self.assertEqual({("version",):((utf8_revision_id,),)},
540
            index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
541
542
    def test_read_ignore_corrupted_lines(self):
543
        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.
544
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
545
            "corrupted",
546
            "corrupted options 0 1 .b .c ",
547
            "version options 0 1 :"
548
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
549
        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.
550
        self.assertEqual(1, len(index.keys()))
551
        self.assertEqual(set([("version",)]), index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
552
553
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
554
        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.
555
        index = self.get_knit_index(transport, "filename", "r")
556
        self.assertRaises(KnitHeaderError, index.keys)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
557
558
    def test_read_duplicate_entries(self):
559
        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.
560
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
561
            "parent options 0 1 :",
562
            "version options1 0 1 0 :",
563
            "version options2 1 2 .other :",
564
            "version options3 3 4 0 .other :"
565
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
566
        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.
567
        self.assertEqual(2, len(index.keys()))
2592.3.8 by Robert Collins
Remove unneeded pulib method lookup on private class _KnitIndex.
568
        # check that the index used is the first one written. (Specific
569
        # 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.
570
        self.assertEqual("1", index._dictionary_compress([("version",)]))
571
        self.assertEqual((("version",), 3, 4), index.get_position(("version",)))
572
        self.assertEqual(["options3"], index.get_options(("version",)))
573
        self.assertEqual({("version",):(("parent",), ("other",))},
574
            index.get_parent_map([("version",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
575
576
    def test_read_compressed_parents(self):
577
        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.
578
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
579
            "a option 0 1 :",
580
            "b option 0 1 0 :",
581
            "c option 0 1 1 0 :",
582
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
583
        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.
584
        self.assertEqual({("b",):(("a",),), ("c",):(("b",), ("a",))},
585
            index.get_parent_map([("b",), ("c",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
586
587
    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
588
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
589
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
590
        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.
591
            _KndxIndex.HEADER
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
592
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
593
        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.
594
        index.add_records([
595
            ((utf8_revision_id,), ["option"], ((utf8_revision_id,), 0, 1), [])])
596
        call = transport.calls.pop(0)
597
        # call[1][1] is a StringIO - we can't test it by simple equality.
598
        self.assertEqual('put_file_non_atomic', call[0])
599
        self.assertEqual('filename.kndx', call[1][0])
600
        # With no history, _KndxIndex writes a new index:
601
        self.assertEqual(_KndxIndex.HEADER +
602
            "\n%s option 0 1  :" % (utf8_revision_id,),
603
            call[1][1].getvalue())
604
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
605
606
    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
607
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
608
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
609
        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.
610
            _KndxIndex.HEADER
611
            ])
612
        index = self.get_knit_index(transport, "filename", "r")
613
        index.add_records([
614
            (("version",), ["option"], (("version",), 0, 1), [(utf8_revision_id,)])])
615
        call = transport.calls.pop(0)
616
        # call[1][1] is a StringIO - we can't test it by simple equality.
617
        self.assertEqual('put_file_non_atomic', call[0])
618
        self.assertEqual('filename.kndx', call[1][0])
619
        # With no history, _KndxIndex writes a new index:
620
        self.assertEqual(_KndxIndex.HEADER +
621
            "\nversion option 0 1 .%s :" % (utf8_revision_id,),
622
            call[1][1].getvalue())
623
        self.assertEqual({'create_parent_dir': True}, call[2])
624
625
    def test_keys(self):
626
        transport = MockTransport([
627
            _KndxIndex.HEADER
628
            ])
629
        index = self.get_knit_index(transport, "filename", "r")
630
631
        self.assertEqual(set(), index.keys())
632
633
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
634
        self.assertEqual(set([("a",)]), index.keys())
635
636
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
637
        self.assertEqual(set([("a",)]), index.keys())
638
639
        index.add_records([(("b",), ["option"], (("b",), 0, 1), [])])
640
        self.assertEqual(set([("a",), ("b",)]), index.keys())
641
642
    def add_a_b(self, index, random_id=None):
643
        kwargs = {}
644
        if random_id is not None:
645
            kwargs["random_id"] = random_id
646
        index.add_records([
647
            (("a",), ["option"], (("a",), 0, 1), [("b",)]),
648
            (("a",), ["opt"], (("a",), 1, 2), [("c",)]),
649
            (("b",), ["option"], (("b",), 2, 3), [("a",)])
650
            ], **kwargs)
651
652
    def assertIndexIsAB(self, index):
653
        self.assertEqual({
654
            ('a',): (('c',),),
655
            ('b',): (('a',),),
656
            },
657
            index.get_parent_map(index.keys()))
658
        self.assertEqual((("a",), 1, 2), index.get_position(("a",)))
659
        self.assertEqual((("b",), 2, 3), index.get_position(("b",)))
660
        self.assertEqual(["opt"], index.get_options(("a",)))
661
662
    def test_add_versions(self):
663
        transport = MockTransport([
664
            _KndxIndex.HEADER
665
            ])
666
        index = self.get_knit_index(transport, "filename", "r")
667
668
        self.add_a_b(index)
669
        call = transport.calls.pop(0)
670
        # call[1][1] is a StringIO - we can't test it by simple equality.
671
        self.assertEqual('put_file_non_atomic', call[0])
672
        self.assertEqual('filename.kndx', call[1][0])
673
        # With no history, _KndxIndex writes a new index:
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
674
        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.
675
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
676
            "\na option 0 1 .b :"
677
            "\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.
678
            "\nb option 2 3 0 :",
679
            call[1][1].getvalue())
680
        self.assertEqual({'create_parent_dir': True}, call[2])
681
        self.assertIndexIsAB(index)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
682
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
683
    def test_add_versions_random_id_is_accepted(self):
684
        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.
685
            _KndxIndex.HEADER
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
686
            ])
687
        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.
688
        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
689
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
690
    def test_delay_create_and_add_versions(self):
691
        transport = MockTransport()
692
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.
693
        index = self.get_knit_index(transport, "filename", "w")
694
        # dir_mode=0777)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
695
        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.
696
        self.add_a_b(index)
697
        #self.assertEqual(
698
        #[    {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
699
        #    kwargs)
700
        # Two calls: one during which we load the existing index (and when its
701
        # missing create it), then a second where we write the contents out.
702
        self.assertEqual(2, len(transport.calls))
703
        call = transport.calls.pop(0)
704
        self.assertEqual('put_file_non_atomic', call[0])
705
        self.assertEqual('filename.kndx', call[1][0])
706
        # With no history, _KndxIndex writes a new index:
707
        self.assertEqual(_KndxIndex.HEADER, call[1][1].getvalue())
708
        self.assertEqual({'create_parent_dir': True}, call[2])
709
        call = transport.calls.pop(0)
710
        # call[1][1] is a StringIO - we can't test it by simple equality.
711
        self.assertEqual('put_file_non_atomic', call[0])
712
        self.assertEqual('filename.kndx', call[1][0])
713
        # With no history, _KndxIndex writes a new index:
714
        self.assertEqual(
715
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
716
            "\na option 0 1 .b :"
717
            "\na opt 1 2 .c :"
718
            "\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.
719
            call[1][1].getvalue())
720
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
721
722
    def test_get_position(self):
723
        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.
724
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
725
            "a option 0 1 :",
726
            "b option 1 2 :"
727
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
728
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
729
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
        self.assertEqual((("a",), 0, 1), index.get_position(("a",)))
731
        self.assertEqual((("b",), 1, 2), index.get_position(("b",)))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
732
733
    def test_get_method(self):
734
        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.
735
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
736
            "a fulltext,unknown 0 1 :",
737
            "b unknown,line-delta 1 2 :",
738
            "c bad 3 4 :"
739
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
740
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
741
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
742
        self.assertEqual("fulltext", index.get_method("a"))
743
        self.assertEqual("line-delta", index.get_method("b"))
744
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
745
746
    def test_get_options(self):
747
        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.
748
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
749
            "a opt1 0 1 :",
750
            "b opt2,opt3 1 2 :"
751
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
752
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
753
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
754
        self.assertEqual(["opt1"], index.get_options("a"))
755
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
756
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
757
    def test_get_parent_map(self):
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
758
        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.
759
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
760
            "a option 0 1 :",
761
            "b option 1 2 0 .c :",
762
            "c option 1 2 1 0 .e :"
763
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
764
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
765
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
766
        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.
767
            ("a",):(),
768
            ("b",):(("a",), ("c",)),
769
            ("c",):(("b",), ("a",), ("e",)),
770
            }, index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
771
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
772
    def test_impossible_parent(self):
773
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
774
        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.
775
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
776
            "a option 0 1 :",
777
            "b option 0 1 4 :"  # We don't have a 4th record
778
            ])
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.
779
        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.
780
        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.
781
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
782
        except TypeError, e:
783
            if (str(e) == ('exceptions must be strings, classes, or instances,'
784
                           ' not exceptions.IndexError')
785
                and sys.version_info[0:2] >= (2,5)):
786
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
787
                                  ' raising new style exceptions with python'
788
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
789
            else:
790
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
791
792
    def test_corrupted_parent(self):
793
        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.
794
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
795
            "a option 0 1 :",
796
            "b option 0 1 :",
797
            "c option 0 1 1v :", # Can't have a parent of '1v'
798
            ])
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.
799
        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.
800
        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.
801
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
802
        except TypeError, e:
803
            if (str(e) == ('exceptions must be strings, classes, or instances,'
804
                           ' not exceptions.ValueError')
805
                and sys.version_info[0:2] >= (2,5)):
806
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
807
                                  ' raising new style exceptions with python'
808
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
809
            else:
810
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
811
812
    def test_corrupted_parent_in_list(self):
813
        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.
814
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
815
            "a option 0 1 :",
816
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
817
            "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.
818
            ])
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.
819
        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.
820
        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.
821
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
822
        except TypeError, e:
823
            if (str(e) == ('exceptions must be strings, classes, or instances,'
824
                           ' not exceptions.ValueError')
825
                and sys.version_info[0:2] >= (2,5)):
826
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
827
                                  ' raising new style exceptions with python'
828
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
829
            else:
830
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
831
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
832
    def test_invalid_position(self):
833
        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.
834
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
835
            "a option 1v 1 :",
836
            ])
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.
837
        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.
838
        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.
839
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
840
        except TypeError, e:
841
            if (str(e) == ('exceptions must be strings, classes, or instances,'
842
                           ' not exceptions.ValueError')
843
                and sys.version_info[0:2] >= (2,5)):
844
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
845
                                  ' raising new style exceptions with python'
846
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
847
            else:
848
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
849
850
    def test_invalid_size(self):
851
        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.
852
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
853
            "a option 1 1v :",
854
            ])
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.
855
        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.
856
        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.
857
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
858
        except TypeError, e:
859
            if (str(e) == ('exceptions must be strings, classes, or instances,'
860
                           ' not exceptions.ValueError')
861
                and sys.version_info[0:2] >= (2,5)):
862
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
863
                                  ' raising new style exceptions with python'
864
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
865
            else:
866
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
867
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
868
    def test_short_line(self):
869
        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.
870
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
871
            "a option 0 10  :",
872
            "b option 10 10 0", # This line isn't terminated, ignored
873
            ])
874
        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.
875
        self.assertEqual(set([('a',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
876
877
    def test_skip_incomplete_record(self):
878
        # A line with bogus data should just be skipped
879
        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.
880
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
881
            "a option 0 10  :",
882
            "b option 10 10 0", # This line isn't terminated, ignored
883
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
884
            ])
885
        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.
886
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
887
888
    def test_trailing_characters(self):
889
        # A line with bogus data should just be skipped
890
        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.
891
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
892
            "a option 0 10  :",
893
            "b option 10 10 0 :a", # This line has extra trailing characters
894
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
895
            ])
896
        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.
897
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
898
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
899
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
900
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
901
902
    _test_needs_features = [CompiledKnitFeature]
903
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.
904
    def get_knit_index(self, transport, name, mode):
905
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
906
        orig = knit._load_data
907
        def reset():
908
            knit._load_data = orig
909
        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
910
        from bzrlib._knit_load_data_c import _load_data_c
911
        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.
912
        allow_writes = lambda: mode == 'w'
913
        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.
914
915
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
916
class KnitTests(TestCaseWithTransport):
917
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
918
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.
919
    def make_test_knit(self, annotate=False, name='test'):
920
        mapper = ConstantMapper(name)
921
        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
922
923
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
924
class TestBadShaError(KnitTests):
925
    """Tests for handling of sha errors."""
926
927
    def test_exception_has_text(self):
928
        # having the failed text included in the error allows for recovery.
929
        source = self.make_test_knit()
930
        target = self.make_test_knit(name="target")
931
        if not source._max_delta_chain:
932
            raise TestNotApplicable(
933
                "cannot get delta-caused sha failures without deltas.")
934
        # create a basis
935
        basis = ('basis',)
936
        broken = ('broken',)
937
        source.add_lines(basis, (), ['foo\n'])
938
        source.add_lines(broken, (basis,), ['foo\n', 'bar\n'])
939
        # Seed target with a bad basis text
940
        target.add_lines(basis, (), ['gam\n'])
941
        target.insert_record_stream(
942
            source.get_record_stream([broken], 'unordered', False))
943
        err = self.assertRaises(errors.KnitCorrupt,
944
            target.get_record_stream([broken], 'unordered', True).next)
945
        self.assertEqual(['gam\n', 'bar\n'], err.content)
3787.1.2 by Robert Collins
Ensure SHA1KnitCorrupt formats ok.
946
        # Test for formatting with live data
947
        self.assertStartsWith(str(err), "Knit ")
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
948
949
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
950
class TestKnitIndex(KnitTests):
951
952
    def test_add_versions_dictionary_compresses(self):
953
        """Adding versions to the index should update the lookup dict"""
954
        knit = self.make_test_knit()
955
        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.
956
        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
957
        self.check_file_contents('test.kndx',
958
            '# bzr knit index 8\n'
959
            '\n'
960
            'a-1 fulltext 0 0  :'
961
            )
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.
962
        idx.add_records([
963
            (('a-2',), ['fulltext'], (('a-2',), 0, 0), [('a-1',)]),
964
            (('a-3',), ['fulltext'], (('a-3',), 0, 0), [('a-2',)]),
965
            ])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
966
        self.check_file_contents('test.kndx',
967
            '# bzr knit index 8\n'
968
            '\n'
969
            'a-1 fulltext 0 0  :\n'
970
            'a-2 fulltext 0 0 0 :\n'
971
            'a-3 fulltext 0 0 1 :'
972
            )
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.
973
        self.assertEqual(set([('a-3',), ('a-1',), ('a-2',)]), idx.keys())
974
        self.assertEqual({
975
            ('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False)),
976
            ('a-2',): ((('a-2',), 0, 0), None, (('a-1',),), ('fulltext', False)),
977
            ('a-3',): ((('a-3',), 0, 0), None, (('a-2',),), ('fulltext', False)),
978
            }, idx.get_build_details(idx.keys()))
979
        self.assertEqual({('a-1',):(),
980
            ('a-2',):(('a-1',),),
981
            ('a-3',):(('a-2',),),},
982
            idx.get_parent_map(idx.keys()))
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
983
984
    def test_add_versions_fails_clean(self):
985
        """If add_versions fails in the middle, it restores a pristine state.
986
987
        Any modifications that are made to the index are reset if all versions
988
        cannot be added.
989
        """
990
        # This cheats a little bit by passing in a generator which will
991
        # raise an exception before the processing finishes
992
        # Other possibilities would be to have an version with the wrong number
993
        # of entries, or to make the backing transport unable to write any
994
        # files.
995
996
        knit = self.make_test_knit()
997
        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.
998
        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
999
1000
        class StopEarly(Exception):
1001
            pass
1002
1003
        def generate_failure():
1004
            """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.
1005
            yield (('a-2',), ['fulltext'], (None, 0, 0), ('a-1',))
1006
            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
1007
            raise StopEarly()
1008
1009
        # 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.
1010
        def assertA1Only():
1011
            self.assertEqual(set([('a-1',)]), set(idx.keys()))
1012
            self.assertEqual(
1013
                {('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False))},
1014
                idx.get_build_details([('a-1',)]))
1015
            self.assertEqual({('a-1',):()}, idx.get_parent_map(idx.keys()))
1016
1017
        assertA1Only()
1018
        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
1019
        # 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.
1020
        assertA1Only()
2171.1.1 by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt.
1021
1022
    def test_knit_index_ignores_empty_files(self):
1023
        # There was a race condition in older bzr, where a ^C at the right time
1024
        # could leave an empty .kndx file, which bzr would later claim was a
1025
        # corrupted file since the header was not present. In reality, the file
1026
        # just wasn't created, so it should be ignored.
1027
        t = get_transport('.')
1028
        t.put_bytes('test.kndx', '')
1029
1030
        knit = self.make_test_knit()
1031
1032
    def test_knit_index_checks_header(self):
1033
        t = get_transport('.')
1034
        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.
1035
        k = self.make_test_knit()
1036
        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.
1037
1038
1039
class TestGraphIndexKnit(KnitTests):
1040
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
1041
1042
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1043
        builder = GraphIndexBuilder(ref_lists)
1044
        for node, references, value in nodes:
1045
            builder.add_node(node, references, value)
1046
        stream = builder.finish()
1047
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1048
        size = trans.put_file(name, stream)
1049
        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.
1050
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1051
    def two_graph_index(self, deltas=False, catch_adds=False):
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1052
        """Build a two-graph index.
1053
1054
        :param deltas: If true, use underlying indices with two node-ref
1055
            lists and 'parent' set to a delta-compressed against tail.
1056
        """
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.
1057
        # build a complex graph across several indices.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1058
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1059
            # delta compression inn the index
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1060
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1061
                (('tip', ), 'N0 100', ([('parent', )], [], )),
1062
                (('tail', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1063
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1064
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
1065
                (('separate', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1066
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1067
            # just blob location and graph in the index.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1068
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1069
                (('tip', ), 'N0 100', ([('parent', )], )),
1070
                (('tail', ), '', ([], ))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1071
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1072
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
1073
                (('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.
1074
        combined_index = CombinedGraphIndex([index1, index2])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1075
        if catch_adds:
1076
            self.combined_index = combined_index
1077
            self.caught_entries = []
1078
            add_callback = self.catch_add
1079
        else:
1080
            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.
1081
        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.
1082
            add_callback=add_callback)
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
1083
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.
1084
    def test_keys(self):
1085
        index = self.two_graph_index()
1086
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1087
            set(index.keys()))
2592.3.9 by Robert Collins
Implement KnitGraphIndex.has_version.
1088
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1089
    def test_get_position(self):
1090
        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.
1091
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position(('tip',)))
1092
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position(('parent',)))
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1093
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1094
    def test_get_method_deltas(self):
1095
        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.
1096
        self.assertEqual('fulltext', index.get_method(('tip',)))
1097
        self.assertEqual('line-delta', index.get_method(('parent',)))
2592.3.11 by Robert Collins
Implement KnitGraphIndex.get_method.
1098
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1099
    def test_get_method_no_deltas(self):
1100
        # check that the parent-history lookup is ignored with deltas=False.
1101
        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.
1102
        self.assertEqual('fulltext', index.get_method(('tip',)))
1103
        self.assertEqual('fulltext', index.get_method(('parent',)))
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1104
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1105
    def test_get_options_deltas(self):
1106
        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.
1107
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1108
        self.assertEqual(['line-delta'], index.get_options(('parent',)))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1109
1110
    def test_get_options_no_deltas(self):
1111
        # check that the parent-history lookup is ignored with deltas=False.
1112
        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.
1113
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1114
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1115
1116
    def test_get_parent_map(self):
1117
        index = self.two_graph_index()
1118
        self.assertEqual({('parent',):(('tail',), ('ghost',))},
1119
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1120
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1121
    def catch_add(self, entries):
1122
        self.caught_entries.append(entries)
1123
1124
    def test_add_no_callback_errors(self):
1125
        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.
1126
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1127
            [(('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.
1128
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1129
    def test_add_version_smoke(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1130
        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.
1131
        index.add_records([(('new',), 'fulltext,no-eol', (None, 50, 60),
1132
            [('separate',)])])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1133
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1134
            self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1135
1136
    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.
1137
        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.
1138
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1139
            [(('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.
1140
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1141
1142
    def test_add_version_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1143
        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.
1144
        # 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.
1145
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
1146
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
1147
        # position/length are ignored (because each pack could have fulltext or
1148
        # delta, and be at a different position.
1149
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1150
            [('parent',)])])
1151
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1152
            [('parent',)])])
1153
        # but neither should have added data:
1154
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1155
        
1156
    def test_add_version_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1157
        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.
1158
        # 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.
1159
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1160
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1161
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1162
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1163
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1164
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1165
        # 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.
1166
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1167
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1168
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1169
        
1170
    def test_add_versions_nodeltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1171
        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.
1172
        index.add_records([
1173
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1174
                (('new2',), 'fulltext', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1175
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1176
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
1177
            (('new2', ), ' 0 6', ((('new',),),))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1178
            sorted(self.caught_entries[0]))
1179
        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.
1180
1181
    def test_add_versions_deltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1182
        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.
1183
        index.add_records([
1184
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1185
                (('new2',), 'line-delta', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1186
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1187
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
1188
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1189
            sorted(self.caught_entries[0]))
1190
        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.
1191
1192
    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.
1193
        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.
1194
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1195
            [(('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.
1196
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1197
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1198
    def test_add_versions_random_id_accepted(self):
1199
        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.
1200
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1201
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1202
    def test_add_versions_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1203
        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.
1204
        # 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.
1205
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100),
1206
            [('parent',)])])
1207
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100),
1208
            [('parent',)])])
1209
        # position/length are ignored (because each pack could have fulltext or
1210
        # delta, and be at a different position.
1211
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1212
            [('parent',)])])
1213
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1214
            [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1215
        # 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.
1216
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1217
        
1218
    def test_add_versions_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1219
        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.
1220
        # 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.
1221
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1222
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1223
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1224
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1225
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1226
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1227
        # 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.
1228
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1229
            [(('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.
1230
        # 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.
1231
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1232
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)]),
1233
             (('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.
1234
        self.assertEqual([], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1235
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.
1236
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1237
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.
1238
    """Tests for knits using _KnitGraphIndex with no parents."""
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1239
1240
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1241
        builder = GraphIndexBuilder(ref_lists)
1242
        for node, references in nodes:
1243
            builder.add_node(node, references)
1244
        stream = builder.finish()
1245
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1246
        size = trans.put_file(name, stream)
1247
        return GraphIndex(trans, name, size)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1248
1249
    def test_parents_deltas_incompatible(self):
1250
        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.
1251
        self.assertRaises(errors.KnitError, _KnitGraphIndex, lambda:True,
1252
            index, deltas=True, parents=False)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1253
1254
    def two_graph_index(self, catch_adds=False):
1255
        """Build a two-graph index.
1256
1257
        :param deltas: If true, use underlying indices with two node-ref
1258
            lists and 'parent' set to a delta-compressed against tail.
1259
        """
1260
        # put several versions in the index.
1261
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1262
            (('tip', ), 'N0 100'),
1263
            (('tail', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1264
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1265
            (('parent', ), ' 100 78'),
1266
            (('separate', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1267
        combined_index = CombinedGraphIndex([index1, index2])
1268
        if catch_adds:
1269
            self.combined_index = combined_index
1270
            self.caught_entries = []
1271
            add_callback = self.catch_add
1272
        else:
1273
            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.
1274
        return _KnitGraphIndex(combined_index, lambda:True, parents=False,
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1275
            add_callback=add_callback)
1276
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.
1277
    def test_keys(self):
1278
        index = self.two_graph_index()
1279
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1280
            set(index.keys()))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1281
1282
    def test_get_position(self):
1283
        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.
1284
        self.assertEqual((index._graph_index._indices[0], 0, 100),
1285
            index.get_position(('tip',)))
1286
        self.assertEqual((index._graph_index._indices[1], 100, 78),
1287
            index.get_position(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1288
1289
    def test_get_method(self):
1290
        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.
1291
        self.assertEqual('fulltext', index.get_method(('tip',)))
1292
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1293
1294
    def test_get_options(self):
1295
        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.
1296
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1297
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1298
1299
    def test_get_parent_map(self):
1300
        index = self.two_graph_index()
1301
        self.assertEqual({('parent',):None},
1302
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1303
1304
    def catch_add(self, entries):
1305
        self.caught_entries.append(entries)
1306
1307
    def test_add_no_callback_errors(self):
1308
        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.
1309
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1310
            [(('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1311
1312
    def test_add_version_smoke(self):
1313
        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.
1314
        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.
1315
        self.assertEqual([[(('new', ), 'N50 60')]],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1316
            self.caught_entries)
1317
1318
    def test_add_version_delta_not_delta_index(self):
1319
        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.
1320
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1321
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1322
        self.assertEqual([], self.caught_entries)
1323
1324
    def test_add_version_same_dup(self):
1325
        index = self.two_graph_index(catch_adds=True)
1326
        # 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.
1327
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1328
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1329
        # position/length are ignored (because each pack could have fulltext or
1330
        # delta, and be at a different position.
1331
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1332
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1333
        # 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.
1334
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1335
        
1336
    def test_add_version_different_dup(self):
1337
        index = self.two_graph_index(catch_adds=True)
1338
        # 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.
1339
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1340
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1341
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1342
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1343
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1344
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1345
        # 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.
1346
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1347
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1348
        self.assertEqual([], self.caught_entries)
1349
        
1350
    def test_add_versions(self):
1351
        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.
1352
        index.add_records([
1353
                (('new',), 'fulltext,no-eol', (None, 50, 60), []),
1354
                (('new2',), 'fulltext', (None, 0, 6), []),
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1355
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1356
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1357
            sorted(self.caught_entries[0]))
1358
        self.assertEqual(1, len(self.caught_entries))
1359
1360
    def test_add_versions_delta_not_delta_index(self):
1361
        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.
1362
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1363
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1364
        self.assertEqual([], self.caught_entries)
1365
1366
    def test_add_versions_parents_not_parents_index(self):
1367
        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.
1368
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1369
            [(('new',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1370
        self.assertEqual([], self.caught_entries)
1371
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1372
    def test_add_versions_random_id_accepted(self):
1373
        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.
1374
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1375
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1376
    def test_add_versions_same_dup(self):
1377
        index = self.two_graph_index(catch_adds=True)
1378
        # 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.
1379
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1380
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1381
        # position/length are ignored (because each pack could have fulltext or
1382
        # delta, and be at a different position.
1383
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1384
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1385
        # 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.
1386
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1387
        
1388
    def test_add_versions_different_dup(self):
1389
        index = self.two_graph_index(catch_adds=True)
1390
        # 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.
1391
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1392
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1393
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1394
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1395
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1396
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1397
        # 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.
1398
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1399
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1400
        # 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.
1401
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1402
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), []),
1403
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1404
        self.assertEqual([], self.caught_entries)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1405
1406
1407
class TestStacking(KnitTests):
1408
1409
    def get_basis_and_test_knit(self):
1410
        basis = self.make_test_knit(name='basis')
3350.8.2 by Robert Collins
stacked get_parent_map.
1411
        basis = RecordingVersionedFilesDecorator(basis)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1412
        test = self.make_test_knit(name='test')
1413
        test.add_fallback_versioned_files(basis)
1414
        return basis, test
1415
1416
    def test_add_fallback_versioned_files(self):
1417
        basis = self.make_test_knit(name='basis')
1418
        test = self.make_test_knit(name='test')
1419
        # It must not error; other tests test that the fallback is referred to
1420
        # when accessing data.
1421
        test.add_fallback_versioned_files(basis)
1422
1423
    def test_add_lines(self):
3350.8.9 by Robert Collins
define behaviour for add_lines with stacked storage.
1424
        # lines added to the test are not added to the basis
1425
        basis, test = self.get_basis_and_test_knit()
1426
        key = ('foo',)
1427
        key_basis = ('bar',)
1428
        key_cross_border = ('quux',)
1429
        key_delta = ('zaphod',)
1430
        test.add_lines(key, (), ['foo\n'])
1431
        self.assertEqual({}, basis.get_parent_map([key]))
1432
        # lines added to the test that reference across the stack do a
1433
        # fulltext.
1434
        basis.add_lines(key_basis, (), ['foo\n'])
1435
        basis.calls = []
1436
        test.add_lines(key_cross_border, (key_basis,), ['foo\n'])
1437
        self.assertEqual('fulltext', test._index.get_method(key_cross_border))
1438
        self.assertEqual([("get_parent_map", set([key_basis]))], basis.calls)
1439
        # Subsequent adds do delta.
3350.8.14 by Robert Collins
Review feedback.
1440
        basis.calls = []
3350.8.9 by Robert Collins
define behaviour for add_lines with stacked storage.
1441
        test.add_lines(key_delta, (key_cross_border,), ['foo\n'])
1442
        self.assertEqual('line-delta', test._index.get_method(key_delta))
1443
        self.assertEqual([], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1444
1445
    def test_annotate(self):
3350.8.8 by Robert Collins
Stacking and knits don't play nice for annotation yet.
1446
        # annotations from the test knit are answered without asking the basis
1447
        basis, test = self.get_basis_and_test_knit()
1448
        key = ('foo',)
1449
        key_basis = ('bar',)
1450
        key_missing = ('missing',)
1451
        test.add_lines(key, (), ['foo\n'])
1452
        details = test.annotate(key)
1453
        self.assertEqual([(key, 'foo\n')], details)
1454
        self.assertEqual([], basis.calls)
1455
        # But texts that are not in the test knit are looked for in the basis
1456
        # directly.
1457
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1458
        basis.calls = []
1459
        details = test.annotate(key_basis)
1460
        self.assertEqual([(key_basis, 'foo\n'), (key_basis, 'bar\n')], details)
3350.9.1 by Robert Collins
Redo annotate more simply, using just the public interfaces for VersionedFiles.
1461
        # Not optimised to date:
1462
        # self.assertEqual([("annotate", key_basis)], basis.calls)
1463
        self.assertEqual([('get_parent_map', set([key_basis])),
1464
            ('get_parent_map', set([key_basis])),
1465
            ('get_parent_map', set([key_basis])),
1466
            ('get_record_stream', [key_basis], 'unordered', True)],
1467
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1468
1469
    def test_check(self):
3517.4.19 by Martin Pool
Update test for knit.check() to expect it to recurse into fallback vfs
1470
        # At the moment checking a stacked knit does implicitly check the
1471
        # fallback files.  
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1472
        basis, test = self.get_basis_and_test_knit()
1473
        test.check()
1474
1475
    def test_get_parent_map(self):
3350.8.2 by Robert Collins
stacked get_parent_map.
1476
        # parents in the test knit are answered without asking the basis
1477
        basis, test = self.get_basis_and_test_knit()
1478
        key = ('foo',)
1479
        key_basis = ('bar',)
1480
        key_missing = ('missing',)
1481
        test.add_lines(key, (), [])
1482
        parent_map = test.get_parent_map([key])
1483
        self.assertEqual({key: ()}, parent_map)
1484
        self.assertEqual([], basis.calls)
1485
        # But parents that are not in the test knit are looked for in the basis
1486
        basis.add_lines(key_basis, (), [])
1487
        basis.calls = []
1488
        parent_map = test.get_parent_map([key, key_basis, key_missing])
1489
        self.assertEqual({key: (),
1490
            key_basis: ()}, parent_map)
1491
        self.assertEqual([("get_parent_map", set([key_basis, key_missing]))],
1492
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1493
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1494
    def test_get_record_stream_unordered_fulltexts(self):
1495
        # records from the test knit are answered without asking the basis:
1496
        basis, test = self.get_basis_and_test_knit()
1497
        key = ('foo',)
1498
        key_basis = ('bar',)
1499
        key_missing = ('missing',)
1500
        test.add_lines(key, (), ['foo\n'])
1501
        records = list(test.get_record_stream([key], 'unordered', True))
1502
        self.assertEqual(1, len(records))
1503
        self.assertEqual([], basis.calls)
1504
        # Missing (from test knit) objects are retrieved from the basis:
1505
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1506
        basis.calls = []
1507
        records = list(test.get_record_stream([key_basis, key_missing],
1508
            'unordered', True))
1509
        self.assertEqual(2, len(records))
1510
        calls = list(basis.calls)
1511
        for record in records:
1512
            self.assertSubset([record.key], (key_basis, key_missing))
1513
            if record.key == key_missing:
1514
                self.assertIsInstance(record, AbsentContentFactory)
1515
            else:
1516
                reference = list(basis.get_record_stream([key_basis],
1517
                    'unordered', True))[0]
1518
                self.assertEqual(reference.key, record.key)
1519
                self.assertEqual(reference.sha1, record.sha1)
1520
                self.assertEqual(reference.storage_kind, record.storage_kind)
1521
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
1522
                    record.get_bytes_as(record.storage_kind))
1523
                self.assertEqual(reference.get_bytes_as('fulltext'),
1524
                    record.get_bytes_as('fulltext'))
3350.8.14 by Robert Collins
Review feedback.
1525
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1526
        # ask which fallbacks have which parents.
1527
        self.assertEqual([
1528
            ("get_parent_map", set([key_basis, key_missing])),
1529
            ("get_record_stream", [key_basis], 'unordered', True)],
1530
            calls)
1531
1532
    def test_get_record_stream_ordered_fulltexts(self):
1533
        # ordering is preserved down into the fallback store.
1534
        basis, test = self.get_basis_and_test_knit()
1535
        key = ('foo',)
1536
        key_basis = ('bar',)
1537
        key_basis_2 = ('quux',)
1538
        key_missing = ('missing',)
1539
        test.add_lines(key, (key_basis,), ['foo\n'])
1540
        # Missing (from test knit) objects are retrieved from the basis:
1541
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
1542
        basis.add_lines(key_basis_2, (), ['quux\n'])
1543
        basis.calls = []
1544
        # ask for in non-topological order
1545
        records = list(test.get_record_stream(
1546
            [key, key_basis, key_missing, key_basis_2], 'topological', True))
1547
        self.assertEqual(4, len(records))
1548
        results = []
1549
        for record in records:
1550
            self.assertSubset([record.key],
1551
                (key_basis, key_missing, key_basis_2, key))
1552
            if record.key == key_missing:
1553
                self.assertIsInstance(record, AbsentContentFactory)
1554
            else:
1555
                results.append((record.key, record.sha1, record.storage_kind,
1556
                    record.get_bytes_as('fulltext')))
1557
        calls = list(basis.calls)
1558
        order = [record[0] for record in results]
1559
        self.assertEqual([key_basis_2, key_basis, key], order)
1560
        for result in results:
1561
            if result[0] == key:
1562
                source = test
1563
            else:
1564
                source = basis
1565
            record = source.get_record_stream([result[0]], 'unordered',
1566
                True).next()
1567
            self.assertEqual(record.key, result[0])
1568
            self.assertEqual(record.sha1, result[1])
1569
            self.assertEqual(record.storage_kind, result[2])
1570
            self.assertEqual(record.get_bytes_as('fulltext'), result[3])
3350.8.14 by Robert Collins
Review feedback.
1571
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1572
        # ask which fallbacks have which parents.
1573
        self.assertEqual([
1574
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
1575
            # unordered is asked for by the underlying worker as it still
1576
            # buffers everything while answering - which is a problem!
1577
            ("get_record_stream", [key_basis_2, key_basis], 'unordered', True)],
1578
            calls)
1579
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1580
    def test_get_record_stream_unordered_deltas(self):
1581
        # records from the test knit are answered without asking the basis:
1582
        basis, test = self.get_basis_and_test_knit()
1583
        key = ('foo',)
1584
        key_basis = ('bar',)
1585
        key_missing = ('missing',)
1586
        test.add_lines(key, (), ['foo\n'])
1587
        records = list(test.get_record_stream([key], 'unordered', False))
1588
        self.assertEqual(1, len(records))
1589
        self.assertEqual([], basis.calls)
1590
        # Missing (from test knit) objects are retrieved from the basis:
1591
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1592
        basis.calls = []
1593
        records = list(test.get_record_stream([key_basis, key_missing],
1594
            'unordered', False))
1595
        self.assertEqual(2, len(records))
1596
        calls = list(basis.calls)
1597
        for record in records:
1598
            self.assertSubset([record.key], (key_basis, key_missing))
1599
            if record.key == key_missing:
1600
                self.assertIsInstance(record, AbsentContentFactory)
1601
            else:
1602
                reference = list(basis.get_record_stream([key_basis],
1603
                    'unordered', False))[0]
1604
                self.assertEqual(reference.key, record.key)
1605
                self.assertEqual(reference.sha1, record.sha1)
1606
                self.assertEqual(reference.storage_kind, record.storage_kind)
1607
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
1608
                    record.get_bytes_as(record.storage_kind))
3350.8.14 by Robert Collins
Review feedback.
1609
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1610
        # ask which fallbacks have which parents.
1611
        self.assertEqual([
1612
            ("get_parent_map", set([key_basis, key_missing])),
1613
            ("get_record_stream", [key_basis], 'unordered', False)],
1614
            calls)
1615
1616
    def test_get_record_stream_ordered_deltas(self):
1617
        # ordering is preserved down into the fallback store.
1618
        basis, test = self.get_basis_and_test_knit()
1619
        key = ('foo',)
1620
        key_basis = ('bar',)
1621
        key_basis_2 = ('quux',)
1622
        key_missing = ('missing',)
1623
        test.add_lines(key, (key_basis,), ['foo\n'])
1624
        # Missing (from test knit) objects are retrieved from the basis:
1625
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
1626
        basis.add_lines(key_basis_2, (), ['quux\n'])
1627
        basis.calls = []
1628
        # ask for in non-topological order
1629
        records = list(test.get_record_stream(
1630
            [key, key_basis, key_missing, key_basis_2], 'topological', False))
1631
        self.assertEqual(4, len(records))
1632
        results = []
1633
        for record in records:
1634
            self.assertSubset([record.key],
1635
                (key_basis, key_missing, key_basis_2, key))
1636
            if record.key == key_missing:
1637
                self.assertIsInstance(record, AbsentContentFactory)
1638
            else:
1639
                results.append((record.key, record.sha1, record.storage_kind,
1640
                    record.get_bytes_as(record.storage_kind)))
1641
        calls = list(basis.calls)
1642
        order = [record[0] for record in results]
1643
        self.assertEqual([key_basis_2, key_basis, key], order)
1644
        for result in results:
1645
            if result[0] == key:
1646
                source = test
1647
            else:
1648
                source = basis
1649
            record = source.get_record_stream([result[0]], 'unordered',
1650
                False).next()
1651
            self.assertEqual(record.key, result[0])
1652
            self.assertEqual(record.sha1, result[1])
1653
            self.assertEqual(record.storage_kind, result[2])
1654
            self.assertEqual(record.get_bytes_as(record.storage_kind), result[3])
3350.8.14 by Robert Collins
Review feedback.
1655
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1656
        # ask which fallbacks have which parents.
1657
        self.assertEqual([
1658
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
1659
            ("get_record_stream", [key_basis_2, key_basis], 'topological', False)],
1660
            calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1661
1662
    def test_get_sha1s(self):
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1663
        # sha1's in the test knit are answered without asking the basis
1664
        basis, test = self.get_basis_and_test_knit()
1665
        key = ('foo',)
1666
        key_basis = ('bar',)
1667
        key_missing = ('missing',)
1668
        test.add_lines(key, (), ['foo\n'])
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
1669
        key_sha1sum = osutils.sha('foo\n').hexdigest()
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1670
        sha1s = test.get_sha1s([key])
1671
        self.assertEqual({key: key_sha1sum}, sha1s)
1672
        self.assertEqual([], basis.calls)
1673
        # But texts that are not in the test knit are looked for in the basis
1674
        # directly (rather than via text reconstruction) so that remote servers
1675
        # etc don't have to answer with full content.
1676
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
1677
        basis_sha1sum = osutils.sha('foo\nbar\n').hexdigest()
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1678
        basis.calls = []
1679
        sha1s = test.get_sha1s([key, key_missing, key_basis])
1680
        self.assertEqual({key: key_sha1sum,
1681
            key_basis: basis_sha1sum}, sha1s)
1682
        self.assertEqual([("get_sha1s", set([key_basis, key_missing]))],
1683
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1684
1685
    def test_insert_record_stream(self):
3350.8.10 by Robert Collins
Stacked insert_record_stream.
1686
        # records are inserted as normal; insert_record_stream builds on
3350.8.14 by Robert Collins
Review feedback.
1687
        # add_lines, so a smoke test should be all that's needed:
3350.8.10 by Robert Collins
Stacked insert_record_stream.
1688
        key = ('foo',)
1689
        key_basis = ('bar',)
1690
        key_delta = ('zaphod',)
1691
        basis, test = self.get_basis_and_test_knit()
1692
        source = self.make_test_knit(name='source')
1693
        basis.add_lines(key_basis, (), ['foo\n'])
1694
        basis.calls = []
1695
        source.add_lines(key_basis, (), ['foo\n'])
1696
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
1697
        stream = source.get_record_stream([key_delta], 'unordered', False)
1698
        test.insert_record_stream(stream)
1699
        self.assertEqual([("get_parent_map", set([key_basis]))],
1700
            basis.calls)
1701
        self.assertEqual({key_delta:(key_basis,)},
1702
            test.get_parent_map([key_delta]))
1703
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
1704
            'unordered', True).next().get_bytes_as('fulltext'))
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1705
1706
    def test_iter_lines_added_or_present_in_keys(self):
3350.8.5 by Robert Collins
Iter_lines_added_or_present_in_keys stacks.
1707
        # Lines from the basis are returned, and lines for a given key are only
1708
        # returned once. 
1709
        key1 = ('foo1',)
1710
        key2 = ('foo2',)
1711
        # all sources are asked for keys:
1712
        basis, test = self.get_basis_and_test_knit()
1713
        basis.add_lines(key1, (), ["foo"])
1714
        basis.calls = []
1715
        lines = list(test.iter_lines_added_or_present_in_keys([key1]))
1716
        self.assertEqual([("foo\n", key1)], lines)
1717
        self.assertEqual([("iter_lines_added_or_present_in_keys", set([key1]))],
1718
            basis.calls)
1719
        # keys in both are not duplicated:
1720
        test.add_lines(key2, (), ["bar\n"])
1721
        basis.add_lines(key2, (), ["bar\n"])
1722
        basis.calls = []
1723
        lines = list(test.iter_lines_added_or_present_in_keys([key2]))
1724
        self.assertEqual([("bar\n", key2)], lines)
1725
        self.assertEqual([], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1726
1727
    def test_keys(self):
3350.8.4 by Robert Collins
Vf.keys() stacking support.
1728
        key1 = ('foo1',)
1729
        key2 = ('foo2',)
1730
        # all sources are asked for keys:
1731
        basis, test = self.get_basis_and_test_knit()
1732
        keys = test.keys()
1733
        self.assertEqual(set(), set(keys))
1734
        self.assertEqual([("keys",)], basis.calls)
1735
        # keys from a basis are returned:
1736
        basis.add_lines(key1, (), [])
1737
        basis.calls = []
1738
        keys = test.keys()
1739
        self.assertEqual(set([key1]), set(keys))
1740
        self.assertEqual([("keys",)], basis.calls)
1741
        # keys in both are not duplicated:
1742
        test.add_lines(key2, (), [])
1743
        basis.add_lines(key2, (), [])
1744
        basis.calls = []
1745
        keys = test.keys()
1746
        self.assertEqual(2, len(keys))
1747
        self.assertEqual(set([key1, key2]), set(keys))
1748
        self.assertEqual([("keys",)], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1749
1750
    def test_add_mpdiffs(self):
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1751
        # records are inserted as normal; add_mpdiff builds on
3350.8.14 by Robert Collins
Review feedback.
1752
        # add_lines, so a smoke test should be all that's needed:
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1753
        key = ('foo',)
1754
        key_basis = ('bar',)
1755
        key_delta = ('zaphod',)
1756
        basis, test = self.get_basis_and_test_knit()
1757
        source = self.make_test_knit(name='source')
1758
        basis.add_lines(key_basis, (), ['foo\n'])
1759
        basis.calls = []
1760
        source.add_lines(key_basis, (), ['foo\n'])
1761
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
1762
        diffs = source.make_mpdiffs([key_delta])
1763
        test.add_mpdiffs([(key_delta, (key_basis,),
1764
            source.get_sha1s([key_delta])[key_delta], diffs[0])])
1765
        self.assertEqual([("get_parent_map", set([key_basis])),
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
1766
            ('get_record_stream', [key_basis], 'unordered', True),
1767
            ('get_parent_map', set([key_basis]))],
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1768
            basis.calls)
1769
        self.assertEqual({key_delta:(key_basis,)},
1770
            test.get_parent_map([key_delta]))
1771
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
1772
            'unordered', True).next().get_bytes_as('fulltext'))
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1773
1774
    def test_make_mpdiffs(self):
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
1775
        # Generating an mpdiff across a stacking boundary should detect parent
1776
        # texts regions.
1777
        key = ('foo',)
1778
        key_left = ('bar',)
1779
        key_right = ('zaphod',)
1780
        basis, test = self.get_basis_and_test_knit()
1781
        basis.add_lines(key_left, (), ['bar\n'])
1782
        basis.add_lines(key_right, (), ['zaphod\n'])
1783
        basis.calls = []
1784
        test.add_lines(key, (key_left, key_right),
1785
            ['bar\n', 'foo\n', 'zaphod\n'])
1786
        diffs = test.make_mpdiffs([key])
1787
        self.assertEqual([
1788
            multiparent.MultiParent([multiparent.ParentText(0, 0, 0, 1),
1789
                multiparent.NewText(['foo\n']),
1790
                multiparent.ParentText(1, 0, 2, 1)])],
1791
            diffs)
1792
        self.assertEqual(4, len(basis.calls))
1793
        self.assertEqual([
1794
            ("get_parent_map", set([key_left, key_right])),
1795
            ("get_parent_map", set([key_left, key_right])),
1796
            ("get_parent_map", set([key_left, key_right])),
1797
            ],
1798
            basis.calls[:3])
3350.8.14 by Robert Collins
Review feedback.
1799
        last_call = basis.calls[3]
1800
        self.assertEqual('get_record_stream', last_call[0])
1801
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
1802
        self.assertEqual('unordered', last_call[2])
1803
        self.assertEqual(True, last_call[3])