/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 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
21
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
22
from bzrlib.errors import (
23
    RevisionAlreadyPresent,
24
    KnitHeaderError,
25
    RevisionNotPresent,
26
    NoSuchFile,
27
    )
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
28
from bzrlib.knit import (
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
29
    KnitContent,
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
30
    KnitVersionedFile,
31
    KnitPlainFactory,
32
    KnitAnnotateFactory,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
33
    _KnitIndex,
34
    WeaveToKnit,
35
    )
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
36
from bzrlib.osutils import split_lines
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
37
from bzrlib.tests import TestCase, TestCaseWithTransport
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
38
from bzrlib.transport import TransportLogger, get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
39
from bzrlib.transport.memory import MemoryTransport
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
40
from bzrlib.weave import Weave
41
42
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
43
class KnitContentTests(TestCase):
44
45
    def test_constructor(self):
46
        content = KnitContent([])
47
48
    def test_text(self):
49
        content = KnitContent([])
50
        self.assertEqual(content.text(), [])
51
52
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
53
        self.assertEqual(content.text(), ["text1", "text2"])
54
55
    def test_annotate(self):
56
        content = KnitContent([])
57
        self.assertEqual(content.annotate(), [])
58
59
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
60
        self.assertEqual(content.annotate(),
61
            [("origin1", "text1"), ("origin2", "text2")])
62
63
    def test_annotate_iter(self):
64
        content = KnitContent([])
65
        it = content.annotate_iter()
66
        self.assertRaises(StopIteration, it.next)
67
68
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
69
        it = content.annotate_iter()
70
        self.assertEqual(it.next(), ("origin1", "text1"))
71
        self.assertEqual(it.next(), ("origin2", "text2"))
72
        self.assertRaises(StopIteration, it.next)
73
74
    def test_copy(self):
75
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
76
        copy = content.copy()
77
        self.assertIsInstance(copy, KnitContent)
78
        self.assertEqual(copy.annotate(),
79
            [("origin1", "text1"), ("origin2", "text2")])
80
81
    def test_line_delta(self):
82
        content1 = KnitContent([("", "a"), ("", "b")])
83
        content2 = KnitContent([("", "a"), ("", "a"), ("", "c")])
84
        self.assertEqual(content1.line_delta(content2),
85
            [(1, 2, 2, [("", "a"), ("", "c")])])
86
87
    def test_line_delta_iter(self):
88
        content1 = KnitContent([("", "a"), ("", "b")])
89
        content2 = KnitContent([("", "a"), ("", "a"), ("", "c")])
90
        it = content1.line_delta_iter(content2)
91
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
92
        self.assertRaises(StopIteration, it.next)
93
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
94
95
class MockTransport(object):
96
97
    def __init__(self, file_lines=None):
98
        self.file_lines = file_lines
99
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
100
        # We have no base directory for the MockTransport
101
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
102
103
    def get(self, filename):
104
        if self.file_lines is None:
105
            raise NoSuchFile(filename)
106
        else:
107
            return StringIO("\n".join(self.file_lines))
108
109
    def __getattr__(self, name):
110
        def queue_call(*args, **kwargs):
111
            self.calls.append((name, args, kwargs))
112
        return queue_call
113
114
115
class LowLevelKnitIndexTests(TestCase):
116
117
    def test_no_such_file(self):
118
        transport = MockTransport()
119
120
        self.assertRaises(NoSuchFile, _KnitIndex, transport, "filename", "r")
121
        self.assertRaises(NoSuchFile, _KnitIndex, transport,
122
            "filename", "w", create=False)
123
124
    def test_create_file(self):
125
        transport = MockTransport()
126
127
        index = _KnitIndex(transport, "filename", "w",
128
            file_mode="wb", create=True)
129
        self.assertEqual(
130
                ("put_bytes_non_atomic",
131
                    ("filename", index.HEADER), {"mode": "wb"}),
132
                transport.calls.pop(0))
133
134
    def test_delay_create_file(self):
135
        transport = MockTransport()
136
137
        index = _KnitIndex(transport, "filename", "w",
138
            create=True, file_mode="wb", create_parent_dir=True,
139
            delay_create=True, dir_mode=0777)
140
        self.assertEqual([], transport.calls)
141
142
        index.add_versions([])
143
        name, (filename, f), kwargs = transport.calls.pop(0)
144
        self.assertEqual("put_file_non_atomic", name)
145
        self.assertEqual(
146
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
147
            kwargs)
148
        self.assertEqual("filename", filename)
149
        self.assertEqual(index.HEADER, f.read())
150
151
        index.add_versions([])
152
        self.assertEqual(("append_bytes", ("filename", ""), {}),
153
            transport.calls.pop(0))
154
155
    def test_read_utf8_version_id(self):
156
        transport = MockTransport([
157
            _KnitIndex.HEADER,
158
            u"version-\N{CYRILLIC CAPITAL LETTER A}"
159
                u" option 0 1 :".encode("utf-8")
160
            ])
161
        index = _KnitIndex(transport, "filename", "r")
162
        self.assertTrue(
163
            index.has_version(u"version-\N{CYRILLIC CAPITAL LETTER A}"))
164
165
    def test_read_utf8_parents(self):
166
        transport = MockTransport([
167
            _KnitIndex.HEADER,
168
            u"version option 0 1"
169
                u" .version-\N{CYRILLIC CAPITAL LETTER A} :".encode("utf-8")
170
            ])
171
        index = _KnitIndex(transport, "filename", "r")
172
        self.assertEqual([u"version-\N{CYRILLIC CAPITAL LETTER A}"],
173
            index.get_parents_with_ghosts("version"))
174
175
    def test_read_ignore_corrupted_lines(self):
