/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for Knit data structure"""
18
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
19
from cStringIO import StringIO
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
20
import difflib
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
21
import gzip
22
import sha
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
23
import sys
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
24
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
25
from bzrlib import (
26
    errors,
2484.1.5 by John Arbash Meinel
Simplistic implementations of custom parsers for options and parents
27
    generate_ids,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
28
    knit,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
29
    pack,
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
30
    )
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
31
from bzrlib.errors import (
32
    RevisionAlreadyPresent,
33
    KnitHeaderError,
34
    RevisionNotPresent,
35
    NoSuchFile,
36
    )
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
37
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
38
from bzrlib.knit import (
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
39
    AnnotatedKnitContent,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
40
    DATA_SUFFIX,
41
    INDEX_SUFFIX,
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
42
    KnitContent,
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.
43
    KnitGraphIndex,
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
44
    KnitVersionedFile,
45
    KnitPlainFactory,
46
    KnitAnnotateFactory,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
47
    _KnitAccess,
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
48
    _KnitData,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
49
    _KnitIndex,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
50
    make_file_knit,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
51
    _PackAccess,
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.
52
    PlainKnitContent,
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
53
    _StreamAccess,
54
    _StreamIndex,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
55
    WeaveToKnit,
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
56
    KnitSequenceMatcher,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
57
    )
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
58
from bzrlib.osutils import split_lines
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
59
from bzrlib.tests import (
60
    Feature,
61
    TestCase,
62
    TestCaseWithMemoryTransport,
63
    TestCaseWithTransport,
64
    )
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
65
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
66
from bzrlib.transport.memory import MemoryTransport
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
67
from bzrlib.tuned_gzip import GzipFile
2535.3.15 by Andrew Bennetts
Add KnitVersionedFile.get_stream_as_bytes, start smart implementation of RemoteRepository.get_data_stream.
68
from bzrlib.util import bencode
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
69
from bzrlib.weave import Weave
70
71
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
72
class _CompiledKnitFeature(Feature):
73
74
    def _probe(self):
75
        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
76
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
77
        except ImportError:
78
            return False
79
        return True
80
81
    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
82
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
83
84
CompiledKnitFeature = _CompiledKnitFeature()
85
86
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
class KnitContentTestsMixin(object):
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
88
89
    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.
90
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
91
92
    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.
93
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
94
        self.assertEqual(content.text(), [])
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
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
97
        self.assertEqual(content.text(), ["text1", "text2"])
98
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.
99
    def test_copy(self):
100
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
101
        copy = content.copy()
102
        self.assertIsInstance(copy, content.__class__)
103
        self.assertEqual(copy.annotate(), content.annotate())
104
105
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
106
        """Assert that the derived matching blocks match real output"""
107
        source_lines = source.splitlines(True)
108
        target_lines = target.splitlines(True)
109
        def nl(line):
110
            if noeol and not line.endswith('\n'):
111
                return line + '\n'
112
            else:
113
                return line
114
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
115
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
116
        line_delta = source_content.line_delta(target_content)
117
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
118
            source_lines, target_lines))
119
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
120
        matcher_blocks = list(list(matcher.get_matching_blocks()))
121
        self.assertEqual(matcher_blocks, delta_blocks)
122
123
    def test_get_line_delta_blocks(self):
124
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
125
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
126
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
127
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
128
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
129
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
130
        self.assertDerivedBlocksEqual(TEXT_1A, '')
131
        self.assertDerivedBlocksEqual('', TEXT_1A)
132
        self.assertDerivedBlocksEqual('', '')
133
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
134
135
    def test_get_line_delta_blocks_noeol(self):
136
        """Handle historical knit deltas safely
137
138
        Some existing knit deltas don't consider the last line to differ
139
        when the only difference whether it has a final newline.
140
141
        New knit deltas appear to always consider the last line to differ
142
        in this case.
