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