176
        transport = MockTransport([
177
            _KnitIndex.HEADER,
178
            "corrupted",
179
            "corrupted options 0 1 .b .c ",
180
            "version options 0 1 :"
181
            ])
182
        index = _KnitIndex(transport, "filename", "r")
183
        self.assertEqual(1, index.num_versions())
184
        self.assertTrue(index.has_version(u"version"))
185
186
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
187
        transport = MockTransport(['not a bzr knit index header\n'])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
188
        self.assertRaises(KnitHeaderError,
189
            _KnitIndex, transport, "filename", "r")
190
191
    def test_read_duplicate_entries(self):
192
        transport = MockTransport([
193
            _KnitIndex.HEADER,
194
            "parent options 0 1 :",
195
            "version options1 0 1 0 :",
196
            "version options2 1 2 .other :",
197
            "version options3 3 4 0 .other :"
198
            ])
199
        index = _KnitIndex(transport, "filename", "r")
200
        self.assertEqual(2, index.num_versions())
201
        self.assertEqual(1, index.lookup(u"version"))
202
        self.assertEqual((3, 4), index.get_position(u"version"))
203
        self.assertEqual(["options3"], index.get_options(u"version"))
204
        self.assertEqual([u"parent", u"other"],
205
            index.get_parents_with_ghosts(u"version"))
206
207
    def test_read_compressed_parents(self):
208
        transport = MockTransport([
209
            _KnitIndex.HEADER,
210
            "a option 0 1 :",
211
            "b option 0 1 0 :",
212
            "c option 0 1 1 0 :",
213
            ])
214
        index = _KnitIndex(transport, "filename", "r")
215
        self.assertEqual([u"a"], index.get_parents(u"b"))
216
        self.assertEqual([u"b", u"a"], index.get_parents(u"c"))
217
218
    def test_write_utf8_version_id(self):
219
        transport = MockTransport([
220
            _KnitIndex.HEADER
221
            ])
222
        index = _KnitIndex(transport, "filename", "r")
223
        index.add_version(u"version-\N{CYRILLIC CAPITAL LETTER A}",
224
            ["option"], 0, 1, [])
225
        self.assertEqual(("append_bytes", ("filename",
226
            u"\nversion-\N{CYRILLIC CAPITAL LETTER A}"
227
                u" option 0 1  :".encode("utf-8")),
228
            {}),
229
            transport.calls.pop(0))
230
231
    def test_write_utf8_parents(self):
232
        transport = MockTransport([
233
            _KnitIndex.HEADER
234
            ])
235
        index = _KnitIndex(transport, "filename", "r")
236
        index.add_version(u"version", ["option"], 0, 1,
237
            [u"version-\N{CYRILLIC CAPITAL LETTER A}"])
238
        self.assertEqual(("append_bytes", ("filename",
239
            u"\nversion option 0 1"
240
                u" .version-\N{CYRILLIC CAPITAL LETTER A} :".encode("utf-8")),
241
            {}),
242
            transport.calls.pop(0))
243
244
    def test_get_graph(self):
245
        transport = MockTransport()
246
        index = _KnitIndex(transport, "filename", "w", create=True)
247
        self.assertEqual([], index.get_graph())
248
249
        index.add_version(u"a", ["option"], 0, 1, [u"b"])
250
        self.assertEqual([(u"a", [u"b"])], index.get_graph())
251
252
        index.add_version(u"c", ["option"], 0, 1, [u"d"])
253
        self.assertEqual([(u"a", [u"b"]), (u"c", [u"d"])],
254
            sorted(index.get_graph()))
255
256
    def test_get_ancestry(self):
257
        transport = MockTransport([
258
            _KnitIndex.HEADER,
259
            "a option 0 1 :",
260
            "b option 0 1 0 .e :",
261
            "c option 0 1 1 0 :",
262
            "d option 0 1 2 .f :"
263
            ])
264
        index = _KnitIndex(transport, "filename", "r")
265
266
        self.assertEqual([], index.get_ancestry([]))
267
        self.assertEqual([u"a"], index.get_ancestry([u"a"]))
268
        self.assertEqual([u"a", u"b"], index.get_ancestry([u"b"]))
269
        self.assertEqual([u"a", u"b", u"c"], index.get_ancestry([u"c"]))
270
        self.assertEqual([u"a", u"b", u"c", u"d"], index.get_ancestry([u"d"]))
271
        self.assertEqual([u"a", u"b"], index.get_ancestry([u"a", u"b"]))
272
        self.assertEqual([u"a", u"b", u"c"], index.get_ancestry([u"a", u"c"]))
273
274
        self.assertRaises(RevisionNotPresent, index.get_ancestry, [u"e"])
275
276
    def test_get_ancestry_with_ghosts(self):
277
        transport = MockTransport([
278
            _KnitIndex.HEADER,
279
            "a option 0 1 :",
280
            "b option 0 1 0 .e :",
281
            "c option 0 1 0 .f .g :",
282
            "d option 0 1 2 .h .j .k :"
283
            ])
284
        index = _KnitIndex(transport, "filename", "r")
285
286
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
287
        self.assertEqual([u"a"], index.get_ancestry_with_ghosts([u"a"]))
288
        self.assertEqual([u"a", u"e", u"b"],
289
            index.get_ancestry_with_ghosts([u"b"]))
290
        self.assertEqual([u"a", u"g", u"f", u"c"],
291
            index.get_ancestry_with_ghosts([u"c"]))
292
        self.assertEqual([u"a", u"g", u"f", u"c", u"k", u"j", u"h", u"d"],
293
            index.get_ancestry_with_ghosts([u"d"]))
294
        self.assertEqual([u"a", u"e", u"b"],
295
            index.get_ancestry_with_ghosts([u"a", u"b"]))
296
        self.assertEqual([u"a", u"g", u"f", u"c"],
297
            index.get_ancestry_with_ghosts([u"a", u"c"]))