143
        """
144
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
145
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
146
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
147
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
148
149
150
class TestPlainKnitContent(TestCase, KnitContentTestsMixin):
151
152
    def _make_content(self, lines):
153
        annotated_content = AnnotatedKnitContent(lines)
154
        return PlainKnitContent(annotated_content.text(), 'bogus')
155
156
    def test_annotate(self):
157
        content = self._make_content([])
158
        self.assertEqual(content.annotate(), [])
159
160
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
161
        self.assertEqual(content.annotate(),
162
            [("bogus", "text1"), ("bogus", "text2")])
163
164
    def test_annotate_iter(self):
165
        content = self._make_content([])
166
        it = content.annotate_iter()
167
        self.assertRaises(StopIteration, it.next)
168
169
        content = self._make_content([("bogus", "text1"), ("bogus", "text2")])
170
        it = content.annotate_iter()
171
        self.assertEqual(it.next(), ("bogus", "text1"))
172
        self.assertEqual(it.next(), ("bogus", "text2"))
173
        self.assertRaises(StopIteration, it.next)
174
175
    def test_line_delta(self):
176
        content1 = self._make_content([("", "a"), ("", "b")])
177
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
178
        self.assertEqual(content1.line_delta(content2),
179
            [(1, 2, 2, ["a", "c"])])
180
181
    def test_line_delta_iter(self):
182
        content1 = self._make_content([("", "a"), ("", "b")])
183
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
184
        it = content1.line_delta_iter(content2)
185
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
186
        self.assertRaises(StopIteration, it.next)
187
188
189
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
190
191
    def _make_content(self, lines):
192
        return AnnotatedKnitContent(lines)
193
194
    def test_annotate(self):
195
        content = self._make_content([])
196
        self.assertEqual(content.annotate(), [])
197
198
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
199
        self.assertEqual(content.annotate(),
200
            [("origin1", "text1"), ("origin2", "text2")])
201
202
    def test_annotate_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.
203
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
204
        it = content.annotate_iter()
205
        self.assertRaises(StopIteration, it.next)
206
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.
207
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
208
        it = content.annotate_iter()
209
        self.assertEqual(it.next(), ("origin1", "text1"))
210
        self.assertEqual(it.next(), ("origin2", "text2"))
211
        self.assertRaises(StopIteration, it.next)
212
213
    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.
214
        content1 = self._make_content([("", "a"), ("", "b")])
215
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
216
        self.assertEqual(content1.line_delta(content2),
217
            [(1, 2, 2, [("", "a"), ("", "c")])])
218
219
    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.
220
        content1 = self._make_content([("", "a"), ("", "b")])
221
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
222
        it = content1.line_delta_iter(content2)
223
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
224
        self.assertRaises(StopIteration, it.next)
225
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
226
227
class MockTransport(object):
228
229
    def __init__(self, file_lines=None):
230
        self.file_lines = file_lines
231
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
232
        # We have no base directory for the MockTransport
233
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
234
235
    def get(self, filename):
236
        if self.file_lines is None:
237
            raise NoSuchFile(filename)
238
        else:
239
            return StringIO("\n".join(self.file_lines))
240
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
241
    def readv(self, relpath, offsets):
242
        fp = self.get(relpath)
243
        for offset, size in offsets:
244
            fp.seek(offset)
245
            yield offset, fp.read(size)
246
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
247
    def __getattr__(self, name):
248
        def queue_call(*args, **kwargs):
249
            self.calls.append((name, args, kwargs))
250
        return queue_call
251
252
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
253
class KnitRecordAccessTestsMixin(object):
254
    """Tests for getting and putting knit records."""
255
256
    def assertAccessExists(self, access):
257
        """Ensure the data area for access has been initialised/exists."""
258
        raise NotImplementedError(self.assertAccessExists)
259
260
    def test_add_raw_records(self):
261
        """Add_raw_records adds records retrievable later."""
262
        access = self.get_access()
263
        memos = access.add_raw_records([10], '1234567890')
264
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
265
 
266
    def test_add_several_raw_records(self):
267
        """add_raw_records with many records and read some back."""
268
        access = self.get_access()
269
        memos = access.add_raw_records([10, 2, 5], '12345678901234567')
270
        self.assertEqual(['1234567890', '12', '34567'],
271
            list(access.get_raw_records(memos)))
272
        self.assertEqual(['1234567890'],
273
            list(access.get_raw_records(memos[0:1])))
274
        self.assertEqual(['12'],
275
            list(access.get_raw_records(memos[1:2])))
276
        self.assertEqual(['34567'],
277
            list(access.get_raw_records(memos[2:3])))
278
        self.assertEqual(['1234567890', '34567'],
279
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
280
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
281
    def test_create(self):
282
        """create() should make a file on disk."""
283
        access = self.get_access()
284
        access.create()
285
        self.assertAccessExists(access)
286
287
    def test_open_file(self):
288
        """open_file never errors."""
289
        access = self.get_access()
290
        access.open_file()
291
292
293
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
294
    """Tests for the .kndx implementation."""
295
296
    def assertAccessExists(self, access):
297
        self.assertNotEqual(None, access.open_file())
298
299
    def get_access(self):
300
        """Get a .knit style access instance."""
301
        access = _KnitAccess(self.get_transport(), "foo.knit", None, None,
302
            False, False)
303
        return access
304
    
305
306
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
307
    """Tests for the pack based access."""
308
309
    def assertAccessExists(self, access):
310
        # as pack based access has no backing unless an index maps data, this
311
        # is a no-op.
312
        pass
313
314
    def get_access(self):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
315
        return self._get_access()[0]
316
317
    def _get_access(self, packname='packfile', index='FOO'):
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
318
        transport = self.get_transport()
319
        def write_data(bytes):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
320
            transport.append_bytes(packname, bytes)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
321
        writer = pack.ContainerWriter(write_data)
322
        writer.begin()
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
323
        indices = {index:(transport, packname)}
324
        access = _PackAccess(indices, writer=(writer, index))
325
        return access, writer
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
326
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
327
    def test_read_from_several_packs(self):
328
        access, writer = self._get_access()
329
        memos = []
330
        memos.extend(access.add_raw_records([10], '1234567890'))
331
        writer.end()
332
        access, writer = self._get_access('pack2', 'FOOBAR')
333
        memos.extend(access.add_raw_records([5], '12345'))
334
        writer.end()
335
        access, writer = self._get_access('pack3', 'BAZ')
336
        memos.extend(access.add_raw_records([5], 'alpha'))
337
        writer.end()
338
        transport = self.get_transport()
339
        access = _PackAccess({"FOO":(transport, 'packfile'),
340
            "FOOBAR":(transport, 'pack2'),
341
            "BAZ":(transport, 'pack3')})
342
        self.assertEqual(['1234567890', '12345', 'alpha'],
343
            list(access.get_raw_records(memos)))
344
        self.assertEqual(['1234567890'],
345
            list(access.get_raw_records(memos[0:1])))
346
        self.assertEqual(['12345'],
347
            list(access.get_raw_records(memos[1:2])))
348
        self.assertEqual(['alpha'],
349
            list(access.get_raw_records(memos[2:3])))
350
        self.assertEqual(['1234567890', 'alpha'],
351
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
352
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
353
    def test_set_writer(self):
354
        """The writer should be settable post construction."""
355
        access = _PackAccess({})
356
        transport = self.get_transport()
357
        packname = 'packfile'
358
        index = 'foo'
359
        def write_data(bytes):
360
            transport.append_bytes(packname, bytes)
361
        writer = pack.ContainerWriter(write_data)
362
        writer.begin()
363
        access.set_writer(writer, index, (transport, packname))
364
        memos = access.add_raw_records([10], '1234567890')
365
        writer.end()
366
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
367
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
368
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
369
class LowLevelKnitDataTests(TestCase):
370
371
    def create_gz_content(self, text):
372
        sio = StringIO()
373
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
374
        gz_file.write(text)
375
        gz_file.close()
376
        return sio.getvalue()
377
378
    def test_valid_knit_data(self):
379
        sha1sum = sha.new('foo\nbar\n').hexdigest()
380
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
381
                                        'foo\n'
382
                                        'bar\n'
383
                                        'end rev-id-1\n'
384
                                        % (sha1sum,))
385
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
386
        access = _KnitAccess(transport, 'filename', None, None, False, False)
387
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
388
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
389
390
        contents = data.read_records(records)
391
        self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
392
393
        raw_contents = list(data.read_records_iter_raw(records))
394
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
395
396
    def test_not_enough_lines(self):
397
        sha1sum = sha.new('foo\n').hexdigest()
398
        # record says 2 lines data says 1
399
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
400
                                        'foo\n'
401
                                        'end rev-id-1\n'
402
                                        % (sha1sum,))
403
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
404
        access = _KnitAccess(transport, 'filename', None, None, False, False)
405
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
406
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
407
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
408
409
        # read_records_iter_raw won't detect that sort of mismatch/corruption
410
        raw_contents = list(data.read_records_iter_raw(records))
411
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
412
413
    def test_too_many_lines(self):
414
        sha1sum = sha.new('foo\nbar\n').hexdigest()
415
        # record says 1 lines data says 2
416
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
417
                                        'foo\n'
418
                                        'bar\n'
419
                                        'end rev-id-1\n'
420
                                        % (sha1sum,))
421
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
422
        access = _KnitAccess(transport, 'filename', None, None, False, False)
423
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
424
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
425
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
426
427
        # read_records_iter_raw won't detect that sort of mismatch/corruption
428
        raw_contents = list(data.read_records_iter_raw(records))
429
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
430
431
    def test_mismatched_version_id(self):
432
        sha1sum = sha.new('foo\nbar\n').hexdigest()
433
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
434
                                        'foo\n'
435
                                        'bar\n'
436
                                        'end rev-id-1\n'
437
                                        % (sha1sum,))
438
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
439
        access = _KnitAccess(transport, 'filename', None, None, False, False)
440
        data = _KnitData(access=access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
441
        # We are asking for rev-id-2, but the data is rev-id-1
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
442
        records = [('rev-id-2', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
443
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
444
445
        # read_records_iter_raw will notice if we request the wrong version.
446
        self.assertRaises(errors.KnitCorrupt, list,
447
                          data.read_records_iter_raw(records))
448
449
    def test_uncompressed_data(self):
450
        sha1sum = sha.new('foo\nbar\n').hexdigest()
451
        txt = ('version rev-id-1 2 %s\n'
452
               'foo\n'
453
               'bar\n'
454
               'end rev-id-1\n'
455
               % (sha1sum,))
456
        transport = MockTransport([txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
457
        access = _KnitAccess(transport, 'filename', None, None, False, False)
458
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
459
        records = [('rev-id-1', (None, 0, len(txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
460
461
        # We don't have valid gzip data ==> corrupt
462
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
463
464
        # read_records_iter_raw will notice the bad data
465
        self.assertRaises(errors.KnitCorrupt, list,
466
                          data.read_records_iter_raw(records))
467
468
    def test_corrupted_data(self):
469
        sha1sum = sha.new('foo\nbar\n').hexdigest()
470
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
471
                                        'foo\n'
472
                                        'bar\n'
473
                                        'end rev-id-1\n'
474
                                        % (sha1sum,))
475
        # Change 2 bytes in the middle to \xff
476
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
477
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
478
        access = _KnitAccess(transport, 'filename', None, None, False, False)
479
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
480
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
481
482
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
483
484
        # read_records_iter_raw will notice if we request the wrong version.
485
        self.assertRaises(errors.KnitCorrupt, list,
486
                          data.read_records_iter_raw(records))
487
488
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
489
class LowLevelKnitIndexTests(TestCase):
490
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
491
    def get_knit_index(self, *args, **kwargs):
492
        orig = knit._load_data
493
        def reset():
494
            knit._load_data = orig
495
        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
496
        from bzrlib._knit_load_data_py import _load_data_py
497
        knit._load_data = _load_data_py
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
498
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
499
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
500
    def test_no_such_file(self):
501
        transport = MockTransport()
502
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
503
        self.assertRaises(NoSuchFile, self.get_knit_index,
504
                          transport, "filename", "r")
505
        self.assertRaises(NoSuchFile, self.get_knit_index,
506
                          transport, "filename", "w", create=False)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
507
508
    def test_create_file(self):
509
        transport = MockTransport()
510
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
511
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
512
            file_mode="wb", create=True)
513
        self.assertEqual(
514
                ("put_bytes_non_atomic",
515
                    ("filename", index.HEADER), {"mode": "wb"}),
516
                transport.calls.pop(0))
517
518
    def test_delay_create_file(self):
519
        transport = MockTransport()
520
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
521
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
522
            create=True, file_mode="wb", create_parent_dir=True,
523
            delay_create=True, dir_mode=0777)
524
        self.assertEqual([], transport.calls)
525
526
        index.add_versions([])
527
        name, (filename, f), kwargs = transport.calls.pop(0)
528
        self.assertEqual("put_file_non_atomic", name)
529
        self.assertEqual(
530
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
531
            kwargs)
532
        self.assertEqual("filename", filename)
533
        self.assertEqual(index.HEADER, f.read())
534
535
        index.add_versions([])
536
        self.assertEqual(("append_bytes", ("filename", ""), {}),
537
            transport.calls.pop(0))
538
539
    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
540
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
541
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
542
        transport = MockTransport([
543
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
544
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
545
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
546
        index = self.get_knit_index(transport, "filename", "r")
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
547
        # _KnitIndex is a private class, and deals in utf8 revision_ids, not
548
        # Unicode revision_ids.
549
        self.assertTrue(index.has_version(utf8_revision_id))
550
        self.assertFalse(index.has_version(unicode_revision_id))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
551
552
    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
553
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
554
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
555
        transport = MockTransport([
556
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
557
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
558
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
559
        index = self.get_knit_index(transport, "filename", "r")
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
560
        self.assertEqual((utf8_revision_id,),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
561
            index.get_parents_with_ghosts("version"))
562
563
    def test_read_ignore_corrupted_lines(self):
564
        transport = MockTransport([
565
            _KnitIndex.HEADER,
566
            "corrupted",
567
            "corrupted options 0 1 .b .c ",
568
            "version options 0 1 :"
569
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
570
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
571
        self.assertEqual(1, index.num_versions())
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
572
        self.assertTrue(index.has_version("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
573
574
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
575
        transport = MockTransport(['not a bzr knit index header\n'])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
576
        self.assertRaises(KnitHeaderError,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
577
            self.get_knit_index, transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
578
579
    def test_read_duplicate_entries(self):
580
        transport = MockTransport([
581
            _KnitIndex.HEADER,
582
            "parent options 0 1 :",
583
            "version options1 0 1 0 :",
584
            "version options2 1 2 .other :",
585
            "version options3 3 4 0 .other :"
586
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
587
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
588
        self.assertEqual(2, index.num_versions())
2592.3.8 by Robert Collins
Remove unneeded pulib method lookup on private class _KnitIndex.
589
        # check that the index used is the first one written. (Specific
590
        # to KnitIndex style indices.
591
        self.assertEqual("1", index._version_list_to_index(["version"]))
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
592
        self.assertEqual((None, 3, 4), index.get_position("version"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
593
        self.assertEqual(["options3"], index.get_options("version"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
594
        self.assertEqual(("parent", "other"),
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
595
            index.get_parents_with_ghosts("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
596
597
    def test_read_compressed_parents(self):
598
        transport = MockTransport([
599
            _KnitIndex.HEADER,
600
            "a option 0 1 :",
601
            "b option 0 1 0 :",
602
            "c option 0 1 1 0 :",
603
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
604
        index = self.get_knit_index(transport, "filename", "r")
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
605
        self.assertEqual({"b":("a",), "c":("b", "a")},
606
            index.get_parent_map(["b", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
607
608
    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
609
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
610
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
611
        transport = MockTransport([
612
            _KnitIndex.HEADER
613
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
614
        index = self.get_knit_index(transport, "filename", "r")
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
615
        index.add_version(utf8_revision_id, ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
616
        self.assertEqual(("append_bytes", ("filename",
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
617
            "\n%s option 0 1  :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
618
            {}),
619
            transport.calls.pop(0))
620
621
    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
622
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
623
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
624
        transport = MockTransport([
625
            _KnitIndex.HEADER
626
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
627
        index = self.get_knit_index(transport, "filename", "r")
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
628
        index.add_version("version", ["option"], (None, 0, 1), [utf8_revision_id])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
629
        self.assertEqual(("append_bytes", ("filename",
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
630
            "\nversion option 0 1 .%s :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
631
            {}),
632
            transport.calls.pop(0))
633
634
    def test_get_ancestry(self):
635
        transport = MockTransport([
636
            _KnitIndex.HEADER,
637
            "a option 0 1 :",
638
            "b option 0 1 0 .e :",
639
            "c option 0 1 1 0 :",
640
            "d option 0 1 2 .f :"
641
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
642
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
643
644
        self.assertEqual([], index.get_ancestry([]))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
645
        self.assertEqual(["a"], index.get_ancestry(["a"]))
646
        self.assertEqual(["a", "b"], index.get_ancestry(["b"]))
647
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["c"]))
648
        self.assertEqual(["a", "b", "c", "d"], index.get_ancestry(["d"]))
649
        self.assertEqual(["a", "b"], index.get_ancestry(["a", "b"]))
650
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
651
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
652
        self.assertRaises(RevisionNotPresent, index.get_ancestry, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
653
654
    def test_get_ancestry_with_ghosts(self):
655
        transport = MockTransport([
656
            _KnitIndex.HEADER,
657
            "a option 0 1 :",
658
            "b option 0 1 0 .e :",
659
            "c option 0 1 0 .f .g :",
660
            "d option 0 1 2 .h .j .k :"
661
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
662
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
663
664
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
665
        self.assertEqual(["a"], index.get_ancestry_with_ghosts(["a"]))
666
        self.assertEqual(["a", "e", "b"],
667
            index.get_ancestry_with_ghosts(["b"]))
668
        self.assertEqual(["a", "g", "f", "c"],
669
            index.get_ancestry_with_ghosts(["c"]))
670
        self.assertEqual(["a", "g", "f", "c", "k", "j", "h", "d"],
671
            index.get_ancestry_with_ghosts(["d"]))
672
        self.assertEqual(["a", "e", "b"],
673
            index.get_ancestry_with_ghosts(["a", "b"]))
674
        self.assertEqual(["a", "g", "f", "c"],
675
            index.get_ancestry_with_ghosts(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
676
        self.assertEqual(
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
677
            ["a", "g", "f", "c", "e", "b", "k", "j", "h", "d"],
678
            index.get_ancestry_with_ghosts(["b", "d"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
679
680
        self.assertRaises(RevisionNotPresent,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
681
            index.get_ancestry_with_ghosts, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
682
683
    def test_num_versions(self):
684
        transport = MockTransport([
685
            _KnitIndex.HEADER
686
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
687
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
688
689
        self.assertEqual(0, index.num_versions())
690
        self.assertEqual(0, len(index))
691
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
692
        index.add_version("a", ["option"], (None, 0, 1), [])
693
        self.assertEqual(1, index.num_versions())
694
        self.assertEqual(1, len(index))
695
696
        index.add_version("a", ["option2"], (None, 1, 2), [])
697
        self.assertEqual(1, index.num_versions())
698
        self.assertEqual(1, len(index))
699
700
        index.add_version("b", ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
701
        self.assertEqual(2, index.num_versions())
702
        self.assertEqual(2, len(index))
703
704
    def test_get_versions(self):
705
        transport = MockTransport([
706
            _KnitIndex.HEADER
707
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
708
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
709
710
        self.assertEqual([], index.get_versions())
711
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
712
        index.add_version("a", ["option"], (None, 0, 1), [])
713
        self.assertEqual(["a"], index.get_versions())
714
715
        index.add_version("a", ["option"], (None, 0, 1), [])
716
        self.assertEqual(["a"], index.get_versions())
717
718
        index.add_version("b", ["option"], (None, 0, 1), [])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
719
        self.assertEqual(["a", "b"], index.get_versions())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
720
721
    def test_add_version(self):
722
        transport = MockTransport([
723
            _KnitIndex.HEADER
724
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
725
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
726
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
727
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
728
        self.assertEqual(("append_bytes",
729
            ("filename", "\na option 0 1 .b :"),
730
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
731
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
732
        self.assertEqual(1, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
733
        self.assertEqual((None, 0, 1), index.get_position("a"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
734
        self.assertEqual(["option"], index.get_options("a"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
735
        self.assertEqual(("b",), index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
736
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
737
        index.add_version("a", ["opt"], (None, 1, 2), ["c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
738
        self.assertEqual(("append_bytes",
739
            ("filename", "\na opt 1 2 .c :"),
740
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
741
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
742
        self.assertEqual(1, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
743
        self.assertEqual((None, 1, 2), index.get_position("a"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
744
        self.assertEqual(["opt"], index.get_options("a"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
745
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
746
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
747
        index.add_version("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
748
        self.assertEqual(("append_bytes",
749
            ("filename", "\nb option 2 3 0 :"),
750
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
751
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
752
        self.assertEqual(2, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
753
        self.assertEqual((None, 2, 3), index.get_position("b"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
754
        self.assertEqual(["option"], index.get_options("b"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
755
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
756
757
    def test_add_versions(self):
758
        transport = MockTransport([
759
            _KnitIndex.HEADER
760
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
761
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
762
763
        index.add_versions([
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
764
            ("a", ["option"], (None, 0, 1), ["b"]),
765
            ("a", ["opt"], (None, 1, 2), ["c"]),
766
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
767
            ])
768
        self.assertEqual(("append_bytes", ("filename",
769
            "\na option 0 1 .b :"
770
            "\na opt 1 2 .c :"
771
            "\nb option 2 3 0 :"
772
            ), {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
773
        self.assertTrue(index.has_version("a"))
774
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
775
        self.assertEqual(2, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
776
        self.assertEqual((None, 1, 2), index.get_position("a"))
777
        self.assertEqual((None, 2, 3), index.get_position("b"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
778
        self.assertEqual(["opt"], index.get_options("a"))
779
        self.assertEqual(["option"], index.get_options("b"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
780
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
781
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
782
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
783
    def test_add_versions_random_id_is_accepted(self):
784
        transport = MockTransport([
785
            _KnitIndex.HEADER
786
            ])
787
        index = self.get_knit_index(transport, "filename", "r")
788
789
        index.add_versions([
790
            ("a", ["option"], (None, 0, 1), ["b"]),
791
            ("a", ["opt"], (None, 1, 2), ["c"]),
792
            ("b", ["option"], (None, 2, 3), ["a"])
793
            ], random_id=True)
794
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
795
    def test_delay_create_and_add_versions(self):
796
        transport = MockTransport()
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", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
799
            create=True, file_mode="wb", create_parent_dir=True,
800
            delay_create=True, dir_mode=0777)
801
        self.assertEqual([], transport.calls)
802
803
        index.add_versions([
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
804
            ("a", ["option"], (None, 0, 1), ["b"]),
805
            ("a", ["opt"], (None, 1, 2), ["c"]),
806
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
807
            ])
808
        name, (filename, f), kwargs = transport.calls.pop(0)
809
        self.assertEqual("put_file_non_atomic", name)
810
        self.assertEqual(
811
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
812
            kwargs)
813
        self.assertEqual("filename", filename)
814
        self.assertEqual(
815
            index.HEADER +
816
            "\na option 0 1 .b :"
817
            "\na opt 1 2 .c :"
818
            "\nb option 2 3 0 :",
819
            f.read())
820
821
    def test_has_version(self):
822
        transport = MockTransport([
823
            _KnitIndex.HEADER,
824
            "a option 0 1 :"
825
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
826
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
827
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
828
        self.assertTrue(index.has_version("a"))
829
        self.assertFalse(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
830
831
    def test_get_position(self):
832
        transport = MockTransport([
833
            _KnitIndex.HEADER,
834
            "a option 0 1 :",
835
            "b option 1 2 :"
836
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
837
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
838
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
839
        self.assertEqual((None, 0, 1), index.get_position("a"))
840
        self.assertEqual((None, 1, 2), index.get_position("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
841
842
    def test_get_method(self):
843
        transport = MockTransport([
844
            _KnitIndex.HEADER,
845
            "a fulltext,unknown 0 1 :",
846
            "b unknown,line-delta 1 2 :",
847
            "c bad 3 4 :"
848
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
849
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
850
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
851
        self.assertEqual("fulltext", index.get_method("a"))
852
        self.assertEqual("line-delta", index.get_method("b"))
853
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
854
855
    def test_get_options(self):
856
        transport = MockTransport([
857
            _KnitIndex.HEADER,
858
            "a opt1 0 1 :",
859
            "b opt2,opt3 1 2 :"
860
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
861
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
862
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
863
        self.assertEqual(["opt1"], index.get_options("a"))
864
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
865
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
866
    def test_get_parent_map(self):
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
867
        transport = MockTransport([
868
            _KnitIndex.HEADER,
869
            "a option 0 1 :",
870
            "b option 1 2 0 .c :",
871
            "c option 1 2 1 0 .e :"
872
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
873
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
874
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
875
        self.assertEqual({
876
            "a":(),
877
            "b":("a", "c"),
878
            "c":("b", "a", "e"),
879
            }, index.get_parent_map(["a", "b", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
880
881
    def test_get_parents_with_ghosts(self):
882
        transport = MockTransport([
883
            _KnitIndex.HEADER,
884
            "a option 0 1 :",
885
            "b option 1 2 0 .c :",
886
            "c option 1 2 1 0 .e :"
887
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
888
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
889
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
890
        self.assertEqual((), index.get_parents_with_ghosts("a"))
891
        self.assertEqual(("a", "c"), index.get_parents_with_ghosts("b"))
892
        self.assertEqual(("b", "a", "e"),
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
893
            index.get_parents_with_ghosts("c"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
894
895
    def test_check_versions_present(self):
896
        transport = MockTransport([
897
            _KnitIndex.HEADER,
898
            "a option 0 1 :",
899
            "b option 0 1 :"
900
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
901
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
902
903
        check = index.check_versions_present
904
905
        check([])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
906
        check(["a"])
907
        check(["b"])
908
        check(["a", "b"])
909
        self.assertRaises(RevisionNotPresent, check, ["c"])
910
        self.assertRaises(RevisionNotPresent, check, ["a", "b", "c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
911
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
912
    def test_impossible_parent(self):
913
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
914
        transport = MockTransport([
915
            _KnitIndex.HEADER,
916
            "a option 0 1 :",
917
            "b option 0 1 4 :"  # We don't have a 4th record
918
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
919
        try:
920
            self.assertRaises(errors.KnitCorrupt,
921
                              self.get_knit_index, transport, 'filename', 'r')
922
        except TypeError, e:
923
            if (str(e) == ('exceptions must be strings, classes, or instances,'
924
                           ' not exceptions.IndexError')
925
                and sys.version_info[0:2] >= (2,5)):
926
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
927
                                  ' raising new style exceptions with python'
928
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
929
            else:
930
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
931
932
    def test_corrupted_parent(self):
933
        transport = MockTransport([
934
            _KnitIndex.HEADER,
935
            "a option 0 1 :",
936
            "b option 0 1 :",
937
            "c option 0 1 1v :", # Can't have a parent of '1v'
938
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
939
        try:
940
            self.assertRaises(errors.KnitCorrupt,
941
                              self.get_knit_index, transport, 'filename', 'r')
942
        except TypeError, e:
943
            if (str(e) == ('exceptions must be strings, classes, or instances,'
944
                           ' not exceptions.ValueError')
945
                and sys.version_info[0:2] >= (2,5)):
946
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
947
                                  ' raising new style exceptions with python'
948
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
949
            else:
950
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
951
952
    def test_corrupted_parent_in_list(self):
953
        transport = MockTransport([
954
            _KnitIndex.HEADER,
955
            "a option 0 1 :",
956
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
957
            "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.
958
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
959
        try:
960
            self.assertRaises(errors.KnitCorrupt,
961
                              self.get_knit_index, transport, 'filename', 'r')
962
        except TypeError, e:
963
            if (str(e) == ('exceptions must be strings, classes, or instances,'
964
                           ' not exceptions.ValueError')
965
                and sys.version_info[0:2] >= (2,5)):
966
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
967
                                  ' raising new style exceptions with python'
968
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
969
            else:
970
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
971
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
972
    def test_invalid_position(self):
973
        transport = MockTransport([
974
            _KnitIndex.HEADER,
975
            "a option 1v 1 :",
976
            ])
977
        try:
978
            self.assertRaises(errors.KnitCorrupt,
979
                              self.get_knit_index, transport, 'filename', 'r')
980
        except TypeError, e:
981
            if (str(e) == ('exceptions must be strings, classes, or instances,'
982
                           ' not exceptions.ValueError')
983
                and sys.version_info[0:2] >= (2,5)):
984
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
985
                                  ' raising new style exceptions with python'
986
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
987
            else:
988
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
989
990
    def test_invalid_size(self):
991
        transport = MockTransport([
992
            _KnitIndex.HEADER,
993
            "a option 1 1v :",
994
            ])
995
        try:
996
            self.assertRaises(errors.KnitCorrupt,
997
                              self.get_knit_index, transport, 'filename', 'r')
998
        except TypeError, e:
999
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1000
                           ' not exceptions.ValueError')
1001
                and sys.version_info[0:2] >= (2,5)):
1002
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1003
                                  ' raising new style exceptions with python'
1004
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
1005
            else:
1006
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1007
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1008
    def test_short_line(self):
1009
        transport = MockTransport([
1010
            _KnitIndex.HEADER,
1011
            "a option 0 10  :",
1012
            "b option 10 10 0", # This line isn't terminated, ignored
1013
            ])
1014
        index = self.get_knit_index(transport, "filename", "r")
1015
        self.assertEqual(['a'], index.get_versions())
1016
1017
    def test_skip_incomplete_record(self):
1018
        # A line with bogus data should just be skipped
1019
        transport = MockTransport([
1020
            _KnitIndex.HEADER,
1021
            "a option 0 10  :",
1022
            "b option 10 10 0", # This line isn't terminated, ignored
1023
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1024
            ])
1025
        index = self.get_knit_index(transport, "filename", "r")
1026
        self.assertEqual(['a', 'c'], index.get_versions())
1027
1028
    def test_trailing_characters(self):
1029
        # A line with bogus data should just be skipped
1030
        transport = MockTransport([
1031
            _KnitIndex.HEADER,
1032
            "a option 0 10  :",
1033
            "b option 10 10 0 :a", # This line has extra trailing characters
1034
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1035
            ])
1036
        index = self.get_knit_index(transport, "filename", "r")
1037
        self.assertEqual(['a', 'c'], index.get_versions())
1038
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
1039
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1040
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1041
1042
    _test_needs_features = [CompiledKnitFeature]
1043
1044
    def get_knit_index(self, *args, **kwargs):
1045
        orig = knit._load_data
1046
        def reset():
1047
            knit._load_data = orig
1048
        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
1049
        from bzrlib._knit_load_data_c import _load_data_c
1050
        knit._load_data = _load_data_c
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1051
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1052
1053
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1054
class KnitTests(TestCaseWithTransport):
1055
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1056
2535.3.27 by Andrew Bennetts
Merge from bzr.dev.
1057
    def make_test_knit(self, annotate=False, delay_create=False, index=None,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1058
                       name='test', delta=True, access_mode='w'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1059
        if not annotate:
1060
            factory = KnitPlainFactory()
1061
        else:
1062
            factory = None
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1063
        if index is None:
1064
            index = _KnitIndex(get_transport('.'), name + INDEX_SUFFIX,
1065
                access_mode, create=True, file_mode=None,
1066
                create_parent_dir=False, delay_create=delay_create,
1067
                dir_mode=None, get_scope=lambda:None)
1068
        access = _KnitAccess(get_transport('.'), name + DATA_SUFFIX, None,
1069
            None, delay_create, False)
1070
        return KnitVersionedFile(name, get_transport('.'), factory=factory,
1071
            create=True, delay_create=delay_create, index=index,
1072
            access_method=access, delta=delta)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1073
2535.3.53 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
1074
    def assertRecordContentEqual(self, knit, version_id, candidate_content):
1075
        """Assert that some raw record content matches the raw record content