298
        self.assertEqual(
299
            [u"a", u"g", u"f", u"c", u"e", u"b", u"k", u"j", u"h", u"d"],
300
            index.get_ancestry_with_ghosts([u"b", u"d"]))
301
302
        self.assertRaises(RevisionNotPresent,
303
            index.get_ancestry_with_ghosts, [u"e"])
304
305
    def test_num_versions(self):
306
        transport = MockTransport([
307
            _KnitIndex.HEADER
308
            ])
309
        index = _KnitIndex(transport, "filename", "r")
310
311
        self.assertEqual(0, index.num_versions())
312
        self.assertEqual(0, len(index))
313
314
        index.add_version(u"a", ["option"], 0, 1, [])
315
        self.assertEqual(1, index.num_versions())
316
        self.assertEqual(1, len(index))
317
318
        index.add_version(u"a", ["option2"], 1, 2, [])
319
        self.assertEqual(1, index.num_versions())
320
        self.assertEqual(1, len(index))
321
322
        index.add_version(u"b", ["option"], 0, 1, [])
323
        self.assertEqual(2, index.num_versions())
324
        self.assertEqual(2, len(index))
325
326
    def test_get_versions(self):
327
        transport = MockTransport([
328
            _KnitIndex.HEADER
329
            ])
330
        index = _KnitIndex(transport, "filename", "r")
331
332
        self.assertEqual([], index.get_versions())
333
334
        index.add_version(u"a", ["option"], 0, 1, [])
335
        self.assertEqual([u"a"], index.get_versions())
336
337
        index.add_version(u"a", ["option"], 0, 1, [])
338
        self.assertEqual([u"a"], index.get_versions())
339
340
        index.add_version(u"b", ["option"], 0, 1, [])
341
        self.assertEqual([u"a", u"b"], index.get_versions())
342
343
    def test_idx_to_name(self):
344
        transport = MockTransport([
345
            _KnitIndex.HEADER,
346
            "a option 0 1 :",
347
            "b option 0 1 :"
348
            ])
349
        index = _KnitIndex(transport, "filename", "r")
350
351
        self.assertEqual(u"a", index.idx_to_name(0))
352
        self.assertEqual(u"b", index.idx_to_name(1))
353
        self.assertEqual(u"b", index.idx_to_name(-1))
354
        self.assertEqual(u"a", index.idx_to_name(-2))
355
356
    def test_lookup(self):
357
        transport = MockTransport([
358
            _KnitIndex.HEADER,
359
            "a option 0 1 :",
360
            "b option 0 1 :"
361
            ])
362
        index = _KnitIndex(transport, "filename", "r")
363
364
        self.assertEqual(0, index.lookup(u"a"))
365
        self.assertEqual(1, index.lookup(u"b"))
366
367
    def test_add_version(self):
368
        transport = MockTransport([
369
            _KnitIndex.HEADER
370
            ])
371
        index = _KnitIndex(transport, "filename", "r")
372
373
        index.add_version(u"a", ["option"], 0, 1, [u"b"])
374
        self.assertEqual(("append_bytes",
375
            ("filename", "\na option 0 1 .b :"),
376
            {}), transport.calls.pop(0))
377
        self.assertTrue(index.has_version(u"a"))
378
        self.assertEqual(1, index.num_versions())
379
        self.assertEqual((0, 1), index.get_position(u"a"))
380
        self.assertEqual(["option"], index.get_options(u"a"))
381
        self.assertEqual([u"b"], index.get_parents_with_ghosts(u"a"))
382
383
        index.add_version(u"a", ["opt"], 1, 2, [u"c"])
384
        self.assertEqual(("append_bytes",
385
            ("filename", "\na opt 1 2 .c :"),
386
            {}), transport.calls.pop(0))
387
        self.assertTrue(index.has_version(u"a"))
388
        self.assertEqual(1, index.num_versions())
389
        self.assertEqual((1, 2), index.get_position(u"a"))
390
        self.assertEqual(["opt"], index.get_options(u"a"))
391
        self.assertEqual([u"c"], index.get_parents_with_ghosts(u"a"))
392
393
        index.add_version(u"b", ["option"], 2, 3, [u"a"])
394
        self.assertEqual(("append_bytes",
395
            ("filename", "\nb option 2 3 0 :"),
396
            {}), transport.calls.pop(0))
397
        self.assertTrue(index.has_version(u"b"))
398
        self.assertEqual(2, index.num_versions())
399
        self.assertEqual((2, 3), index.get_position(u"b"))
400
        self.assertEqual(["option"], index.get_options(u"b"))
401
        self.assertEqual([u"a"], index.get_parents_with_ghosts(u"b"))
402
403
    def test_add_versions(self):
404
        transport = MockTransport([
405
            _KnitIndex.HEADER
406
            ])
407
        index = _KnitIndex(transport, "filename", "r")
408
409
        index.add_versions([
410
            (u"a", ["option"], 0, 1, [u"b"]),
411
            (u"a", ["opt"], 1, 2, [u"c"]),
412
            (u"b", ["option"], 2, 3, [u"a"])
413
            ])
414
        self.assertEqual(("append_bytes", ("filename",
415
            "\na option 0 1 .b :"
416
            "\na opt 1 2 .c :"
417
            "\nb option 2 3 0 :"
418
            ), {}), transport.calls.pop(0))
419
        self.assertTrue(index.has_version(u"a"))
420
        self.assertTrue(index.has_version(u"b"))
421
        self.assertEqual(2, index.num_versions())
422
        self.assertEqual((1, 2), index.get_position(u"a"))
423
        self.assertEqual((2, 3), index.get_position(u"b"))
424
        self.assertEqual(["opt"], index.get_options(u"a"))
425
        self.assertEqual(["option"], index.get_options(u"b"))