1076
        for a particular version_id in the given knit.
1077
        """
1078
        index_memo = knit._index.get_position(version_id)
1079
        record = (version_id, index_memo)
1080
        [(_, expected_content)] = list(knit._data.read_records_iter_raw([record]))
1081
        self.assertEqual(expected_content, candidate_content)
1082
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1083
1084
class BasicKnitTests(KnitTests):
1085
1086
    def add_stock_one_and_one_a(self, k):
1087
        k.add_lines('text-1', [], split_lines(TEXT_1))
1088
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
1089
1090
    def test_knit_constructor(self):
1091
        """Construct empty k"""
1092
        self.make_test_knit()
1093
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
1094
    def test_make_explicit_index(self):
1095
        """We can supply an index to use."""
1096
        knit = KnitVersionedFile('test', get_transport('.'),
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1097
            index='strangelove', access_method="a")
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
1098
        self.assertEqual(knit._index, 'strangelove')
1099
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1100
    def test_knit_add(self):
1101
        """Store one text in knit and retrieve"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1102
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1103
        k.add_lines('text-1', [], split_lines(TEXT_1))
1104
        self.assertTrue(k.has_version('text-1'))
1105
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1106
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1107
    def test_newline_empty_lines(self):
1108
        # ensure that ["\n"] round trips ok.
1109
        knit = self.make_test_knit()
1110
        knit.add_lines('a', [], ["\n"])
1111
        knit.add_lines_with_ghosts('b', [], ["\n"])
1112
        self.assertEqual(["\n"], knit.get_lines('a'))
1113
        self.assertEqual(["\n"], knit.get_lines('b'))
1114
        self.assertEqual(['fulltext'], knit._index.get_options('a'))
1115
        self.assertEqual(['fulltext'], knit._index.get_options('b'))
1116
        knit.add_lines('c', ['a'], ["\n"])
1117
        knit.add_lines_with_ghosts('d', ['b'], ["\n"])
1118
        self.assertEqual(["\n"], knit.get_lines('c'))
1119
        self.assertEqual(["\n"], knit.get_lines('d'))
1120
        self.assertEqual(['line-delta'], knit._index.get_options('c'))
1121
        self.assertEqual(['line-delta'], knit._index.get_options('d'))
1122
1123
    def test_empty_lines(self):
1124
        # bizarrely, [] is not listed as having no-eol. 
1125
        knit = self.make_test_knit()
1126
        knit.add_lines('a', [], [])
1127
        knit.add_lines_with_ghosts('b', [], [])
1128
        self.assertEqual([], knit.get_lines('a'))
1129
        self.assertEqual([], knit.get_lines('b'))
1130
        self.assertEqual(['fulltext'], knit._index.get_options('a'))
1131
        self.assertEqual(['fulltext'], knit._index.get_options('b'))
1132
        knit.add_lines('c', ['a'], [])
1133
        knit.add_lines_with_ghosts('d', ['b'], [])
1134
        self.assertEqual([], knit.get_lines('c'))
1135
        self.assertEqual([], knit.get_lines('d'))
1136
        self.assertEqual(['line-delta'], knit._index.get_options('c'))
1137
        self.assertEqual(['line-delta'], knit._index.get_options('d'))
1138
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1139
    def test_knit_reload(self):
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1140
        # test that the content in a reloaded knit is correct
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1141
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1142
        k.add_lines('text-1', [], split_lines(TEXT_1))