426
        self.assertEqual([u"c"], index.get_parents_with_ghosts(u"a"))
427
        self.assertEqual([u"a"], index.get_parents_with_ghosts(u"b"))
428
429
    def test_delay_create_and_add_versions(self):
430
        transport = MockTransport()
431
432
        index = _KnitIndex(transport, "filename", "w",
433
            create=True, file_mode="wb", create_parent_dir=True,
434
            delay_create=True, dir_mode=0777)
435
        self.assertEqual([], transport.calls)
436
437
        index.add_versions([
438
            (u"a", ["option"], 0, 1, [u"b"]),
439
            (u"a", ["opt"], 1, 2, [u"c"]),
440
            (u"b", ["option"], 2, 3, [u"a"])
441
            ])
442
        name, (filename, f), kwargs = transport.calls.pop(0)
443
        self.assertEqual("put_file_non_atomic", name)
444
        self.assertEqual(
445
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
446
            kwargs)
447
        self.assertEqual("filename", filename)
448
        self.assertEqual(
449
            index.HEADER +
450
            "\na option 0 1 .b :"
451
            "\na opt 1 2 .c :"
452
            "\nb option 2 3 0 :",
453
            f.read())
454
455
    def test_has_version(self):
456
        transport = MockTransport([
457
            _KnitIndex.HEADER,
458
            "a option 0 1 :"
459
            ])
460
        index = _KnitIndex(transport, "filename", "r")
461
462
        self.assertTrue(index.has_version(u"a"))
463
        self.assertFalse(index.has_version(u"b"))
464
465
    def test_get_position(self):
466
        transport = MockTransport([
467
            _KnitIndex.HEADER,
468
            "a option 0 1 :",
469
            "b option 1 2 :"
470
            ])
471
        index = _KnitIndex(transport, "filename", "r")
472
473
        self.assertEqual((0, 1), index.get_position(u"a"))
474
        self.assertEqual((1, 2), index.get_position(u"b"))
475
476
    def test_get_method(self):
477
        transport = MockTransport([
478
            _KnitIndex.HEADER,
479
            "a fulltext,unknown 0 1 :",
480
            "b unknown,line-delta 1 2 :",
481
            "c bad 3 4 :"
482
            ])
483
        index = _KnitIndex(transport, "filename", "r")
484
485
        self.assertEqual("fulltext", index.get_method(u"a"))
486
        self.assertEqual("line-delta", index.get_method(u"b"))
487
        self.assertRaises(AssertionError, index.get_method, u"c")
488
489
    def test_get_options(self):
490
        transport = MockTransport([
491
            _KnitIndex.HEADER,
492
            "a opt1 0 1 :",
493
            "b opt2,opt3 1 2 :"
494
            ])
495
        index = _KnitIndex(transport, "filename", "r")
496
497
        self.assertEqual(["opt1"], index.get_options(u"a"))
498
        self.assertEqual(["opt2", "opt3"], index.get_options(u"b"))
499
500
    def test_get_parents(self):
501
        transport = MockTransport([
502
            _KnitIndex.HEADER,
503
            "a option 0 1 :",
504
            "b option 1 2 0 .c :",
505
            "c option 1 2 1 0 .e :"
506
            ])
507
        index = _KnitIndex(transport, "filename", "r")
508
509
        self.assertEqual([], index.get_parents(u"a"))
510
        self.assertEqual([u"a", u"c"], index.get_parents(u"b"))
511
        self.assertEqual([u"b", u"a"], index.get_parents(u"c"))
512
513
    def test_get_parents_with_ghosts(self):
514
        transport = MockTransport([
515
            _KnitIndex.HEADER,
516
            "a option 0 1 :",
517
            "b option 1 2 0 .c :",
518
            "c option 1 2 1 0 .e :"
519
            ])
520
        index = _KnitIndex(transport, "filename", "r")
521
522
        self.assertEqual([], index.get_parents_with_ghosts(u"a"))
523
        self.assertEqual([u"a", u"c"], index.get_parents_with_ghosts(u"b"))
524
        self.assertEqual([u"b", u"a", u"e"],
525
            index.get_parents_with_ghosts(u"c"))
526
527
    def test_check_versions_present(self):
528
        transport = MockTransport([
529
            _KnitIndex.HEADER,
530
            "a option 0 1 :",
531
            "b option 0 1 :"
532
            ])
533
        index = _KnitIndex(transport, "filename", "r")
534
535
        check = index.check_versions_present
536
537
        check([])
538
        check([u"a"])
539
        check([u"b"])
540
        check([u"a", u"b"])
541
        self.assertRaises(RevisionNotPresent, check, [u"c"])
542
        self.assertRaises(RevisionNotPresent, check, [u"a", u"b", u"c"])
543
544
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
545
class KnitTests(TestCaseWithTransport):
546
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
547
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
548
    def make_test_knit(self, annotate=False, delay_create=False):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
549
        if not annotate:
550
            factory = KnitPlainFactory()
551
        else:
552
            factory = None
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
553
        return KnitVersionedFile('test', get_transport('.'), access_mode='w',
554
                                 factory=factory, create=True,
555
                                 delay_create=delay_create)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
556
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
557
558
class BasicKnitTests(KnitTests):
559
560
    def add_stock_one_and_one_a(self, k):
561
        k.add_lines('text-1', [], split_lines(TEXT_1))
562
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
563
564
    def test_knit_constructor(self):
565
        """Construct empty k"""
566
        self.make_test_knit()
567
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
568
    def test_knit_add(self):
569
        """Store one text in knit and retrieve"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
570
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
571
        k.add_lines('text-1', [], split_lines(TEXT_1))
572
        self.assertTrue(k.has_version('text-1'))
573
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
574
575
    def test_knit_reload(self):
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
576
        # 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.
577
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
578
        k.add_lines('text-1', [], split_lines(TEXT_1))
579
        del k
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
580
        k2 = KnitVersionedFile('test', get_transport('.'), access_mode='r', factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
581
        self.assertTrue(k2.has_version('text-1'))
582
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
583
584
    def test_knit_several(self):
585
        """Store several texts in a knit"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
586
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
587
        k.add_lines('text-1', [], split_lines(TEXT_1))
588
        k.add_lines('text-2', [], split_lines(TEXT_2))
589
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
590
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
591
        
592
    def test_repeated_add(self):
593
        """Knit traps attempt to replace existing version"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
594
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
595
        k.add_lines('text-1', [], split_lines(TEXT_1))
596
        self.assertRaises(RevisionAlreadyPresent, 
597
                k.add_lines,
598
                'text-1', [], split_lines(TEXT_1))
599
600
    def test_empty(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
601
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
602
        k.add_lines('text-1', [], [])
603
        self.assertEquals(k.get_lines('text-1'), [])
604
605
    def test_incomplete(self):
606
        """Test if texts without a ending line-end can be inserted and
607
        extracted."""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
608
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
609
        k.add_lines('text-1', [], ['a\n',    'b'  ])
610
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1666.1.6 by Robert Collins
Make knit the default format.
611
        # reopening ensures maximum room for confusion
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
612
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
613
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
614
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
615
616
    def test_delta(self):
617
        """Expression of knit delta as lines"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
618
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
619
        td = list(line_delta(TEXT_1.splitlines(True),
620
                             TEXT_1A.splitlines(True)))
621
        self.assertEqualDiff(''.join(td), delta_1_1a)
622
        out = apply_line_delta(TEXT_1.splitlines(True), td)
623
        self.assertEqualDiff(''.join(out), TEXT_1A)
624
625
    def test_add_with_parents(self):
626
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
627
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
628
        self.add_stock_one_and_one_a(k)
629
        self.assertEquals(k.get_parents('text-1'), [])
630
        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
631
632
    def test_ancestry(self):
633
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
634
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
635
        self.add_stock_one_and_one_a(k)
636
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
637
638
    def test_add_delta(self):