1143
        del k
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1144
        k2 = make_file_knit('test', get_transport('.'), access_mode='r',
1145
            factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1146
        self.assertTrue(k2.has_version('text-1'))
1147
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1148
1149
    def test_knit_several(self):
1150
        """Store several texts in a knit"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1151
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1152
        k.add_lines('text-1', [], split_lines(TEXT_1))
1153
        k.add_lines('text-2', [], split_lines(TEXT_2))
1154
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1155
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
1156
        
1157
    def test_repeated_add(self):
1158
        """Knit traps attempt to replace existing version"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1159
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1160
        k.add_lines('text-1', [], split_lines(TEXT_1))
1161
        self.assertRaises(RevisionAlreadyPresent, 
1162
                k.add_lines,
1163
                'text-1', [], split_lines(TEXT_1))
1164
1165
    def test_empty(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1166
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1167
        k.add_lines('text-1', [], [])
1168
        self.assertEquals(k.get_lines('text-1'), [])
1169
1170
    def test_incomplete(self):
1171
        """Test if texts without a ending line-end can be inserted and
1172
        extracted."""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1173
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1174
        k.add_lines('text-1', [], ['a\n',    'b'  ])
1175
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1666.1.6 by Robert Collins
Make knit the default format.
1176
        # reopening ensures maximum room for confusion
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1177
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1178
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
1179
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1180
1181
    def test_delta(self):
1182
        """Expression of knit delta as lines"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1183
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1184
        td = list(line_delta(TEXT_1.splitlines(True),
1185
                             TEXT_1A.splitlines(True)))
1186
        self.assertEqualDiff(''.join(td), delta_1_1a)
1187
        out = apply_line_delta(TEXT_1.splitlines(True), td)
1188
        self.assertEqualDiff(''.join(out), TEXT_1A)
1189
1190
    def test_add_with_parents(self):
1191
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1192
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1193
        self.add_stock_one_and_one_a(k)
3287.5.2 by Robert Collins
Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code.
1194
        self.assertEqual({'text-1':(), 'text-1a':('text-1',)},
1195
            k.get_parent_map(['text-1', 'text-1a']))
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1196
1197
    def test_ancestry(self):
1198
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1199
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1200
        self.add_stock_one_and_one_a(k)
1201
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
1202
1203
    def test_add_delta(self):
1204
        """Store in knit with parents"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1205
        k = self.make_test_knit(annotate=False)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1206
        self.add_stock_one_and_one_a(k)
1596.2.7 by Robert Collins
Remove the requirement for reannotation in knit joins.
1207
        k.clear_cache()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1208
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1209
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1210
    def test_add_delta_knit_graph_index(self):
1211
        """Does adding work with a KnitGraphIndex."""
1212
        index = InMemoryGraphIndex(2)
1213
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1214
            deltas=True)
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1215
        k = self.make_test_knit(annotate=True, index=knit_index)
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1216
        self.add_stock_one_and_one_a(k)
1217
        k.clear_cache()
1218
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1219
        # check the index had the right data added.
1220
        self.assertEqual(set([
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1221
            (index, ('text-1', ), ' 0 127', ((), ())),
1222
            (index, ('text-1a', ), ' 127 140', ((('text-1', ),), (('text-1', ),))),
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1223
            ]), set(index.iter_all_entries()))
1224
        # we should not have a .kndx file
1225
        self.assertFalse(get_transport('.').has('test.kndx'))
1226
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1227
    def test_annotate(self):
1228
        """Annotations"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1229
        k = self.make_test_knit(annotate=True, name='knit')
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1230
        self.insert_and_test_small_annotate(k)
1231
1232
    def insert_and_test_small_annotate(self, k):
1233
        """test annotation with k works correctly."""
1234
        k.add_lines('text-1', [], ['a\n', 'b\n'])
1235
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
1236
1237
        origins = k.annotate('text-2')
1238
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1239
        self.assertEquals(origins[1], ('text-2', 'c\n'))
1240
1241
    def test_annotate_fulltext(self):
1242
        """Annotations"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1243
        k = self.make_test_knit(annotate=True, name='knit', delta=False)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1244
        self.insert_and_test_small_annotate(k)
1245
1246
    def test_annotate_merge_1(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1247
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1248
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
1249
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
1250
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
1251
        origins = k.annotate('text-am')
1252
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
1253
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1254
1255
    def test_annotate_merge_2(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1256
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1257
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1258
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1259
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
1260
        origins = k.annotate('text-am')
1261
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1262
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1263
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1264
1265
    def test_annotate_merge_9(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1266
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1267
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1268
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1269
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
1270
        origins = k.annotate('text-am')
1271
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1272
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1273
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1274
1275
    def test_annotate_merge_3(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1276
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1277
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1278
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
1279
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
1280
        origins = k.annotate('text-am')
1281
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1282
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1283
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1284
1285
    def test_annotate_merge_4(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1286
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1287
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1288
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1289
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
1290
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
1291
        origins = k.annotate('text-am')
1292
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1293
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1294
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1295
1296
    def test_annotate_merge_5(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1297
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1298
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1299
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
1300
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
1301
        k.add_lines('text-am',
1302
                    ['text-a1', 'text-a2', 'text-a3'],
1303
                    ['a\n', 'e\n', 'z\n'])
1304
        origins = k.annotate('text-am')
1305
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1306
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
1307
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
1308
1309
    def test_annotate_file_cherry_pick(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1310
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1311
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
1312
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
1313
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
1314
        origins = k.annotate('text-3')
1315
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1316
        self.assertEquals(origins[1], ('text-1', 'b\n'))
1317
        self.assertEquals(origins[2], ('text-1', 'c\n'))
1318
2851.4.6 by Ian Clatworthy
review tweaks
1319
    def _test_join_with_factories(self, k1_factory, k2_factory):
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1320
        k1 = make_file_knit('test1', get_transport('.'), factory=k1_factory, create=True)
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1321
        k1.add_lines('text-a', [], ['a1\n', 'a2\n', 'a3\n'])
1322
        k1.add_lines('text-b', ['text-a'], ['a1\n', 'b2\n', 'a3\n'])
1323
        k1.add_lines('text-c', [], ['c1\n', 'c2\n', 'c3\n'])
1324
        k1.add_lines('text-d', ['text-c'], ['c1\n', 'd2\n', 'd3\n'])
1325
        k1.add_lines('text-m', ['text-b', 'text-d'], ['a1\n', 'b2\n', 'd3\n'])
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1326
        k2 = make_file_knit('test2', get_transport('.'), factory=k2_factory, create=True)
2851.4.6 by Ian Clatworthy
review tweaks
1327
        count = k2.join(k1, version_ids=['text-m'])
1328
        self.assertEquals(count, 5)
1329
        self.assertTrue(k2.has_version('text-a'))
1330
        self.assertTrue(k2.has_version('text-c'))
1331
        origins = k2.annotate('text-m')
1332
        self.assertEquals(origins[0], ('text-a', 'a1\n'))
1333
        self.assertEquals(origins[1], ('text-b', 'b2\n'))
1334
        self.assertEquals(origins[2], ('text-d', 'd3\n'))
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1335
1336
    def test_knit_join_plain_to_plain(self):
1337
        """Test joining a plain knit with a plain knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1338
        self._test_join_with_factories(KnitPlainFactory(), KnitPlainFactory())
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1339
1340
    def test_knit_join_anno_to_anno(self):
1341
        """Test joining an annotated knit with an annotated knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1342
        self._test_join_with_factories(None, None)
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1343
1344
    def test_knit_join_anno_to_plain(self):
1345
        """Test joining an annotated knit with a plain knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1346
        self._test_join_with_factories(None, KnitPlainFactory())
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1347
1348
    def test_knit_join_plain_to_anno(self):
1349
        """Test joining a plain knit with an annotated knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1350
        self._test_join_with_factories(KnitPlainFactory(), None)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1351
1352
    def test_reannotate(self):
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1353
        k1 = make_file_knit('knit1', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1354
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1355
        # 0
1356
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1357
        # 1
1358
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1359
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1360
        k2 = make_file_knit('test2', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1361
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1362
        k2.join(k1, version_ids=['text-b'])
1363
1364
        # 2
1365
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
1366
        # 2
1367
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
1368
        # 3
1369
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
1370
1371
        # test-c will have index 3
1372
        k1.join(k2, version_ids=['text-c'])
1373
1374
        lines = k1.get_lines('text-c')
1375
        self.assertEquals(lines, ['z\n', 'c\n'])
1376
1377
        origins = k1.annotate('text-c')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
1378
        self.assertEquals(origins[0], ('text-c', 'z\n'))
1379
        self.assertEquals(origins[1], ('text-b', 'c\n'))
1380
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
1381
    def test_get_line_delta_texts(self):
1382
        """Make sure we can call get_texts on text with reused line deltas"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1383
        k1 = make_file_knit('test1', get_transport('.'),
1384
            factory=KnitPlainFactory(), create=True)
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
1385
        for t in range(3):
1386
            if t == 0:
1387
                parents = []
1388
            else:
1389
                parents = ['%d' % (t-1)]
1390
            k1.add_lines('%d' % t, parents, ['hello\n'] * t)
1391
        k1.get_texts(('%d' % t) for t in range(3))
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1392
        
1393
    def test_iter_lines_reads_in_order(self):
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1394
        instrumented_t = get_transport('trace+memory:///')
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1395
        k1 = make_file_knit('id', instrumented_t, create=True, delta=True)
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1396
        self.assertEqual([('get', 'id.kndx',)], instrumented_t._activity)
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1397
        # add texts with no required ordering
1398
        k1.add_lines('base', [], ['text\n'])
1399
        k1.add_lines('base2', [], ['text2\n'])
1400
        k1.clear_cache()
2745.5.4 by Robert Collins
Review feedback.
1401
        # clear the logged activity, but preserve the list instance in case of
1402
        # clones pointing at it.
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1403
        del instrumented_t._activity[:]
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1404
        # request a last-first iteration
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1405
        results = list(k1.iter_lines_added_or_present_in_versions(
1406
            ['base2', 'base']))
2745.5.6 by Robert Collins
Fix knit test fallout from final readv api change.
1407
        self.assertEqual(
1408
            [('readv', 'id.knit', [(0, 87), (87, 89)], False, None)],
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1409
            instrumented_t._activity)
2975.3.1 by Robert Collins
Change (without backwards compatibility) the
1410
        self.assertEqual([('text\n', 'base'), ('text2\n', 'base2')], results)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1411
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1412
    def test_knit_format(self):
1413
        # this tests that a new knit index file has the expected content
1414
        # and that is writes the data we expect as records are added.
1415
        knit = self.make_test_knit(True)
1946.2.1 by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories
1416
        # Now knit files are not created until we first add data to them
1666.1.6 by Robert Collins
Make knit the default format.
1417
        self.assertFileEqual("# bzr knit index 8\n", 'test.kndx')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1418
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1419
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1420
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1421
            "\n"
1422
            "revid fulltext 0 84 .a_ghost :",
1423
            'test.kndx')
1424
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
1425
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1426
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1427
            "\nrevid fulltext 0 84 .a_ghost :"
1428
            "\nrevid2 line-delta 84 82 0 :",
1429
            'test.kndx')
1430
        # we should be able to load this file again
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1431
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1432
        self.assertEqual(['revid', 'revid2'], knit.versions())
1433
        # write a short write to the file and ensure that its ignored
2484.1.23 by John Arbash Meinel
When we append a new line, don't use text mode
1434
        indexfile = file('test.kndx', 'ab')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1435
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1436
        indexfile.close()
1437
        # we should be able to load this file again
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1438
        knit = make_file_knit('test', get_transport('.'), access_mode='w')
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1439
        self.assertEqual(['revid', 'revid2'], knit.versions())
1440
        # and add a revision with the same id the failed write had
1441
        knit.add_lines('revid3', ['revid2'], ['a\n'])
1442
        # and when reading it revid3 should now appear.
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1443
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1444
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
3287.5.2 by Robert Collins
Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code.
1445
        self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1446
1946.2.1 by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories
1447
    def test_delay_create(self):
1448
        """Test that passing delay_create=True creates files late"""
1449
        knit = self.make_test_knit(annotate=True, delay_create=True)
1450
        self.failIfExists('test.knit')
1451
        self.failIfExists('test.kndx')
1452
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1453
        self.failUnlessExists('test.knit')
1454
        self.assertFileEqual(
1455
            "# bzr knit index 8\n"
1456
            "\n"
1457
            "revid fulltext 0 84 .a_ghost :",
1458
            'test.kndx')
1459
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1460
    def test_create_parent_dir(self):
1461
        """create_parent_dir can create knits in nonexistant dirs"""
1462
        # Has no effect if we don't set 'delay_create'
1463
        trans = get_transport('.')
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1464
        self.assertRaises(NoSuchFile, make_file_knit, 'dir/test',
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1465
                          trans, access_mode='w', factory=None,
1466
                          create=True, create_parent_dir=True)
1467
        # Nothing should have changed yet
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1468
        knit = make_file_knit('dir/test', trans, access_mode='w',
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1469
                                 factory=None, create=True,
1470
                                 create_parent_dir=True,
1471
                                 delay_create=True)
1472
        self.failIfExists('dir/test.knit')
1473
        self.failIfExists('dir/test.kndx')
1474
        self.failIfExists('dir')
1475
        knit.add_lines('revid', [], ['a\n'])
1476
        self.failUnlessExists('dir')
1477
        self.failUnlessExists('dir/test.knit')
1478
        self.assertFileEqual(
1479
            "# bzr knit index 8\n"
1480
            "\n"
1481
            "revid fulltext 0 84  :",
1482
            'dir/test.kndx')
1483
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1484
    def test_create_mode_700(self):
1485
        trans = get_transport('.')
1486
        if not trans._can_roundtrip_unix_modebits():
1487
            # Can't roundtrip, so no need to run this test
1488
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1489
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1490
            create=True, create_parent_dir=True, delay_create=True,
1491
            file_mode=0600, dir_mode=0700)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1492
        knit.add_lines('revid', [], ['a\n'])
1493
        self.assertTransportMode(trans, 'dir', 0700)
1494
        self.assertTransportMode(trans, 'dir/test.knit', 0600)
1495
        self.assertTransportMode(trans, 'dir/test.kndx', 0600)
1496
1497
    def test_create_mode_770(self):
1498
        trans = get_transport('.')
1499
        if not trans._can_roundtrip_unix_modebits():
1500
            # Can't roundtrip, so no need to run this test
1501
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1502
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1503
            create=True, create_parent_dir=True, delay_create=True,
1504
            file_mode=0660, dir_mode=0770)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1505
        knit.add_lines('revid', [], ['a\n'])
1506
        self.assertTransportMode(trans, 'dir', 0770)
1507
        self.assertTransportMode(trans, 'dir/test.knit', 0660)
1508
        self.assertTransportMode(trans, 'dir/test.kndx', 0660)
1509
1510
    def test_create_mode_777(self):
1511
        trans = get_transport('.')
1512
        if not trans._can_roundtrip_unix_modebits():
1513
            # Can't roundtrip, so no need to run this test
1514
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1515
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1516
            create=True, create_parent_dir=True, delay_create=True,
1517
            file_mode=0666, dir_mode=0777)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1518
        knit.add_lines('revid', [], ['a\n'])
1519
        self.assertTransportMode(trans, 'dir', 0777)
1520
        self.assertTransportMode(trans, 'dir/test.knit', 0666)
1521
        self.assertTransportMode(trans, 'dir/test.kndx', 0666)
1522
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1523
    def test_plan_merge(self):
1524
        my_knit = self.make_test_knit(annotate=True)
1525
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
1526
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
1527
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
1664.2.3 by Aaron Bentley
Add failing test case
1528
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1529
        for plan_line, expected_line in zip(plan, AB_MERGE):
1530
            self.assertEqual(plan_line, expected_line)
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1531
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1532
    def test_get_stream_empty(self):
1533
        """Get a data stream for an empty knit file."""
1534
        k1 = self.make_test_knit()
1535
        format, data_list, reader_callable = k1.get_data_stream([])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1536
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1537
        self.assertEqual([], data_list)
1538
        content = reader_callable(None)
1539
        self.assertEqual('', content)
1540
        self.assertIsInstance(content, str)
1541
1542
    def test_get_stream_one_version(self):
1543
        """Get a data stream for a single record out of a knit containing just
1544
        one record.
1545
        """
1546
        k1 = self.make_test_knit()
1547
        test_data = [
1548
            ('text-a', [], TEXT_1),
1549
            ]
1550
        expected_data_list = [
1551
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1552
            ('text-a', ['fulltext'], 122, ()),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1553
           ]
1554
        for version_id, parents, lines in test_data:
1555
            k1.add_lines(version_id, parents, split_lines(lines))
1556
1557
        format, data_list, reader_callable = k1.get_data_stream(['text-a'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1558
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1559
        self.assertEqual(expected_data_list, data_list)
1560
        # There's only one record in the knit, so the content should be the
1561
        # entire knit data file's contents.
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1562
        self.assertEqual(k1.transport.get_bytes(k1._data._access._filename),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1563
                         reader_callable(None))
1564
        
1565
    def test_get_stream_get_one_version_of_many(self):
1566
        """Get a data stream for just one version out of a knit containing many
1567
        versions.
1568
        """
1569
        k1 = self.make_test_knit()
1570
        # Insert the same data as test_knit_join, as they seem to cover a range
1571
        # of cases (no parents, one parent, multiple parents).
1572
        test_data = [
1573
            ('text-a', [], TEXT_1),
1574
            ('text-b', ['text-a'], TEXT_1),
1575
            ('text-c', [], TEXT_1),
1576
            ('text-d', ['text-c'], TEXT_1),
1577
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1578
            ]
1579
        expected_data_list = [
1580
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1581
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1582
            ]
1583
        for version_id, parents, lines in test_data:
1584
            k1.add_lines(version_id, parents, split_lines(lines))
1585
1586
        format, data_list, reader_callable = k1.get_data_stream(['text-m'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1587
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1588
        self.assertEqual(expected_data_list, data_list)
1589
        self.assertRecordContentEqual(k1, 'text-m', reader_callable(None))
1590
        
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1591
    def test_get_data_stream_unordered_index(self):
1592
        """Get a data stream when the knit index reports versions out of order.
1593
1594
        https://bugs.launchpad.net/bzr/+bug/164637
1595
        """
1596
        k1 = self.make_test_knit()
1597
        test_data = [
1598
            ('text-a', [], TEXT_1),
1599
            ('text-b', ['text-a'], TEXT_1),
1600
            ('text-c', [], TEXT_1),
1601
            ('text-d', ['text-c'], TEXT_1),
1602
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1603
            ]
1604
        for version_id, parents, lines in test_data:
1605
            k1.add_lines(version_id, parents, split_lines(lines))
1606
        # monkey-patch versions method to return out of order, as if coming
1607
        # from multiple independently indexed packs
1608
        original_versions = k1.versions
1609
        k1.versions = lambda: reversed(original_versions())
1610
        expected_data_list = [
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1611
            ('text-a', ['fulltext'], 122, ()),
1612
            ('text-b', ['line-delta'], 84, ('text-a',))]
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1613
        # now check the fulltext is first and the delta second
1614
        format, data_list, _ = k1.get_data_stream(['text-a', 'text-b'])
1615
        self.assertEqual('knit-plain', format)
1616
        self.assertEqual(expected_data_list, data_list)
1617
        # and that's true if we ask for them in the opposite order too
1618
        format, data_list, _ = k1.get_data_stream(['text-b', 'text-a'])
1619
        self.assertEqual(expected_data_list, data_list)
1620
        # also try requesting more versions
1621
        format, data_list, _ = k1.get_data_stream([
1622
            'text-m', 'text-b', 'text-a'])
1623
        self.assertEqual([
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1624
            ('text-a', ['fulltext'], 122, ()),
1625
            ('text-b', ['line-delta'], 84, ('text-a',)),
1626
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1627
            ], data_list)
1628
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1629
    def test_get_stream_ghost_parent(self):
1630
        """Get a data stream for a version with a ghost parent."""
1631
        k1 = self.make_test_knit()
1632
        # Test data
1633
        k1.add_lines('text-a', [], split_lines(TEXT_1))
1634
        k1.add_lines_with_ghosts('text-b', ['text-a', 'text-ghost'],
1635
                                 split_lines(TEXT_1))
1636
        # Expected data
1637
        expected_data_list = [
1638
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1639
            ('text-b', ['line-delta'], 84, ('text-a', 'text-ghost')),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1640
            ]
1641
        
1642
        format, data_list, reader_callable = k1.get_data_stream(['text-b'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1643
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1644
        self.assertEqual(expected_data_list, data_list)
1645
        self.assertRecordContentEqual(k1, 'text-b', reader_callable(None))
1646
    
1647
    def test_get_stream_get_multiple_records(self):
1648
        """Get a stream for multiple records of a knit."""
1649
        k1 = self.make_test_knit()
1650
        # Insert the same data as test_knit_join, as they seem to cover a range
1651
        # of cases (no parents, one parent, multiple parents).
1652
        test_data = [
1653
            ('text-a', [], TEXT_1),
1654
            ('text-b', ['text-a'], TEXT_1),
1655
            ('text-c', [], TEXT_1),
1656
            ('text-d', ['text-c'], TEXT_1),
1657
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1658
            ]
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1659
        for version_id, parents, lines in test_data:
1660
            k1.add_lines(version_id, parents, split_lines(lines))
1661
1662
        # This test is actually a bit strict as the order in which they're
1663
        # returned is not defined.  This matches the current (deterministic)
1664
        # behaviour.
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1665
        expected_data_list = [
1666
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1667
            ('text-d', ['line-delta'], 84, ('text-c',)),
1668
            ('text-b', ['line-delta'], 84, ('text-a',)),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1669
            ]
1670
        # Note that even though we request the revision IDs in a particular
1671
        # order, the data stream may return them in any order it likes.  In this
1672
        # case, they'll be in the order they were inserted into the knit.
1673
        format, data_list, reader_callable = k1.get_data_stream(
1674
            ['text-d', 'text-b'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1675
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1676
        self.assertEqual(expected_data_list, data_list)
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1677
        # must match order they're returned
1678
        self.assertRecordContentEqual(k1, 'text-d', reader_callable(84))
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1679
        self.assertRecordContentEqual(k1, 'text-b', reader_callable(84))
1680
        self.assertEqual('', reader_callable(None),
1681
                         "There should be no more bytes left to read.")
1682
1683
    def test_get_stream_all(self):
1684
        """Get a data stream for all the records in a knit.
1685
1686
        This exercises fulltext records, line-delta records, records with
1687
        various numbers of parents, and reading multiple records out of the
1688
        callable.  These cases ought to all be exercised individually by the
1689
        other test_get_stream_* tests; this test is basically just paranoia.
1690
        """
1691
        k1 = self.make_test_knit()
1692
        # Insert the same data as test_knit_join, as they seem to cover a range
1693
        # of cases (no parents, one parent, multiple parents).
1694
        test_data = [
1695
            ('text-a', [], TEXT_1),
1696
            ('text-b', ['text-a'], TEXT_1),
1697
            ('text-c', [], TEXT_1),
1698
            ('text-d', ['text-c'], TEXT_1),
1699
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1700
           ]
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1701
        for version_id, parents, lines in test_data:
1702
            k1.add_lines(version_id, parents, split_lines(lines))
1703
1704
        # This test is actually a bit strict as the order in which they're
1705
        # returned is not defined.  This matches the current (deterministic)
1706
        # behaviour.
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1707
        expected_data_list = [
1708
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1709
            ('text-a', ['fulltext'], 122, ()),
1710
            ('text-b', ['line-delta'], 84, ('text-a',)),
1711
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1712
            ('text-c', ['fulltext'], 121, ()),
1713
            ('text-d', ['line-delta'], 84, ('text-c',)),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1714
            ]
1715
        format, data_list, reader_callable = k1.get_data_stream(
1716
            ['text-a', 'text-b', 'text-c', 'text-d', 'text-m'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1717
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1718
        self.assertEqual(expected_data_list, data_list)
1719
        for version_id, options, length, parents in expected_data_list:
1720
            bytes = reader_callable(length)
1721
            self.assertRecordContentEqual(k1, version_id, bytes)
1722
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1723
    def assertKnitFilesEqual(self, knit1, knit2):
1724
        """Assert that the contents of the index and data files of two knits are
1725
        equal.
1726
        """
1727
        self.assertEqual(
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1728
            knit1.transport.get_bytes(knit1._data._access._filename),
1729
            knit2.transport.get_bytes(knit2._data._access._filename))
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1730
        self.assertEqual(
1731
            knit1.transport.get_bytes(knit1._index._filename),
1732
            knit2.transport.get_bytes(knit2._index._filename))
1733
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1734
    def assertKnitValuesEqual(self, left, right):
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
1735
        """Assert that the texts, annotations and graph of left and right are
1736
        the same.
1737
        """
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1738
        self.assertEqual(set(left.versions()), set(right.versions()))
1739
        for version in left.versions():
1740
            self.assertEqual(left.get_parents_with_ghosts(version),
1741
                right.get_parents_with_ghosts(version))
1742
            self.assertEqual(left.get_lines(version),
1743
                right.get_lines(version))
1744
            self.assertEqual(left.annotate(version),
1745
                right.annotate(version))
1746
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1747
    def test_insert_data_stream_empty(self):
1748
        """Inserting a data stream with no records should not put any data into
1749
        the knit.
1750
        """
1751
        k1 = self.make_test_knit()
1752
        k1.insert_data_stream(
1753
            (k1.get_format_signature(), [], lambda ignored: ''))
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1754
        self.assertEqual('', k1.transport.get_bytes(k1._data._access._filename),
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1755
                         "The .knit should be completely empty.")
1756
        self.assertEqual(k1._index.HEADER,
1757
                         k1.transport.get_bytes(k1._index._filename),
1758
                         "The .kndx should have nothing apart from the header.")
1759
1760
    def test_insert_data_stream_one_record(self):
1761
        """Inserting a data stream with one record from a knit with one record
1762
        results in byte-identical files.
1763
        """
1764
        source = self.make_test_knit(name='source')
1765
        source.add_lines('text-a', [], split_lines(TEXT_1))
1766
        data_stream = source.get_data_stream(['text-a'])
1767
        target = self.make_test_knit(name='target')
1768
        target.insert_data_stream(data_stream)
1769
        self.assertKnitFilesEqual(source, target)
1770
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1771
    def test_insert_data_stream_annotated_unannotated(self):
1772
        """Inserting an annotated datastream to an unannotated knit works."""
1773
        # case one - full texts.
1774
        source = self.make_test_knit(name='source', annotate=True)
1775
        target = self.make_test_knit(name='target', annotate=False)
1776
        source.add_lines('text-a', [], split_lines(TEXT_1))
1777
        target.insert_data_stream(source.get_data_stream(['text-a']))
1778
        self.assertKnitValuesEqual(source, target)
1779
        # case two - deltas.
1780
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_2))
1781
        target.insert_data_stream(source.get_data_stream(['text-b']))
1782
        self.assertKnitValuesEqual(source, target)
1783
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
1784
    def test_insert_data_stream_unannotated_annotated(self):
1785
        """Inserting an unannotated datastream to an annotated knit works."""
1786
        # case one - full texts.
1787
        source = self.make_test_knit(name='source', annotate=False)
1788
        target = self.make_test_knit(name='target', annotate=True)
1789
        source.add_lines('text-a', [], split_lines(TEXT_1))
1790
        target.insert_data_stream(source.get_data_stream(['text-a']))
1791
        self.assertKnitValuesEqual(source, target)
1792
        # case two - deltas.
1793
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_2))
1794
        target.insert_data_stream(source.get_data_stream(['text-b']))
1795
        self.assertKnitValuesEqual(source, target)
1796
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1797
    def test_insert_data_stream_records_already_present(self):
1798
        """Insert a data stream where some records are alreday present in the
1799
        target, and some not.  Only the new records are inserted.
1800
        """
1801
        source = self.make_test_knit(name='source')
1802
        target = self.make_test_knit(name='target')
1803
        # Insert 'text-a' into both source and target
1804
        source.add_lines('text-a', [], split_lines(TEXT_1))
1805
        target.insert_data_stream(source.get_data_stream(['text-a']))
1806
        # Insert 'text-b' into just the source.
1807
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
1808
        # Get a data stream of both text-a and text-b, and insert it.
1809
        data_stream = source.get_data_stream(['text-a', 'text-b'])
1810
        target.insert_data_stream(data_stream)
1811
        # The source and target will now be identical.  This means the text-a
1812
        # record was not added a second time.
1813
        self.assertKnitFilesEqual(source, target)
1814
1815
    def test_insert_data_stream_multiple_records(self):
1816
        """Inserting a data stream of all records from a knit with multiple
1817
        records results in byte-identical files.
1818
        """
1819
        source = self.make_test_knit(name='source')
1820
        source.add_lines('text-a', [], split_lines(TEXT_1))
1821
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
1822
        source.add_lines('text-c', [], split_lines(TEXT_1))
1823
        data_stream = source.get_data_stream(['text-a', 'text-b', 'text-c'])
1824
        
1825
        target = self.make_test_knit(name='target')
1826
        target.insert_data_stream(data_stream)
1827
        
1828
        self.assertKnitFilesEqual(source, target)
1829
1830
    def test_insert_data_stream_ghost_parent(self):
1831
        """Insert a data stream with a record that has a ghost parent."""
1832
        # Make a knit with a record, text-a, that has a ghost parent.
1833
        source = self.make_test_knit(name='source')
1834
        source.add_lines_with_ghosts('text-a', ['text-ghost'],
1835
                                     split_lines(TEXT_1))
1836
        data_stream = source.get_data_stream(['text-a'])
1837
1838
        target = self.make_test_knit(name='target')
1839
        target.insert_data_stream(data_stream)
1840
1841
        self.assertKnitFilesEqual(source, target)
1842
1843
        # The target knit object is in a consistent state, i.e. the record we
1844
        # just added is immediately visible.
1845
        self.assertTrue(target.has_version('text-a'))
3287.6.5 by Robert Collins
Deprecate VersionedFile.has_ghost.
1846
        self.assertFalse(target.has_version('text-ghost'))
1847
        self.assertEqual({'text-a':('text-ghost',)},
1848
            target.get_parent_map(['text-a', 'text-ghost']))
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1849
        self.assertEqual(split_lines(TEXT_1), target.get_lines('text-a'))
1850
1851
    def test_insert_data_stream_inconsistent_version_lines(self):
1852
        """Inserting a data stream which has different content for a version_id
1853
        than already exists in the knit will raise KnitCorrupt.
1854
        """
1855
        source = self.make_test_knit(name='source')
1856
        target = self.make_test_knit(name='target')
1857
        # Insert a different 'text-a' into both source and target
1858
        source.add_lines('text-a', [], split_lines(TEXT_1))
1859
        target.add_lines('text-a', [], split_lines(TEXT_2))
1860
        # Insert a data stream with conflicting content into the target
1861
        data_stream = source.get_data_stream(['text-a'])
1862
        self.assertRaises(
1863
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1864
1865
    def test_insert_data_stream_inconsistent_version_parents(self):
1866
        """Inserting a data stream which has different parents for a version_id
1867
        than already exists in the knit will raise KnitCorrupt.
1868
        """
1869
        source = self.make_test_knit(name='source')
1870
        target = self.make_test_knit(name='target')
1871
        # Insert a different 'text-a' into both source and target.  They differ
1872
        # only by the parents list, the content is the same.
1873
        source.add_lines_with_ghosts('text-a', [], split_lines(TEXT_1))
1874
        target.add_lines_with_ghosts('text-a', ['a-ghost'], split_lines(TEXT_1))
1875
        # Insert a data stream with conflicting content into the target
1876
        data_stream = source.get_data_stream(['text-a'])
1877
        self.assertRaises(
1878
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1879
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1880
    def test_insert_data_stream_unknown_format(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1881
        """A data stream in a different format to the target knit cannot be
1882
        inserted.
1883
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1884
        It will raise KnitDataStreamUnknown because the fallback code will fail
1885
        to make a knit. In future we may need KnitDataStreamIncompatible again,
1886
        for more exotic cases.
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1887
        """
1888
        data_stream = ('fake-format-signature', [], lambda _: '')
1889
        target = self.make_test_knit(name='target')
1890
        self.assertRaises(
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1891
            errors.KnitDataStreamUnknown,
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1892
            target.insert_data_stream, data_stream)
1893
2535.3.5 by Andrew Bennetts
Batch writes as much as possible in insert_data_stream.
1894
    #  * test that a stream of "already present version, then new version"
1895
    #    inserts correctly.
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1896
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1897
1898
    def assertMadeStreamKnit(self, source_knit, versions, target_knit):
1899
        """Assert that a knit made from a stream is as expected."""
1900
        a_stream = source_knit.get_data_stream(versions)
1901
        expected_data = a_stream[2](None)
1902
        a_stream = source_knit.get_data_stream(versions)
1903
        a_knit = target_knit._knit_from_datastream(a_stream)
1904
        self.assertEqual(source_knit.factory.__class__,
1905
            a_knit.factory.__class__)
1906
        self.assertIsInstance(a_knit._data._access, _StreamAccess)
1907
        self.assertIsInstance(a_knit._index, _StreamIndex)
1908
        self.assertEqual(a_knit._index.data_list, a_stream[1])
1909
        self.assertEqual(a_knit._data._access.data, expected_data)
1910
        self.assertEqual(a_knit.filename, target_knit.filename)
1911
        self.assertEqual(a_knit.transport, target_knit.transport)
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
1912
        self.assertEqual(a_knit._index, a_knit._data._access.stream_index)
1913
        self.assertEqual(target_knit, a_knit._data._access.backing_knit)
1914
        self.assertIsInstance(a_knit._data._access.orig_factory,
1915
            source_knit.factory.__class__)
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1916
1917
    def test__knit_from_data_stream_empty(self):
1918
        """Create a knit object from a datastream."""
1919
        annotated = self.make_test_knit(name='source', annotate=True)
1920
        plain = self.make_test_knit(name='target', annotate=False)
1921
        # case 1: annotated source
1922
        self.assertMadeStreamKnit(annotated, [], annotated)
1923
        self.assertMadeStreamKnit(annotated, [], plain)
1924
        # case 2: plain source
1925
        self.assertMadeStreamKnit(plain, [], annotated)
1926
        self.assertMadeStreamKnit(plain, [], plain)
1927
1928
    def test__knit_from_data_stream_unknown_format(self):
1929
        annotated = self.make_test_knit(name='source', annotate=True)
1930
        self.assertRaises(errors.KnitDataStreamUnknown,
1931
            annotated._knit_from_datastream, ("unknown", None, None))
1932
1933
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1934
TEXT_1 = """\
1935
Banana cup cakes:
1936
1937
- bananas
1938
- eggs
1939
- broken tea cups
1940
"""
1941
1942
TEXT_1A = """\
1943
Banana cup cake recipe
1944
(serves 6)
1945
1946
- bananas
1947
- eggs
1948
- broken tea cups
1949
- self-raising flour
1950
"""
1951
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1952
TEXT_1B = """\
1953
Banana cup cake recipe
1954
1955
- bananas (do not use plantains!!!)
1956
- broken tea cups
1957
- flour
1958
"""
1959
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1960
delta_1_1a = """\
1961
0,1,2
1962
Banana cup cake recipe
1963
(serves 6)
1964
5,5,1
1965
- self-raising flour
1966
"""
1967
1968
TEXT_2 = """\
1969
Boeuf bourguignon
1970
1971
- beef
1972
- red wine
1973
- small onions
1974
- carrot
1975
- mushrooms
1976
"""
1977
1664.2.3 by Aaron Bentley
Add failing test case
1978
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
1979
new-a|(serves 6)
1980
unchanged|
1981
killed-b|- bananas
1982
killed-b|- eggs
1983
new-b|- bananas (do not use plantains!!!)
1984
unchanged|- broken tea cups
1985
new-a|- self-raising flour
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1986
new-b|- flour
1987
"""
1664.2.3 by Aaron Bentley
Add failing test case
1988
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
1989
1990
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1991
def line_delta(from_lines, to_lines):
1992
    """Generate line-based delta from one text to another"""
1993
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
1994
    for op in s.get_opcodes():
1995
        if op[0] == 'equal':
1996
            continue
1997
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
1998
        for i in range(op[3], op[4]):
1999
            yield to_lines[i]
2000
2001
2002
def apply_line_delta(basis_lines, delta_lines):
2003
    """Apply a line-based perfect diff
2004
    
2005
    basis_lines -- text to apply the patch to
2006
    delta_lines -- diff instructions and content
2007
    """
2008
    out = basis_lines[:]
2009
    i = 0
2010
    offset = 0
2011
    while i < len(delta_lines):
2012
        l = delta_lines[i]
2013
        a, b, c = map(long, l.split(','))
2014
        i = i + 1
2015
        out[offset+a:offset+b] = delta_lines[i:i+c]
2016
        i = i + c
2017
        offset = offset + (b - a) + c
2018
    return out
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
2019
2020
2021
class TestWeaveToKnit(KnitTests):
2022
2023
    def test_weave_to_knit_matches(self):
2024
        # check that the WeaveToKnit is_compatible function
2025
        # registers True for a Weave to a Knit.
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
2026
        w = Weave(get_scope=lambda:None)
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
2027
        k = self.make_test_knit()
2028
        self.failUnless(WeaveToKnit.is_compatible(w, k))
2029
        self.failIf(WeaveToKnit.is_compatible(k, w))
2030
        self.failIf(WeaveToKnit.is_compatible(w, w))
2031
        self.failIf(WeaveToKnit.is_compatible(k, k))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2032
2033
2034
class TestKnitCaching(KnitTests):
2035
    
2850.1.1 by Robert Collins
* ``KnitVersionedFile.add*`` will no longer cache added records even when
2036
    def create_knit(self):
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2037
        k = self.make_test_knit(True)
2038
        k.add_lines('text-1', [], split_lines(TEXT_1))
2039
        k.add_lines('text-2', [], split_lines(TEXT_2))
2040
        return k
2041
2042
    def test_no_caching(self):
2043
        k = self.create_knit()
2044
        # Nothing should be cached without setting 'enable_cache'
2045
        self.assertEqual({}, k._data._cache)
2046
2047
    def test_cache_data_read_raw(self):
2048
        k = self.create_knit()
2049
2050
        # Now cache and read
2051
        k.enable_cache()
2052
2053
        def read_one_raw(version):
2054
            pos_map = k._get_components_positions([version])
3224.1.13 by John Arbash Meinel
Revert the _get_component_positions api
2055
            method, index_memo, next = pos_map[version]
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2056
            lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2057
            self.assertEqual(1, len(lst))
2058
            return lst[0]
2059
2060
        val = read_one_raw('text-1')
1863.1.8 by John Arbash Meinel
Removing disk-backed-cache
2061
        self.assertEqual({'text-1':val[1]}, k._data._cache)
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2062
2063
        k.clear_cache()
2064
        # After clear, new reads are not cached
2065
        self.assertEqual({}, k._data._cache)
2066
2067
        val2 = read_one_raw('text-1')
2068
        self.assertEqual(val, val2)
2069
        self.assertEqual({}, k._data._cache)
2070
2071
    def test_cache_data_read(self):
2072
        k = self.create_knit()
2073
2074
        def read_one(version):
2075
            pos_map = k._get_components_positions([version])
3224.1.13 by John Arbash Meinel
Revert the _get_component_positions api
2076
            method, index_memo, next = pos_map[version]
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2077
            lst = list(k._data.read_records_iter([(version, index_memo)]))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2078
            self.assertEqual(1, len(lst))
2079
            return lst[0]
2080
2081
        # Now cache and read
2082
        k.enable_cache()
2083
2084
        val = read_one('text-2')
2085
        self.assertEqual(['text-2'], k._data._cache.keys())
2086
        self.assertEqual('text-2', val[0])
2087
        content, digest = k._data._parse_record('text-2',
2088
                                                k._data._cache['text-2'])
2089
        self.assertEqual(content, val[1])
2090
        self.assertEqual(digest, val[2])
2091
2092
        k.clear_cache()
2093
        self.assertEqual({}, k._data._cache)
2094
2095
        val2 = read_one('text-2')
2096
        self.assertEqual(val, val2)
2097
        self.assertEqual({}, k._data._cache)
2098
2099
    def test_cache_read(self):
2100
        k = self.create_knit()
2101
        k.enable_cache()
2102
2103
        text = k.get_text('text-1')
2104
        self.assertEqual(TEXT_1, text)
2105
        self.assertEqual(['text-1'], k._data._cache.keys())
2106
2107
        k.clear_cache()
2108
        self.assertEqual({}, k._data._cache)
2109
2110
        text = k.get_text('text-1')
2111
        self.assertEqual(TEXT_1, text)
2112
        self.assertEqual({}, k._data._cache)
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2113
2114
2115
class TestKnitIndex(KnitTests):
2116
2117
    def test_add_versions_dictionary_compresses(self):
2118
        """Adding versions to the index should update the lookup dict"""
2119
        knit = self.make_test_knit()
2120
        idx = knit._index
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2121
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2122
        self.check_file_contents('test.kndx',
2123
            '# bzr knit index 8\n'
2124
            '\n'
2125
            'a-1 fulltext 0 0  :'
2126
            )
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2127
        idx.add_versions([('a-2', ['fulltext'], (None, 0, 0), ['a-1']),
2128
                          ('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
2129
                         ])
2130
        self.check_file_contents('test.kndx',
2131
            '# bzr knit index 8\n'
2132
            '\n'
2133
            'a-1 fulltext 0 0  :\n'
2134
            'a-2 fulltext 0 0 0 :\n'
2135
            'a-3 fulltext 0 0 1 :'
2136
            )
2137
        self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2138
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0),
2139
                          'a-2':('a-2', ['fulltext'], 0, 0, ('a-1',), 1),
2140
                          'a-3':('a-3', ['fulltext'], 0, 0, ('a-2',), 2),
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2141
                         }, idx._cache)
2142
2143
    def test_add_versions_fails_clean(self):
2144
        """If add_versions fails in the middle, it restores a pristine state.
2145
2146
        Any modifications that are made to the index are reset if all versions
2147
        cannot be added.
2148
        """
2149
        # This cheats a little bit by passing in a generator which will
2150
        # raise an exception before the processing finishes
2151
        # Other possibilities would be to have an version with the wrong number
2152
        # of entries, or to make the backing transport unable to write any
2153
        # files.
2154
2155
        knit = self.make_test_knit()
2156
        idx = knit._index
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2157
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2158
2159
        class StopEarly(Exception):
2160
            pass
2161
2162
        def generate_failure():
2163
            """Add some entries and then raise an exception"""
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2164
            yield ('a-2', ['fulltext'], (None, 0, 0), ('a-1',))
2165
            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
2166
            raise StopEarly()
2167
2168
        # Assert the pre-condition
2169
        self.assertEqual(['a-1'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2170
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2171
2172
        self.assertRaises(StopEarly, idx.add_versions, generate_failure())
2173
2174
        # And it shouldn't be modified
2175
        self.assertEqual(['a-1'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2176
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2171.1.1 by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt.
2177
2178
    def test_knit_index_ignores_empty_files(self):
2179
        # There was a race condition in older bzr, where a ^C at the right time
2180
        # could leave an empty .kndx file, which bzr would later claim was a
2181
        # corrupted file since the header was not present. In reality, the file
2182
        # just wasn't created, so it should be ignored.
2183
        t = get_transport('.')
2184
        t.put_bytes('test.kndx', '')
2185
2186
        knit = self.make_test_knit()
2187
2188
    def test_knit_index_checks_header(self):
2189
        t = get_transport('.')
2190
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
2191
2196.2.1 by John Arbash Meinel
Merge Dmitry's optimizations and minimize the actual diff.
2192
        self.assertRaises(KnitHeaderError, self.make_test_knit)
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.
2193
2194
2195
class TestGraphIndexKnit(KnitTests):
2196
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
2197
2198
    def make_g_index(self, name, ref_lists=0, nodes=[]):
2199
        builder = GraphIndexBuilder(ref_lists)
2200
        for node, references, value in nodes:
2201
            builder.add_node(node, references, value)
2202
        stream = builder.finish()
2203
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
2204
        size = trans.put_file(name, stream)
2205
        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.
2206
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2207
    def two_graph_index(self, deltas=False, catch_adds=False):
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2208
        """Build a two-graph index.
2209
2210
        :param deltas: If true, use underlying indices with two node-ref
2211
            lists and 'parent' set to a delta-compressed against tail.
2212
        """
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.
2213
        # build a complex graph across several indices.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2214
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2215
            # delta compression inn the index
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2216
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2217
                (('tip', ), 'N0 100', ([('parent', )], [], )),
2218
                (('tail', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2219
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2220
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
2221
                (('separate', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2222
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2223
            # just blob location and graph in the index.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2224
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2225
                (('tip', ), 'N0 100', ([('parent', )], )),
2226
                (('tail', ), '', ([], ))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2227
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2228
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
2229
                (('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.
2230
        combined_index = CombinedGraphIndex([index1, index2])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2231
        if catch_adds:
2232
            self.combined_index = combined_index
2233
            self.caught_entries = []
2234
            add_callback = self.catch_add
2235
        else:
2236
            add_callback = None
2237
        return KnitGraphIndex(combined_index, deltas=deltas,
2238
            add_callback=add_callback)
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2239
2240
    def test_get_ancestry(self):
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2241
        # get_ancestry is defined as eliding ghosts, not erroring.
2242
        index = self.two_graph_index()
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2243
        self.assertEqual([], index.get_ancestry([]))
2244
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
2245
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
2246
        self.assertEqual(['tail', 'parent'], index.get_ancestry(['parent']))
2247
        self.assertEqual(['tail', 'parent', 'tip'], index.get_ancestry(['tip']))
2248
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
2249
            (['tail', 'parent', 'tip', 'separate'],
2250
             ['separate', 'tail', 'parent', 'tip'],
2251
            ))
2252
        # and without topo_sort
2253
        self.assertEqual(set(['separate']),
2254
            set(index.get_ancestry(['separate'], topo_sorted=False)))
2255
        self.assertEqual(set(['tail']),
2256
            set(index.get_ancestry(['tail'], topo_sorted=False)))
2257
        self.assertEqual(set(['tail', 'parent']),
2258
            set(index.get_ancestry(['parent'], topo_sorted=False)))
2259
        self.assertEqual(set(['tail', 'parent', 'tip']),
2260
            set(index.get_ancestry(['tip'], topo_sorted=False)))
2261
        self.assertEqual(set(['separate', 'tail', 'parent', 'tip']),
2262
            set(index.get_ancestry(['tip', 'separate'])))
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2263
        # asking for a ghost makes it go boom.
2264
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2265
2266
    def test_get_ancestry_with_ghosts(self):
2267
        index = self.two_graph_index()
2268
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2269
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
2270
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
2271
        self.assertTrue(index.get_ancestry_with_ghosts(['parent']) in
2272
            (['tail', 'ghost', 'parent'],
2273
             ['ghost', 'tail', 'parent'],
2274
            ))
2275
        self.assertTrue(index.get_ancestry_with_ghosts(['tip']) in
2276
            (['tail', 'ghost', 'parent', 'tip'],
2277
             ['ghost', 'tail', 'parent', 'tip'],
2278
            ))
2279
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
2280
            (['tail', 'ghost', 'parent', 'tip', 'separate'],
2281
             ['ghost', 'tail', 'parent', 'tip', 'separate'],
2282
             ['separate', 'tail', 'ghost', 'parent', 'tip'],
2283
             ['separate', 'ghost', 'tail', 'parent', 'tip'],
2284
            ))
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2285
        # asking for a ghost makes it go boom.
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2286
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
2592.3.5 by Robert Collins
Implement KnitGraphIndex.num_versions.
2287
2288
    def test_num_versions(self):
2289
        index = self.two_graph_index()
2290
        self.assertEqual(4, index.num_versions())
2592.3.6 by Robert Collins
Implement KnitGraphIndex.get_versions.
2291
2292
    def test_get_versions(self):
2293
        index = self.two_graph_index()
2294
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
2295
            set(index.get_versions()))
2296
2592.3.9 by Robert Collins
Implement KnitGraphIndex.has_version.
2297
    def test_has_version(self):
2298
        index = self.two_graph_index()
2299
        self.assertTrue(index.has_version('tail'))
2300
        self.assertFalse(index.has_version('ghost'))
2301
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
2302
    def test_get_position(self):
2303
        index = self.two_graph_index()
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2304
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
2305
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
2306
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2307
    def test_get_method_deltas(self):
2308
        index = self.two_graph_index(deltas=True)
2592.3.11 by Robert Collins
Implement KnitGraphIndex.get_method.
2309
        self.assertEqual('fulltext', index.get_method('tip'))
2310
        self.assertEqual('line-delta', index.get_method('parent'))
2311
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2312
    def test_get_method_no_deltas(self):
2313
        # check that the parent-history lookup is ignored with deltas=False.
2314
        index = self.two_graph_index(deltas=False)
2315
        self.assertEqual('fulltext', index.get_method('tip'))
2316
        self.assertEqual('fulltext', index.get_method('parent'))
2317
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2318
    def test_get_options_deltas(self):
2319
        index = self.two_graph_index(deltas=True)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2320
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2321
        self.assertEqual(['line-delta'], index.get_options('parent'))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2322
2323
    def test_get_options_no_deltas(self):
2324
        # check that the parent-history lookup is ignored with deltas=False.
2325
        index = self.two_graph_index(deltas=False)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2326
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2327
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2328
2592.3.15 by Robert Collins
Implement KnitGraphIndex.get_parents/get_parents_with_ghosts.
2329
    def test_get_parents_with_ghosts(self):
2330
        index = self.two_graph_index()
2331
        self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
2592.3.43 by Robert Collins
A knit iter_parents API.
2332
        # and errors on ghosts.
2333
        self.assertRaises(errors.RevisionNotPresent,
2334
            index.get_parents_with_ghosts, 'ghost')
2592.3.15 by Robert Collins
Implement KnitGraphIndex.get_parents/get_parents_with_ghosts.
2335
2592.3.16 by Robert Collins
Implement KnitGraphIndex.check_versions_present.
2336
    def test_check_versions_present(self):
2337
        # ghosts should not be considered present
2338
        index = self.two_graph_index()
2339
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2340
            ['ghost'])
2341
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2342
            ['tail', 'ghost'])
2343
        index.check_versions_present(['tail', 'separate'])
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2344
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2345
    def catch_add(self, entries):
2346
        self.caught_entries.append(entries)
2347
2348
    def test_add_no_callback_errors(self):
2349
        index = self.two_graph_index()
2350
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2351
            '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.
2352
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2353
    def test_add_version_smoke(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2354
        index = self.two_graph_index(catch_adds=True)
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2355
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2356
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2357
            self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2358
2359
    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.
2360
        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.
2361
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2362
            '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.
2363
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2364
2365
    def test_add_version_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2366
        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.
2367
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2368
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2369
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2370
        # but neither should have added data.
2371
        self.assertEqual([[], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2372
        
2373
    def test_add_version_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2374
        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.
2375
        # change options
2376
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2377
            'tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])
2378
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2379
            'tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])
2380
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2381
            'tip', 'fulltext', (None, 0, 100), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2382
        # position/length
2383
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2384
            'tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2385
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2386
            'tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2387
        # parents
2388
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2389
            'tip', 'fulltext,no-eol', (None, 0, 100), [])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2390
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2391
        
2392
    def test_add_versions_nodeltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2393
        index = self.two_graph_index(catch_adds=True)
2394
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2395
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
2396
                ('new2', 'fulltext', (None, 0, 6), ['new']),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2397
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2398
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
2399
            (('new2', ), ' 0 6', ((('new',),),))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2400
            sorted(self.caught_entries[0]))
2401
        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.
2402
2403
    def test_add_versions_deltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2404
        index = self.two_graph_index(deltas=True, catch_adds=True)
2405
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2406
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
2407
                ('new2', 'line-delta', (None, 0, 6), ['new']),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2408
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2409
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
2410
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2411
            sorted(self.caught_entries[0]))
2412
        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.
2413
2414
    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.
2415
        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.
2416
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2417
            [('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.
2418
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2419
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
2420
    def test_add_versions_random_id_accepted(self):
2421
        index = self.two_graph_index(catch_adds=True)
2422
        index.add_versions([], random_id=True)
2423
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2424
    def test_add_versions_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2425
        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.
2426
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2427
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2428
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2429
        # but neither should have added data.
2430
        self.assertEqual([[], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2431
        
2432
    def test_add_versions_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2433
        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.
2434
        # change options
2435
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2436
            [('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2437
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2438
            [('tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])])
2439
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2440
            [('tip', 'fulltext', (None, 0, 100), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2441
        # position/length
2442
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2443
            [('tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2444
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2445
            [('tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2446
        # parents
2447
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2448
            [('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.
2449
        # change options in the second record
2450
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2451
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent']),
2452
             ('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.
2453
        self.assertEqual([], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2454
2455
class TestNoParentsGraphIndexKnit(KnitTests):
2456
    """Tests for knits using KnitGraphIndex with no parents."""
2457
2458
    def make_g_index(self, name, ref_lists=0, nodes=[]):
2459
        builder = GraphIndexBuilder(ref_lists)
2460
        for node, references in nodes:
2461
            builder.add_node(node, references)
2462
        stream = builder.finish()
2463
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
2464
        size = trans.put_file(name, stream)
2465
        return GraphIndex(trans, name, size)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2466
2467
    def test_parents_deltas_incompatible(self):
2468
        index = CombinedGraphIndex([])
2469
        self.assertRaises(errors.KnitError, KnitGraphIndex, index,
2470
            deltas=True, parents=False)
2471
2472
    def two_graph_index(self, catch_adds=False):
2473
        """Build a two-graph index.
2474
2475
        :param deltas: If true, use underlying indices with two node-ref
2476
            lists and 'parent' set to a delta-compressed against tail.
2477
        """
2478
        # put several versions in the index.
2479
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2480
            (('tip', ), 'N0 100'),
2481
            (('tail', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2482
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2483
            (('parent', ), ' 100 78'),
2484
            (('separate', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2485
        combined_index = CombinedGraphIndex([index1, index2])
2486
        if catch_adds:
2487
            self.combined_index = combined_index
2488
            self.caught_entries = []
2489
            add_callback = self.catch_add
2490
        else:
2491
            add_callback = None
2492
        return KnitGraphIndex(combined_index, parents=False,
2493
            add_callback=add_callback)
2494
2495
    def test_get_ancestry(self):
2496
        # with no parents, ancestry is always just the key.
2497
        index = self.two_graph_index()
2498
        self.assertEqual([], index.get_ancestry([]))
2499
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
2500
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
2501
        self.assertEqual(['parent'], index.get_ancestry(['parent']))
2502
        self.assertEqual(['tip'], index.get_ancestry(['tip']))
2503
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
2504
            (['tip', 'separate'],
2505
             ['separate', 'tip'],
2506
            ))
2507
        # asking for a ghost makes it go boom.
2508
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
2509
2510
    def test_get_ancestry_with_ghosts(self):
2511
        index = self.two_graph_index()
2512
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2513
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
2514
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
2515
        self.assertEqual(['parent'], index.get_ancestry_with_ghosts(['parent']))
2516
        self.assertEqual(['tip'], index.get_ancestry_with_ghosts(['tip']))
2517
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
2518
            (['tip', 'separate'],
2519
             ['separate', 'tip'],
2520
            ))
2521
        # asking for a ghost makes it go boom.
2522
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
2523
2524
    def test_num_versions(self):
2525
        index = self.two_graph_index()
2526
        self.assertEqual(4, index.num_versions())
2527
2528
    def test_get_versions(self):
2529
        index = self.two_graph_index()
2530
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
2531
            set(index.get_versions()))
2532
2533
    def test_has_version(self):
2534
        index = self.two_graph_index()
2535
        self.assertTrue(index.has_version('tail'))
2536
        self.assertFalse(index.has_version('ghost'))
2537
2538
    def test_get_position(self):
2539
        index = self.two_graph_index()
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2540
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
2541
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2542
2543
    def test_get_method(self):
2544
        index = self.two_graph_index()
2545
        self.assertEqual('fulltext', index.get_method('tip'))
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2546
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2547
2548
    def test_get_options(self):
2549
        index = self.two_graph_index()
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2550
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2551
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2552
2553
    def test_get_parents_with_ghosts(self):
2554
        index = self.two_graph_index()
2555
        self.assertEqual((), index.get_parents_with_ghosts('parent'))
2592.3.43 by Robert Collins
A knit iter_parents API.
2556
        # and errors on ghosts.
2557
        self.assertRaises(errors.RevisionNotPresent,
2558
            index.get_parents_with_ghosts, 'ghost')
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2559
2560
    def test_check_versions_present(self):
2561
        index = self.two_graph_index()
2562
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2563
            ['missing'])
2564
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2565
            ['tail', 'missing'])
2566
        index.check_versions_present(['tail', 'separate'])
2567
2568
    def catch_add(self, entries):
2569
        self.caught_entries.append(entries)
2570
2571
    def test_add_no_callback_errors(self):
2572
        index = self.two_graph_index()
2573
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2574
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2575
2576
    def test_add_version_smoke(self):
2577
        index = self.two_graph_index(catch_adds=True)
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2578
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), [])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2579
        self.assertEqual([[(('new', ), 'N50 60')]],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2580
            self.caught_entries)
2581
2582
    def test_add_version_delta_not_delta_index(self):
2583
        index = self.two_graph_index(catch_adds=True)
2584
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2585
            'new', 'no-eol,line-delta', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2586
        self.assertEqual([], self.caught_entries)
2587
2588
    def test_add_version_same_dup(self):
2589
        index = self.two_graph_index(catch_adds=True)
2590
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2591
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), [])
2592
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2593
        # but neither should have added data.
2594
        self.assertEqual([[], []], self.caught_entries)
2595
        
2596
    def test_add_version_different_dup(self):
2597
        index = self.two_graph_index(catch_adds=True)
2598
        # change options
2599
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2600
            'tip', 'no-eol,line-delta', (None, 0, 100), [])
2601
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2602
            'tip', 'line-delta,no-eol', (None, 0, 100), [])
2603
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2604
            'tip', 'fulltext', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2605
        # position/length
2606
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2607
            'tip', 'fulltext,no-eol', (None, 50, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2608
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2609
            'tip', 'fulltext,no-eol', (None, 0, 1000), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2610
        # parents
2611
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2612
            'tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2613
        self.assertEqual([], self.caught_entries)
2614
        
2615
    def test_add_versions(self):
2616
        index = self.two_graph_index(catch_adds=True)
2617
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2618
                ('new', 'fulltext,no-eol', (None, 50, 60), []),
2619
                ('new2', 'fulltext', (None, 0, 6), []),
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2620
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2621
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2622
            sorted(self.caught_entries[0]))
2623
        self.assertEqual(1, len(self.caught_entries))
2624
2625
    def test_add_versions_delta_not_delta_index(self):
2626
        index = self.two_graph_index(catch_adds=True)
2627
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2628
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2629
        self.assertEqual([], self.caught_entries)
2630
2631
    def test_add_versions_parents_not_parents_index(self):
2632
        index = self.two_graph_index(catch_adds=True)
2633
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2634
            [('new', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2635
        self.assertEqual([], self.caught_entries)
2636
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
2637
    def test_add_versions_random_id_accepted(self):
2638
        index = self.two_graph_index(catch_adds=True)
2639
        index.add_versions([], random_id=True)
2640
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2641
    def test_add_versions_same_dup(self):
2642
        index = self.two_graph_index(catch_adds=True)
2643
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2644
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), [])])
2645
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2646
        # but neither should have added data.
2647
        self.assertEqual([[], []], self.caught_entries)
2648
        
2649
    def test_add_versions_different_dup(self):
2650
        index = self.two_graph_index(catch_adds=True)
2651
        # change options
2652
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2653
            [('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2654
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2655
            [('tip', 'line-delta,no-eol', (None, 0, 100), [])])
2656
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2657
            [('tip', 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2658
        # position/length
2659
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2660
            [('tip', 'fulltext,no-eol', (None, 50, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2661
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2662
            [('tip', 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2663
        # parents
2664
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2665
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2666
        # change options in the second record
2667
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2668
            [('tip', 'fulltext,no-eol', (None, 0, 100), []),
2669
             ('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2670
        self.assertEqual([], self.caught_entries)
2671
3015.2.19 by Robert Collins
Don't include the pack container length in the lengths given by get_data_stream.
2672
class TestPackKnits(KnitTests):
2673
    """Tests that use a _PackAccess and KnitGraphIndex."""
2674
2675
    def test_get_data_stream_packs_ignores_pack_overhead(self):
2676
        # Packs have an encoding overhead that should not be included in the
2677
        # 'size' field of a data stream, because it is not returned by the
2678
        # raw_reading functions - it is why index_memo's are opaque, and
2679
        # get_data_stream was abusing this.
2680
        packname = 'test.pack'
2681
        transport = self.get_transport()
2682
        def write_data(bytes):
2683
            transport.append_bytes(packname, bytes)
2684
        writer = pack.ContainerWriter(write_data)
2685
        writer.begin()
2686
        index = InMemoryGraphIndex(2)
2687
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
2688
            deltas=True)
2689
        indices = {index:(transport, packname)}
2690
        access = _PackAccess(indices, writer=(writer, index))
2691
        k = KnitVersionedFile('test', get_transport('.'),
2692
            delta=True, create=True, index=knit_index, access_method=access)
2693
        # insert something into the knit
2694
        k.add_lines('text-1', [], ["foo\n"])
2695
        # get a data stream for it
2696
        stream = k.get_data_stream(['text-1'])
2697
        # if the stream has been incorrectly assembled, we will get a short read
2698
        # reading from the stream (as streams have no trailer)
2699
        expected_length = stream[1][0][2]
2700
        # we use -1 to do the read, so that if a trailer is added this test
2701
        # will fail and we'll adjust it to handle that case correctly, rather
2702
        # than allowing an over-read that is bogus.
2703
        self.assertEqual(expected_length, len(stream[2](-1)))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2704
2705
2706
class Test_StreamIndex(KnitTests):
2707
2708
    def get_index(self, knit, stream):
2709
        """Get a _StreamIndex from knit and stream."""
2710
        return knit._knit_from_datastream(stream)._index
2711
2712
    def assertIndexVersions(self, knit, versions):
2713
        """Check that the _StreamIndex versions are those of the stream."""
2714
        index = self.get_index(knit, knit.get_data_stream(versions))
2715
        self.assertEqual(set(index.get_versions()), set(versions))
2716
        # check we didn't get duplicates
2717
        self.assertEqual(len(index.get_versions()), len(versions))
2718
2719
    def assertIndexAncestry(self, knit, ancestry_versions, versions, result):
2720
        """Check the result of a get_ancestry call on knit."""
2721
        index = self.get_index(knit, knit.get_data_stream(versions))
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2722
        self.assertEqual(
2723
            set(result),
2724
            set(index.get_ancestry(ancestry_versions, False)))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2725
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2726
    def assertGetMethod(self, knit, versions, version, result):
2727
        index = self.get_index(knit, knit.get_data_stream(versions))
2728
        self.assertEqual(result, index.get_method(version))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2729
2730
    def assertGetOptions(self, knit, version, options):
2731
        index = self.get_index(knit, knit.get_data_stream(version))
2732
        self.assertEqual(options, index.get_options(version))
2733
2734
    def assertGetPosition(self, knit, versions, version, result):
2735
        index = self.get_index(knit, knit.get_data_stream(versions))
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2736
        if result[1] is None:
2737
            result = (result[0], index, result[2], result[3])
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2738
        self.assertEqual(result, index.get_position(version))
2739
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2740
    def assertGetParentsWithGhosts(self, knit, versions, version, parents):
2741
        index = self.get_index(knit, knit.get_data_stream(versions))
2742
        self.assertEqual(parents, index.get_parents_with_ghosts(version))
2743
2744
    def make_knit_with_4_versions_2_dags(self):
2745
        knit = self.make_test_knit()
2746
        knit.add_lines('a', [], ["foo"])
2747
        knit.add_lines('b', [], [])
2748
        knit.add_lines('c', ['b', 'a'], [])
2749
        knit.add_lines_with_ghosts('d', ['e', 'f'], [])
2750
        return knit
2751
2752
    def test_versions(self):
2753
        """The versions of a StreamIndex are those of the datastream."""
2754
        knit = self.make_knit_with_4_versions_2_dags()
2755
        # ask for most permutations, which catches bugs like falling back to the
2756
        # target knit, or showing ghosts, etc.
2757
        self.assertIndexVersions(knit, [])
2758
        self.assertIndexVersions(knit, ['a'])
2759
        self.assertIndexVersions(knit, ['b'])
2760
        self.assertIndexVersions(knit, ['c'])
2761
        self.assertIndexVersions(knit, ['d'])
2762
        self.assertIndexVersions(knit, ['a', 'b'])
2763
        self.assertIndexVersions(knit, ['b', 'c'])
2764
        self.assertIndexVersions(knit, ['a', 'c'])
2765
        self.assertIndexVersions(knit, ['a', 'b', 'c'])
2766
        self.assertIndexVersions(knit, ['a', 'b', 'c', 'd'])
2767
2768
    def test_construct(self):
2769
        """Constructing a StreamIndex generates index data."""
2770
        data_list = [('text-a', ['fulltext'], 127, []),
2771
            ('text-b', ['option'], 128, ['text-c'])]
3224.1.8 by John Arbash Meinel
Add noeol to the return signature of get_build_details.
2772
        index = _StreamIndex(data_list, None)
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2773
        self.assertEqual({'text-a':(['fulltext'], (0, 127), []),
2774
            'text-b':(['option'], (127, 127 + 128), ['text-c'])},
2775
            index._by_version)
2776
2777
    def test_get_ancestry(self):
2778
        knit = self.make_knit_with_4_versions_2_dags()
2779
        self.assertIndexAncestry(knit, ['a'], ['a'], ['a'])
2780
        self.assertIndexAncestry(knit, ['b'], ['b'], ['b'])
2781
        self.assertIndexAncestry(knit, ['c'], ['c'], ['c'])
2782
        self.assertIndexAncestry(knit, ['c'], ['a', 'b', 'c'],
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2783
            set(['a', 'b', 'c']))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2784
        self.assertIndexAncestry(knit, ['c', 'd'], ['a', 'b', 'c', 'd'],
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2785
            set(['a', 'b', 'c', 'd']))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2786
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2787
    def test_get_method(self):
2788
        knit = self.make_knit_with_4_versions_2_dags()
2789
        self.assertGetMethod(knit, ['a'], 'a', 'fulltext')
2790
        self.assertGetMethod(knit, ['c'], 'c', 'line-delta')
2791
        # get_method on a basis that is not in the datastream (but in the
2792
        # backing knit) returns 'fulltext', because thats what we'll create as
2793
        # we thunk across.
2794
        self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2795
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2796
    def test_get_options(self):
2797
        knit = self.make_knit_with_4_versions_2_dags()
2798
        self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
2799
        self.assertGetOptions(knit, 'c', ['line-delta'])
2800
2801
    def test_get_parents_with_ghosts(self):
2802
        knit = self.make_knit_with_4_versions_2_dags()
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2803
        self.assertGetParentsWithGhosts(knit, ['a'], 'a', ())
2804
        self.assertGetParentsWithGhosts(knit, ['c'], 'c', ('b', 'a'))
2805
        self.assertGetParentsWithGhosts(knit, ['d'], 'd', ('e', 'f'))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2806
2807
    def test_get_position(self):
2808
        knit = self.make_knit_with_4_versions_2_dags()
3052.2.6 by Andrew Bennetts
Fix typo in comment.
2809
        # get_position returns (thunk_flag, index(can be None), start, end) for
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2810
        # _StreamAccess to use.
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2811
        self.assertGetPosition(knit, ['a'], 'a', (False, None, 0, 78))
2812
        self.assertGetPosition(knit, ['a', 'c'], 'c', (False, None, 78, 156))
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2813
        # get_position on a text that is not in the datastream (but in the
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2814
        # backing knit) returns (True, 'versionid', None, None) - and then the
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2815
        # access object can construct the relevant data as needed.
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2816
        self.assertGetPosition(knit, ['a', 'c'], 'b', (True, 'b', None, None))
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2817
2818
2819
class Test_StreamAccess(KnitTests):
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2820
2821
    def get_index_access(self, knit, stream):
2822
        """Get a _StreamAccess from knit and stream."""
2823
        knit =  knit._knit_from_datastream(stream)
2824
        return knit._index, knit._data._access
2825
2826
    def assertGetRawRecords(self, knit, versions):
2827
        index, access = self.get_index_access(knit,
2828
            knit.get_data_stream(versions))
2829
        # check that every version asked for can be obtained from the resulting
2830
        # access object.
2831
        # batch
2832
        memos = []
2833
        for version in versions:
2834
            memos.append(knit._index.get_position(version))
2835
        original = {}
2836
        for version, data in zip(
2837
            versions, knit._data._access.get_raw_records(memos)):
2838
            original[version] = data
2839
        memos = []
2840
        for version in versions:
2841
            memos.append(index.get_position(version))
2842
        streamed = {}
2843
        for version, data in zip(versions, access.get_raw_records(memos)):
2844
            streamed[version] = data
2845
        self.assertEqual(original, streamed)
2846
        # individually
2847
        for version in versions:
2848
            data = list(access.get_raw_records(
2849
                [index.get_position(version)]))[0]
2850
            self.assertEqual(original[version], data)
2851
2852
    def make_knit_with_two_versions(self):
2853
        knit = self.make_test_knit()
2854
        knit.add_lines('a', [], ["foo"])
2855
        knit.add_lines('b', [], ["bar"])
2856
        return knit
2857
2858
    def test_get_raw_records(self):
2859
        knit = self.make_knit_with_two_versions()
2860
        self.assertGetRawRecords(knit, ['a', 'b'])
2861
        self.assertGetRawRecords(knit, ['a'])
2862
        self.assertGetRawRecords(knit, ['b'])
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2863
    
2864
    def test_get_raw_record_from_backing_knit(self):
2865
        # the thunk layer should create an artificial A on-demand when needed.
2866
        source_knit = self.make_test_knit(name='plain', annotate=False)
2867
        target_knit = self.make_test_knit(name='annotated', annotate=True)
2868
        source_knit.add_lines("A", [], ["Foo\n"])
2869
        # Give the target A, so we can try to thunk across to it.
2870
        target_knit.join(source_knit)
2871
        index, access = self.get_index_access(target_knit,
2872
            source_knit.get_data_stream([]))
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2873
        raw_data = list(access.get_raw_records([(True, "A", None, None)]))[0]
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2874
        df = GzipFile(mode='rb', fileobj=StringIO(raw_data))
2875
        self.assertEqual(
2876
            'version A 1 5d36b88bb697a2d778f024048bafabd443d74503\n'
2877
            'Foo\nend A\n',
2878
            df.read())
2879
2880
    def test_asking_for_thunk_stream_is_not_plain_errors(self):
2881
        knit = self.make_test_knit(name='annotated', annotate=True)
2882
        knit.add_lines("A", [], ["Foo\n"])
2883
        index, access = self.get_index_access(knit,
2884
            knit.get_data_stream([]))
2885
        self.assertRaises(errors.KnitCorrupt,
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2886
            list, access.get_raw_records([(True, "A", None, None)]))
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
2887
2888
2889
class TestFormatSignatures(KnitTests):
2890
2891
    def test_knit_format_signatures(self):
2892
        """Different formats of knit have different signature strings."""
2893
        knit = self.make_test_knit(name='a', annotate=True)
2894
        self.assertEqual('knit-annotated', knit.get_format_signature())
2895
        knit = self.make_test_knit(name='p', annotate=False)
2896
        self.assertEqual('knit-plain', knit.get_format_signature())