639
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
640
        k = KnitVersionedFile('test', get_transport('.'), factory=KnitPlainFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
641
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
642
        self.add_stock_one_and_one_a(k)
1596.2.7 by Robert Collins
Remove the requirement for reannotation in knit joins.
643
        k.clear_cache()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
644
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
645
646
    def test_annotate(self):
647
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
648
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
649
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
650
        self.insert_and_test_small_annotate(k)
651
652
    def insert_and_test_small_annotate(self, k):
653
        """test annotation with k works correctly."""
654
        k.add_lines('text-1', [], ['a\n', 'b\n'])
655
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
656
657
        origins = k.annotate('text-2')
658
        self.assertEquals(origins[0], ('text-1', 'a\n'))
659
        self.assertEquals(origins[1], ('text-2', 'c\n'))
660
661
    def test_annotate_fulltext(self):
662
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
663
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
664
            delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
665
        self.insert_and_test_small_annotate(k)
666
667
    def test_annotate_merge_1(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
668
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
669
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
670
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
671
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
672
        origins = k.annotate('text-am')
673
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
674
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
675
676
    def test_annotate_merge_2(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
677
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
678
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
679
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
680
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
681
        origins = k.annotate('text-am')
682
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
683
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
684
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
685
686
    def test_annotate_merge_9(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
687
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
688
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
689
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
690
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
691
        origins = k.annotate('text-am')
692
        self.assertEquals(origins[0], ('text-am', 'k\n'))
693
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
694
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
695
696
    def test_annotate_merge_3(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
697
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
698
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
699
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
700
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
701
        origins = k.annotate('text-am')
702
        self.assertEquals(origins[0], ('text-am', 'k\n'))
703
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
704
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
705
706
    def test_annotate_merge_4(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
707
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
708
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
709
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
710
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
711
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
712
        origins = k.annotate('text-am')
713
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
714
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
715
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
716
717
    def test_annotate_merge_5(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
718
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
719
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
720
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
721
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
722
        k.add_lines('text-am',
723
                    ['text-a1', 'text-a2', 'text-a3'],
724
                    ['a\n', 'e\n', 'z\n'])
725
        origins = k.annotate('text-am')
726
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
727
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
728
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
729
730
    def test_annotate_file_cherry_pick(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
731
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
732
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
733
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
734
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
735
        origins = k.annotate('text-3')
736
        self.assertEquals(origins[0], ('text-1', 'a\n'))
737
        self.assertEquals(origins[1], ('text-1', 'b\n'))
738
        self.assertEquals(origins[2], ('text-1', 'c\n'))
739
740
    def test_knit_join(self):
741
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
742
        k1 = KnitVersionedFile('test1', get_transport('.'), factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
743
        k1.add_lines('text-a', [], split_lines(TEXT_1))
744
        k1.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
745
746
        k1.add_lines('text-c', [], split_lines(TEXT_1))
747
        k1.add_lines('text-d', ['text-c'], split_lines(TEXT_1))
748
749
        k1.add_lines('text-m', ['text-b', 'text-d'], split_lines(TEXT_1))
750
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
751
        k2 = KnitVersionedFile('test2', get_transport('.'), factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
752
        count = k2.join(k1, version_ids=['text-m'])
753
        self.assertEquals(count, 5)
754
        self.assertTrue(k2.has_version('text-a'))
755
        self.assertTrue(k2.has_version('text-c'))
756
757
    def test_reannotate(self):
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
758
        k1 = KnitVersionedFile('knit1', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
759
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
760
        # 0
761
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
762
        # 1
763
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
764
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
765
        k2 = KnitVersionedFile('test2', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
766
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
767
        k2.join(k1, version_ids=['text-b'])
768
769
        # 2
770
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
771
        # 2
772
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
773
        # 3
774
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
775
776
        # test-c will have index 3
777
        k1.join(k2, version_ids=['text-c'])
778
779
        lines = k1.get_lines('text-c')
780
        self.assertEquals(lines, ['z\n', 'c\n'])
781
782
        origins = k1.annotate('text-c')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
783
        self.assertEquals(origins[0], ('text-c', 'z\n'))
784
        self.assertEquals(origins[1], ('text-b', 'c\n'))
785
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
786
    def test_get_line_delta_texts(self):
787
        """Make sure we can call get_texts on text with reused line deltas"""
788
        k1 = KnitVersionedFile('test1', get_transport('.'), 
789
                               factory=KnitPlainFactory(), create=True)
790
        for t in range(3):
791
            if t == 0:
792
                parents = []
793
            else:
794
                parents = ['%d' % (t-1)]
795
            k1.add_lines('%d' % t, parents, ['hello\n'] * t)
796
        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.
797
        
798
    def test_iter_lines_reads_in_order(self):
799
        t = MemoryTransport()
800
        instrumented_t = TransportLogger(t)
801
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
802
        self.assertEqual([('id.kndx',)], instrumented_t._calls)
803
        # add texts with no required ordering
804
        k1.add_lines('base', [], ['text\n'])
805
        k1.add_lines('base2', [], ['text2\n'])
806
        k1.clear_cache()
807
        instrumented_t._calls = []
808
        # request a last-first iteration
809
        results = list(k1.iter_lines_added_or_present_in_versions(['base2', 'base']))
1628.1.2 by Robert Collins
More knit micro-optimisations.
810
        self.assertEqual([('id.knit', [(0, 87), (87, 89)])], instrumented_t._calls)
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.
811
        self.assertEqual(['text\n', 'text2\n'], results)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
812
1563.2.13 by Robert Collins
InterVersionedFile implemented.
813
    def test_create_empty_annotated(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
814
        k1 = self.make_test_knit(True)
1563.2.13 by Robert Collins
InterVersionedFile implemented.
815
        # 0
816
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
817
        k2 = k1.create_empty('t', MemoryTransport())
818
        self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
819
        self.assertEqual(k1.delta, k2.delta)
820
        # the generic test checks for empty content and file class
821
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
822
    def test_knit_format(self):
823
        # this tests that a new knit index file has the expected content
824
        # and that is writes the data we expect as records are added.
825
        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
826
        # Now knit files are not created until we first add data to them
1666.1.6 by Robert Collins
Make knit the default format.
827
        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.
828
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
829
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
830
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
831
            "\n"
832
            "revid fulltext 0 84 .a_ghost :",
833
            'test.kndx')
834
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
835
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
836
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
837
            "\nrevid fulltext 0 84 .a_ghost :"
838
            "\nrevid2 line-delta 84 82 0 :",
839
            'test.kndx')
840
        # we should be able to load this file again
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
841
        knit = KnitVersionedFile('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.
842
        self.assertEqual(['revid', 'revid2'], knit.versions())
843
        # write a short write to the file and ensure that its ignored
844
        indexfile = file('test.kndx', 'at')
845
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
846
        indexfile.close()
847
        # we should be able to load this file again
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
848
        knit = KnitVersionedFile('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.
849
        self.assertEqual(['revid', 'revid2'], knit.versions())
850
        # and add a revision with the same id the failed write had
851
        knit.add_lines('revid3', ['revid2'], ['a\n'])
852
        # and when reading it revid3 should now appear.
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
853
        knit = KnitVersionedFile('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.
854
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
855
        self.assertEqual(['revid2'], knit.get_parents('revid3'))
856
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
857
    def test_delay_create(self):
858
        """Test that passing delay_create=True creates files late"""
859
        knit = self.make_test_knit(annotate=True, delay_create=True)
860
        self.failIfExists('test.knit')
861
        self.failIfExists('test.kndx')
862
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
863
        self.failUnlessExists('test.knit')
864
        self.assertFileEqual(
865
            "# bzr knit index 8\n"
866
            "\n"
867
            "revid fulltext 0 84 .a_ghost :",
868
            'test.kndx')
869
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
870
    def test_create_parent_dir(self):
871
        """create_parent_dir can create knits in nonexistant dirs"""
872
        # Has no effect if we don't set 'delay_create'
873
        trans = get_transport('.')
874
        self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
875
                          trans, access_mode='w', factory=None,
876
                          create=True, create_parent_dir=True)
877
        # Nothing should have changed yet
878
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
879
                                 factory=None, create=True,
880
                                 create_parent_dir=True,
881
                                 delay_create=True)
882
        self.failIfExists('dir/test.knit')
883
        self.failIfExists('dir/test.kndx')
884
        self.failIfExists('dir')
885
        knit.add_lines('revid', [], ['a\n'])
886
        self.failUnlessExists('dir')
887
        self.failUnlessExists('dir/test.knit')
888
        self.assertFileEqual(
889
            "# bzr knit index 8\n"
890
            "\n"
891
            "revid fulltext 0 84  :",
892
            'dir/test.kndx')
893
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
894
    def test_create_mode_700(self):
895
        trans = get_transport('.')
896
        if not trans._can_roundtrip_unix_modebits():
897
            # Can't roundtrip, so no need to run this test
898
            return
899
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
900
                                 factory=None, create=True,
901
                                 create_parent_dir=True,
902
                                 delay_create=True,
903
                                 file_mode=0600,
904
                                 dir_mode=0700)
905
        knit.add_lines('revid', [], ['a\n'])
906
        self.assertTransportMode(trans, 'dir', 0700)
907
        self.assertTransportMode(trans, 'dir/test.knit', 0600)
908
        self.assertTransportMode(trans, 'dir/test.kndx', 0600)
909
910
    def test_create_mode_770(self):
911
        trans = get_transport('.')
912
        if not trans._can_roundtrip_unix_modebits():
913
            # Can't roundtrip, so no need to run this test
914
            return
915
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
916
                                 factory=None, create=True,
917
                                 create_parent_dir=True,
918
                                 delay_create=True,
919
                                 file_mode=0660,
920
                                 dir_mode=0770)
921
        knit.add_lines('revid', [], ['a\n'])
922
        self.assertTransportMode(trans, 'dir', 0770)
923
        self.assertTransportMode(trans, 'dir/test.knit', 0660)
924
        self.assertTransportMode(trans, 'dir/test.kndx', 0660)
925
926
    def test_create_mode_777(self):
927
        trans = get_transport('.')
928
        if not trans._can_roundtrip_unix_modebits():
929
            # Can't roundtrip, so no need to run this test
930
            return
931
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
932
                                 factory=None, create=True,
933
                                 create_parent_dir=True,
934
                                 delay_create=True,
935
                                 file_mode=0666,
936
                                 dir_mode=0777)
937
        knit.add_lines('revid', [], ['a\n'])
938
        self.assertTransportMode(trans, 'dir', 0777)
939
        self.assertTransportMode(trans, 'dir/test.knit', 0666)
940
        self.assertTransportMode(trans, 'dir/test.kndx', 0666)
941
1664.2.1 by Aaron Bentley
Start work on plan_merge test
942
    def test_plan_merge(self):
943
        my_knit = self.make_test_knit(annotate=True)
944
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
945
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
946
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
1664.2.3 by Aaron Bentley
Add failing test case
947
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
948
        for plan_line, expected_line in zip(plan, AB_MERGE):
949
            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.
950
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
951
952
TEXT_1 = """\
953
Banana cup cakes:
954
955
- bananas
956
- eggs
957
- broken tea cups
958
"""
959
960
TEXT_1A = """\
961
Banana cup cake recipe
962
(serves 6)
963
964
- bananas
965
- eggs
966
- broken tea cups
967
- self-raising flour
968
"""
969
1664.2.1 by Aaron Bentley
Start work on plan_merge test
970
TEXT_1B = """\
971
Banana cup cake recipe
972
973
- bananas (do not use plantains!!!)
974
- broken tea cups
975
- flour
976
"""
977
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
978
delta_1_1a = """\
979
0,1,2
980
Banana cup cake recipe
981
(serves 6)
982
5,5,1
983
- self-raising flour
984
"""
985
986
TEXT_2 = """\
987
Boeuf bourguignon
988
989
- beef
990
- red wine
991
- small onions
992
- carrot
993
- mushrooms
994
"""
995
1664.2.3 by Aaron Bentley
Add failing test case
996
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
997
new-a|(serves 6)
998
unchanged|
999
killed-b|- bananas
1000
killed-b|- eggs
1001
new-b|- bananas (do not use plantains!!!)
1002
unchanged|- broken tea cups
1003
new-a|- self-raising flour
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1004
new-b|- flour
1005
"""
1664.2.3 by Aaron Bentley
Add failing test case
1006
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
1007
1008
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1009
def line_delta(from_lines, to_lines):
1010
    """Generate line-based delta from one text to another"""
1011
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
1012
    for op in s.get_opcodes():
1013
        if op[0] == 'equal':
1014
            continue
1015
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
1016
        for i in range(op[3], op[4]):
1017
            yield to_lines[i]
1018
1019
1020
def apply_line_delta(basis_lines, delta_lines):
1021
    """Apply a line-based perfect diff
1022
    
1023
    basis_lines -- text to apply the patch to
1024
    delta_lines -- diff instructions and content
1025
    """
1026
    out = basis_lines[:]
1027
    i = 0
1028
    offset = 0
1029
    while i < len(delta_lines):
1030
        l = delta_lines[i]
1031
        a, b, c = map(long, l.split(','))
1032
        i = i + 1
1033
        out[offset+a:offset+b] = delta_lines[i:i+c]
1034
        i = i + c
1035
        offset = offset + (b - a) + c
1036
    return out
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1037
1038
1039
class TestWeaveToKnit(KnitTests):
1040
1041
    def test_weave_to_knit_matches(self):
1042
        # check that the WeaveToKnit is_compatible function
1043
        # registers True for a Weave to a Knit.
1044
        w = Weave()
1045
        k = self.make_test_knit()
1046
        self.failUnless(WeaveToKnit.is_compatible(w, k))
1047
        self.failIf(WeaveToKnit.is_compatible(k, w))
1048
        self.failIf(WeaveToKnit.is_compatible(w, w))
1049
        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
1050
1051
1052
class TestKnitCaching(KnitTests):
1053
    
1054
    def create_knit(self, cache_add=False):
1055
        k = self.make_test_knit(True)
1056
        if cache_add:
1057
            k.enable_cache()
1058
1059
        k.add_lines('text-1', [], split_lines(TEXT_1))
1060
        k.add_lines('text-2', [], split_lines(TEXT_2))
1061
        return k
1062
1063
    def test_no_caching(self):
1064
        k = self.create_knit()
1065
        # Nothing should be cached without setting 'enable_cache'
1066
        self.assertEqual({}, k._data._cache)
1067
1068
    def test_cache_add_and_clear(self):
1069
        k = self.create_knit(True)
1070
1071
        self.assertEqual(['text-1', 'text-2'], sorted(k._data._cache.keys()))
1072
1073
        k.clear_cache()
1074
        self.assertEqual({}, k._data._cache)
1075
1076
    def test_cache_data_read_raw(self):
1077
        k = self.create_knit()
1078
1079
        # Now cache and read
1080
        k.enable_cache()
1081
1082
        def read_one_raw(version):
1083
            pos_map = k._get_components_positions([version])
1084
            method, pos, size, next = pos_map[version]
1085
            lst = list(k._data.read_records_iter_raw([(version, pos, size)]))
1086
            self.assertEqual(1, len(lst))
1087
            return lst[0]
1088
1089
        val = read_one_raw('text-1')
1863.1.8 by John Arbash Meinel
Removing disk-backed-cache
1090
        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
1091
1092
        k.clear_cache()
1093
        # After clear, new reads are not cached
1094
        self.assertEqual({}, k._data._cache)
1095
1096
        val2 = read_one_raw('text-1')
1097
        self.assertEqual(val, val2)
1098
        self.assertEqual({}, k._data._cache)
1099
1100
    def test_cache_data_read(self):
1101
        k = self.create_knit()
1102
1103
        def read_one(version):
1104
            pos_map = k._get_components_positions([version])
1105
            method, pos, size, next = pos_map[version]
1106
            lst = list(k._data.read_records_iter([(version, pos, size)]))
1107
            self.assertEqual(1, len(lst))
1108
            return lst[0]
1109
1110
        # Now cache and read
1111
        k.enable_cache()
1112
1113
        val = read_one('text-2')
1114
        self.assertEqual(['text-2'], k._data._cache.keys())
1115
        self.assertEqual('text-2', val[0])
1116
        content, digest = k._data._parse_record('text-2',
1117
                                                k._data._cache['text-2'])
1118
        self.assertEqual(content, val[1])
1119
        self.assertEqual(digest, val[2])
1120
1121
        k.clear_cache()
1122
        self.assertEqual({}, k._data._cache)
1123
1124
        val2 = read_one('text-2')
1125
        self.assertEqual(val, val2)
1126
        self.assertEqual({}, k._data._cache)
1127
1128
    def test_cache_read(self):
1129
        k = self.create_knit()
1130
        k.enable_cache()
1131
1132
        text = k.get_text('text-1')
1133
        self.assertEqual(TEXT_1, text)
1134
        self.assertEqual(['text-1'], k._data._cache.keys())
1135
1136
        k.clear_cache()
1137
        self.assertEqual({}, k._data._cache)
1138
1139
        text = k.get_text('text-1')
1140
        self.assertEqual(TEXT_1, text)
1141
        self.assertEqual({}, k._data._cache)
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1142
1143
1144
class TestKnitIndex(KnitTests):
1145
1146
    def test_add_versions_dictionary_compresses(self):
1147
        """Adding versions to the index should update the lookup dict"""
1148
        knit = self.make_test_knit()
1149
        idx = knit._index
1150
        idx.add_version('a-1', ['fulltext'], 0, 0, [])
1151
        self.check_file_contents('test.kndx',
1152
            '# bzr knit index 8\n'
1153
            '\n'
1154
            'a-1 fulltext 0 0  :'
1155
            )
1156
        idx.add_versions([('a-2', ['fulltext'], 0, 0, ['a-1']),
1157
                          ('a-3', ['fulltext'], 0, 0, ['a-2']),
1158
                         ])
1159
        self.check_file_contents('test.kndx',
1160
            '# bzr knit index 8\n'
1161
            '\n'
1162
            'a-1 fulltext 0 0  :\n'
1163
            'a-2 fulltext 0 0 0 :\n'
1164
            'a-3 fulltext 0 0 1 :'
1165
            )
1166
        self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
1167
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0),
1168
                          'a-2':('a-2', ['fulltext'], 0, 0, ['a-1'], 1),
1169
                          'a-3':('a-3', ['fulltext'], 0, 0, ['a-2'], 2),
1170
                         }, idx._cache)
1171
1172
    def test_add_versions_fails_clean(self):
1173
        """If add_versions fails in the middle, it restores a pristine state.
1174
1175
        Any modifications that are made to the index are reset if all versions
1176
        cannot be added.
1177
        """
1178
        # This cheats a little bit by passing in a generator which will
1179
        # raise an exception before the processing finishes
1180
        # Other possibilities would be to have an version with the wrong number
1181
        # of entries, or to make the backing transport unable to write any
1182
        # files.
1183
1184
        knit = self.make_test_knit()
1185
        idx = knit._index
1186
        idx.add_version('a-1', ['fulltext'], 0, 0, [])
1187
1188
        class StopEarly(Exception):
1189
            pass
1190
1191
        def generate_failure():
1192
            """Add some entries and then raise an exception"""
1193
            yield ('a-2', ['fulltext'], 0, 0, ['a-1'])
1194
            yield ('a-3', ['fulltext'], 0, 0, ['a-2'])
1195
            raise StopEarly()
1196
1197
        # Assert the pre-condition
1198
        self.assertEqual(['a-1'], idx._history)
1199
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
1200
1201
        self.assertRaises(StopEarly, idx.add_versions, generate_failure())
1202
1203
        # And it shouldn't be modified
1204
        self.assertEqual(['a-1'], idx._history)
1205
        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.
1206
1207
    def test_knit_index_ignores_empty_files(self):
1208
        # There was a race condition in older bzr, where a ^C at the right time
1209
        # could leave an empty .kndx file, which bzr would later claim was a
1210
        # corrupted file since the header was not present. In reality, the file
1211
        # just wasn't created, so it should be ignored.
1212
        t = get_transport('.')
1213
        t.put_bytes('test.kndx', '')
1214
1215
        knit = self.make_test_knit()
1216
1217
    def test_knit_index_checks_header(self):
1218
        t = get_transport('.')
1219
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
1220
2196.2.1 by John Arbash Meinel
Merge Dmitry's optimizations and minimize the actual diff.
1221
        self.assertRaises(KnitHeaderError, self.make_test_knit)